From d02d1449dac26992a3f0e705b312d0ddf771d408 Mon Sep 17 00:00:00 2001 From: Antonio Morrone Date: Tue, 25 Jul 2023 09:49:22 +0200 Subject: [PATCH] fix(RREL-17): use rpc accounts for dev purposes only (#117) * fix(RREL-17): use rpc accounts for dev purposes only * test: enable all tests * test: fix test descriptions --- package-lock.json | 41 ++++++++++++++++++++ package.json | 3 +- src/commands/Register.ts | 31 ++------------- src/commands/findWealthyAccount.ts | 36 ++++++++++++++++++ test/unit/findWealthAccounts.spec.ts | 56 ++++++++++++++++++++++++++++ 5 files changed, 138 insertions(+), 29 deletions(-) create mode 100644 src/commands/findWealthyAccount.ts create mode 100644 test/unit/findWealthAccounts.spec.ts diff --git a/package-lock.json b/package-lock.json index a2af9d3..72f1118 100644 --- a/package-lock.json +++ b/package-lock.json @@ -56,6 +56,7 @@ "prettier": "^2.8.3", "sinon": "^15.0.1", "ts-node": "^10.9.1", + "tsconfig-paths": "^4.2.0", "typescript": "4.8.2" } }, @@ -12822,6 +12823,15 @@ "node": ">=8" } }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/strip-final-newline": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", @@ -13355,6 +13365,20 @@ "node": ">=0.3.1" } }, + "node_modules/tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", + "dev": true, + "dependencies": { + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/tslib": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", @@ -23861,6 +23885,12 @@ "ansi-regex": "^5.0.1" } }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true + }, "strip-final-newline": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", @@ -24247,6 +24277,17 @@ } } }, + "tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", + "dev": true, + "requires": { + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, "tslib": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", diff --git a/package.json b/package.json index 13c7cc1..fe2d0ff 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,7 @@ "register": "ts-node src/commands/Register.ts", "start": "ts-node src/commands/Start.ts", "tdd": "npm run test -- --watch --watch-files src,test", - "test": "ALLOW_CONFIG_MUTATIONS=true npx mocha -r ts-node/register --extensions ts 'test/**/*.{test,spec}.ts'" + "test": "ALLOW_CONFIG_MUTATIONS=true npx mocha -r ts-node/register -r tsconfig-paths/register --extensions ts 'test/**/*.{test,spec}.ts'" }, "lint-staged": { "*": "npx embedme \"*.md\"", @@ -121,6 +121,7 @@ "prettier": "^2.8.3", "sinon": "^15.0.1", "ts-node": "^10.9.1", + "tsconfig-paths": "^4.2.0", "typescript": "4.8.2" } } diff --git a/src/commands/Register.ts b/src/commands/Register.ts index 350defa..d0c7aa8 100755 --- a/src/commands/Register.ts +++ b/src/commands/Register.ts @@ -6,6 +6,7 @@ import { BigNumber, constants, Signer, utils, Wallet } from 'ethers'; import log from 'loglevel'; import { getServerConfig, RegisterConfig } from '../ServerConfigParams'; import { isSameAddress, sleep } from '../Utils'; +import { findWealthyAccount } from './findWealthyAccount'; //TODO: This is almost the same type as RegisterConfig from /ServerConfigParams type RegisterOptions = { @@ -18,32 +19,6 @@ type RegisterOptions = { unstakeDelay: BigNumber; }; -const findWealthyAccount = async ( - rpcProvider: JsonRpcProvider, - requiredBalance: BigNumber = utils.parseUnits('2', 'ether') -): Promise => { - let accounts: string[] = []; - try { - accounts = await rpcProvider.listAccounts(); - for (let i = 0; i < accounts.length; i++) { - const signer = rpcProvider.getSigner(i); - const balance = await signer.getBalance(); - if (balance.gte(requiredBalance)) { - log.info(`Found funded account ${await signer.getAddress()}`); - - return signer; - } - } - } catch (error) { - log.error('Failed to retrieve accounts and balances:', error); - } - throw new Error( - `could not find unlocked account with sufficient balance; all accounts:\n - ${accounts.join( - '\n - ' - )}` - ); -}; - const waitForRelay = async ( httpClient: HttpClient, relayUrl: string, @@ -164,7 +139,7 @@ const register = async ( log.info('Executed Transactions', transactions); }; -const retreiveSigner = async ( +const retrieveSigner = async ( rpcProvider: JsonRpcProvider, privateKey?: string, mnemonic?: string @@ -204,7 +179,7 @@ const executeRegister = async (): Promise => { await register(rpcProvider, { relayHub: relayHub || contracts.relayHubAddress, relayUrl: serverUrl, - signer: await retreiveSigner(rpcProvider, privateKey, mnemonic), + signer: await retrieveSigner(rpcProvider, privateKey, mnemonic), stake: utils.parseEther(stake.toString()), funds: utils.parseEther(funds.toString()), unstakeDelay: BigNumber.from(unstakeDelay), diff --git a/src/commands/findWealthyAccount.ts b/src/commands/findWealthyAccount.ts new file mode 100644 index 0000000..55fad20 --- /dev/null +++ b/src/commands/findWealthyAccount.ts @@ -0,0 +1,36 @@ +import type { JsonRpcProvider } from '@ethersproject/providers'; +import { BigNumber, Signer, utils } from 'ethers'; +import log from 'loglevel'; + +export const REGTEST_CHAIN_ID = 33; + +export const findWealthyAccount = async ( + rpcProvider: JsonRpcProvider, + requiredBalance: BigNumber = utils.parseUnits('2', 'ether') +): Promise => { + const { chainId } = await rpcProvider.getNetwork(); + if (chainId !== REGTEST_CHAIN_ID) { + throw new Error('Unlocked accounts are allowed for testing purposes only'); + } + + let accounts: string[] = []; + try { + accounts = await rpcProvider.listAccounts(); + for (let i = 0; i < accounts.length; i++) { + const signer = rpcProvider.getSigner(i); + const balance = await signer.getBalance(); + if (balance.gte(requiredBalance)) { + log.info(`Found funded account ${await signer.getAddress()}`); + + return signer; + } + } + } catch (error) { + log.error('Failed to retrieve accounts and balances:', error); + } + throw new Error( + `could not find unlocked account with sufficient balance; all accounts:\n - ${accounts.join( + '\n - ' + )}` + ); +}; diff --git a/test/unit/findWealthAccounts.spec.ts b/test/unit/findWealthAccounts.spec.ts new file mode 100644 index 0000000..ab919ed --- /dev/null +++ b/test/unit/findWealthAccounts.spec.ts @@ -0,0 +1,56 @@ +import { expect, use } from 'chai'; +import { utils, type providers } from 'ethers'; +import { + REGTEST_CHAIN_ID, + findWealthyAccount, +} from 'src/commands/findWealthyAccount'; +import chaiAsPromised from 'chai-as-promised'; + +use(chaiAsPromised); + +const TESTNET_CHAIN_ID = 31; +const MAINNET_CHAIN_ID = 30; + +describe('findWealthAccounts', function () { + const expectedSigner = { + getAddress: () => '0x123abc', + getBalance: () => Promise.resolve(utils.parseUnits('2', 'ether')), + }; + + const getMockedProvider = (expectedChainId: number) => + ({ + getNetwork: () => ({ chainId: expectedChainId }), + listAccounts: () => [1, 2, 3], + getSigner: () => expectedSigner, + } as unknown as providers.JsonRpcProvider); + + const expectFindWealthyAccountToFail = async (chainId: number) => { + const mockRpcProvider = getMockedProvider(chainId); + await expect(findWealthyAccount(mockRpcProvider)).to.rejectedWith( + 'Unlocked accounts are allowed for testing purposes only' + ); + }; + + it('should not raise an error if it is used for dev purposes', async function () { + const mockRpcProvider = getMockedProvider(REGTEST_CHAIN_ID); + const account = await findWealthyAccount(mockRpcProvider); + expect(account).to.be.eq(expectedSigner); + }); + + it('should raise an error if it is used on Testnet', async function () { + await expectFindWealthyAccountToFail(TESTNET_CHAIN_ID); + }); + + it('should raise an error if it is used on Mainnet', async function () { + await expectFindWealthyAccountToFail(MAINNET_CHAIN_ID); + }); + + it('should raise an error if no accounts are found with enough balance', async function () { + const mockRpcProvider = getMockedProvider(REGTEST_CHAIN_ID); + await expect( + findWealthyAccount(mockRpcProvider, utils.parseUnits('3', 'ether')) + ).to.rejectedWith( + 'could not find unlocked account with sufficient balance;' + ); + }); +});