From d5fbf235ac69e3af70ae499bc3ce6f1eed6038b1 Mon Sep 17 00:00:00 2001 From: michael1011 Date: Mon, 10 Feb 2025 21:53:08 +0100 Subject: [PATCH 1/4] chore: bump elements to v23.2.6 --- docker/build.py | 4 ++-- docker/regtest/startRegtest.sh | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docker/build.py b/docker/build.py index 1d00207d..20b9221b 100755 --- a/docker/build.py +++ b/docker/build.py @@ -42,7 +42,7 @@ class Image: BITCOIN_VERSION = "28.1" LITECOIN_VERSION = "0.21.4" -ELEMENTS_VERSION = "23.2.5" +ELEMENTS_VERSION = "23.2.6" GETH_VERSION = "1.14.12" C_LIGHTNING_VERSION = "24.11.1" @@ -108,7 +108,7 @@ class Image: ], ), "regtest": Image( - tag="4.6.4", + tag="4.6.5", arguments=[ UBUNTU_VERSION, BITCOIN_BUILD_ARG, diff --git a/docker/regtest/startRegtest.sh b/docker/regtest/startRegtest.sh index e6b30f65..404c0477 100755 --- a/docker/regtest/startRegtest.sh +++ b/docker/regtest/startRegtest.sh @@ -29,7 +29,7 @@ docker run \ -p 9735:9735 \ -p 9293:9293 \ -p 9292:9292 \ - boltz/regtest:4.6.4 + boltz/regtest:4.6.5 docker exec regtest bash -c "cp /root/.lightning/regtest/*.pem /root/.lightning/regtest/certs" docker exec regtest chmod -R 777 /root/.lightning/regtest/certs From 8c5c7cca3b88ca378fe419adab92bced576d5eaf Mon Sep 17 00:00:00 2001 From: michael1011 Date: Mon, 10 Feb 2025 22:06:41 +0100 Subject: [PATCH 2/4] feat: periodically rescan all EVM events --- .../contracts/ContractEventHandler.ts | 63 +++++-------------- .../contracts/ContractEventHandler.spec.ts | 8 +-- 2 files changed, 21 insertions(+), 50 deletions(-) diff --git a/lib/wallet/ethereum/contracts/ContractEventHandler.ts b/lib/wallet/ethereum/contracts/ContractEventHandler.ts index c566f42c..f966f9e6 100644 --- a/lib/wallet/ethereum/contracts/ContractEventHandler.ts +++ b/lib/wallet/ethereum/contracts/ContractEventHandler.ts @@ -53,8 +53,8 @@ type Events = { }; class ContractEventHandler extends TypedEventEmitter { - // Check for missed claims every 10 minutes - private static readonly claimCheckInterval = 1_000 * 60 * 10; + // Check for missed events every 5 minutes + private static readonly missedEventsCheckInterval = 1_000 * 60 * 5; private version!: bigint; @@ -63,8 +63,8 @@ class ContractEventHandler extends TypedEventEmitter { private networkDetails!: NetworkDetails; - private lastClaimCheck = 0; - private lastClaimCheckInterval: NodeJS.Timeout | undefined; + private rescanLastHeight = 0; + private rescanInterval: NodeJS.Timeout | undefined; constructor(private readonly logger: Logger) { super(); @@ -83,27 +83,27 @@ class ContractEventHandler extends TypedEventEmitter { this.networkDetails = networkDetails; this.logger.verbose( - `Starting ${this.networkDetails.name} contract event subscriptions`, + `Starting ${this.networkDetails.name} contracts v${version} event subscriptions`, ); await this.subscribeContractEvents(); - this.lastClaimCheck = await provider.getBlockNumber(); - this.lastClaimCheckInterval = setInterval(async () => { + this.rescanLastHeight = await provider.getBlockNumber(); + this.rescanInterval = setInterval(async () => { try { - await this.checkMissedClaims(provider); + await this.checkMissedEvents(provider); } catch (error) { this.logger.error( - `Error checking missed claims ${this.networkDetails.name}: ${formatError(error)}`, + `Error checking for missed events of ${this.networkDetails.name} contracts v${version}: ${formatError(error)}`, ); } - }, ContractEventHandler.claimCheckInterval); + }, ContractEventHandler.missedEventsCheckInterval); }; public destroy = () => { - if (this.lastClaimCheckInterval) { - clearInterval(this.lastClaimCheckInterval); - this.lastClaimCheckInterval = undefined; + if (this.rescanInterval) { + clearInterval(this.rescanInterval); + this.rescanInterval = undefined; } }; @@ -269,42 +269,13 @@ class ContractEventHandler extends TypedEventEmitter { ); }; - private checkMissedClaims = async (provider: Provider) => { + private checkMissedEvents = async (provider: Provider) => { this.logger.debug( - `Checking for missed claims ${this.networkDetails.name} from block ${this.lastClaimCheck}`, + `Checking for missed events of ${this.networkDetails.name} contracts v${this.version} from block ${this.rescanLastHeight}`, ); - const currentHeight = await provider.getBlockNumber(); - const [etherClaims, erc20Claims] = await Promise.all([ - this.etherSwap.queryFilter( - this.etherSwap.filters.Claim(), - this.lastClaimCheck, - ), - this.erc20Swap.queryFilter( - this.erc20Swap.filters.Claim(), - this.lastClaimCheck, - ), - ]); - - this.lastClaimCheck = currentHeight; - - for (const claim of etherClaims) { - this.emit('eth.claim', { - version: this.version, - transactionHash: claim.transactionHash, - preimageHash: parseBuffer(claim.topics[1]), - preimage: parseBuffer(claim.args!.preimage), - }); - } - - for (const claim of erc20Claims) { - this.emit('erc20.claim', { - version: this.version, - transactionHash: claim.transactionHash, - preimageHash: parseBuffer(claim.topics[1]), - preimage: parseBuffer(claim.args!.preimage), - }); - } + await this.rescan(this.rescanLastHeight); + this.rescanLastHeight = currentHeight; }; } diff --git a/test/integration/wallet/ethereum/contracts/ContractEventHandler.spec.ts b/test/integration/wallet/ethereum/contracts/ContractEventHandler.spec.ts index 3e2961a7..c0ea19c0 100644 --- a/test/integration/wallet/ethereum/contracts/ContractEventHandler.spec.ts +++ b/test/integration/wallet/ethereum/contracts/ContractEventHandler.spec.ts @@ -375,8 +375,8 @@ describe('ContractEventHandler', () => { await Promise.all([lockupPromise, claimPromise, refundPromise]); }); - test('should check missed claims', async () => { - contractEventHandler['lastClaimCheck'] = + test('should check for missed events', async () => { + contractEventHandler['rescanLastHeight'] = await setup.provider.getBlockNumber(); const preimage = randomBytes(32); @@ -408,10 +408,10 @@ describe('ContractEventHandler', () => { }); }); - await contractEventHandler['checkMissedClaims'](setup.provider); + await contractEventHandler['checkMissedEvents'](setup.provider); await claimPromise; - expect(contractEventHandler['lastClaimCheck']).toEqual( + expect(contractEventHandler['rescanLastHeight']).toEqual( await setup.provider.getBlockNumber(), ); }); From ab911696b42508d8328c230d6ec808336472b5ce Mon Sep 17 00:00:00 2001 From: michael1011 Date: Mon, 10 Feb 2025 22:43:15 +0100 Subject: [PATCH 3/4] feat: avoid EVM nonce reuse by checking db --- .../PendingEthereumTransactionRepository.ts | 12 ++++++ lib/wallet/ethereum/InjectedProvider.ts | 16 +++++++- ...ndingEthereumTransactionRepository.spec.ts | 37 +++++++++++++++++++ .../wallet/ethereum/EthereumManager.spec.ts | 1 + .../wallet/ethereum/InjectedProvider.spec.ts | 28 ++++++++++++++ 5 files changed, 92 insertions(+), 2 deletions(-) create mode 100644 test/integration/db/repositories/PendingEthereumTransactionRepository.spec.ts diff --git a/lib/db/repositories/PendingEthereumTransactionRepository.ts b/lib/db/repositories/PendingEthereumTransactionRepository.ts index b8149b87..df663321 100644 --- a/lib/db/repositories/PendingEthereumTransactionRepository.ts +++ b/lib/db/repositories/PendingEthereumTransactionRepository.ts @@ -5,6 +5,18 @@ class PendingEthereumTransactionRepository { return PendingEthereumTransaction.findAll(); }; + public static getHighestNonce = async (): Promise => { + const nonce = await PendingEthereumTransaction.max< + number, + PendingEthereumTransaction + >('nonce'); + if (nonce === null || nonce === undefined) { + return undefined; + } + + return nonce + 1; + }; + public static addTransaction = ( hash: string, nonce: number, diff --git a/lib/wallet/ethereum/InjectedProvider.ts b/lib/wallet/ethereum/InjectedProvider.ts index 2fd31232..6eb4f761 100644 --- a/lib/wallet/ethereum/InjectedProvider.ts +++ b/lib/wallet/ethereum/InjectedProvider.ts @@ -243,11 +243,23 @@ class InjectedProvider implements Provider { return this.forwardMethodNullable('getTransaction', transactionHash); }; - public getTransactionCount = ( + public getTransactionCount = async ( addressOrName: string, blockTag?: BlockTag, ): Promise => { - return this.forwardMethod('getTransactionCount', addressOrName, blockTag); + { + const highestNonce = + await PendingEthereumTransactionRepository.getHighestNonce(); + if (highestNonce !== undefined) { + return highestNonce; + } + } + + return await this.forwardMethod( + 'getTransactionCount', + addressOrName, + blockTag, + ); }; public getTransactionReceipt = ( diff --git a/test/integration/db/repositories/PendingEthereumTransactionRepository.spec.ts b/test/integration/db/repositories/PendingEthereumTransactionRepository.spec.ts new file mode 100644 index 00000000..a55c6f5a --- /dev/null +++ b/test/integration/db/repositories/PendingEthereumTransactionRepository.spec.ts @@ -0,0 +1,37 @@ +import Logger from '../../../../lib/Logger'; +import Database from '../../../../lib/db/Database'; +import PendingEthereumTransaction from '../../../../lib/db/models/PendingEthereumTransaction'; +import PendingEthereumTransactionRepository from '../../../../lib/db/repositories/PendingEthereumTransactionRepository'; + +describe('PendingEthereumTransactionRepository', () => { + let database: Database; + + beforeAll(async () => { + database = new Database(Logger.disabledLogger, Database.memoryDatabase); + await database.init(); + }); + + beforeEach(async () => { + await PendingEthereumTransaction.truncate(); + }); + + afterAll(async () => { + await database.close(); + }); + + describe('getHighestNonce', () => { + test('should get highest nonce when there are no pending transactions', async () => { + await expect( + PendingEthereumTransactionRepository.getHighestNonce(), + ).resolves.toEqual(undefined); + }); + + test('should get highest nonce when there are pending transactions', async () => { + await PendingEthereumTransactionRepository.addTransaction('txHash', 20); + + await expect( + PendingEthereumTransactionRepository.getHighestNonce(), + ).resolves.toEqual(21); + }); + }); +}); diff --git a/test/integration/wallet/ethereum/EthereumManager.spec.ts b/test/integration/wallet/ethereum/EthereumManager.spec.ts index 0adb5dfe..10630a96 100644 --- a/test/integration/wallet/ethereum/EthereumManager.spec.ts +++ b/test/integration/wallet/ethereum/EthereumManager.spec.ts @@ -20,6 +20,7 @@ jest.mock( () => ({ getTransactions: jest.fn().mockResolvedValue([]), addTransaction: jest.fn().mockResolvedValue(null), + getHighestNonce: jest.fn().mockResolvedValue(undefined), }), ); jest.mock('../../../../lib/db/repositories/ChainTipRepository'); diff --git a/test/integration/wallet/ethereum/InjectedProvider.spec.ts b/test/integration/wallet/ethereum/InjectedProvider.spec.ts index 1d4a8d69..b63d8120 100644 --- a/test/integration/wallet/ethereum/InjectedProvider.spec.ts +++ b/test/integration/wallet/ethereum/InjectedProvider.spec.ts @@ -16,6 +16,7 @@ jest.mock( '../../../../lib/db/repositories/PendingEthereumTransactionRepository', () => ({ addTransaction: jest.fn().mockResolvedValue(null), + getHighestNonce: jest.fn().mockResolvedValue(undefined), }), ); @@ -54,6 +55,33 @@ describe('InjectedProvider', () => { ).resolves.toEqual(null); }); + describe('getTransactionCount', () => { + const address = '0x0000000000000000000000000000000000000000'; + + afterAll(() => { + PendingEthereumTransactionRepository.getHighestNonce = jest + .fn() + .mockResolvedValue(undefined); + }); + + test('should get transaction count from provider when there are no pending transactions', async () => { + PendingEthereumTransactionRepository.getHighestNonce = jest + .fn() + .mockResolvedValue(undefined); + await expect(provider.getTransactionCount(address)).resolves.toEqual(0); + }); + + test('should get transaction count from db when there are pending transactions', async () => { + const highestNonce = 10; + PendingEthereumTransactionRepository.getHighestNonce = jest + .fn() + .mockResolvedValue(highestNonce); + await expect(provider.getTransactionCount(address)).resolves.toEqual( + highestNonce, + ); + }); + }); + test('should save broadcast transactions to database', async () => { const setup = await getSigner(); await fundSignerWallet(setup.signer, setup.etherBase); From 17bfad0c82d39e98ca73069a547bcdc879092e68 Mon Sep 17 00:00:00 2001 From: michael1011 Date: Tue, 11 Feb 2025 01:01:04 +0100 Subject: [PATCH 4/4] chore: remove CORS from backend API --- lib/Config.ts | 2 -- lib/api/Api.ts | 12 ------------ package-lock.json | 13 +------------ package.json | 1 - 4 files changed, 1 insertion(+), 27 deletions(-) diff --git a/lib/Config.ts b/lib/Config.ts index 169fe927..cd0bc2b9 100644 --- a/lib/Config.ts +++ b/lib/Config.ts @@ -125,7 +125,6 @@ type EthereumConfig = RskConfig & { type ApiConfig = { host: string; port: number; - cors?: string | string[]; }; type GrpcConfig = { @@ -269,7 +268,6 @@ class Config { api: { host: '127.0.0.1', port: 9001, - cors: '*', }, grpc: { diff --git a/lib/api/Api.ts b/lib/api/Api.ts index a1ed5ed2..ee14e78c 100644 --- a/lib/api/Api.ts +++ b/lib/api/Api.ts @@ -1,4 +1,3 @@ -import cors from 'cors'; import express, { Application, NextFunction, Request, Response } from 'express'; import { ApiConfig } from '../Config'; import Logger from '../Logger'; @@ -22,17 +21,6 @@ class Api { this.app = express(); this.app.set('trust proxy', 'loopback'); - if (config.cors === undefined || config.cors.length !== 0) { - this.app.use( - cors({ - origin: config.cors || '*', - methods: 'GET,HEAD,PUT,PATCH,POST,DELETE', - preflightContinue: false, - optionsSuccessStatus: 204, - }), - ); - } - this.app.use( express.json({ verify(req, _, buf: Buffer, encoding: string) { diff --git a/package-lock.json b/package-lock.json index e564da4d..05d25403 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,7 +30,6 @@ "bolt11": "^1.4.1", "boltz-core": "^2.2.1", "colors": "^1.4.0", - "cors": "^2.8.5", "ecpair": "^2.1.0", "ethers": "^6.13.5", "express": "^4.21.2", @@ -4735,17 +4734,6 @@ "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", "license": "MIT" }, - "node_modules/cors": { - "version": "2.8.5", - "license": "MIT", - "dependencies": { - "object-assign": "^4", - "vary": "^1" - }, - "engines": { - "node": ">= 0.10" - } - }, "node_modules/create-hash": { "version": "1.2.0", "license": "MIT", @@ -10102,6 +10090,7 @@ }, "node_modules/object-assign": { "version": "4.1.1", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" diff --git a/package.json b/package.json index c154cd50..4b464203 100644 --- a/package.json +++ b/package.json @@ -87,7 +87,6 @@ "bolt11": "^1.4.1", "boltz-core": "^2.2.1", "colors": "^1.4.0", - "cors": "^2.8.5", "ecpair": "^2.1.0", "ethers": "^6.13.5", "express": "^4.21.2",