From e0b24f5e5df771a0adfdb0c2e3465d81f515bb43 Mon Sep 17 00:00:00 2001 From: Antonio Date: Wed, 3 Jan 2024 14:23:15 +0100 Subject: [PATCH] fix: remove unused tests --- test-to-migrate/EnvelopingUtils.test.ts | 902 ------- test-to-migrate/Flows.test.ts | 238 -- test-to-migrate/GasAttacks.test.ts | 361 --- test-to-migrate/HttpWrapper.test.ts | 32 - test-to-migrate/KeyManager.test.ts | 88 - test-to-migrate/RSKAddressValidator.test.ts | 116 - test-to-migrate/RelayHubPenalizations.test.ts | 665 ------ .../RelayHubRegistrationsManagement.test.ts | 173 -- test-to-migrate/SampleRecipient.test.ts | 58 - test-to-migrate/StakeManagement.test.ts | 403 ---- test-to-migrate/TestEnvironment.test.ts | 206 -- test-to-migrate/TestEnvironment.ts | 213 -- test-to-migrate/TestSetup.ts | 511 ---- test-to-migrate/TestUtils.ts | 704 ------ test-to-migrate/TxStoreManager.test.ts | 142 -- test-to-migrate/Utils.test.ts | 202 -- test-to-migrate/Utils.ts | 284 --- test-to-migrate/VersionRegistry.test.ts | 294 --- test-to-migrate/WalletFactory.test.ts | 1663 ------------- test-to-migrate/common/VersionManager.test.ts | 36 - .../dummies/BadContractInteractor.ts | 54 - test-to-migrate/dummies/BadHttpClient.ts | 64 - test-to-migrate/dummies/BadRelayClient.ts | 40 - .../dummies/BadRelayedTransactionValidator.ts | 34 - .../regressions/PayableWithEmit.test.ts | 19 - .../relayclient/AccountManager.test.ts | 237 -- .../relayclient/Configurator.test.ts | 66 - .../relayclient/ContractInteractor.test.ts | 86 - .../relayclient/KnownRelaysManager.test.ts | 615 ----- .../relayclient/RelayClient.test.ts | 1558 ------------ .../relayclient/RelayProvider.test.ts | 1174 --------- .../relayclient/RelaySelectionManager.test.ts | 523 ---- .../relayclient/SmartWalletDiscovery.test.ts | 2118 ----------------- .../relayserver/NetworkSimulation.test.ts | 108 - .../relayserver/RegistrationManager.test.ts | 602 ----- .../relayserver/RelayServer.test.ts | 1037 -------- .../RelayServerRequestsProfiling.test.ts | 93 - .../relayserver/ServerConfigParams.test.ts | 246 -- .../relayserver/ServerTestEnvironment.ts | 402 ---- .../relayserver/ServerTestUtils.ts | 112 - .../relayserver/TransactionManager.test.ts | 107 - test-to-migrate/server-config.json | 9 - 42 files changed, 16595 deletions(-) delete mode 100644 test-to-migrate/EnvelopingUtils.test.ts delete mode 100644 test-to-migrate/Flows.test.ts delete mode 100644 test-to-migrate/GasAttacks.test.ts delete mode 100644 test-to-migrate/HttpWrapper.test.ts delete mode 100644 test-to-migrate/KeyManager.test.ts delete mode 100644 test-to-migrate/RSKAddressValidator.test.ts delete mode 100644 test-to-migrate/RelayHubPenalizations.test.ts delete mode 100644 test-to-migrate/RelayHubRegistrationsManagement.test.ts delete mode 100644 test-to-migrate/SampleRecipient.test.ts delete mode 100644 test-to-migrate/StakeManagement.test.ts delete mode 100644 test-to-migrate/TestEnvironment.test.ts delete mode 100644 test-to-migrate/TestEnvironment.ts delete mode 100644 test-to-migrate/TestSetup.ts delete mode 100644 test-to-migrate/TestUtils.ts delete mode 100644 test-to-migrate/TxStoreManager.test.ts delete mode 100644 test-to-migrate/Utils.test.ts delete mode 100644 test-to-migrate/Utils.ts delete mode 100644 test-to-migrate/VersionRegistry.test.ts delete mode 100644 test-to-migrate/WalletFactory.test.ts delete mode 100644 test-to-migrate/common/VersionManager.test.ts delete mode 100644 test-to-migrate/dummies/BadContractInteractor.ts delete mode 100644 test-to-migrate/dummies/BadHttpClient.ts delete mode 100644 test-to-migrate/dummies/BadRelayClient.ts delete mode 100644 test-to-migrate/dummies/BadRelayedTransactionValidator.ts delete mode 100644 test-to-migrate/regressions/PayableWithEmit.test.ts delete mode 100644 test-to-migrate/relayclient/AccountManager.test.ts delete mode 100644 test-to-migrate/relayclient/Configurator.test.ts delete mode 100644 test-to-migrate/relayclient/ContractInteractor.test.ts delete mode 100644 test-to-migrate/relayclient/KnownRelaysManager.test.ts delete mode 100644 test-to-migrate/relayclient/RelayClient.test.ts delete mode 100644 test-to-migrate/relayclient/RelayProvider.test.ts delete mode 100644 test-to-migrate/relayclient/RelaySelectionManager.test.ts delete mode 100644 test-to-migrate/relayclient/SmartWalletDiscovery.test.ts delete mode 100644 test-to-migrate/relayserver/NetworkSimulation.test.ts delete mode 100644 test-to-migrate/relayserver/RegistrationManager.test.ts delete mode 100644 test-to-migrate/relayserver/RelayServer.test.ts delete mode 100644 test-to-migrate/relayserver/RelayServerRequestsProfiling.test.ts delete mode 100644 test-to-migrate/relayserver/ServerConfigParams.test.ts delete mode 100644 test-to-migrate/relayserver/ServerTestEnvironment.ts delete mode 100644 test-to-migrate/relayserver/ServerTestUtils.ts delete mode 100644 test-to-migrate/relayserver/TransactionManager.test.ts delete mode 100644 test-to-migrate/server-config.json diff --git a/test-to-migrate/EnvelopingUtils.test.ts b/test-to-migrate/EnvelopingUtils.test.ts deleted file mode 100644 index d146f909..00000000 --- a/test-to-migrate/EnvelopingUtils.test.ts +++ /dev/null @@ -1,902 +0,0 @@ -import { ChildProcessWithoutNullStreams } from 'child_process'; -import { BN, toBuffer } from 'ethereumjs-util'; -import { - SmartWalletFactoryInstance, - RelayHubInstance, - SmartWalletInstance, - TestDeployVerifierEverythingAcceptedInstance, - TestRecipientInstance, - TestTokenInstance, - TestVerifierEverythingAcceptedInstance -} from '@rsksmart/rif-relay-contracts/types/truffle-contracts'; -import { - createSmartWalletFactory, - deployHub, - getTestingEnvironment, - getWebSocketUrl, - startRelay, - stopRelay -} from './TestUtils'; -import { - EnvelopingConfig, - constants, - isSameAddress -} from '@rsksmart/rif-relay-common'; -import { - TypedRequestData, - DeployRequest, - RelayRequest -} from '@rsksmart/rif-relay-contracts'; -import { randomHex, toChecksumAddress } from 'web3-utils'; -import { PrefixedHexString } from 'ethereumjs-tx'; -import sigUtil from 'eth-sig-util'; -import Web3 from 'web3'; -// @ts-ignore -import abiDecoder from 'abi-decoder'; -import { hdkey as EthereumHDKey } from 'ethereumjs-wallet'; -import { - DiscoveryConfig, - SmartWalletDiscovery, - configure, - Enveloping, - SignatureProvider, - AccountKeypair -} from '@rsksmart/rif-relay-client'; -import { WebsocketProvider } from 'web3-core'; -import { RIF_RELAY_URL } from './Utils'; - -contract('Enveloping utils', function (accounts) { - describe('Relay-related functionalities', function () { - const TestRecipient = artifacts.require('tests/TestRecipient'); - const TestToken = artifacts.require('TestToken'); - const SmartWallet = artifacts.require('SmartWallet'); - const TestVerifierEverythingAccepted = artifacts.require( - 'tests/TestVerifierEverythingAccepted' - ); - const TestDeployVerifierEverythingAccepted = artifacts.require( - 'tests/TestDeployVerifierEverythingAccepted' - ); - const SmartWalletFactory = artifacts.require('SmartWalletFactory'); - - const localhost = RIF_RELAY_URL; - const message = 'hello world'; - - // @ts-ignore - abiDecoder.addABI(TestRecipient.abi); - // @ts-ignore - abiDecoder.addABI(SmartWalletFactory.abi); - let enveloping: Enveloping; - let tokenContract: TestTokenInstance; - let relayHub: RelayHubInstance; - let verifier: TestVerifierEverythingAcceptedInstance; - let deployVerifier: TestDeployVerifierEverythingAcceptedInstance; - let factory: SmartWalletFactoryInstance; - let sWalletTemplate: SmartWalletInstance; - let testRecipient: TestRecipientInstance; - let chainId: number; - let workerAddress: string; - let config: EnvelopingConfig; - let fundedAccount: AccountKeypair; - let gaslessAccount: AccountKeypair; - let relayproc: ChildProcessWithoutNullStreams; - let swAddress: string; - let index: string; - - const signatureProvider: SignatureProvider = { - sign: (dataToSign: TypedRequestData) => { - const privKey = toBuffer( - '0x082f57b8084286a079aeb9f2d0e17e565ced44a2cb9ce4844e6d4b9d89f3f595' - ); - // @ts-ignore - return sigUtil.signTypedData_v4(privKey, { data: dataToSign }); - }, - verifySign: ( - signature: PrefixedHexString, - dataToSign: TypedRequestData, - request: RelayRequest | DeployRequest - ) => { - // @ts-ignore - const rec = sigUtil.recoverTypedSignature_v4({ - data: dataToSign, - sig: signature - }); - return isSameAddress(request.request.from, rec); - } - }; - - const deploySmartWallet = async function deploySmartWallet( - tokenContract: string, - tokenAmount: string, - tokenGas: string - ): Promise { - const deployRequest = await enveloping.createDeployRequest( - gaslessAccount.address, - tokenContract, - tokenAmount, - tokenGas, - '1000000000', - index - ); - const deploySignature = enveloping.signDeployRequest( - signatureProvider, - deployRequest - ); - const httpDeployRequest = - await enveloping.generateDeployTransactionRequest( - deploySignature, - deployRequest - ); - const sentDeployTransaction = await enveloping.sendTransaction( - localhost, - httpDeployRequest - ); - return sentDeployTransaction.transaction - ?.hash(true) - .toString('hex'); - }; - - const assertSmartWalletDeployedCorrectly = - async function assertSmartWalletDeployedCorrectly( - swAddress: string - ): Promise { - const deployedCode = await web3.eth.getCode(swAddress); - let expectedCode = await factory.getCreationBytecode(); - expectedCode = - '0x' + expectedCode.slice(20, expectedCode.length); - assert.equal(deployedCode, expectedCode); - }; - - const relayTransaction = async function relayTransaction( - tokenContract: string, - tokenAmount: string, - tokenGas: string - ): Promise { - const encodedFunction = testRecipient.contract.methods - .emitMessage(message) - .encodeABI(); - const relayRequest = await enveloping.createRelayRequest( - gaslessAccount.address, - testRecipient.address, - swAddress, - encodedFunction, - tokenContract, - tokenAmount, - tokenGas - ); - const relaySignature = enveloping.signRelayRequest( - signatureProvider, - relayRequest - ); - const httpRelayRequest = - await enveloping.generateRelayTransactionRequest( - relaySignature, - relayRequest - ); - const sentRelayTransaction = await enveloping.sendTransaction( - localhost, - httpRelayRequest - ); - return sentRelayTransaction.transaction?.hash(true).toString('hex'); - }; - - before(async () => { - gaslessAccount = { - privateKey: toBuffer( - '0x082f57b8084286a079aeb9f2d0e17e565ced44a2cb9ce4844e6d4b9d89f3f595' - ), - address: '0x09a1eda29f664ac8f68106f6567276df0c65d859' - }; - fundedAccount = { - privateKey: toBuffer( - '0xc85ef7d79691fe79573b1a7064c19c1a9819ebdbd1faaab1a8ec92344438aaf4' - ), - address: '0xcd2a3d9f938e13cd947ec05abc7fe734df8dd826' - }; - testRecipient = await TestRecipient.new(); - sWalletTemplate = await SmartWallet.new(); - verifier = await TestVerifierEverythingAccepted.new(); - deployVerifier = await TestDeployVerifierEverythingAccepted.new(); - factory = await createSmartWalletFactory(sWalletTemplate); - chainId = (await getTestingEnvironment()).chainId; - tokenContract = await TestToken.new(); - relayHub = await deployHub(); - }); - - beforeEach(async () => { - index = randomHex(32); - swAddress = await factory.getSmartWalletAddress( - gaslessAccount.address, - constants.ZERO_ADDRESS, - index - ); - - const partialConfig: Partial = { - relayHubAddress: relayHub.address, - smartWalletFactoryAddress: factory.address, - chainId: chainId, - relayVerifierAddress: verifier.address, - deployVerifierAddress: deployVerifier.address, - preferredRelays: [RIF_RELAY_URL] - }; - - config = configure(partialConfig); - const serverData = await startRelay(relayHub, { - stake: 1e18, - delay: 3600 * 24 * 7, - url: 'asd', - relayOwner: fundedAccount.address, - gasPriceFactor: 1, - // @ts-ignore - rskNodeUrl: web3.currentProvider.host, - relayVerifierAddress: verifier.address, - deployVerifierAddress: deployVerifier.address - }); - relayproc = serverData.proc; - workerAddress = serverData.worker; - enveloping = new Enveloping( - config, - web3, - workerAddress, - workerAddress - ); - await enveloping._init(); - }); - - afterEach(async function () { - await stopRelay(relayproc); - }); - - it('Should deploy a smart wallet correctly and relay a tx using enveloping utils without tokens', async () => { - const expectedInitialCode = await web3.eth.getCode(swAddress); - assert.equal('0x', expectedInitialCode); - - const txDeployHash = await deploySmartWallet( - constants.ZERO_ADDRESS, - '0', - '0' - ); - - if (txDeployHash === undefined) { - assert.fail( - 'Transacion has not been send or it threw an error' - ); - } - - let txReceipt = await web3.eth.getTransactionReceipt(txDeployHash); - let logs = abiDecoder.decodeLogs(txReceipt.logs); - - const deployedEvent = logs.find( - (e: any) => e != null && e.name === 'Deployed' - ); - assert.equal( - swAddress.toLowerCase(), - deployedEvent.events[0].value.toLowerCase() - ); - - await assertSmartWalletDeployedCorrectly(swAddress); - - const txRelayHash = await relayTransaction( - constants.ZERO_ADDRESS, - '0', - '0' - ); - - if (txRelayHash === undefined) { - assert.fail( - 'Transacion has not been send or it threw an error' - ); - } - - txReceipt = await web3.eth.getTransactionReceipt(txRelayHash); - logs = abiDecoder.decodeLogs(txReceipt.logs); - - const sampleRecipientEmittedEvent = logs.find( - (e: any) => e != null && e.name === 'SampleRecipientEmitted' - ); - - assert.equal(message, sampleRecipientEmittedEvent.events[0].value); - assert.equal( - swAddress.toLowerCase(), - sampleRecipientEmittedEvent.events[1].value.toLowerCase() - ); - assert.equal( - workerAddress.toLowerCase(), - sampleRecipientEmittedEvent.events[2].value.toLowerCase() - ); - }); - - it('Should deploy a smart wallet correctly and relay a tx using enveloping utils paying with tokens', async () => { - const expectedInitialCode = await web3.eth.getCode(swAddress); - const balanceTransfered = new BN(10); - assert.equal('0x', expectedInitialCode); - await tokenContract.mint('100', swAddress); - const previousBalance = await tokenContract.balanceOf( - workerAddress - ); - - const txDeployHash = await deploySmartWallet( - tokenContract.address, - '10', - '50000' - ); - - if (txDeployHash === undefined) { - assert.fail( - 'Transacion has not been send or it threw an error' - ); - } - - let txReceipt = await web3.eth.getTransactionReceipt(txDeployHash); - let logs = abiDecoder.decodeLogs(txReceipt.logs); - - const deployedEvent = logs.find( - (e: any) => e != null && e.name === 'Deployed' - ); - assert.equal( - swAddress.toLowerCase(), - deployedEvent.events[0].value.toLowerCase() - ); - - await assertSmartWalletDeployedCorrectly(swAddress); - - const newBalance = await tokenContract.balanceOf(workerAddress); - assert.equal( - newBalance.toNumber(), - previousBalance.add(balanceTransfered).toNumber() - ); - - const txRelayHash = await relayTransaction( - tokenContract.address, - '10', - '50000' - ); - - if (txRelayHash === undefined) { - assert.fail('Transacion has not been send'); - } - - const finalBalance = await tokenContract.balanceOf(workerAddress); - - txReceipt = await web3.eth.getTransactionReceipt(txRelayHash); - logs = abiDecoder.decodeLogs(txReceipt.logs); - - const sampleRecipientEmittedEvent = logs.find( - (e: any) => e != null && e.name === 'SampleRecipientEmitted' - ); - - assert.equal(message, sampleRecipientEmittedEvent.events[0].value); - assert.equal( - swAddress.toLowerCase(), - sampleRecipientEmittedEvent.events[1].value.toLowerCase() - ); - assert.equal( - workerAddress.toLowerCase(), - sampleRecipientEmittedEvent.events[2].value.toLowerCase() - ); - - assert.equal( - finalBalance.toNumber(), - newBalance.add(balanceTransfered).toNumber() - ); - }); - }); - - describe('discoverAccountsFromExtendedPublicKeys', function () { - const currentPassword = '0'; - const TestToken = artifacts.require('TestToken'); - const SmartWallet = artifacts.require('SmartWallet'); - const SmartWalletFactory = artifacts.require('SmartWalletFactory'); - let socketProvider: WebsocketProvider; - let byteCodeHash: string; - const mnemonic = - 'figure arrow make ginger educate drip thing theory champion faint vendor push'; - let currentWeb3: Web3; - const discoverableAccounts = new Set(); - const usedPublicKeys: string[] = []; - let token: TestTokenInstance; - let factory: SmartWalletFactoryInstance; - let chainId: number; - - before(async function () { - chainId = (await getTestingEnvironment()).chainId; - socketProvider = new Web3.providers.WebsocketProvider( - getWebSocketUrl() - ); - - currentWeb3 = new Web3(socketProvider); - - TestToken.setProvider(socketProvider, undefined); - SmartWallet.setProvider(socketProvider, undefined); - SmartWalletFactory.setProvider(socketProvider, undefined); - - const sWalletTemplate = await SmartWallet.new(); - factory = await SmartWalletFactory.new(sWalletTemplate.address); - byteCodeHash = currentWeb3.utils.keccak256( - await factory.getCreationBytecode() - ); - token = await TestToken.new(); - - const rootKey: EthereumHDKey = - SmartWalletDiscovery.getRootExtKeyFromMnemonic( - mnemonic, - currentPassword - ); - - for (let accIdx = 0; accIdx < 2; accIdx++) { - const firstAccountRoot = rootKey.derivePath( - `m/44'/37310'/${accIdx}'/0` - ); - usedPublicKeys.push( - firstAccountRoot.publicExtendedKey().toString() - ); - - for (let i = 0; i < 20; i++) { - const account = firstAccountRoot - .deriveChild(i) - .getWallet() - .getAddressString(); - await currentWeb3.eth.sendTransaction({ - from: accounts[0], - to: account, - value: 1 - }); - discoverableAccounts.add(account); - - for (let j = 0; j < 20; j++) { - const swAddress = calculateSmartWalletAddress( - factory.address, - account, - constants.ZERO_ADDRESS, - j, - byteCodeHash - ); - await currentWeb3.eth.sendTransaction({ - from: accounts[0], - to: swAddress, - value: 1 - }); - discoverableAccounts.add(swAddress); - } - } - } - }); - - after(function () { - const socketProvider = - currentWeb3.currentProvider as WebsocketProvider; - socketProvider.disconnect(0, ''); - assert.isFalse( - socketProvider.connected, - 'Socket connection did not end' - ); - }); - - function calculateSmartWalletAddress( - factory: string, - ownerEOA: string, - recoverer: string, - walletIndex: number, - bytecodeHash: string - ): string { - const salt: string = - web3.utils.soliditySha3( - { t: 'address', v: ownerEOA }, - { t: 'address', v: recoverer }, - { t: 'uint256', v: walletIndex } - ) ?? ''; - - const _data: string = - web3.utils.soliditySha3( - { t: 'bytes1', v: '0xff' }, - { t: 'address', v: factory }, - { t: 'bytes32', v: salt }, - { t: 'bytes32', v: bytecodeHash } - ) ?? ''; - - return toChecksumAddress( - '0x' + _data.slice(26, _data.length), - chainId - ); - } - - it('Should discover Accounts using External Public Keys', async () => { - const config: DiscoveryConfig = new DiscoveryConfig({ - factory: factory.address, - recoverer: constants.ZERO_ADDRESS, - isCustomWallet: false, - isTestNet: true - }); - - const discoveredAccounts = - await Enveloping.discoverAccountsFromExtendedPublicKeys( - config, - socketProvider, - usedPublicKeys, - [token.address] - ); - assert.equal( - discoveredAccounts.length, - 40, - 'Incorrect number of EOA Accounts discovered' - ); - - for (let i = 0; i < discoveredAccounts.length; i++) { - const discoveredAccount = discoveredAccounts[i]; - assert.equal( - discoveredAccount.swAccounts.length, - 20, - `incorrect sw accounts discovered for address ${discoveredAccount.eoaAccount}` - ); - - const account = discoveredAccount.eoaAccount; - assert.isTrue( - discoverableAccounts.has(account), - 'Discovered Account not part of Discoverable Set' - ); - discoverableAccounts.delete(account); - - for (let j = 0; j < 20; j++) { - const swAccount = discoveredAccount.swAccounts[j]; - assert.isTrue( - discoverableAccounts.has(swAccount), - 'Discovered SWAccount not part of Discoverable Set' - ); - discoverableAccounts.delete(swAccount); - } - } - - assert.isTrue( - discoverableAccounts.size === 0, - 'Some Discoverable Accounts were not found' - ); - }); - }); - - describe('discoverAccountsUsingMnemonic', function () { - const currentPassword = '1'; - const TestToken = artifacts.require('TestToken'); - const SmartWallet = artifacts.require('SmartWallet'); - const SmartWalletFactory = artifacts.require('SmartWalletFactory'); - let socketProvider: WebsocketProvider; - let byteCodeHash: string; - const mnemonic = - 'figure arrow make ginger educate drip thing theory champion faint vendor push'; - let currentWeb3: Web3; - const discoverableAccounts = new Set(); - const usedPublicKeys: string[] = []; - let token: TestTokenInstance; - let factory: SmartWalletFactoryInstance; - let chainId: number; - - before(async function () { - chainId = (await getTestingEnvironment()).chainId; - socketProvider = new Web3.providers.WebsocketProvider( - getWebSocketUrl() - ); - - currentWeb3 = new Web3(socketProvider); - - TestToken.setProvider(socketProvider, undefined); - SmartWallet.setProvider(socketProvider, undefined); - SmartWalletFactory.setProvider(socketProvider, undefined); - - const sWalletTemplate = await SmartWallet.new(); - factory = await SmartWalletFactory.new(sWalletTemplate.address); - byteCodeHash = currentWeb3.utils.keccak256( - await factory.getCreationBytecode() - ); - token = await TestToken.new(); - - const rootKey: EthereumHDKey = - SmartWalletDiscovery.getRootExtKeyFromMnemonic( - mnemonic, - currentPassword - ); - - for (let accIdx = 0; accIdx < 2; accIdx++) { - const firstAccountRoot = rootKey.derivePath( - `m/44'/37310'/${accIdx}'/0` - ); - usedPublicKeys.push( - firstAccountRoot.publicExtendedKey().toString() - ); - - for (let i = 0; i < 20; i++) { - const account = firstAccountRoot - .deriveChild(i) - .getWallet() - .getAddressString(); - await currentWeb3.eth.sendTransaction({ - from: accounts[0], - to: account, - value: 1 - }); - discoverableAccounts.add(account); - - for (let j = 0; j < 20; j++) { - const swAddress = calculateSmartWalletAddress( - factory.address, - account, - constants.ZERO_ADDRESS, - j, - byteCodeHash - ); - await currentWeb3.eth.sendTransaction({ - from: accounts[0], - to: swAddress, - value: 1 - }); - discoverableAccounts.add(swAddress); - } - } - } - }); - - after(function () { - const socketProvider = - currentWeb3.currentProvider as WebsocketProvider; - socketProvider.disconnect(0, ''); - assert.isFalse( - socketProvider.connected, - 'Socket connection did not end' - ); - }); - - function calculateSmartWalletAddress( - factory: string, - ownerEOA: string, - recoverer: string, - walletIndex: number, - bytecodeHash: string - ): string { - const salt: string = - web3.utils.soliditySha3( - { t: 'address', v: ownerEOA }, - { t: 'address', v: recoverer }, - { t: 'uint256', v: walletIndex } - ) ?? ''; - - const _data: string = - web3.utils.soliditySha3( - { t: 'bytes1', v: '0xff' }, - { t: 'address', v: factory }, - { t: 'bytes32', v: salt }, - { t: 'bytes32', v: bytecodeHash } - ) ?? ''; - - return toChecksumAddress( - '0x' + _data.slice(26, _data.length), - chainId - ); - } - - it('Should discover Accounts using Mnemonic', async () => { - const config: DiscoveryConfig = new DiscoveryConfig({ - factory: factory.address, - recoverer: constants.ZERO_ADDRESS, - isCustomWallet: false, - isTestNet: true - }); - - const discoveredAccounts = - await Enveloping.discoverAccountsUsingMnemonic( - config, - socketProvider, - mnemonic, - currentPassword, - [token.address] - ); - - assert.equal( - discoveredAccounts.length, - 40, - 'Incorrect number of EOA Accounts discovered' - ); - - for (let i = 0; i < discoveredAccounts.length; i++) { - assert.equal( - discoveredAccounts[i].swAccounts.length, - 20, - `incorrect sw accounts discovered for address ${discoveredAccounts[i].eoaAccount}` - ); - const account = discoveredAccounts[i].eoaAccount; - assert.isTrue( - discoverableAccounts.has(account), - 'Discovered Account not part of Discoverable Set' - ); - discoverableAccounts.delete(account); - - for (let j = 0; j < 20; j++) { - const swAccount = discoveredAccounts[i].swAccounts[j]; - assert.isTrue( - discoverableAccounts.has(swAccount), - 'Discovered SWAccount not part of Discoverable Set' - ); - discoverableAccounts.delete(swAccount); - } - } - - assert.isTrue( - discoverableAccounts.size === 0, - 'Some Discoverable Accounts were not found' - ); - }); - }); - - describe('discoverAccounts - using template method', function () { - const currentPassword = '2'; - const TestToken = artifacts.require('TestToken'); - const SmartWallet = artifacts.require('SmartWallet'); - const SmartWalletFactory = artifacts.require('SmartWalletFactory'); - let socketProvider: WebsocketProvider; - let byteCodeHash: string; - const mnemonic = - 'figure arrow make ginger educate drip thing theory champion faint vendor push'; - let currentWeb3: Web3; - const discoverableAccounts = new Set(); - const usedPublicKeys: string[] = []; - let token: TestTokenInstance; - let factory: SmartWalletFactoryInstance; - let chainId: number; - - async function getUsedPubKey( - accountIdx: number - ): Promise { - return accountIdx < usedPublicKeys.length - ? usedPublicKeys[accountIdx] - : undefined; - } - - before(async function () { - chainId = (await getTestingEnvironment()).chainId; - socketProvider = new Web3.providers.WebsocketProvider( - getWebSocketUrl() - ); - - currentWeb3 = new Web3(socketProvider); - - TestToken.setProvider(socketProvider, undefined); - SmartWallet.setProvider(socketProvider, undefined); - SmartWalletFactory.setProvider(socketProvider, undefined); - - const sWalletTemplate = await SmartWallet.new(); - factory = await SmartWalletFactory.new(sWalletTemplate.address); - byteCodeHash = currentWeb3.utils.keccak256( - await factory.getCreationBytecode() - ); - token = await TestToken.new(); - - const rootKey: EthereumHDKey = - SmartWalletDiscovery.getRootExtKeyFromMnemonic( - mnemonic, - currentPassword - ); - - for (let accIdx = 0; accIdx < 2; accIdx++) { - const firstAccountRoot = rootKey.derivePath( - `m/44'/37310'/${accIdx}'/0` - ); - usedPublicKeys.push( - firstAccountRoot.publicExtendedKey().toString() - ); - - for (let i = 0; i < 20; i++) { - const account = firstAccountRoot - .deriveChild(i) - .getWallet() - .getAddressString(); - await currentWeb3.eth.sendTransaction({ - from: accounts[0], - to: account, - value: 1 - }); - discoverableAccounts.add(account); - - for (let j = 0; j < 20; j++) { - const swAddress = calculateSmartWalletAddress( - factory.address, - account, - constants.ZERO_ADDRESS, - j, - byteCodeHash - ); - await currentWeb3.eth.sendTransaction({ - from: accounts[0], - to: swAddress, - value: 1 - }); - discoverableAccounts.add(swAddress); - } - } - } - }); - - after(function () { - const socketProvider = - currentWeb3.currentProvider as WebsocketProvider; - socketProvider.disconnect(0, ''); - assert.isFalse( - socketProvider.connected, - 'Socket connection did not end' - ); - }); - - function calculateSmartWalletAddress( - factory: string, - ownerEOA: string, - recoverer: string, - walletIndex: number, - bytecodeHash: string - ): string { - const salt: string = - web3.utils.soliditySha3( - { t: 'address', v: ownerEOA }, - { t: 'address', v: recoverer }, - { t: 'uint256', v: walletIndex } - ) ?? ''; - - const _data: string = - web3.utils.soliditySha3( - { t: 'bytes1', v: '0xff' }, - { t: 'address', v: factory }, - { t: 'bytes32', v: salt }, - { t: 'bytes32', v: bytecodeHash } - ) ?? ''; - - return toChecksumAddress( - '0x' + _data.slice(26, _data.length), - chainId - ); - } - - it('Should discover Accounts using template method', async () => { - const config: DiscoveryConfig = new DiscoveryConfig({ - factory: factory.address, - recoverer: constants.ZERO_ADDRESS, - isCustomWallet: false, - isTestNet: true - }); - - const discoveredAccounts = await Enveloping.discoverAccounts( - config, - socketProvider, - getUsedPubKey, - [token.address] - ); - - assert.equal( - discoveredAccounts.length, - 40, - 'Incorrect number of EOA Accounts discovered' - ); - - for (let i = 0; i < discoveredAccounts.length; i++) { - assert.equal( - discoveredAccounts[i].swAccounts.length, - 20, - `incorrect sw accounts discovered for address ${discoveredAccounts[i].eoaAccount}` - ); - const account = discoveredAccounts[i].eoaAccount; - assert.isTrue( - discoverableAccounts.has(account), - 'Discovered Account not part of Discoverable Set' - ); - discoverableAccounts.delete(account); - - for (let j = 0; j < 20; j++) { - const swAccount = discoveredAccounts[i].swAccounts[j]; - assert.isTrue( - discoverableAccounts.has(swAccount), - 'Discovered SWAccount not part of Discoverable Set' - ); - discoverableAccounts.delete(swAccount); - } - } - - assert.isTrue( - discoverableAccounts.size === 0, - 'Some Discoverable Accounts were not found' - ); - }); - }); -}); diff --git a/test-to-migrate/Flows.test.ts b/test-to-migrate/Flows.test.ts deleted file mode 100644 index be3f63bf..00000000 --- a/test-to-migrate/Flows.test.ts +++ /dev/null @@ -1,238 +0,0 @@ -// test various flows, in multiple modes: -// once in Direct mode, and once in Relay (gasless) mode. -// the two modes must behave just the same (with an exception of gasless test, which must fail on direct mode, and must -// succeed in gasless) -// the entire 'contract' test is doubled. all tests titles are prefixed by either "Direct:" or "Relay:" - -import { - RelayHubInstance, - TestVerifierEverythingAcceptedInstance, - TestRecipientInstance, - SmartWalletInstance, - SmartWalletFactoryInstance, - TestDeployVerifierEverythingAcceptedInstance -} from '@rsksmart/rif-relay-contracts/types/truffle-contracts'; -import { - deployHub, - startRelay, - stopRelay, - getTestingEnvironment, - createSmartWalletFactory, - createSmartWallet, - getExistingGaslessAccount -} from './TestUtils'; -import { ChildProcessWithoutNullStreams } from 'child_process'; -import { EnvelopingConfig } from '@rsksmart/rif-relay-common'; -import { toBuffer } from 'ethereumjs-util'; -import { - AccountKeypair, - RelayProvider, - Address -} from '@rsksmart/rif-relay-client'; -import { HttpProvider } from 'web3-core'; - -const TestRecipient = artifacts.require('tests/TestRecipient'); -const TestVerifierEverythingAccepted = artifacts.require( - 'tests/TestVerifierEverythingAccepted' -); -const TestDeployVerifierEverythingAccepted = artifacts.require( - 'tests/TestDeployVerifierEverythingAccepted' -); - -const Penalizer = artifacts.require('Penalizer'); -const SmartWallet = artifacts.require('SmartWallet'); - -const options = [ - { - title: 'Direct-', - relay: false - }, - { - title: 'Relayed-', - relay: true - } -]; - -options.forEach((params) => { - contract(params.title + 'Flow', function (accounts) { - let from: Address; - let sr: TestRecipientInstance; - let verifier: TestVerifierEverythingAcceptedInstance; - let deployVerifier: TestDeployVerifierEverythingAcceptedInstance; - let rhub: RelayHubInstance; - let relayproc: ChildProcessWithoutNullStreams; - let relayClientConfig: Partial; - let fundedAccount: AccountKeypair; - let gaslessAccount: AccountKeypair; - - before(async () => { - const gasPriceFactor = 1.2; - - // An accound already funded on RSK - fundedAccount = { - privateKey: toBuffer( - '0xc85ef7d79691fe79573b1a7064c19c1a9819ebdbd1faaab1a8ec92344438aaf4' - ), - address: '0xcd2a3d9f938e13cd947ec05abc7fe734df8dd826' - }; - - // An account from RSK that has been depleted to ensure it has no funds - gaslessAccount = await getExistingGaslessAccount(); - - const p = await Penalizer.new(); - verifier = await TestVerifierEverythingAccepted.new(); - deployVerifier = await TestDeployVerifierEverythingAccepted.new(); - - rhub = await deployHub(p.address); - if (params.relay) { - process.env.relaylog = 'true'; - - relayproc = ( - await startRelay(rhub, { - workerTargetBalance: 0.6e18, - stake: 1e18, - delay: 3600 * 24 * 7, - url: 'asd', - relayOwner: fundedAccount.address, - // @ts-ignore - rskNodeUrl: web3.currentProvider.host, - gasPriceFactor, - relaylog: process.env.relaylog, - relayVerifierAddress: verifier.address, - deployVerifierAddress: deployVerifier.address - }) - ).proc; - console.log('relay started'); - from = gaslessAccount.address; - } else { - from = fundedAccount.address; - } - sr = await TestRecipient.new(); - }); - - after(async function () { - await stopRelay(relayproc); - }); - - if (params.relay) { - before(params.title + 'enable relay', async function () { - const env = await getTestingEnvironment(); - const sWalletTemplate: SmartWalletInstance = - await SmartWallet.new(); - const factory: SmartWalletFactoryInstance = - await createSmartWalletFactory(sWalletTemplate); - const smartWalletInstance: SmartWalletInstance = - await createSmartWallet( - accounts[0], - gaslessAccount.address, - factory, - gaslessAccount.privateKey, - env.chainId - ); - relayClientConfig = { - logLevel: 5, - relayHubAddress: rhub.address, - relayVerifierAddress: verifier.address, - deployVerifierAddress: deployVerifier.address, - chainId: env.chainId, - forwarderAddress: smartWalletInstance.address - }; - - const relayProvider = new RelayProvider( - web3.currentProvider as HttpProvider, - relayClientConfig - ); - relayProvider.addAccount(gaslessAccount); - - // web3.setProvider(relayProvider) - - // NOTE: in real application its enough to set the provider in web3. - // however, in Truffle, all contracts are built BEFORE the test have started, and COPIED the web3, - // so changing the global one is not enough... - TestRecipient.web3.setProvider(relayProvider); - }); - } - - it(params.title + 'send normal transaction', async () => { - console.log('running emitMessage (should succeed)'); - let res; - try { - const gas = await sr.contract.methods - .emitMessage('hello') - .estimateGas(); - res = params.relay - ? await sr.emitMessage('hello', { from }) - : await sr.emitMessage('hello', { from, gas }); - } catch (e) { - console.log('error is ', e.message); - throw e; - } - assert.equal('hello', res.logs[0].args.message); - }); - - it(params.title + 'send gasless transaction', async () => { - console.log('gasless=' + gaslessAccount.address); - - console.log( - 'running gasless-emitMessage (should fail for direct, succeed for relayed)' - ); - let ex: Error | undefined; - try { - const res = await sr.emitMessage('hello, from gasless', { - from: gaslessAccount.address, - gas: 1e6 - }); - console.log( - 'res after gasless emit:', - res.logs[0].args.message - ); - } catch (e) { - ex = e; - } - - if (params.relay) { - // eslint-disable-next-line @typescript-eslint/restrict-template-expressions - assert.ok( - ex == null, - `should succeed sending gasless transaction through relay. got: ${ex?.toString()}` - ); - } else { - // In RSK if the account doesn't have funds the error message received is 'the sender account doesn't exist' - // eslint-disable-next-line @typescript-eslint/no-base-to-string,@typescript-eslint/restrict-template-expressions - assert.ok( - ex.toString().indexOf('insufficient funds') > 0, - `Expected Error with 'insufficient funds'. got: ${ex?.toString()}` - ); - } - }); - - it( - params.title + 'running testRevert (should always fail)', - async () => { - await asyncShouldThrow(async () => { - await sr.testRevert({ from: from }); - }, 'revert'); - } - ); - - async function asyncShouldThrow( - asyncFunc: () => Promise, - str?: string - ): Promise { - const msg = str ?? 'Error'; - let ex: Error | undefined; - try { - await asyncFunc(); - } catch (e) { - ex = e; - } - assert.ok(ex != null, `Expected to throw ${msg} but threw nothing`); - const isExpectedError = ex?.toString().includes(msg); - // eslint-disable-next-line @typescript-eslint/restrict-template-expressions - assert.ok( - isExpectedError, - `Expected to throw ${msg} but threw ${ex?.message}` - ); - } - }); -}); diff --git a/test-to-migrate/GasAttacks.test.ts b/test-to-migrate/GasAttacks.test.ts deleted file mode 100644 index fe34d569..00000000 --- a/test-to-migrate/GasAttacks.test.ts +++ /dev/null @@ -1,361 +0,0 @@ -import { ether } from '@openzeppelin/test-helpers'; -import chai from 'chai'; -import { - getLocalEip712Signature, - Environment -} from '@rsksmart/rif-relay-common'; -import { - RelayRequest, - cloneRelayRequest, - TypedRequestData -} from '@rsksmart/rif-relay-contracts'; -// @ts-ignore -import abiDecoder from 'abi-decoder'; -import { IWalletFactory, IRelayHub } from '@rsksmart/rif-relay-contracts'; -import { - RelayHubInstance, - PenalizerInstance, - TestRecipientInstance, - IForwarderInstance, - TestVerifierEverythingAcceptedInstance, - SmartWalletInstance, - SmartWalletFactoryInstance, - TestTokenInstance -} from '@rsksmart/rif-relay-contracts/types/truffle-contracts'; -import { - deployHub, - getTestingEnvironment, - createSmartWallet, - getGaslessAccount, - createSmartWalletFactory -} from './TestUtils'; - -import chaiAsPromised from 'chai-as-promised'; -import { AccountKeypair } from '@rsksmart/rif-relay-client'; -import { toBN } from 'web3-utils'; - -const { assert } = chai.use(chaiAsPromised); -const SmartWallet = artifacts.require('SmartWallet'); -const Penalizer = artifacts.require('Penalizer'); -const TestVerifierEverythingAccepted = artifacts.require( - 'TestVerifierEverythingAccepted' -); -const TestRecipient = artifacts.require('TestRecipient'); - -// @ts-ignore -abiDecoder.addABI(TestRecipient.abi); -abiDecoder.addABI(IWalletFactory.abi); -abiDecoder.addABI(IRelayHub.abi); - -contract('RelayHub', function ([_, relayOwner, relayManager, relayWorker]) { - let chainId: number; - let relayHub: string; - let penalizer: PenalizerInstance; - let relayHubInstance: RelayHubInstance; - let recipientContract: TestRecipientInstance; - let verifierContract: TestVerifierEverythingAcceptedInstance; - let forwarderInstance: IForwarderInstance; - let target: string; - let verifier: string; - let forwarder: string; - let gaslessAccount: AccountKeypair; - const gasLimit = '1000000'; - const gasPrice = '1'; - let sharedRelayRequestData: RelayRequest; - let env: Environment; - let token: TestTokenInstance; - let factory: SmartWalletFactoryInstance; - - describe('relayCall', function () { - beforeEach(async function () { - env = await getTestingEnvironment(); - chainId = env.chainId; - - penalizer = await Penalizer.new(); - relayHubInstance = await deployHub(penalizer.address); - verifierContract = await TestVerifierEverythingAccepted.new(); - gaslessAccount = await getGaslessAccount(); - - const smartWalletTemplate: SmartWalletInstance = - await SmartWallet.new(); - factory = await createSmartWalletFactory(smartWalletTemplate); - recipientContract = await TestRecipient.new(); - const testToken = artifacts.require('TestToken'); - token = await testToken.new(); - target = recipientContract.address; - verifier = verifierContract.address; - relayHub = relayHubInstance.address; - - forwarderInstance = await createSmartWallet( - _, - gaslessAccount.address, - factory, - gaslessAccount.privateKey, - chainId - ); - forwarder = forwarderInstance.address; - await token.mint('1000', forwarder); - - sharedRelayRequestData = { - request: { - relayHub: relayHub, - to: target, - data: '0x', - from: gaslessAccount.address, - nonce: (await forwarderInstance.nonce()).toString(), - value: '0', - gas: gasLimit, - tokenContract: token.address, - tokenAmount: '1', - tokenGas: '50000', - validUntilTime: '0' - }, - relayData: { - gasPrice, - feesReceiver: relayWorker, - callForwarder: forwarder, - callVerifier: verifier - } - }; - }); - - context('with staked and registered relay', function () { - const url = 'http://relay.com'; - const message = 'Enveloping RelayHub'; - - let relayRequest: RelayRequest; - let encodedFunction: string; - let signatureWithPermissiveVerifier: string; - - beforeEach(async function () { - await relayHubInstance.stakeForAddress(relayManager, 1000, { - value: ether('2'), - from: relayOwner - }); - - // truffle-contract doesn't let us create method data from the class, we need an actual instance - encodedFunction = recipientContract.contract.methods - .emitMessage(message) - .encodeABI(); - - await relayHubInstance.addRelayWorkers([relayWorker], { - from: relayManager - }); - await relayHubInstance.registerRelayServer(url, { - from: relayManager - }); - relayRequest = cloneRelayRequest(sharedRelayRequestData); - relayRequest.request.data = encodedFunction; - - const dataToSign = new TypedRequestData( - chainId, - forwarder, - relayRequest - ); - signatureWithPermissiveVerifier = getLocalEip712Signature( - dataToSign, - gaslessAccount.privateKey - ); - }); - - context('with funded verifier', function () { - /** - * The User's SmartWallet cannot be attacked by the Relayer with the following attack: - * The RelayServer carefully chooses the gas to send in the transaction in order to be able - * to collect the token payment from the user but avoiding the execution of the destination contract - * - * The design of the SmartWallet smart contract makes it obvious, but this test is another way of demonstrating this attack is not possible. - * This test works by increasing the gas sent to the transaction one by one (n times) until the transaction passes. - * All the other previous n-1 cases (where the gas was not enough) we prove that the transaction reverts as a whole, without any state changes - * - * This suite does not run by default, because this test can take hours to complete. - * If you want to try this test simply run it stand alone or add it in .circleci/config.yml - * - */ - it('relayCall atomicity cannot be broken using user-entered gas attacks', async function () { - this.timeout(0); - let gasSent = 42300; // less gas wont even pass the node's transaction cost validation - // 121620 is the gas used for this transaction in order to succeed - let succeed = false; - - const startTime = new Date().toISOString(); - - while (!succeed) { - const nonceBefore = await forwarderInstance.nonce(); - const workerTokenBalanceBefore = await token.balanceOf( - relayWorker - ); - const workerRBTCBalanceBefore = toBN( - await web3.eth.getBalance(relayWorker) - ); - const swTokenBalanceBefore = await token.balanceOf( - forwarder - ); - const swRBTCBalanceBefore = toBN( - await web3.eth.getBalance(forwarder) - ); - const ownerTokenBalanceBefore = await token.balanceOf( - gaslessAccount.address - ); - const ownerRBTCBalanceBefore = toBN( - await web3.eth.getBalance(gaslessAccount.address) - ); - let transaction: Truffle.TransactionResponse; - - try { - transaction = await relayHubInstance.relayCall( - relayRequest, - signatureWithPermissiveVerifier, - { - from: relayWorker, - gasPrice, - gas: gasSent - } - ); - } catch (error) { - const nonceAfter = await forwarderInstance.nonce(); - assert.isTrue( - nonceBefore.eq(nonceAfter), - 'Smart wallet nonce must be unchanged' - ); - - const workerTokenBalanceAfter = - await token.balanceOf(relayWorker); - assert.isTrue( - workerTokenBalanceBefore.eq( - workerTokenBalanceAfter - ), - 'Worker token balance must be unchanged' - ); - - const swTokenBalanceAfter = await token.balanceOf( - forwarder - ); - assert.isTrue( - swTokenBalanceBefore.eq(swTokenBalanceAfter), - 'Smart wallet token balance must be unchanged' - ); - - const workerRBTCBalanceAfter = toBN( - await web3.eth.getBalance(relayWorker) - ); - - if ( - JSON.stringify(error).includes( - 'basic cost is above the gas limit' - ) - ) { - assert.isTrue( - workerRBTCBalanceBefore.eq( - workerRBTCBalanceAfter - ), - `RBTC balance of the worker must be the same, gasSent is ${gasSent}, balance before is ${workerRBTCBalanceBefore.toString()}, balance after is ${workerRBTCBalanceAfter.toString()}` - ); - } else { - assert.isTrue( - workerRBTCBalanceBefore.gt( - workerRBTCBalanceAfter - ), - `RBTC balance of the worker must have decreased, gasSent is ${gasSent}, balance before is ${workerRBTCBalanceBefore.toString()}, balance after is ${workerRBTCBalanceAfter.toString()}` - ); - } - - const swRBTCBalanceAfter = toBN( - await web3.eth.getBalance(forwarder) - ); - assert.isTrue( - swRBTCBalanceBefore.eq(swRBTCBalanceAfter), - 'Smart Wallet RBTC balance, if any, must be unchanged' - ); - - const ownerTokenBalanceAfter = - await token.balanceOf(gaslessAccount.address); - assert.isTrue( - ownerTokenBalanceBefore.eq( - ownerTokenBalanceAfter - ), - 'Smart wallet owner token balance, if any, must be unchanged' - ); - - const ownerRBTCBalanceAfter = toBN( - await web3.eth.getBalance( - gaslessAccount.address - ) - ); - assert.isTrue( - ownerRBTCBalanceBefore.eq( - ownerRBTCBalanceAfter - ), - 'Smart Wallet owner RBTC balance, if any, must be unchanged' - ); - - gasSent++; - continue; - } - - console.log('It worked with gas: ', gasSent); - - const nonceAfter = await forwarderInstance.nonce(); - assert.equal( - nonceBefore.addn(1).toNumber(), - nonceAfter.toNumber() - ); - - const receipt = await web3.eth.getTransactionReceipt( - transaction.tx - ); - const logs = abiDecoder.decodeLogs(receipt.logs); - const sampleRecipientEmittedEvent = logs.find( - (e: any) => - e != null && e.name === 'SampleRecipientEmitted' - ); - - assert.equal( - message, - sampleRecipientEmittedEvent.events[0].value - ); - assert.equal( - forwarder.toLowerCase(), - sampleRecipientEmittedEvent.events[1].value.toLowerCase() - ); - assert.equal( - relayWorker.toLowerCase(), - sampleRecipientEmittedEvent.events[2].value.toLowerCase() - ); - - const transactionRelayedEvent = logs.find( - (e: any) => - e != null && e.name === 'TransactionRelayed' - ); - - assert.isNotNull(transactionRelayedEvent); - - const workerTokenBalanceAfter = await token.balanceOf( - relayWorker - ); - assert.isTrue( - workerTokenBalanceBefore - .add(toBN(1)) - .eq(workerTokenBalanceAfter), - 'Worker token balance did not increase' - ); - - const swTokenBalanceAfter = await token.balanceOf( - forwarder - ); - assert.isTrue( - swTokenBalanceBefore.eq( - swTokenBalanceAfter.add(toBN(1)) - ), - 'Smart wallet token balance did not decrease' - ); - - succeed = true; - } - console.log('Start Time: ', startTime); - console.log('End time: ', new Date().toISOString()); - }); - }); - }); - }); -}); diff --git a/test-to-migrate/HttpWrapper.test.ts b/test-to-migrate/HttpWrapper.test.ts deleted file mode 100644 index 5019ce5e..00000000 --- a/test-to-migrate/HttpWrapper.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { HttpWrapper } from '@rsksmart/rif-relay-client'; -import chai from 'chai'; -import chaiAsPromised from 'chai-as-promised'; - -const { expect, assert } = chai.use(chaiAsPromised); - -describe('HttpWrapper', () => { - it('connect to node, get version', async () => { - const http = new HttpWrapper(); - // @ts-ignore - const url = web3.currentProvider.host; - const res = await http.sendPromise(url, { - jsonrpc: '2.0', - method: 'net_version', - id: 123 - }); - - assert.equal(123, res.id, JSON.stringify(res)); // just verify its a valid response - }); - - it('should fail on connection refused', async () => { - const http = new HttpWrapper(); - const res = http.sendPromise('http://localhost:44321', { - jsonrpc: '2.0', - method: 'net_version', - id: 123 - }); - await expect(res).to.be.eventually.rejectedWith( - 'connect ECONNREFUSED 127.0.0.1:44321' - ); - }); -}); diff --git a/test-to-migrate/KeyManager.test.ts b/test-to-migrate/KeyManager.test.ts deleted file mode 100644 index 93617241..00000000 --- a/test-to-migrate/KeyManager.test.ts +++ /dev/null @@ -1,88 +0,0 @@ -/* global */ - -import fs from 'fs'; -import { KeyManager, KEYSTORE_FILENAME } from '@rsksmart/rif-relay-server'; - -// NOTICE: this dir is removed in 'after', do not use this in any other test -const workdir = '/tmp/enveloping/test/key_manager'; -const keyStoreFilePath = workdir + '/' + KEYSTORE_FILENAME; - -function cleanFolder(): void { - if (fs.existsSync(keyStoreFilePath)) { - fs.unlinkSync(keyStoreFilePath); - } - if (fs.existsSync(workdir)) { - fs.rmdirSync(workdir); - } -} - -contract('KeyManager', function () { - describe('in-memory', () => { - let mkm: any; - - before(() => { - mkm = new KeyManager(10, undefined, Buffer.from('seed1234')); - }); - it('should return key', () => { - // for a given seed, the addresses and privkeys are known.. - const k0 = mkm.getAddress(0); - // @ts-ignore - assert.deepEqual( - mkm._privateKeys[k0].toString('hex'), - '98bd175008b68dfd5a6aca0584d5a040032f2469656569d5d428161b776d27ff' - ); - assert.equal(k0, '0x56558253d657baa29cfe9f0a808b7d19d5d80b9c'); - }); - it('should return another key for different index', () => { - const k1 = mkm.getAddress(1); - // @ts-ignore - assert.equal( - mkm._privateKeys[k1].toString('hex'), - 'e52f32d373b0b38be3800ec9070af883a63c4fd2857c5b0f249180a2c303eb7e' - ); - assert.equal(k1, '0xe2ceef58b3e5a8816c52b00067830b8e1afd82da'); - }); - }); - describe('file-based KeyManager', () => { - let fkmA: any; - - before('create key manager', function () { - cleanFolder(); - fkmA = new KeyManager(20, workdir); - assert.isTrue( - fs.existsSync(workdir), - 'test keystore dir should exist already' - ); - }); - - it('should get key pair', function () { - const key = fkmA.getAddress(1); - assert.isTrue(web3.utils.isAddress(key)); - // @ts-ignore - assert.equal(fkmA._privateKeys[key].length, 32); - }); - - it('should get the same key when reloading', () => { - const addrA = fkmA.getAddress(0); - const addrA10 = fkmA.getAddress(10); - const fkmB = new KeyManager(20, workdir) as any; - const addrB = fkmB.getAddress(0); - assert.equal(addrA, addrB); - // @ts-ignore - assert.equal( - fkmA._privateKeys[addrA].toString('hex'), - fkmB._privateKeys[addrB].toString('hex') - ); - - const addrB10 = fkmB.getAddress(10); - assert.equal(addrA10, addrB10); - // @ts-ignore - assert.equal( - fkmA._privateKeys[addrA10].toString('hex'), - fkmB._privateKeys[addrB10].toString('hex') - ); - }); - - after('remove keystore', cleanFolder); - }); -}); diff --git a/test-to-migrate/RSKAddressValidator.test.ts b/test-to-migrate/RSKAddressValidator.test.ts deleted file mode 100644 index 752e2fde..00000000 --- a/test-to-migrate/RSKAddressValidator.test.ts +++ /dev/null @@ -1,116 +0,0 @@ -import { TestRSKAddressValidatorInstance } from '@rsksmart/rif-relay-contracts/types/truffle-contracts'; -import { toChecksumAddress } from 'ethereumjs-util'; - -const TestRSKAddressValidator = artifacts.require('TestRSKAddressValidator'); - -contract('RSKAddressValidator', function () { - let addressValidator: TestRSKAddressValidatorInstance; - // let chainId: number - - before(async () => { - // chainId = (await getTestingEnvironment()).chainId - }); - it('should return true on check with data signed with zero', async function () { - const messageHash = - '0xf7cf90057f86838e5efd677f4741003ab90910e4e2736ff4d7999519d162d1ed'; - - const v = 27; - const r = - '3fba4230c7b08aa69affd0b7425b8666eb5e7d36f9ef84efeee5ba612c8df0bc'; - const s = - '6b212d83adb8364e5519a051894f4a513b7eb68d087d710bf21e70775d31a97b'; - - const signature = `0x${r}${s}${v.toString(16)}`; - - addressValidator = await TestRSKAddressValidator.new(); - const addr = await addressValidator.getAddress(messageHash, signature); - expect(addr).to.be.equal('0xdcc703c0E500B653Ca82273B7BFAd8045D85a470'); - - const res = await addressValidator.compareAddressWithZeroPK( - messageHash, - signature - ); - expect(res).to.be.equal(false); - }); - - it('should return true on check with data signed with zero with message hash from tx', async function () { - const messageHash = - '0x4d3e45a3a5908513a10012e30a04fb2b438bab7da2acb93084e2f15a5eb55e8b'; - - const v = 27; - const r = - '90ef8cbc9ce5999887d32f3f5adf5292ada96b9506b51980f219d60271cf300c'; - const s = - '3e59fb0088da48b32cb4d83f17af47dd7340cd0dab15ac214b7039b65ee8876d'; - - const signature = `0x${r}${s}${v.toString(16)}`; - - addressValidator = await TestRSKAddressValidator.new(); - const addr = await addressValidator.getAddress(messageHash, signature); - expect(addr).to.be.equal('0xdcc703c0E500B653Ca82273B7BFAd8045D85a470'); - - const res = await addressValidator.compareAddressWithZeroPK( - messageHash, - signature - ); - expect(res).to.be.equal(false); - }); - - it('should return FALSE on check with small case address and TRUE on check with checksummed address', async function () { - const messageHash = - '0x4d3e45a3a5908513a10012e30a04fb2b438bab7da2acb93084e2f15a5eb55e8b'; - const v = 27; - const r = - '90ef8cbc9ce5999887d32f3f5adf5292ada96b9506b51980f219d60271cf300c'; - const s = - '3e59fb0088da48b32cb4d83f17af47dd7340cd0dab15ac214b7039b65ee8876d'; - - const signature = `0x${r}${s}${v.toString(16)}`; - - addressValidator = await TestRSKAddressValidator.new(); - const addr = await addressValidator.getAddress(messageHash, signature); - expect(addr).to.be.not.equal( - '0xdcc703c0e500b653ca82273b7bfad8045d85a470' - ); - console.log( - 'WARN: In testnet or mainnet with EIP1191, the chainID must be added to toChecksumAddress in order to pass' - ); - expect(addr).to.be.equal( - toChecksumAddress('0xdcc703c0e500b653ca82273b7bfad8045d85a470') - ); - }); - - it('should return true on check with small case address on solidity', async function () { - const messageHash = - '0x4d3e45a3a5908513a10012e30a04fb2b438bab7da2acb93084e2f15a5eb55e8b'; - - const v = 27; - const r = - '90ef8cbc9ce5999887d32f3f5adf5292ada96b9506b51980f219d60271cf300c'; - const s = - '3e59fb0088da48b32cb4d83f17af47dd7340cd0dab15ac214b7039b65ee8876d'; - - const signature = `0x${r}${s}${v.toString(16)}`; - - const addr = '0xdcc703c0e500b653ca82273b7bfad8045d85a470'; - - addressValidator = await TestRSKAddressValidator.new(); - const areEqualSmallCase = - await addressValidator.compareAddressWithSigner( - messageHash, - signature, - addr - ); - expect(areEqualSmallCase).to.be.true; - - const addrChecksummed = toChecksumAddress(addr); - - const areEqualChecksummed = - await addressValidator.compareAddressWithSigner( - messageHash, - signature, - addrChecksummed - ); - expect(areEqualChecksummed).to.be.true; - }); -}); diff --git a/test-to-migrate/RelayHubPenalizations.test.ts b/test-to-migrate/RelayHubPenalizations.test.ts deleted file mode 100644 index a7cd130e..00000000 --- a/test-to-migrate/RelayHubPenalizations.test.ts +++ /dev/null @@ -1,665 +0,0 @@ -/* eslint-disable @typescript-eslint/require-await */ -// This rule seems to be flickering and buggy - does not understand async arrow functions correctly -import { - balance, - ether, - expectEvent, - expectRevert -} from '@openzeppelin/test-helpers'; -import BN from 'bn.js'; -import { Transaction, TransactionOptions } from 'ethereumjs-tx'; -import { privateToAddress, stripZeros, toBuffer } from 'ethereumjs-util'; -import { encode } from 'rlp'; -import { expect } from 'chai'; -import { - PenalizerInstance, - RelayHubInstance, - SmartWalletInstance, - SmartWalletFactoryInstance -} from '@rsksmart/rif-relay-contracts/types/truffle-contracts'; -import { - deployHub, - getTestingEnvironment, - createSmartWalletFactory, - createSmartWallet, - getGaslessAccount -} from './TestUtils'; -import { - constants, - getRawTxOptions, - isRsk, - Environment -} from '@rsksmart/rif-relay-common'; -import { cloneRelayRequest, RelayRequest } from '@rsksmart/rif-relay-contracts'; -import { AccountKeypair } from '@rsksmart/rif-relay-client'; -import TransactionResponse = Truffle.TransactionResponse; - -const RelayHub = artifacts.require('RelayHub'); -const Penalizer = artifacts.require('Penalizer'); -const TestVerifierEverythingAccepted = artifacts.require( - 'TestVerifierEverythingAccepted' -); -const SmartWallet = artifacts.require('SmartWallet'); - -contract( - 'RelayHub Penalizations', - function ([ - defaultAccount, - relayOwner, - relayWorker, - otherRelayWorker, - sender, - other, - relayManager, - otherRelayManager, - thirdRelayWorker, - reporterRelayManager - ]) { - // eslint-disable-line no-unused-vars - let relayHub: RelayHubInstance; - let penalizer: PenalizerInstance; - let env: Environment; - let transactionOptions: TransactionOptions; - - const relayRequest: RelayRequest = { - request: { - relayHub: constants.ZERO_ADDRESS, - to: '0x1820b744B33945482C17Dc37218C01D858EBc714', - data: '0x1234', - from: constants.ZERO_ADDRESS, - nonce: '0', - value: '0', - gas: '1000000', - tokenContract: constants.ZERO_ADDRESS, - tokenAmount: '0', - tokenGas: '0', - validUntilTime: '0' - }, - relayData: { - gasPrice: '50', - feesReceiver: relayWorker, - callForwarder: constants.ZERO_ADDRESS, - callVerifier: constants.ZERO_ADDRESS - } - }; - - // RSK requires a different relay's private key, original was '6370fd033278c143179d81c5526140625662b8daa446c22ee2d73db3707e620c' - const relayCallArgs = { - gasPrice: 50, - gasLimit: 1000000, - nonce: 0, - privateKey: - '88fcad7d65de4bf854b88191df9bf38648545e7e5ea367dff6e025b06a28244d' // RSK relay's private key - }; - - describe('penalizations', function () { - const stake = ether('1'); - - before(async function () { - for (const addr of [ - relayOwner, - relayWorker, - otherRelayWorker, - sender, - other, - relayManager, - otherRelayManager, - thirdRelayWorker - ]) { - console.log(addr); - } - - penalizer = await Penalizer.new(); - relayHub = await deployHub(penalizer.address); - env = await getTestingEnvironment(); - const networkId = await web3.eth.net.getId(); - const chain = await web3.eth.net.getNetworkType(); - transactionOptions = getRawTxOptions( - env.chainId, - networkId, - chain - ); - - await relayHub.stakeForAddress(relayManager, 1000, { - from: relayOwner, - value: ether('1'), - gasPrice: '1' - }); - - await relayHub.addRelayWorkers([relayWorker], { - from: relayManager, - gasPrice: '1' - }); - // @ts-ignore - Object.keys(RelayHub.events).forEach(function (topic) { - // @ts-ignore - RelayHub.network.events[topic] = RelayHub.events[topic]; - }); - // @ts-ignore - Object.keys(RelayHub.events).forEach(function (topic) { - // @ts-ignore - Penalizer.network.events[topic] = RelayHub.events[topic]; - }); - - await relayHub.stakeForAddress(reporterRelayManager, 1000, { - value: ether('1'), - from: relayOwner - }); - }); - - describe('penalization access control (relay manager only)', function () { - before(async function () { - const env: Environment = await getTestingEnvironment(); - const receipt: any = await web3.eth.sendTransaction({ - from: thirdRelayWorker, - to: other, - value: ether('0.5'), - gasPrice: '1' - }); - const transactionHash = receipt.transactionHash; - - ({ - data: penalizableTxData, - signature: penalizableTxSignature - } = await getDataAndSignatureFromHash( - transactionHash, - env - )); - }); - - let penalizableTxData: string; - let penalizableTxSignature: string; - - it('penalizeRepeatedNonce', async function () { - await expectRevert( - penalizer.penalizeRepeatedNonce( - penalizableTxData, - penalizableTxSignature, - penalizableTxData, - penalizableTxSignature, - relayHub.address, - { from: other } - ), - 'Unknown relay manager' - ); - }); - }); - - describe('penalizable behaviors', function () { - describe('repeated relay nonce', function () { - beforeEach('staking for relay', async function () { - await relayHub.stakeForAddress(relayManager, 1000, { - value: stake, - from: relayOwner, - gasPrice: '1' - }); - }); - - it('penalizes transactions with same nonce and different data', async function () { - const env: Environment = await getTestingEnvironment(); - const chainId: number = env.chainId; - const relayRequest: RelayRequest = - await createRelayRequest(); - - const txDataSigA = getDataAndSignature( - relayRequest, - relayCallArgs, - chainId - ); - - relayRequest.request.data = '0xabcd'; - - const txDataSigB = getDataAndSignature( - relayRequest, - relayCallArgs, - chainId - ); - - const rskDifference: number = isRsk(env) ? 210000 : 0; - - await expectPenalization( - async (opts) => - await penalizer.penalizeRepeatedNonce( - txDataSigA.data, - txDataSigA.signature, - txDataSigB.data, - txDataSigB.signature, - relayHub.address, - opts - ), - rskDifference - ); - }); - - it('penalizes transactions with same nonce and different gas limit', async function () { - const env: Environment = await getTestingEnvironment(); - const chainId: number = env.chainId; - const relayRequest: RelayRequest = - await createRelayRequest(); - - const txDataSigA = getDataAndSignature( - relayRequest, - relayCallArgs, - chainId - ); - const txDataSigB = getDataAndSignature( - relayRequest, - Object.assign({}, relayCallArgs, { gasLimit: 100 }), - chainId - ); - - const rskDifference: number = isRsk(env) ? 185000 : 0; - - await expectPenalization( - async (opts) => - await penalizer.penalizeRepeatedNonce( - txDataSigA.data, - txDataSigA.signature, - txDataSigB.data, - txDataSigB.signature, - relayHub.address, - opts - ), - rskDifference - ); - }); - - it('penalizes transactions with same nonce and different value', async function () { - const env: Environment = await getTestingEnvironment(); - const chainId: number = env.chainId; - const relayRequest: RelayRequest = - await createRelayRequest(); - - const txDataSigA = getDataAndSignature( - relayRequest, - relayCallArgs, - chainId - ); - const txDataSigB = getDataAndSignature( - relayRequest, - Object.assign({}, relayCallArgs, { value: 100 }), - chainId - ); - - const rskDifference: number = isRsk(env) ? 185000 : 0; - - await expectPenalization( - async (opts) => - await penalizer.penalizeRepeatedNonce( - txDataSigA.data, - txDataSigA.signature, - txDataSigB.data, - txDataSigB.signature, - relayHub.address, - opts - ), - rskDifference - ); - }); - - it('does not penalize transactions with same nonce and data, value, gasLimit, destination', async function () { - const env: Environment = await getTestingEnvironment(); - const chainId: number = env.chainId; - const relayRequest: RelayRequest = - await createRelayRequest(); - - const txDataSigA = getDataAndSignature( - relayRequest, - relayCallArgs, - chainId - ); - const txDataSigB = getDataAndSignature( - relayRequest, - Object.assign({}, relayCallArgs, { gasPrice: 70 }), - chainId - ); // only gasPrice may be different - - await expectRevert( - penalizer.penalizeRepeatedNonce( - txDataSigA.data, - txDataSigA.signature, - txDataSigB.data, - txDataSigB.signature, - relayHub.address, - { from: reporterRelayManager } - ), - 'tx is equal' - ); - }); - - it('does not penalize transactions with different nonces', async function () { - const env: Environment = await getTestingEnvironment(); - const chainId: number = env.chainId; - const relayRequest: RelayRequest = - await createRelayRequest(); - - const txDataSigA = getDataAndSignature( - relayRequest, - relayCallArgs, - chainId - ); - const txDataSigB = getDataAndSignature( - relayRequest, - Object.assign({}, relayCallArgs, { nonce: 1 }), - chainId - ); - - await expectRevert( - penalizer.penalizeRepeatedNonce( - txDataSigA.data, - txDataSigA.signature, - txDataSigB.data, - txDataSigB.signature, - relayHub.address, - { from: reporterRelayManager } - ), - 'Different nonce' - ); - }); - - it('does not penalize transactions with same nonce from different relays', async function () { - const env: Environment = await getTestingEnvironment(); - const chainId: number = env.chainId; - const privateKey = - '0123456789012345678901234567890123456789012345678901234567890123'; - const relayRequest: RelayRequest = - await createRelayRequest(); - - const txDataSigA = getDataAndSignature( - relayRequest, - relayCallArgs, - chainId - ); - const txDataSigB = getDataAndSignature( - relayRequest, - Object.assign({}, relayCallArgs, { - privateKey: privateKey - }), - chainId - ); - - await expectRevert( - penalizer.penalizeRepeatedNonce( - txDataSigA.data, - txDataSigA.signature, - txDataSigB.data, - txDataSigB.signature, - relayHub.address, - { from: reporterRelayManager } - ), - 'Different signer' - ); - }); - - it('does not penalize with the same pair of transactions twice', async function () { - const env: Environment = await getTestingEnvironment(); - const chainId: number = env.chainId; - const relayRequest: RelayRequest = - await createRelayRequest(); - - const txDataSigA = getDataAndSignature( - relayRequest, - relayCallArgs, - chainId - ); - const txDataSigB = getDataAndSignature( - relayRequest, - Object.assign({}, relayCallArgs, { value: 100 }), - chainId - ); - - const rskDifference: number = isRsk(env) ? 185000 : 0; - - await expectPenalization( - async (opts) => - await penalizer.penalizeRepeatedNonce( - txDataSigA.data, - txDataSigA.signature, - txDataSigB.data, - txDataSigB.signature, - relayHub.address, - opts - ), - rskDifference - ); - - // stake relayer again to attempt to penalize again with the same set of transactions. It must fail. - await relayHub.stakeForAddress(relayManager, 1000, { - from: relayOwner, - value: ether('1'), - gasPrice: '1' - }); - - await expectRevert( - penalizer.penalizeRepeatedNonce( - txDataSigA.data, - txDataSigA.signature, - txDataSigB.data, - txDataSigB.signature, - relayHub.address, - { from: reporterRelayManager } - ), - 'Transactions already penalized' - ); - - // attempt to penalize with one of the previous transactions a new one. It must succeed - const txDataSigC = getDataAndSignature( - relayRequest, - Object.assign({}, relayCallArgs, { value: 200 }), - chainId - ); - - await expectPenalization( - async (opts) => - await penalizer.penalizeRepeatedNonce( - txDataSigA.data, - txDataSigA.signature, - txDataSigC.data, - txDataSigC.signature, - relayHub.address, - opts - ), - rskDifference - ); - }); - }); - }); - - function encodeRelayCallEIP155( - relayRequest: RelayRequest, - relayCallArgs: any - ): Transaction { - const privateKey = Buffer.from(relayCallArgs.privateKey, 'hex'); - const relayWorker = - '0x' + privateToAddress(privateKey).toString('hex'); - - relayRequest.relayData.feesReceiver = relayWorker; - - const encodedCall = relayHub.contract.methods - .relayCall(relayRequest, '0xabcdef123456') - .encodeABI(); - - const transaction = new Transaction( - { - nonce: relayCallArgs.nonce, - gasLimit: relayCallArgs.gasLimit, - gasPrice: relayCallArgs.gasPrice, - to: relayHub.address, - value: relayCallArgs.value, - data: encodedCall - }, - transactionOptions - ); - - transaction.sign(Buffer.from(relayCallArgs.privateKey, 'hex')); - return transaction; - } - - async function getDataAndSignatureFromHash( - txHash: string, - env: Environment - ): Promise<{ data: string; signature: string }> { - // @ts-ignore - const rpcTx = await web3.eth.getTransaction(txHash); - // eslint: this is stupid how many checks for 0 there are - // @ts-ignore - // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions - if (!env.chainId && parseInt(rpcTx.v, 16) > 28) { - throw new Error('Missing ChainID for EIP-155 signature'); - } - // @ts-ignore - // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions - if (env.chainId && !isRsk(env) && parseInt(rpcTx.v, 16) <= 28) { - throw new Error('Passed ChainID for non-EIP-155 signature'); - } - // @ts-ignore - const tx = new Transaction( - { - nonce: new BN(rpcTx.nonce), - gasPrice: new BN(rpcTx.gasPrice), - gasLimit: new BN(rpcTx.gas), - to: rpcTx.to, - value: new BN(rpcTx.value), - data: rpcTx.input, - // @ts-ignore - v: rpcTx.v, - // @ts-ignore - r: rpcTx.r, - // @ts-ignore - s: rpcTx.s - }, - transactionOptions - ); - - return getDataAndSignatureFromTx(tx, env.chainId); - } - - function getDataAndSignature( - relayRequest: RelayRequest, - relayCallArgs: any, - chainId: number - ): { data: string; signature: string } { - const tx = encodeRelayCallEIP155(relayRequest, relayCallArgs); - return getDataAndSignatureFromTx(tx, chainId); - } - - function getDataAndSignatureFromTx( - tx: Transaction, - chainId: number - ): { data: string; signature: string } { - const input = [ - tx.nonce, - tx.gasPrice, - tx.gasLimit, - tx.to, - tx.value, - tx.data - ]; - // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions - if (chainId) { - input.push( - toBuffer(chainId), - stripZeros(toBuffer(0)), - stripZeros(toBuffer(0)) - ); - } - let v = parseInt(tx.v.toString('hex'), 16); - if (v > 28) { - v -= chainId * 2 + 8; - } - const data = `0x${encode(input).toString('hex')}`; - const signature = `0x${ - '00'.repeat(32 - tx.r.length) + tx.r.toString('hex') - }${ - '00'.repeat(32 - tx.s.length) + tx.s.toString('hex') - }${v.toString(16)}`; - - return { - data, - signature - }; - } - - async function createRelayRequest(): Promise { - const smartWallet = await getSmartWalletAddress(); - const verifier = await TestVerifierEverythingAccepted.new(); - - const r: RelayRequest = cloneRelayRequest(relayRequest); - r.request.from = smartWallet.address; - r.request.relayHub = relayHub.address; - r.relayData.callForwarder = smartWallet.address; - r.relayData.callVerifier = verifier.address; - - return r; - } - - async function getSmartWalletAddress(): Promise { - const gaslessAccount: AccountKeypair = - await getGaslessAccount(); - - const smartWalletTemplate: SmartWalletInstance = - await SmartWallet.new(); - const factory: SmartWalletFactoryInstance = - await createSmartWalletFactory(smartWalletTemplate); - const smartWallet: SmartWalletInstance = - await createSmartWallet( - defaultAccount, - gaslessAccount.address, - factory, - gaslessAccount.privateKey, - env.chainId - ); - - return smartWallet; - } - }); - - // Receives a function that will penalize the relay and tests that call for a penalization, including checking the - // emitted event and penalization reward transfer. Returns the transaction receipt. - async function expectPenalization( - penalizeWithOpts: ( - opts: Truffle.TransactionDetails - ) => Promise, - rskDifference = 0 - ): Promise { - const reporterBalanceTracker = await balance.tracker( - reporterRelayManager - ); - const stakeBalanceTracker = await balance.tracker(relayHub.address); - const stakeInfo = await relayHub.stakes(relayManager); - // @ts-ignore (names) - const stake = stakeInfo.stake; - const expectedReward = stake.divn(2); - - // A gas price of zero makes checking the balance difference simpler - // RSK: Setting gasPrice to 1 since the RSKJ node doesn't support transactions with a gas price lower than 0.06 gwei - const receipt = await penalizeWithOpts({ - from: reporterRelayManager, - gasPrice: 1 - }); - - expectEvent.inLogs(receipt.logs, 'StakePenalized', { - relayManager: relayManager, - beneficiary: reporterRelayManager, - reward: expectedReward - }); - - const delta = await reporterBalanceTracker.delta(); - const halfStake = stake.divn(2); - const difference = halfStake.sub(delta); - - // The reporter gets half of the stake - // expect(delta).to.be.bignumber.aproximately(halfStake) - - // Since RSKJ doesn't support a transaction gas price below 0.06 gwei we need to change the assert - expect(difference).to.be.bignumber.at.most(new BN(rskDifference)); - - // The other half is burned, so RelayHub's balance is decreased by the full stake - expect(await stakeBalanceTracker.delta()).to.be.bignumber.equals( - stake.neg() - ); - - return receipt; - } - } -); diff --git a/test-to-migrate/RelayHubRegistrationsManagement.test.ts b/test-to-migrate/RelayHubRegistrationsManagement.test.ts deleted file mode 100644 index 6a756253..00000000 --- a/test-to-migrate/RelayHubRegistrationsManagement.test.ts +++ /dev/null @@ -1,173 +0,0 @@ -import { ether, expectEvent, expectRevert } from '@openzeppelin/test-helpers'; -import { RelayHubConfiguration } from '@rsksmart/rif-relay-contracts'; - -import { - PenalizerInstance, - RelayHubInstance -} from '@rsksmart/rif-relay-contracts/types/truffle-contracts'; -import { deployHub } from './TestUtils'; - -const Penalizer = artifacts.require('Penalizer'); - -contract( - 'RelayHub Relay Management', - function ([ - _, - relayOwner, - relayManager, - relayWorker1, - relayWorker2, - relayWorker3 - ]) { - const relayUrl = 'http://new-relay.com'; - - console.debug('Unknown', _); - - let relayHub: RelayHubInstance; - let penalizer: PenalizerInstance; - - const maxWorkerCount = 3; - const minimumEntryDepositValue = ether('1').toString(); - const minimumStake = ether('1').toString(); - const minimumUnstakeDelay = 50; - - const hubConfig: Partial = { - maxWorkerCount, - minimumEntryDepositValue, - minimumStake, - minimumUnstakeDelay - }; - - beforeEach(async function () { - penalizer = await Penalizer.new(); - relayHub = await deployHub(penalizer.address, hubConfig); - }); - - context('without stake for relayManager', function () { - it('should not allow relayManager to add relay workers', async function () { - await expectRevert( - relayHub.addRelayWorkers([relayWorker1], { - from: relayManager - }), - 'RelayManager not staked' - ); - }); - context('after stake unlocked for relayManager', function () { - beforeEach(async function () { - await relayHub.stakeForAddress(relayManager, 2000, { - value: ether('2'), - from: relayOwner - }); - await relayHub.addRelayWorkers([relayWorker1], { - from: relayManager - }); - await relayHub.unlockStake(relayManager, { - from: relayOwner - }); - }); - - it('should not allow relayManager to register a relay server', async function () { - await expectRevert( - relayHub.registerRelayServer(relayUrl, { - from: relayManager - }), - 'RelayManager not staked' - ); - }); - }); - }); - - context( - 'with stake for relayManager and no active workers added', - function () { - beforeEach(async function () { - await relayHub.stakeForAddress(relayManager, 2000, { - value: ether('2'), - from: relayOwner - }); - }); - - it('should not allow relayManager to register a relay server', async function () { - await expectRevert( - relayHub.registerRelayServer(relayUrl, { - from: relayManager - }), - 'no relay workers' - ); - }); - - it('should allow relayManager to add multiple workers', async function () { - const newRelayWorkers = [ - relayWorker1, - relayWorker2, - relayWorker3 - ]; - const { logs } = await relayHub.addRelayWorkers( - newRelayWorkers, - { - from: relayManager - } - ); - expectEvent.inLogs(logs, 'RelayWorkersAdded', { - relayManager, - newRelayWorkers, - workersCount: '3' - }); - }); - - it('should not allow relayManager to register already registered workers', async function () { - await relayHub.addRelayWorkers([relayWorker1], { - from: relayManager - }); - await expectRevert( - relayHub.addRelayWorkers([relayWorker1], { - from: relayManager - }), - 'this worker has a manager' - ); - }); - } - ); - - context( - 'with stake for relay manager and active relay workers', - function () { - beforeEach(async function () { - await relayHub.stakeForAddress(relayManager, 2000, { - value: ether('2'), - from: relayOwner - }); - await relayHub.addRelayWorkers([relayWorker1], { - from: relayManager - }); - }); - - it('should not allow relayManager to exceed allowed number of workers', async function () { - const newRelayWorkers = []; - for (let i = 0; i < 11; i++) { - newRelayWorkers.push(relayWorker1); - } - await expectRevert( - relayHub.addRelayWorkers(newRelayWorkers, { - from: relayManager - }), - 'too many workers' - ); - }); - - it('should allow relayManager to update transaction fee and url', async function () { - const { logs } = await relayHub.registerRelayServer( - relayUrl, - { - from: relayManager - } - ); - expectEvent.inLogs(logs, 'RelayServerRegistered', { - relayManager, - relayUrl - }); - }); - } - ); - } -); diff --git a/test-to-migrate/SampleRecipient.test.ts b/test-to-migrate/SampleRecipient.test.ts deleted file mode 100644 index 8e6b2970..00000000 --- a/test-to-migrate/SampleRecipient.test.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { - TestRecipientInstance, - SmartWalletInstance, - SmartWalletFactoryInstance -} from '@rsksmart/rif-relay-contracts/types/truffle-contracts'; -import { - createSmartWalletFactory, - createSmartWallet, - getTestingEnvironment, - getGaslessAccount -} from './TestUtils'; -import { AccountKeypair } from '@rsksmart/rif-relay-client'; - -const TestRecipient = artifacts.require('TestRecipient'); -const SmartWallet = artifacts.require('SmartWallet'); - -contract('SampleRecipient', function (accounts) { - const expectedRealSender = accounts[0]; - const message = 'hello world'; - let sample: TestRecipientInstance; - let gaslessAccount: AccountKeypair; - - before(async function () { - gaslessAccount = await getGaslessAccount(); - const env = await getTestingEnvironment(); - const chainId = env.chainId; - const sWalletTemplate: SmartWalletInstance = await SmartWallet.new(); - const factory: SmartWalletFactoryInstance = - await createSmartWalletFactory(sWalletTemplate); - await createSmartWallet( - accounts[0], - gaslessAccount.address, - factory, - gaslessAccount.privateKey, - chainId - ); - - sample = await TestRecipient.new(); - }); - - it('should emit message with msgSender and Origin', async function () { - const result = await sample.emitMessage(message); - const log = result.logs[0]; - const args = log.args; - assert.equal('SampleRecipientEmitted', log.event); - assert.equal(args.message, message); - assert.equal( - expectedRealSender, - args.msgSender, - 'In this test, msgSender is the caller, since there is no relay involved' - ); - assert.equal( - expectedRealSender, - args.origin, - 'tx.origin must be also the caller, since the test is not using a relayer' - ); - }); -}); diff --git a/test-to-migrate/StakeManagement.test.ts b/test-to-migrate/StakeManagement.test.ts deleted file mode 100644 index 523875a0..00000000 --- a/test-to-migrate/StakeManagement.test.ts +++ /dev/null @@ -1,403 +0,0 @@ -import { - balance, - ether, - expectEvent, - expectRevert -} from '@openzeppelin/test-helpers'; -import { expect } from 'chai'; -import BN from 'bn.js'; -import { evmMineMany, getTestingEnvironment } from './TestUtils'; -import { isRsk, constants } from '@rsksmart/rif-relay-common'; -import { - RelayHubInstance, - PenalizerInstance -} from '@rsksmart/rif-relay-contracts/types/truffle-contracts'; - -const RelayHub = artifacts.require('RelayHub'); -const Penalizer = artifacts.require('Penalizer'); - -contract( - 'StakeManagement', - function ([_, relayManager, _worker, anyRelayHub, owner, nonOwner]) { - console.debug('Unknown', _); - console.debug('Worker', _worker); - - const initialUnstakeDelay = new BN(4); - const initialStake = ether('1'); - - const maxWorkerCount = 1; - const minimumEntryDepositValue = ether('1'); - const minimumStake = ether('1'); - const minimumUnstakeDelay = 1; - - let relayHub: RelayHubInstance; - let penalizer: PenalizerInstance; - - function testCanStake(relayManager: string): void { - it('should allow owner to stake for unowned addresses', async function () { - const { logs } = await relayHub.stakeForAddress( - relayManager, - initialUnstakeDelay, - { - value: initialStake, - from: owner - } - ); - expectEvent.inLogs(logs, 'StakeAdded', { - relayManager, - owner, - stake: initialStake, - unstakeDelay: initialUnstakeDelay - }); - }); - - it('should NOT allow owner to stake for unowned addresses if minimum entry stake is not met', async function () { - await expectRevert( - relayHub.stakeForAddress( - relayManager, - initialUnstakeDelay, - { - value: initialStake.sub(ether('0.00000000000001')), // slighlty less than allowed - from: owner - } - ), - 'Insufficient initial stake' - ); - }); - } - - function testStakeNotValid(): void { - it('should report relayManager stake as not valid', async function () { - const isRelayManagerStaked = - await relayHub.isRelayManagerStaked(relayManager); - expect(isRelayManagerStaked).to.be.false; - }); - } - - describe('with no stake for relay server', function () { - beforeEach(async function () { - penalizer = await Penalizer.new(); - relayHub = await RelayHub.new( - penalizer.address, - maxWorkerCount, - minimumEntryDepositValue, - minimumUnstakeDelay, - minimumStake - ); - }); - - testStakeNotValid(); - - it('should not allow not owner to schedule unlock', async function () { - await expectRevert( - relayHub.unlockStake(nonOwner, { from: owner }), - 'not owner' - ); - }); - - it('relay managers cannot stake for themselves', async function () { - await expectRevert( - relayHub.stakeForAddress( - relayManager, - initialUnstakeDelay, - { - value: initialStake, - from: relayManager - } - ), - 'caller is the relayManager' - ); - }); - - testCanStake(relayManager); - }); - - describe('with stake deposited for relay server', function () { - beforeEach(async function () { - relayHub = await RelayHub.new( - constants.ZERO_ADDRESS, - maxWorkerCount, - minimumEntryDepositValue, - minimumUnstakeDelay, - minimumStake - ); - - await relayHub.stakeForAddress( - relayManager, - initialUnstakeDelay, - { - value: initialStake, - from: owner - } - ); - }); - - it('should not allow to penalize hub', async function () { - await expectRevert( - relayHub.penalize(relayManager, nonOwner, { - from: anyRelayHub - }), - 'Not penalizer' - ); - }); - - it("should allow querying relayManager's stake", async function () { - const { - stake: actualStake, - unstakeDelay: actualUnstakeDelay, - owner: actualOwner - } = (await relayHub.stakes(relayManager)) as any; - expect(actualOwner).to.equal(owner); - expect(actualStake).to.be.bignumber.equal(initialStake); - expect(actualUnstakeDelay).to.be.bignumber.equal( - initialUnstakeDelay - ); - }); - - testCanStake(nonOwner); - - it('should not allow one relayManager stake', async function () { - await expectRevert( - relayHub.stakeForAddress(nonOwner, initialUnstakeDelay, { - from: relayManager - }), - 'sender is a relayManager itself' - ); - }); - - it('owner can increase the relay stake', async function () { - const addedStake = ether('2'); - const stake = initialStake.add(addedStake); - const { logs } = await relayHub.stakeForAddress( - relayManager, - initialUnstakeDelay, - { - value: addedStake, - from: owner - } - ); - expectEvent.inLogs(logs, 'StakeAdded', { - relayManager, - stake, - unstakeDelay: initialUnstakeDelay - }); - - // @ts-ignore (typechain does not declare names or iterator for return types) - const { stake: actualStake } = await relayHub.stakes( - relayManager - ); - expect(actualStake).to.be.bignumber.equal( - initialStake.add(addedStake) - ); - }); - - it('should allow owner to increase the unstake delay', async function () { - const newUnstakeDelay = new BN(5); - const { logs } = await relayHub.stakeForAddress( - relayManager, - newUnstakeDelay, - { from: owner } - ); - expectEvent.inLogs(logs, 'StakeAdded', { - relayManager, - stake: initialStake, - unstakeDelay: newUnstakeDelay - }); - // @ts-ignore (typechain does not declare names or iterator for return types) - const { unstakeDelay: actualUnstakeDelay } = - await relayHub.stakes(relayManager); - expect(actualUnstakeDelay).to.be.bignumber.equal( - newUnstakeDelay - ); - }); - - it('should not allow owner to decrease the unstake delay', async function () { - await expectRevert( - relayHub.stakeForAddress( - relayManager, - initialUnstakeDelay.subn(1), - { from: owner } - ), - 'unstakeDelay cannot be decreased' - ); - }); - - it('not owner cannot stake for owned relayManager address', async function () { - await expectRevert( - relayHub.stakeForAddress( - relayManager, - initialUnstakeDelay, - { from: nonOwner } - ), - 'not owner' - ); - }); - - it('should not allow owner to withdraw stakes when not scheduled', async function () { - await expectRevert( - relayHub.withdrawStake(relayManager, { from: owner }), - 'Withdrawal is not scheduled' - ); - }); - - describe('should not allow not owner to call to', function () { - it('unlock stake', async function () { - await expectRevert( - relayHub.unlockStake(relayManager, { from: nonOwner }), - 'not owner' - ); - }); - it('withdraw stake', async function () { - await expectRevert( - relayHub.withdrawStake(relayManager, { - from: nonOwner - }), - 'not owner' - ); - }); - }); - }); - - describe('with authorized hub', function () { - beforeEach(async function () { - relayHub = await RelayHub.new( - constants.ZERO_ADDRESS, - maxWorkerCount, - minimumEntryDepositValue, - minimumUnstakeDelay, - minimumStake - ); - - await relayHub.stakeForAddress( - relayManager, - initialUnstakeDelay, - { - value: initialStake, - from: owner - } - ); - }); - - it('should report relayManager stake as valid for the authorized hub', async function () { - const isRelayManagerStaked = - await relayHub.isRelayManagerStaked(relayManager); - expect(isRelayManagerStaked).to.be.true; - }); - - describe('should report relayManager stake as not valid for', function () { - it('not staked relayManager', async function () { - const isRelayManagerStaked = - await relayHub.isRelayManagerStaked(nonOwner); - expect(isRelayManagerStaked).to.be.false; - }); - }); - - it('should allow owner to schedule stake unlock', async function () { - const { logs, receipt } = await relayHub.unlockStake( - relayManager, - { from: owner } - ); - const withdrawBlock = initialUnstakeDelay.addn( - receipt.blockNumber - ); - expectEvent.inLogs(logs, 'StakeUnlocked', { - relayManager, - owner, - withdrawBlock - }); - }); - }); - - describe('with unlock scheduled', function () { - beforeEach(async function () { - relayHub = await RelayHub.new( - constants.ZERO_ADDRESS, - maxWorkerCount, - minimumEntryDepositValue, - minimumUnstakeDelay, - minimumStake - ); - - await relayHub.stakeForAddress( - relayManager, - initialUnstakeDelay, - { - value: initialStake, - from: owner - } - ); - - await relayHub.unlockStake(relayManager, { from: owner }); - }); - - testStakeNotValid(); - - it('should not allow owner to schedule unlock again', async function () { - await expectRevert( - relayHub.unlockStake(relayManager, { from: owner }), - 'already pending' - ); - }); - - it('should not allow owner to withdraw stakes before it is due', async function () { - await expectRevert( - relayHub.withdrawStake(relayManager, { from: owner }), - 'Withdrawal is not due' - ); - }); - - it('should allow to withdraw stake after unstakeDelay', async function () { - const env = await getTestingEnvironment(); - - await evmMineMany(initialUnstakeDelay.toNumber()); - const relayOwnerBalanceTracker = await balance.tracker(owner); - const stakeBalanceTracker = await balance.tracker( - relayHub.address - ); - - // We call unstake with a gasPrice of zero to accurately measure the balance change in the relayOwner. - // RSK doesn't support using a gasPrice lower than block's minimum, using 1 instead of 0 here. - const { logs } = await relayHub.withdrawStake(relayManager, { - from: owner, - gasPrice: 1 - }); - expectEvent.inLogs(logs, 'StakeWithdrawn', { - relayManager, - amount: initialStake - }); - - const relayOwnerGain = await relayOwnerBalanceTracker.delta(); - const stakeLoss = await stakeBalanceTracker.delta(); - - const rskDifference: number = isRsk(env) ? 30000 : 0; - const difference = relayOwnerGain.sub(initialStake); - - // expect(relayOwnerGain).to.be.bignumber.equal(initialStake) - expect(difference).to.be.bignumber.at.most( - new BN(rskDifference) - ); - expect(stakeLoss).to.be.bignumber.equal(initialStake.neg()); - }); - - describe('with stake withdrawn', function () { - beforeEach(async function () { - await evmMineMany(initialUnstakeDelay.toNumber()); - await relayHub.withdrawStake(relayManager, { from: owner }); - }); - - it('should have no memory of removed relayManager', async function () { - const { - stake: actualStake, - unstakeDelay: actualUnstakeDelay, - owner: actualOwner - } = (await relayHub.stakes(relayManager)) as any; - expect(actualOwner).to.equal(constants.ZERO_ADDRESS); - expect(actualStake).to.be.bignumber.equal(new BN(0)); - expect(actualUnstakeDelay).to.be.bignumber.equal(new BN(0)); - }); - - testCanStake(nonOwner); - }); - }); - } -); diff --git a/test-to-migrate/TestEnvironment.test.ts b/test-to-migrate/TestEnvironment.test.ts deleted file mode 100644 index d8e1be47..00000000 --- a/test-to-migrate/TestEnvironment.test.ts +++ /dev/null @@ -1,206 +0,0 @@ -import { TestEnvironment, TestEnvironmentInfo } from './TestEnvironment'; -import { HttpProvider } from 'web3-core'; -import { expectEvent } from '@openzeppelin/test-helpers'; -import { - TestRecipientInstance, - SmartWalletFactoryInstance, - TestTokenInstance -} from '@rsksmart/rif-relay-contracts/types/truffle-contracts'; -import { getTestingEnvironment, getGaslessAccount } from './TestUtils'; -import { constants } from '@rsksmart/rif-relay-common'; -import { toHex } from 'web3-utils'; - -const TestRecipient = artifacts.require('TestRecipient'); -const SmartWalletFactory = artifacts.require('SmartWalletFactory'); -const DeployVerifier = artifacts.require('DeployVerifier'); -const RelayVerifier = artifacts.require('RelayVerifier'); -const TestToken = artifacts.require('TestToken'); - -contract('TestEnvironment', function (accounts) { - describe('#start()', function () { - it('should create a valid test environment for other tests to rely on', async function () { - const host = (web3.currentProvider as HttpProvider).host; - const testEnv = await TestEnvironment.start( - host, - 0.6e18, - await getTestingEnvironment() - ); - assert.equal(testEnv.deploymentResult.relayHubAddress.length, 42); - }); - - after(async function () { - await TestEnvironment.stop(); - }); - }); - - describe('using RelayClient', () => { - let testEnvironment: TestEnvironmentInfo; - let tToken: TestTokenInstance; - before(async () => { - const host = - (web3.currentProvider as HttpProvider).host ?? 'localhost'; - tToken = await TestToken.new(); - testEnvironment = await TestEnvironment.start( - host, - 0.6e18, - await getTestingEnvironment() - ); - const dVerifier = await DeployVerifier.at( - testEnvironment.deploymentResult.deployVerifierAddress - ); - await dVerifier.acceptToken(tToken.address, { from: accounts[0] }); - const rVerifier = await RelayVerifier.at( - testEnvironment.deploymentResult.relayVerifierAddress - ); - await rVerifier.acceptToken(tToken.address, { from: accounts[0] }); - }); - - after(async () => { - await TestEnvironment.stop(); - }); - - it('should relay using relayTransaction', async () => { - const sender = await getGaslessAccount(); - const smartWalletFactory: SmartWalletFactoryInstance = - await SmartWalletFactory.at( - testEnvironment.deploymentResult.smartWalletFactoryAddress - ); - const sr: TestRecipientInstance = await TestRecipient.new(); - - testEnvironment.relayProvider.relayClient.accountManager.addAccount( - sender - ); - - await testEnvironment.relayProvider.deploySmartWallet({ - from: sender.address, - to: constants.ZERO_ADDRESS, - value: '0', - // gas: toHex('400000'), - data: '0x', - tokenContract: tToken.address, - tokenAmount: '0', - tokenGas: '0', - recoverer: constants.ZERO_ADDRESS, - index: '0', - callForwarder: - testEnvironment.deploymentResult.smartWalletFactoryAddress, - callVerifier: - testEnvironment.deploymentResult.deployVerifierAddress, - clientId: '1' - }); - - const wallet = await smartWalletFactory.getSmartWalletAddress( - sender.address, - constants.ZERO_ADDRESS, - '0' - ); - const ret = - await testEnvironment.relayProvider.relayClient.relayTransaction( - { - from: sender.address, - to: sr.address, - callForwarder: wallet, - callVerifier: - testEnvironment.deploymentResult - .relayVerifierAddress, - // gas: '0x' + 1e6.toString(16), - data: sr.contract.methods - .emitMessage('hello') - .encodeABI(), - tokenAmount: '0x00', - tokenGas: '0', - tokenContract: tToken.address, - isSmartWalletDeploy: false, - clientId: '1', - relayHub: - testEnvironment.deploymentResult.relayHubAddress - } - ); - - assert.deepEqual( - [...ret.relayingErrors.values(), ...ret.pingErrors.values()], - [] - ); - const events = await sr.contract.getPastEvents(); - assert.equal(events[0].event, 'SampleRecipientEmitted'); - assert.equal( - events[0].returnValues.msgSender.toLowerCase(), - wallet.toLowerCase() - ); - }); - }); - - describe('using RelayProvider', () => { - let testEnvironment: TestEnvironmentInfo; - let tToken: TestTokenInstance; - before(async function () { - const host = - (web3.currentProvider as HttpProvider).host ?? 'localhost'; - testEnvironment = await TestEnvironment.start( - host, - 0.6e18, - await getTestingEnvironment() - ); - tToken = await TestToken.new(); - const dVerifier = await DeployVerifier.at( - testEnvironment.deploymentResult.deployVerifierAddress - ); - await dVerifier.acceptToken(tToken.address, { from: accounts[0] }); - const rVerifier = await RelayVerifier.at( - testEnvironment.deploymentResult.relayVerifierAddress - ); - await rVerifier.acceptToken(tToken.address, { from: accounts[0] }); - }); - - after(async () => { - await TestEnvironment.stop(); - }); - - it('should send relayed transaction through RelayProvider', async () => { - const sender = await getGaslessAccount(); - const smartWalletFactory: SmartWalletFactoryInstance = - await SmartWalletFactory.at( - testEnvironment.deploymentResult.smartWalletFactoryAddress - ); - testEnvironment.relayProvider.addAccount(sender); - - const sr: TestRecipientInstance = await TestRecipient.new(); - await testEnvironment.relayProvider.deploySmartWallet({ - from: sender.address, - to: constants.ZERO_ADDRESS, - value: '0', - gas: toHex('400000'), - data: '0x', - tokenContract: tToken.address, - tokenAmount: '0', - tokenGas: '0', - recoverer: constants.ZERO_ADDRESS, - index: '0', - isSmartWalletDeploy: true, - callForwarder: smartWalletFactory.address, - callVerifier: - testEnvironment.deploymentResult.deployVerifierAddress, - clientId: '1' - }); - - const wallet = await smartWalletFactory.getSmartWalletAddress( - sender.address, - constants.ZERO_ADDRESS, - '0' - ); - - // @ts-ignore - TestRecipient.web3.setProvider(testEnvironment.relayProvider); - - const txDetails = { - from: sender.address, - callVerifier: - testEnvironment.deploymentResult.relayVerifierAddress, - callForwarder: wallet, - tokenContract: tToken.address - }; - const ret = await sr.emitMessage('hello', txDetails); - expectEvent(ret, 'SampleRecipientEmitted', { msgSender: wallet }); - }); - }); -}); diff --git a/test-to-migrate/TestEnvironment.ts b/test-to-migrate/TestEnvironment.ts deleted file mode 100644 index 91a28cca..00000000 --- a/test-to-migrate/TestEnvironment.ts +++ /dev/null @@ -1,213 +0,0 @@ -import net from 'net'; -import { ether } from '@openzeppelin/test-helpers'; -import TestSetup, { DeploymentResult } from './TestSetup'; -import { - KeyManager, - TxStoreManager, - RelayServer, - HttpServer, - ServerConfigParams -} from '@rsksmart/rif-relay-server'; -import { configure, RelayProvider } from '@rsksmart/rif-relay-client'; -import { getNetworkUrl, supportedNetworks } from './Utils'; -import Web3 from 'web3'; -import { - ContractInteractor, - Environment, - defaultEnvironment -} from '@rsksmart/rif-relay-common'; - -export interface TestEnvironmentInfo { - deploymentResult: DeploymentResult; - relayProvider: RelayProvider; - httpServer: HttpServer; - relayUrl: string; -} - -class TestEnvironmentClass { - private httpServer?: HttpServer; - - /** - * - * @param host: - * @param debug - * @return - */ - async start( - host?: string, - workerTargetBalance = 0.003e18, - environment = defaultEnvironment - ): Promise { - await this.stop(); - const _host: string = getNetworkUrl(host); - console.log('_host=', _host); - if (_host == null) { - // eslint-disable-next-line @typescript-eslint/restrict-template-expressions - throw new Error( - `start: expected network (${supportedNetworks().join( - '|' - )}) or url` - ); - } - const commandsLogic = new TestSetup( - _host, - configure({ chainId: environment.chainId }) - ); - const from = await commandsLogic.findWealthyAccount(); - const deploymentResult = await commandsLogic.deployContracts({ - from, - gasPrice: '1', - skipConfirmation: true, - relayHubConfiguration: environment.relayHubConfiguration - }); - - const port = await this._resolveAvailablePort(); - const relayUrl = 'http://127.0.0.1:' + port.toString(); - - await this._runServer( - _host, - deploymentResult, - from, - relayUrl, - port, - workerTargetBalance, - environment - ); - if (this.httpServer == null) { - throw new Error('Failed to run a local Relay Server'); - } - - const registerOptions = { - from, - stake: ether('1'), - funds: ether('1'), - relayUrl: relayUrl, - gasPrice: '1e9', - unstakeDelay: '2000' - }; - const registrationResult = await commandsLogic.registerRelay( - registerOptions - ); - if (registrationResult.success) { - console.log( - 'In-process relay successfully registered:', - JSON.stringify(registrationResult) - ); - } else { - // eslint-disable-next-line @typescript-eslint/restrict-template-expressions - throw new Error( - `Failed to fund relay: ${ - registrationResult.error - } : ${registrationResult?.transactions?.toString()}` - ); - } - - await commandsLogic.waitForRelay(relayUrl); - - const config = configure({ - relayHubAddress: deploymentResult.relayHubAddress, - relayVerifierAddress: deploymentResult.relayVerifierAddress, - deployVerifierAddress: deploymentResult.deployVerifierAddress, - preferredRelays: [relayUrl], - chainId: environment.chainId - }); - - const relayProvider = new RelayProvider( - new Web3.providers.HttpProvider(_host), - config - ); - console.error('== start: ready.'); - return { - deploymentResult, - relayProvider, - relayUrl, - httpServer: this.httpServer - }; - } - - /** - * initialize a local relay - * @private - */ - - private async _resolveAvailablePort(): Promise { - const server = net.createServer(); - await new Promise((resolve) => { - // @ts-ignore - server.listen(0, resolve); - }); - const address = server.address(); - if (address == null || typeof address === 'string') { - throw new Error('Could not find available port'); - } - const relayListenPort = address.port; - server.close(); - return relayListenPort; - } - - async stop(): Promise { - if (this.httpServer !== undefined) { - this.httpServer.stop(); - this.httpServer.close(); - await this.httpServer.backend.transactionManager.txStoreManager.clearAll(); - this.httpServer = undefined; - } - } - - async _runServer( - host: string, - deploymentResult: DeploymentResult, - from: string, - relayUrl: string, - port: number, - workerTargetBalance: number, - environment: Environment = defaultEnvironment - ): Promise { - if (this.httpServer !== undefined) { - return; - } - - const managerKeyManager = new KeyManager(1); - const workersKeyManager = new KeyManager(1); - const txStoreManager = new TxStoreManager({ inMemory: true }); - const contractInteractor = new ContractInteractor( - new Web3.providers.HttpProvider(host), - configure({ - relayHubAddress: deploymentResult.relayHubAddress, - chainId: environment.chainId, - relayVerifierAddress: deploymentResult.relayVerifierAddress, - deployVerifierAddress: deploymentResult.deployVerifierAddress - }) - ); - await contractInteractor.init(); - const relayServerDependencies = { - contractInteractor, - txStoreManager, - managerKeyManager, - workersKeyManager - }; - const relayServerParams: Partial = { - devMode: true, - url: relayUrl, - relayHubAddress: deploymentResult.relayHubAddress, - gasPriceFactor: 1, - logLevel: 1, - checkInterval: 10, - // refreshStateTimeoutBlocks:1, - relayVerifierAddress: deploymentResult.relayVerifierAddress, - deployVerifierAddress: deploymentResult.deployVerifierAddress, - workerTargetBalance - }; - - const relayServer = new RelayServer( - relayServerParams, - relayServerDependencies - ); - await relayServer.init(); - - this.httpServer = new HttpServer(port, relayServer); - this.httpServer.start(); - } -} - -export const TestEnvironment = new TestEnvironmentClass(); diff --git a/test-to-migrate/TestSetup.ts b/test-to-migrate/TestSetup.ts deleted file mode 100644 index 6e03a74e..00000000 --- a/test-to-migrate/TestSetup.ts +++ /dev/null @@ -1,511 +0,0 @@ -// @ts-ignore -import io from 'console-read-write'; -import BN from 'bn.js'; -import HDWalletProvider from '@truffle/hdwallet-provider'; -import Web3 from 'web3'; -import { Contract, SendOptions } from 'web3-eth-contract'; -import { HttpProvider, TransactionReceipt } from 'web3-core'; -import { fromWei, toBN } from 'web3-utils'; -import { merge } from 'lodash'; - -// compiled folder populated by "prepublish" -import { - RelayHub, - Penalizer, - DeployVerifier, - RelayVerifier, - CustomSmartWalletDeployVerifier, - SmartWallet, - SmartWalletFactory, - CustomSmartWallet, - CustomSmartWalletFactory, - VersionRegistry -} from '@rsksmart/rif-relay-contracts'; - -import { - ContractInteractor, - EnvelopingConfig, - isSameAddress, - sleep, - string32, - constants -} from '@rsksmart/rif-relay-common'; -import { RelayHubConfiguration } from '@rsksmart/rif-relay-contracts'; -import { HttpClient, HttpWrapper } from '@rsksmart/rif-relay-client'; -import { ether } from '@openzeppelin/test-helpers'; - -interface RegisterOptions { - from: string; - gasPrice: string | BN; - stake: string | BN; - funds: string | BN; - relayUrl: string; - unstakeDelay: string; -} - -interface DeployOptions { - from: string; - gasPrice: string; - deployVerifierAddress?: string; - relayVerifierAddress?: string; - smartWalletFactoryAddress?: string; - smartWalletTemplateAddress?: string; - relayHubAddress?: string; - penalizerAddress?: string; - registryAddress?: string; - customSmartWalletFactoryAddress?: string; - customSmartWalletTemplateAddress?: string; - customSmartWalletDeployVerifierAddress?: string; - customSmartWalletRelayVerifierAddress?: string; - registryHubId?: string; - verbose?: boolean; - skipConfirmation?: boolean; - relayHubConfiguration: RelayHubConfiguration; -} - -export interface DeploymentResult { - relayHubAddress: string; - penalizerAddress: string; - smartWalletTemplateAddress: string; - smartWalletFactoryAddress: string; - versionRegistryAddress: string; - relayVerifierAddress: string; - deployVerifierAddress: string; - customSmartWalletTemplateAddress: string; - customSmartWalletFactoryAddress: string; - customSmartWalletDeployVerifierAddress: string; - customSmartWalletRelayVerifierAddress: string; -} - -interface RegistrationResult { - success: boolean; - transactions?: string[]; - error?: string; -} - -export default class TestSetup { - private readonly contractInteractor: ContractInteractor; - private readonly httpClient: HttpClient; - private readonly config: EnvelopingConfig; - private readonly web3: Web3; - - constructor(host: string, config: EnvelopingConfig, mnemonic?: string) { - let provider: HttpProvider | HDWalletProvider = - new Web3.providers.HttpProvider(host); - if (mnemonic != null) { - // web3 defines provider type quite narrowly - provider = new HDWalletProvider( - mnemonic, - provider - ) as unknown as HttpProvider; - } - this.httpClient = new HttpClient(new HttpWrapper(), config); - this.contractInteractor = new ContractInteractor(provider, config); - this.config = config; - this.web3 = new Web3(provider); - } - - async findWealthyAccount(requiredBalance = ether('2')): Promise { - let accounts: string[] = []; - try { - accounts = await this.web3.eth.getAccounts(); - for (const account of accounts) { - const balance = new BN(await this.web3.eth.getBalance(account)); - if (balance.gte(requiredBalance)) { - console.log(`Found funded account ${account}`); - return account; - } - } - } catch (error) { - console.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 - ' - )}` - ); - } - - async isRelayReady(relayUrl: string): Promise { - const response = await this.httpClient.getPingResponse(relayUrl); - return response.ready; - } - - async waitForRelay(relayUrl: string, timeout = 60): Promise { - console.error(`Will wait up to ${timeout}s for the relay to be ready`); - - const endTime = Date.now() + timeout * 1000; - while (Date.now() < endTime) { - let isReady = false; - try { - isReady = await this.isRelayReady(relayUrl); - } catch (e) { - console.log(e.message); - } - if (isReady) { - return; - } - await sleep(3000); - } - throw Error(`Relay not ready after ${timeout}s`); - } - - async registerRelay(options: RegisterOptions): Promise { - const transactions: string[] = []; - try { - console.log( - `Registering Enveloping relayer at ${options.relayUrl}` - ); - - const response = await this.httpClient - .getPingResponse(options.relayUrl) - .catch(() => { - throw new Error( - 'could contact not relayer, is it running?' - ); - }); - if (response.ready) { - return { - success: false, - error: 'Already registered' - }; - } - - if (!this.contractInteractor.isInitialized()) { - await this.contractInteractor.init(); - } - - const chainId = this.contractInteractor.chainId; - if (response.chainId !== chainId.toString()) { - throw new Error( - `wrong chain-id: Relayer on (${response.chainId}) but our provider is on (${chainId})` - ); - } - - const relayAddress = response.relayManagerAddress; - const relayHubAddress = - this.config.relayHubAddress ?? response.relayHubAddress; - const relayHub = await this.contractInteractor._createRelayHub( - relayHubAddress - ); - const { stake, unstakeDelay, owner } = await relayHub.getStakeInfo( - relayAddress - ); - - console.log('Current stake info:'); - console.log('Relayer owner: ' + owner); - console.log('Current unstake delay: ' + unstakeDelay); - console.log('current stake=', fromWei(stake, 'ether')); - - if ( - owner !== constants.ZERO_ADDRESS && - !isSameAddress(owner, options.from) - ) { - throw new Error( - `Already owned by ${owner}, our account=${options.from}` - ); - } - - if ( - toBN(unstakeDelay).gte(toBN(options.unstakeDelay)) && - toBN(stake).gte(toBN(options.stake.toString())) - ) { - console.log('Relayer already staked'); - } else { - const stakeValue = toBN(options.stake.toString()).sub( - toBN(stake) - ); - console.log( - `Staking relayer ${fromWei(stakeValue, 'ether')} RBTC`, - stake === '0' - ? '' - : ` (already has ${fromWei(stake, 'ether')} RBTC)` - ); - - const stakeTx = await relayHub.stakeForAddress( - relayAddress, - options.unstakeDelay.toString(), - { - value: stakeValue, - from: options.from, - gas: 1e6, - gasPrice: options.gasPrice - } - ); - transactions.push(stakeTx.tx); - } - - if (isSameAddress(owner, options.from)) { - console.log('Relayer already authorized'); - } - - const bal = await this.contractInteractor.getBalance(relayAddress); - if (toBN(bal).gt(toBN(options.funds.toString()))) { - console.log('Relayer already funded'); - } else { - console.log('Funding relayer'); - - const _fundTx = await this.web3.eth.sendTransaction({ - from: options.from, - to: relayAddress, - value: options.funds, - gas: 1e6, - gasPrice: options.gasPrice - }); - const fundTx = _fundTx as TransactionReceipt; - if (fundTx.transactionHash == null) { - return { - success: false, - error: `Fund transaction reverted: ${JSON.stringify( - _fundTx - )}` - }; - } - transactions.push(fundTx.transactionHash); - } - - await this.waitForRelay(options.relayUrl); - return { - success: true, - transactions - }; - } catch (error) { - return { - success: false, - transactions, - // eslint-disable-next-line @typescript-eslint/restrict-template-expressions - error: `Failed to fund relay: '${error}'` - }; - } - } - - contract(file: any, address?: string): Contract { - return new this.web3.eth.Contract(file.abi, address, { - data: file.bytecode - }); - } - - async deployContracts( - deployOptions: DeployOptions - ): Promise { - const options: Required = { - from: deployOptions.from, - gas: 0, // gas limit will be filled in at deployment - value: 0, - gasPrice: deployOptions.gasPrice ?? (1e9).toString() - }; - - const penalizer = await this.getContract( - Penalizer, - {}, - deployOptions.penalizerAddress, - Object.assign({}, options), - deployOptions.skipConfirmation - ); - - const smartWallet = await this.getContract( - SmartWallet, - {}, - deployOptions.smartWalletTemplateAddress, - Object.assign({}, options), - deployOptions.skipConfirmation - ); - const smartWalletFactory = await this.getContract( - SmartWalletFactory, - { - arguments: [smartWallet.options.address] - }, - deployOptions.smartWalletFactoryAddress, - Object.assign({}, options), - deployOptions.skipConfirmation - ); - - const customSmartWallet = await this.getContract( - CustomSmartWallet, - {}, - deployOptions.customSmartWalletTemplateAddress, - Object.assign({}, options), - deployOptions.skipConfirmation - ); - const customSmartWalletFactory = await this.getContract( - CustomSmartWalletFactory, - { - arguments: [customSmartWallet.options.address] - }, - deployOptions.customSmartWalletFactoryAddress, - Object.assign({}, options), - deployOptions.skipConfirmation - ); - - const rInstance = await this.getContract( - RelayHub, - { - arguments: [ - penalizer.options.address, - deployOptions.relayHubConfiguration.maxWorkerCount, - deployOptions.relayHubConfiguration - .minimumEntryDepositValue, - deployOptions.relayHubConfiguration.minimumUnstakeDelay, - deployOptions.relayHubConfiguration.minimumStake - ] - }, - deployOptions.relayHubAddress, - merge({}, options, { gas: 5e6 }), - deployOptions.skipConfirmation - ); - - const regInstance = await this.getContract( - VersionRegistry, - {}, - deployOptions.registryAddress, - Object.assign({}, options), - deployOptions.skipConfirmation - ); - if (deployOptions.registryHubId != null) { - await regInstance.methods - .addVersion( - string32(deployOptions.registryHubId), - string32('1'), - rInstance.options.address - ) - .send({ from: deployOptions.from }); - console.log( - `== Saved RelayHub address at HubId:"${deployOptions.registryHubId}" to VersionRegistry` - ); - } - - const deployVerifierInstance = await this.getContract( - DeployVerifier, - { - arguments: [smartWalletFactory.options.address] - }, - deployOptions.deployVerifierAddress, - Object.assign({}, options), - deployOptions.skipConfirmation - ); - - const customSmartWalletDeployVerifierInstance = await this.getContract( - CustomSmartWalletDeployVerifier, - { - arguments: [customSmartWalletFactory.options.address] - }, - deployOptions.customSmartWalletDeployVerifierAddress, - Object.assign({}, options), - deployOptions.skipConfirmation - ); - - const relayVerifierInstance = await this.getContract( - RelayVerifier, - { - arguments: [smartWalletFactory.options.address] - }, - deployOptions.relayVerifierAddress, - Object.assign({}, options), - deployOptions.skipConfirmation - ); - - const customSmartWalletRelayVerifierInstance = await this.getContract( - RelayVerifier, - { - arguments: [customSmartWalletFactory.options.address] - }, - deployOptions.customSmartWalletRelayVerifierAddress, - Object.assign({}, options), - deployOptions.skipConfirmation - ); - - // Overriding saved configuration with newly deployed instances - this.config.relayHubAddress = rInstance.options.address; - this.config.deployVerifierAddress = - deployVerifierInstance.options.address; - this.config.relayVerifierAddress = - relayVerifierInstance.options.address; - // this.config.customSmartWalletDeployVerifierAddress = customSmartWalletDeployVerifierInstance.options.address - // this.config.customSmartWalletRelayVerifierAddress = customSmartWalletRelayVerifierInstance.options.address - - return { - relayHubAddress: rInstance.options.address, - penalizerAddress: penalizer.options.address, - smartWalletTemplateAddress: smartWallet.options.address, - smartWalletFactoryAddress: smartWalletFactory.options.address, - versionRegistryAddress: regInstance.options.address, - relayVerifierAddress: relayVerifierInstance.options.address, - deployVerifierAddress: deployVerifierInstance.options.address, - customSmartWalletTemplateAddress: customSmartWallet.options.address, - customSmartWalletFactoryAddress: - customSmartWalletFactory.options.address, - customSmartWalletDeployVerifierAddress: - customSmartWalletDeployVerifierInstance.options.address, - customSmartWalletRelayVerifierAddress: - customSmartWalletRelayVerifierInstance.options.address - }; - } - - private async getContract( - json: any, - constructorArgs: any, - address: string | undefined, - options: Required, - skipConfirmation = false - ): Promise { - const contractName: string = json.contractName; - let contract; - if (address == null) { - const sendMethod = this.contract(json).deploy(constructorArgs); - options.gas = await sendMethod.estimateGas(); - const maxCost = new BN(options.gasPrice).muln(options.gas); - const oneRBTC = ether('1'); - console.log( - `Deploying ${contractName} contract with gas limit of ${options.gas.toLocaleString()} and maximum cost of ~ ${ - maxCost.toNumber() / parseFloat(oneRBTC.toString()) - } RBTC` - ); - if (!skipConfirmation) { - await this.confirm(); - } - const deployPromise = sendMethod.send(merge(options, { gas: 5e6 })); - // eslint-disable-next-line @typescript-eslint/no-floating-promises - deployPromise.on('transactionHash', function (hash) { - console.log(`Transaction broadcast: ${hash}`); - }); - contract = await deployPromise; - console.log( - `Deployed ${contractName} at address ${contract.options.address}\n\n` - ); - } else { - console.log( - `Using ${contractName} at given address ${address}\n\n` - ); - contract = this.contract(json, address); - } - return contract; - } - - async deployVerifier( - options: Required, - skipConfirmation: boolean | undefined - ): Promise { - const verifier = await this.getContract( - DeployVerifier, - {}, - undefined, - Object.assign({}, options), - skipConfirmation - ); - return verifier; - } - - async confirm(): Promise { - let input; - const running = true; - while (running) { - console.log('Confirm (yes/no)?'); - input = await io.read(); - if (input === 'yes') { - return; - } else if (input === 'no') { - throw new Error('User rejected'); - } - } - } -} diff --git a/test-to-migrate/TestUtils.ts b/test-to-migrate/TestUtils.ts deleted file mode 100644 index dd9cd7c2..00000000 --- a/test-to-migrate/TestUtils.ts +++ /dev/null @@ -1,704 +0,0 @@ -/* eslint-disable @typescript-eslint/strict-boolean-expressions */ -import childProcess, { ChildProcessWithoutNullStreams } from 'child_process'; -import fs from 'fs'; -import path from 'path'; -import { ether } from '@openzeppelin/test-helpers'; -import { - RelayHubInstance, - SmartWalletFactoryInstance, - CustomSmartWalletFactoryInstance, - IForwarderInstance, - SmartWalletInstance, - CustomSmartWalletInstance, - TestRecipientInstance -} from '@rsksmart/rif-relay-contracts/types/truffle-contracts'; -import { - HttpWrapper, - HttpClient, - configure, - AccountKeypair -} from '@rsksmart/rif-relay-client'; -import { PrefixedHexString } from 'ethereumjs-tx'; -import { soliditySha3Raw } from 'web3-utils'; -import { HttpProvider } from 'web3-core'; -// @ts-ignore -import { TypedDataUtils, signTypedData_v4 } from 'eth-sig-util'; -import { BN, bufferToHex, toBuffer, privateToAddress } from 'ethereumjs-util'; -// @ts-ignore -import ethWallet from 'ethereumjs-wallet'; -import { - constants, - defaultEnvironment, - Environment, - environments, - getLocalEip712Signature, - sleep -} from '@rsksmart/rif-relay-common'; -import { - DeployRequest, - RelayRequest, - RelayHubConfiguration, - TypedRequestData, - TypedDeployRequestData, - DeployRequestDataType -} from '@rsksmart/rif-relay-contracts'; - -//@ts-ignore -import sourceMapSupport from 'source-map-support'; -import { RIF_RELAY_URL } from './Utils'; -//@ts-ignore -sourceMapSupport.install({ errorFormatterForce: true }); - -const RelayHub = artifacts.require('RelayHub'); - -const DEPLOY_PARAMS = - 'address relayHub,address from,address to,address tokenContract,address recoverer,uint256 value,uint256 nonce,uint256 tokenAmount,uint256 tokenGas,uint256 index,bytes data'; - -const RequestType = { - typeName: 'RelayRequest', - typeSuffix: - 'RelayData relayData)RelayData(uint256 gasPrice,address relayWorker,address callForwarder,address callVerifier)' -}; - -const localhostOne = RIF_RELAY_URL; -export const deployTypeName = `${RequestType.typeName}(${DEPLOY_PARAMS},${RequestType.typeSuffix}`; -export const deployTypeHash = web3.utils.keccak256(deployTypeName); - -// start a background relay process. -// rhub - relay hub contract -// options: -// stake, delay, url, relayOwner: parameters to pass to registerNewRelay, to stake and register it. -// - -export interface RelayServerData { - proc: ChildProcessWithoutNullStreams; - worker: string; - manager: string; -} - -export async function startRelay( - relayHub: RelayHubInstance, - options: any -): Promise { - const args = []; - - const serverWorkDir = '/tmp/enveloping/test/server'; - try { - /* - * It raises an error if the folder doesn't exist on macos. - * If we decide to drop the support for node version minor than v14.14.0 - * we could use [fs.rmDir](https://nodejs.org/docs/latest-v16.x/api/fs.html#fsrmsyncpath-options). - */ - fs.rmdirSync(serverWorkDir, { recursive: true }); - } catch (error) { - console.log( - `startRelay: deletion of ${serverWorkDir} failed. Folder not found` - ); - } - args.push('--workdir', serverWorkDir); - args.push('--devMode', true); - args.push('--checkInterval', 10); - args.push('--logLevel', 5); - args.push('--relayHubAddress', relayHub.address); - const configFile = path.resolve(__dirname, './server-config.json'); - args.push('--config', configFile); - if (options.rskNodeUrl) { - args.push('--rskNodeUrl', options.rskNodeUrl); - } - if (options.gasPriceFactor) { - args.push('--gasPriceFactor', options.gasPriceFactor); - } - if (options.checkInterval) { - args.push('--checkInterval', options.checkInterval); - } - - if (options.deployVerifierAddress) { - args.push('--deployVerifierAddress', options.deployVerifierAddress); - } - if (options.relayVerifierAddress) { - args.push('--relayVerifierAddress', options.relayVerifierAddress); - } - - if (options.trustedVerifiers) { - args.push('--trustedVerifiers', options.trustedVerifiers); - } - - if (options.workerMinBalance) { - args.push('--workerMinBalance', options.workerMinBalance); - } - - if (options.workerTargetBalance) { - args.push('--workerTargetBalance', options.workerTargetBalance); - } - - if (options.managerMinBalance) { - args.push('--managerMinBalance', options.managerMinBalance); - } - - if (options.managerMinStake) { - args.push('--managerMinStake', options.managerMinStake); - } - - if (options.managerTargetBalance) { - args.push('--managerTargetBalance', options.managerTargetBalance); - } - - if (options.minHubWithdrawalBalance) { - args.push('--minHubWithdrawalBalance', options.minHubWithdrawalBalance); - } - - const runServerPath = path.resolve( - __dirname, - '../node_modules/@rsksmart/rif-relay-server/dist/commands/Start.js' - ); - const proc: ChildProcessWithoutNullStreams = childProcess.spawn('node', [ - '--experimental-modules', - runServerPath, - ...args - ]); - - // eslint-disable-next-line @typescript-eslint/no-empty-function - let relaylog = function (_: string): void { - console.debug('Unknown', _); - }; - if (options.relaylog) { - relaylog = (msg: string) => - msg - .split('\n') - .forEach((line) => - console.log(`relay-${proc.pid.toString()}> ${line}`) - ); - } - - await new Promise((resolve, reject) => { - let lastresponse: string; - const listener = (data: any): void => { - const str = data.toString().replace(/\s+$/, ''); - lastresponse = str; - relaylog(str); - if (str.indexOf('Listening on port') >= 0) { - // @ts-ignore - proc.alreadystarted = 1; - resolve(proc); - } - }; - proc.stdout.on('data', listener); - proc.stderr.on('data', listener); - const doaListener = (code: any): void => { - // @ts-ignore - if (!proc.alreadystarted) { - relaylog(`died before init code=${JSON.stringify(code)}`); - reject(new Error(lastresponse)); - } - }; - proc.on('exit', doaListener.bind(proc)); - }); - - let res: any; - const http = new HttpClient(new HttpWrapper(), configure({})); - let count1 = 3; - while (count1-- > 0) { - try { - res = await http.getPingResponse(localhostOne); - if (res) break; - } catch (e) { - console.log('startRelay getaddr error', e); - } - console.log('sleep before cont.'); - await module.exports.sleep(1000); - } - assert.ok(res, "can't ping server"); - // eslint-disable-next-line @typescript-eslint/restrict-template-expressions - assert.ok( - res.relayWorkerAddress, - `server returned unknown response ${res.toString()}` - ); - const relayManagerAddress = res.relayManagerAddress; - console.log('Relay Server Address', relayManagerAddress); - // @ts-ignore - await web3.eth.sendTransaction({ - to: relayManagerAddress, - from: options.relayOwner, - value: ether('2') - }); - - await relayHub.stakeForAddress(relayManagerAddress, options.delay || 2000, { - from: options.relayOwner, - value: options.stake || ether('1') - }); - - // now ping server until it "sees" the stake and funding, and gets "ready" - res = ''; - let count = 25; - while (count-- > 0) { - res = await http.getPingResponse(localhostOne); - if (res?.ready) break; - await sleep(500); - } - assert.ok( - res.ready, - 'Timed out waiting for relay to get staked and registered' - ); - - // TODO: this is temporary hack to make helper test work!!! - // @ts-ignore - proc.relayManagerAddress = relayManagerAddress; - return { - proc, - worker: res.relayWorkerAddress, - manager: relayManagerAddress - }; -} - -export function stopRelay(proc: ChildProcessWithoutNullStreams): void { - proc?.kill(); -} - -export async function increaseTime(time: number): Promise { - return await new Promise((resolve, reject) => { - // @ts-ignore - web3.currentProvider.send( - { - jsonrpc: '2.0', - method: 'evm_increaseTime', - params: [time], - id: Date.now() - }, - (err: Error | null) => { - if (err) return reject(err); - module.exports - .evmMine() - .then((r: any) => resolve(r)) - .catch((e: Error) => reject(e)); - } - ); - }); -} - -export async function evmMineMany(count: number): Promise { - for (let i = 0; i < count; i++) { - await evmMine(); - } -} - -export async function evmMine(): Promise { - return await new Promise((resolve, reject) => { - // @ts-ignore - web3.currentProvider.send( - { - jsonrpc: '2.0', - method: 'evm_mine', - params: [], - id: Date.now() - }, - (e: Error | null, r: any) => { - if (e) { - reject(e); - } else { - resolve(r); - } - } - ); - }); -} - -export async function snapshot(): Promise<{ - id: number; - jsonrpc: string; - result: string; -}> { - return await new Promise((resolve, reject) => { - // @ts-ignore - web3.currentProvider.send( - { - jsonrpc: '2.0', - method: 'evm_snapshot', - id: Date.now() - }, - ( - err: Error | null, - snapshotId: { id: number; jsonrpc: string; result: string } - ) => { - if (err) { - return reject(err); - } - return resolve(snapshotId); - } - ); - }); -} - -export async function revert(id: string): Promise { - return await new Promise((resolve, reject) => { - // @ts-ignore - web3.currentProvider.send( - { - jsonrpc: '2.0', - method: 'evm_revert', - params: [id], - id: Date.now() - }, - (err: Error | null, result: any) => { - if (err) { - return reject(err); - } - return resolve(result); - } - ); - }); -} - -// encode revert reason string as a byte error returned by revert(stirng) -export function encodeRevertReason(reason: string): PrefixedHexString { - return web3.eth.abi.encodeFunctionCall( - { - name: 'Error', - type: 'function', - inputs: [{ name: 'error', type: 'string' }] - }, - [reason] - ); - // return '0x08c379a0' + removeHexPrefix(web3.eth.abi.encodeParameter('string', reason)) -} - -export async function getTestingEnvironment(): Promise { - const networkId = await web3.eth.net.getId(); - return networkId === 33 ? environments.rsk : defaultEnvironment; -} - -export async function deployHub( - penalizer: string = constants.ZERO_ADDRESS, - configOverride: Partial = {} -): Promise { - const relayHubConfiguration: RelayHubConfiguration = { - ...defaultEnvironment.relayHubConfiguration, - ...configOverride - }; - return RelayHub.new( - penalizer, - relayHubConfiguration.maxWorkerCount, - relayHubConfiguration.minimumEntryDepositValue, - relayHubConfiguration.minimumUnstakeDelay, - relayHubConfiguration.minimumStake - ); -} - -export async function createSmartWalletFactory( - template: IForwarderInstance -): Promise { - const SmartWalletFactory = artifacts.require('SmartWalletFactory'); - return SmartWalletFactory.new(template.address); -} - -export async function createSmartWallet( - relayHub: string, - ownerEOA: string, - factory: SmartWalletFactoryInstance, - privKey: Buffer, - chainId = -1, - tokenContract: string = constants.ZERO_ADDRESS, - tokenAmount = '0', - tokenGas = '0', - recoverer: string = constants.ZERO_ADDRESS -): Promise { - chainId = chainId < 0 ? (await getTestingEnvironment()).chainId : chainId; - - const rReq: DeployRequest = { - request: { - relayHub: relayHub, - from: ownerEOA, - to: constants.ZERO_ADDRESS, - value: '0', - nonce: '0', - data: '0x', - tokenContract: tokenContract, - tokenAmount: tokenAmount, - tokenGas: tokenGas, - recoverer: recoverer, - index: '0', - validUntilTime: '0' - }, - relayData: { - gasPrice: '10', - feesReceiver: constants.ZERO_ADDRESS, - callForwarder: constants.ZERO_ADDRESS, - callVerifier: constants.ZERO_ADDRESS - } - }; - - const createdataToSign = new TypedDeployRequestData( - chainId, - factory.address, - rReq - ); - - const deploySignature = getLocalEip712Signature(createdataToSign, privKey); - const encoded = TypedDataUtils.encodeData( - createdataToSign.primaryType, - createdataToSign.message, - createdataToSign.types - ); - const countParams = DeployRequestDataType.length; - const suffixData = bufferToHex(encoded.slice((1 + countParams) * 32)); // keccak256 of suffixData - const txResult = await factory.relayedUserSmartWalletCreation( - rReq.request, - suffixData, - constants.ZERO_ADDRESS, - deploySignature - ); - - console.log( - 'Cost of deploying SmartWallet: ', - txResult.receipt.cumulativeGasUsed - ); - const swAddress = await factory.getSmartWalletAddress( - ownerEOA, - recoverer, - '0' - ); - - const SmartWallet = artifacts.require('SmartWallet'); - const sw: SmartWalletInstance = await SmartWallet.at(swAddress); - - return sw; -} - -export async function createCustomSmartWalletFactory( - template: IForwarderInstance -): Promise { - const CustomSmartWalletFactory = artifacts.require( - 'CustomSmartWalletFactory' - ); - return CustomSmartWalletFactory.new(template.address); -} - -export async function createCustomSmartWallet( - relayHub: string, - ownerEOA: string, - factory: CustomSmartWalletFactoryInstance, - privKey: Buffer, - chainId = -1, - logicAddr: string = constants.ZERO_ADDRESS, - initParams = '0x', - tokenContract: string = constants.ZERO_ADDRESS, - tokenAmount = '0', - tokenGas = '0', - recoverer: string = constants.ZERO_ADDRESS -): Promise { - chainId = chainId < 0 ? (await getTestingEnvironment()).chainId : chainId; - const rReq: DeployRequest = { - request: { - relayHub: relayHub, - from: ownerEOA, - to: logicAddr, - value: '0', - nonce: '0', - data: initParams, - tokenContract: tokenContract, - tokenAmount: tokenAmount, - tokenGas: tokenGas, - recoverer: recoverer, - index: '0', - validUntilTime: '0' - }, - relayData: { - gasPrice: '10', - feesReceiver: constants.ZERO_ADDRESS, - callForwarder: constants.ZERO_ADDRESS, - callVerifier: constants.ZERO_ADDRESS - } - }; - - const createdataToSign = new TypedDeployRequestData( - chainId, - factory.address, - rReq - ); - - const deploySignature = getLocalEip712Signature(createdataToSign, privKey); - const encoded = TypedDataUtils.encodeData( - createdataToSign.primaryType, - createdataToSign.message, - createdataToSign.types - ); - const countParams = DeployRequestDataType.length; - const suffixData = bufferToHex(encoded.slice((1 + countParams) * 32)); // keccak256 of suffixData - const txResult = await factory.relayedUserSmartWalletCreation( - rReq.request, - suffixData, - constants.ZERO_ADDRESS, - deploySignature, - { from: relayHub } - ); - console.log( - 'Cost of deploying SmartWallet: ', - txResult.receipt.cumulativeGasUsed - ); - const swAddress = await factory.getSmartWalletAddress( - ownerEOA, - recoverer, - logicAddr, - soliditySha3Raw({ t: 'bytes', v: initParams }), - '0' - ); - - const CustomSmartWallet = artifacts.require('CustomSmartWallet'); - const sw: CustomSmartWalletInstance = await CustomSmartWallet.at(swAddress); - - return sw; -} - -export async function getGaslessAccount(): Promise { - const a = ethWallet.generate(); - const gaslessAccount = { - privateKey: a.getPrivateKey(), - // eslint-disable-next-line @typescript-eslint/restrict-template-expressions - address: bufferToHex(privateToAddress(a.getPrivateKey())).toLowerCase() - }; - - return gaslessAccount; -} - -// An existing account in RSKJ that have been depleted -export async function getExistingGaslessAccount(): Promise { - const gaslessAccount = { - privateKey: toBuffer( - '0x082f57b8084286a079aeb9f2d0e17e565ced44a2cb9ce4844e6d4b9d89f3f595' - ), - address: '0x09a1eda29f664ac8f68106f6567276df0c65d859' - }; - - const balance = new BN(await web3.eth.getBalance(gaslessAccount.address)); - if (!balance.eqn(0)) { - const receiverAddress = bufferToHex( - privateToAddress(toBuffer(bytes32(1))) - ).toLowerCase(); - - await web3.eth.sendTransaction({ - from: gaslessAccount.address, - to: receiverAddress, - value: balance.subn(21000), - gasPrice: 1, - gas: 21000 - }); - } - - assert( - (await web3.eth.getBalance(gaslessAccount.address)) === '0', - 'Gassless account should have no funds' - ); - - return gaslessAccount; -} - -export function addr(n: number): string { - return '0x' + n.toString().repeat(40).slice(0, 40); -} - -export function bytes32(n: number): string { - return '0x' + n.toString().repeat(64).slice(0, 64); -} - -export function stripHex(s: string): string { - return s.slice(2, s.length); -} - -export function bufferToHexString(b: Buffer): string { - return '0x' + b.toString('hex'); -} - -export async function prepareTransaction( - relayHub: string, - testRecipient: TestRecipientInstance, - account: AccountKeypair, - relayWorker: string, - verifier: string, - nonce: string, - swallet: string, - tokenContract: string, - tokenAmount: string, - tokenGas = '50000', - collectorContract?: string -): Promise<{ relayRequest: RelayRequest; signature: string }> { - const chainId = (await getTestingEnvironment()).chainId; - const relayRequest: RelayRequest = { - request: { - relayHub: relayHub, - to: testRecipient.address, - data: testRecipient.contract.methods - .emitMessage('hello world') - .encodeABI(), - from: account.address, - nonce: nonce, - value: '0', - gas: '200000', - tokenContract: tokenContract, - tokenAmount: tokenAmount, - tokenGas: tokenGas, - validUntilTime: '0' - }, - relayData: { - gasPrice: '1', - feesReceiver: collectorContract ?? relayWorker, - callForwarder: swallet, - callVerifier: verifier - } - }; - - const dataToSign = new TypedRequestData(chainId, swallet, relayRequest); - - const signature = signTypedData_v4(account.privateKey, { - data: dataToSign - }); - - return { - relayRequest, - signature - }; -} - -/** - * Decodes events which satisfies an ABI specification - */ -export function containsEvent( - abi: any, - rawLogs: any, - eventName: string -): boolean { - const eventsAbiByTopic = getEventsAbiByTopic(abi); - return rawLogs.some( - (log: any) => - eventsAbiByTopic.has(log.topics[0]) && - eventsAbiByTopic.get(log.topics[0]).name === eventName - ); -} - -/** - * Get a Map from topics to their corresponding event's ABI - */ -function getEventsAbiByTopic(abi: any): Map { - const eventsAbiByTopic = new Map(); - // @ts-ignore - const logicEvents = abi.filter((elem) => elem.type === 'event'); - // @ts-ignore - logicEvents.forEach((abi) => { - eventsAbiByTopic.set(abi.signature, abi); - }); - return eventsAbiByTopic; -} - -/** - * Not all "signatures" are valid, so using a hard-coded one for predictable error message. - */ -export const INCORRECT_ECDSA_SIGNATURE = - '0xdeadface00000a58b757da7dea5678548be5ff9b16e9d1d87c6157aff6889c0f6a406289908add9ea6c3ef06d033a058de67d057e2c0ae5a02b36854be13b0731c'; - -export const getHostnameFromProvider = (): string => { - const underlyingProvider = web3.currentProvider as HttpProvider; - const providerUrl = new URL(underlyingProvider.host); - const hostname = providerUrl.hostname; - return hostname; -}; - -export const getWebSocketUrl = () => - `ws://${getHostnameFromProvider()}:4445/websocket`; diff --git a/test-to-migrate/TxStoreManager.test.ts b/test-to-migrate/TxStoreManager.test.ts deleted file mode 100644 index 0dad783f..00000000 --- a/test-to-migrate/TxStoreManager.test.ts +++ /dev/null @@ -1,142 +0,0 @@ -import fs from 'fs'; - -import { - ServerAction, - StoredTransaction, - TXSTORE_FILENAME, - TxStoreManager -} from '@rsksmart/rif-relay-server'; - -// NOTICE: this dir is removed in 'after', do not use this in any other test -const workdir = '/tmp/enveloping/test/txstore_manager'; -const txStoreFilePath = `${workdir}/${TXSTORE_FILENAME}`; - -function cleanFolder(): void { - if (fs.existsSync(txStoreFilePath)) { - fs.unlinkSync(txStoreFilePath); - } - if (fs.existsSync(workdir)) { - fs.rmdirSync(workdir); - } - fs.mkdirSync(workdir, { recursive: true }); - fs.writeFileSync(txStoreFilePath, ''); -} - -contract('TxStoreManager', function () { - let txmanager: TxStoreManager; - let tx: StoredTransaction; - let tx2: StoredTransaction; - let tx3: StoredTransaction; - - before('create txstore', async function () { - cleanFolder(); - txmanager = new TxStoreManager({ workdir }); - await txmanager.clearAll(); - // eslint-disable-next-line @typescript-eslint/no-base-to-string - assert.ok(txmanager, 'txstore uninitialized' + txmanager.toString()); - assert.isTrue( - fs.existsSync(workdir), - 'test txstore dir should exist already' - ); - tx = { - from: '', - to: '', - gas: 0, - gasPrice: 0, - data: '', - nonce: 111, - txId: '123456', - serverAction: ServerAction.VALUE_TRANSFER, - creationBlockNumber: 0, - minedBlockNumber: 0, - attempts: 1 - }; - tx2 = { - from: '', - to: '', - gas: 0, - gasPrice: 0, - data: '', - nonce: 112, - txId: '1234567', - serverAction: ServerAction.VALUE_TRANSFER, - creationBlockNumber: 0, - minedBlockNumber: 0, - attempts: 1 - }; - tx3 = { - from: '', - to: '', - gas: 0, - gasPrice: 0, - data: '', - nonce: 113, - txId: '12345678', - serverAction: ServerAction.VALUE_TRANSFER, - creationBlockNumber: 0, - minedBlockNumber: 0, - attempts: 1 - }; - }); - - it('should store and get tx by txId', async function () { - assert.equal(null, await txmanager.getTxById(tx.txId)); - await txmanager.putTx(tx); - const txById = await txmanager.getTxById(tx.txId); - assert.equal(tx.txId, txById.txId); - assert.equal(tx.attempts, txById.attempts); - }); - - it('should get tx by nonce', async function () { - assert.equal( - null, - await txmanager.getTxByNonce(tx.from, tx.nonce + 1234) - ); - const txByNonce = await txmanager.getTxByNonce(tx.from, tx.nonce); - assert.equal(tx.txId, txByNonce.txId); - }); - - it('should remove txs until nonce', async function () { - await txmanager.putTx(tx2); - await txmanager.putTx(tx3); - let txByNonce = await txmanager.getTxByNonce(tx.from, tx.nonce); - assert.equal(tx.txId, txByNonce.txId); - let tx2ByNonce = await txmanager.getTxByNonce(tx.from, tx2.nonce); - assert.equal(tx2.txId, tx2ByNonce.txId); - let tx3ByNonce = await txmanager.getTxByNonce(tx.from, tx3.nonce); - assert.equal(tx3.txId, tx3ByNonce.txId); - assert.deepEqual(3, (await txmanager.getAll()).length); - await txmanager.removeTxsUntilNonce(tx.from, tx2.nonce); - txByNonce = await txmanager.getTxByNonce(tx.from, tx.nonce); - assert.equal(null, txByNonce); - tx2ByNonce = await txmanager.getTxByNonce(tx.from, tx2.nonce); - assert.equal(null, tx2ByNonce); - tx3ByNonce = await txmanager.getTxByNonce(tx.from, tx3.nonce); - assert.equal(tx3.txId, tx3ByNonce.txId); - assert.deepEqual(1, (await txmanager.getAll()).length); - }); - - it('should clear txstore', async function () { - await txmanager.putTx(tx, true); - await txmanager.putTx(tx2, true); - await txmanager.putTx(tx3, true); - await txmanager.clearAll(); - assert.deepEqual([], await txmanager.getAll()); - }); - - it('should NOT store tx twice', async function () { - await txmanager.clearAll(); - await txmanager.putTx(tx); - await txmanager.putTx(tx, true); - assert.deepEqual(1, (await txmanager.getAll()).length); - try { - await txmanager.putTx(tx, false); - assert.fail('should fail storing twice'); - } catch (e) { - assert.include(e.message, 'violates the unique constraint'); - } - assert.deepEqual(1, (await txmanager.getAll()).length); - }); - - after('remove txstore', cleanFolder); -}); diff --git a/test-to-migrate/Utils.test.ts b/test-to-migrate/Utils.test.ts deleted file mode 100644 index 62b1c6d9..00000000 --- a/test-to-migrate/Utils.test.ts +++ /dev/null @@ -1,202 +0,0 @@ -// @ts-ignore -import { recoverTypedSignature_v4 } from 'eth-sig-util'; -import chaiAsPromised from 'chai-as-promised'; -import chai from 'chai'; - -import { - constants, - estimateMaxPossibleRelayCallWithLinearFit, - getLocalEip712Signature -} from '@rsksmart/rif-relay-common'; -import { RelayRequest, TypedRequestData } from '@rsksmart/rif-relay-contracts'; -import { expectEvent } from '@openzeppelin/test-helpers'; -import { - SmartWalletInstance, - TestRecipientInstance, - TestUtilInstance, - SmartWalletFactoryInstance -} from '@rsksmart/rif-relay-contracts/types/truffle-contracts'; -import { PrefixedHexString } from 'ethereumjs-tx'; -import { - encodeRevertReason, - createSmartWalletFactory, - createSmartWallet, - getGaslessAccount -} from './TestUtils'; -import { AccountKeypair } from '@rsksmart/rif-relay-client'; - -//@ts-ignore -import sourceMapSupport from 'source-map-support'; -//@ts-ignore -sourceMapSupport.install({ errorFormatterForce: true }); - -const { assert } = chai.use(chaiAsPromised); - -const TestUtil = artifacts.require('TestUtil'); -const TestRecipient = artifacts.require('TestRecipient'); -const SmartWallet = artifacts.require('SmartWallet'); - -contract('Utils', function (accounts) { - // This test verifies signing typed data with a local implementation of signTypedData - describe('#getLocalEip712Signature()', function () { - // ganache always reports chainId as '1' - let senderAccount: AccountKeypair; - let chainId: number; - let forwarder: PrefixedHexString; - let relayRequest: RelayRequest; - let senderAddress: string; - let senderPrivateKey: Buffer; - let testUtil: TestUtilInstance; - let recipient: TestRecipientInstance; - - let forwarderInstance: SmartWalletInstance; - before(async () => { - senderAccount = await getGaslessAccount(); - senderAddress = senderAccount.address; - senderPrivateKey = senderAccount.privateKey; - testUtil = await TestUtil.new(); - chainId = (await testUtil.libGetChainID()).toNumber(); - const smartWalletTemplate: SmartWalletInstance = - await SmartWallet.new(); - const factory: SmartWalletFactoryInstance = - await createSmartWalletFactory(smartWalletTemplate); - forwarderInstance = await createSmartWallet( - accounts[0], - senderAddress, - factory, - senderPrivateKey, - chainId - ); - forwarder = forwarderInstance.address; - recipient = await TestRecipient.new(); - - const senderNonce = '0'; - const target = recipient.address; - const encodedFunction = '0xdeadbeef'; - const gasPrice = '1'; - const gasLimit = '1000000'; - const verifier = accounts[7]; - const relayWorker = accounts[9]; - - relayRequest = { - request: { - relayHub: testUtil.address, - to: target, - data: encodedFunction, - from: senderAddress, - nonce: senderNonce, - value: '0', - gas: gasLimit, - tokenContract: constants.ZERO_ADDRESS, - tokenAmount: '0', - tokenGas: '0', - validUntilTime: '0' - }, - relayData: { - gasPrice, - feesReceiver: relayWorker, - callForwarder: forwarder, - callVerifier: verifier - } - }; - }); - - it('should generate a valid EIP-712 compatible signature', async function () { - const dataToSign = new TypedRequestData( - chainId, - forwarder, - relayRequest - ); - - const sig = await getLocalEip712Signature( - dataToSign, - senderPrivateKey - ); - - const recoveredAccount = recoverTypedSignature_v4({ - data: dataToSign, - sig - }); - assert.strictEqual( - senderAddress.toLowerCase(), - recoveredAccount.toLowerCase() - ); - - await testUtil.callForwarderVerify(relayRequest, sig); - }); - - describe('#callForwarderVerifyAndCall', () => { - it('should return revert result', async function () { - relayRequest.request.data = await recipient.contract.methods - .testRevert() - .encodeABI(); - const sig = getLocalEip712Signature( - new TypedRequestData(chainId, forwarder, relayRequest), - senderPrivateKey - ); - const ret = await testUtil.callForwarderVerifyAndCall( - relayRequest, - sig - ); - const expectedReturnValue = encodeRevertReason('always fail'); - expectEvent(ret, 'Called', { - success: false, - error: expectedReturnValue - }); - }); - - it('should correctly calculate the linear estimation', async function () { - const gas = 40000; - const tokenGas = 18000; - - const expectedRelayGasNoToken = 127771; - const expectedRelayGasWithToken = 136993; - - const gasNoToken = estimateMaxPossibleRelayCallWithLinearFit( - gas, - 0 - ); - const gasToken = estimateMaxPossibleRelayCallWithLinearFit( - gas, - tokenGas - ); - - assert.equal( - expectedRelayGasNoToken, - gasNoToken, - 'Estimation with no tokenGas differs' - ); - assert.equal( - expectedRelayGasWithToken, - gasToken, - 'Estimation with tokenGas differs' - ); - }); - - it('should call target', async function () { - relayRequest.request.data = recipient.contract.methods - .emitMessage('hello') - .encodeABI(); - relayRequest.request.nonce = ( - await forwarderInstance.nonce() - ).toString(); - - const sig = getLocalEip712Signature( - new TypedRequestData(chainId, forwarder, relayRequest), - senderPrivateKey - ); - const ret = await testUtil.callForwarderVerifyAndCall( - relayRequest, - sig - ); - expectEvent(ret, 'Called', { - error: null - }); - const logs = await recipient.contract.getPastEvents(null, { - fromBlock: 1 - }); - assert.equal(logs[0].event, 'SampleRecipientEmitted'); - }); - }); - }); -}); diff --git a/test-to-migrate/Utils.ts b/test-to-migrate/Utils.ts deleted file mode 100644 index c74b4e0a..00000000 --- a/test-to-migrate/Utils.ts +++ /dev/null @@ -1,284 +0,0 @@ -// TODO: allow reading network URLs from 'truffle-config.js' -import commander, { CommanderStatic } from 'commander'; -import fs from 'fs'; -import path from 'path'; -import { DeploymentResult } from './TestSetup'; -import { RelayHubConfiguration } from '@rsksmart/rif-relay-contracts'; -import * as config from './server-config.json'; - -export const RIF_RELAY_PORT = config.port; -export const RIF_RELAY_URL = config.url; -export const PERSONAL_SIGN_PREFIX = '\x19Ethereum Signed Message:\n'; - -export const networks = new Map([ - ['localhost', 'http://127.0.0.1:4444'], - ['development', 'http://127.0.0.1:4444'], - ['rsktestnet', 'https://public-node.testnet.rsk.co'], - ['rskmainnet', 'https://public-node.rsk.co'] -]); - -export function supportedNetworks(): string[] { - return Array.from(networks.keys()); -} - -export function getNetworkUrl(network = ''): string { - const match = network.match(/^(https?:\/\/.*)/) ?? []; - return networks.get(network) ?? match[0]; -} - -export function getMnemonic(mnemonicFile: string): string | undefined { - if (mnemonicFile == null) { - return; - } - console.log('Using mnemonic from file ' + mnemonicFile); - return fs - .readFileSync(mnemonicFile, { encoding: 'utf8' }) - .replace(/\r?\n|\r/g, ''); -} - -export function getRelayHubConfiguration( - configFile: string -): RelayHubConfiguration | undefined { - if (configFile == null) { - return; - } - console.log('Using hub config from file ' + configFile); - const file = fs.readFileSync(configFile, { encoding: 'utf8' }); - return JSON.parse(file); -} - -export function getRelayVerifierAddress(verifier?: string): string | undefined { - return getAddressFromFile('build/enveloping/RelayVerifier.json', verifier); -} - -export function getDeployVerifierAddress( - verifier?: string -): string | undefined { - return getAddressFromFile('build/enveloping/DeployVerifier.json', verifier); -} - -export function getRelayHubAddress( - defaultAddress?: string -): string | undefined { - return getAddressFromFile('build/enveloping/RelayHub.json', defaultAddress); -} - -export function getRegistryAddress( - defaultAddress?: string -): string | undefined { - return getAddressFromFile( - 'build/enveloping/VersionRegistry.json', - defaultAddress - ); -} - -export function getSmartWalletFactoryAddress( - defaultAddress?: string -): string | undefined { - return getAddressFromFile( - 'build/enveloping/SmartWalletFactory.json', - defaultAddress - ); -} - -export function getCustomSmartWalletFactoryAddress( - defaultAddress?: string -): string | undefined { - return getAddressFromFile( - 'build/enveloping/CustomSmartWalletFactory.json', - defaultAddress - ); -} - -export function getCustomSmartWalletDeployVerifierAddress( - verifier?: string -): string | undefined { - return getAddressFromFile( - 'build/enveloping/CustomSmartWalletDeployVerifier.json', - verifier - ); -} - -export function getCustomSmartWalletRelayVerifierAddress( - verifier?: string -): string | undefined { - return getAddressFromFile( - 'build/enveloping/CustomSmartWalletRelayVerifier.json', - verifier - ); -} - -function getAddressFromFile( - path: string, - defaultAddress?: string -): string | undefined { - if (defaultAddress == null) { - if (fs.existsSync(path)) { - const relayHubDeployInfo = fs.readFileSync(path).toString(); - return JSON.parse(relayHubDeployInfo).address; - } - } - return defaultAddress; -} - -function saveContractToFile( - address: string, - workdir: string, - filename: string -): void { - fs.mkdirSync(workdir, { recursive: true }); - fs.writeFileSync( - path.join(workdir, filename), - `{ "address": "${address}" }` - ); -} - -export function saveDeployment( - deploymentResult: DeploymentResult, - workdir: string -): void { - saveContractToFile( - deploymentResult.penalizerAddress, - workdir, - 'Penalizer.json' - ); - saveContractToFile( - deploymentResult.relayHubAddress, - workdir, - 'RelayHub.json' - ); - saveContractToFile( - deploymentResult.relayVerifierAddress, - workdir, - 'RelayVerifier.json' - ); - saveContractToFile( - deploymentResult.deployVerifierAddress, - workdir, - 'DeployVerifier.json' - ); - saveContractToFile( - deploymentResult.smartWalletTemplateAddress, - workdir, - 'SmartWallet.json' - ); - saveContractToFile( - deploymentResult.smartWalletFactoryAddress, - workdir, - 'SmartWalletFactory.json' - ); - saveContractToFile( - deploymentResult.versionRegistryAddress, - workdir, - 'VersionRegistry.json' - ); - saveContractToFile( - deploymentResult.customSmartWalletTemplateAddress, - workdir, - 'CustomSmartWallet.json' - ); - saveContractToFile( - deploymentResult.customSmartWalletFactoryAddress, - workdir, - 'CustomSmartWalletFactory.json' - ); - saveContractToFile( - deploymentResult.customSmartWalletDeployVerifierAddress, - workdir, - 'CustomSmartWalletDeployVerifier.json' - ); - saveContractToFile( - deploymentResult.customSmartWalletRelayVerifierAddress, - workdir, - 'CustomSmartWalletRelayVerifier.json' - ); -} - -export function showDeployment( - deploymentResult: DeploymentResult, - title: string | undefined -): void { - if (title != null) { - console.log(title); - } - console.log(` - RelayHub: ${deploymentResult.relayHubAddress} - Penalizer: ${deploymentResult.penalizerAddress} - VersionRegistry: ${deploymentResult.versionRegistryAddress} - SmartWalletTemplate: ${deploymentResult.smartWalletTemplateAddress} - SmartWalletFactory: ${deploymentResult.smartWalletFactoryAddress} - RelayVerifier: ${deploymentResult.relayVerifierAddress} - DeployVerifier: ${deploymentResult.deployVerifierAddress} - CustomSmartWalletTemplate: ${deploymentResult.customSmartWalletTemplateAddress} - CustomSmartWalletFactory: ${deploymentResult.customSmartWalletFactoryAddress} - CustomSmartWalletDeployVerifier: ${deploymentResult.customSmartWalletDeployVerifierAddress}) - CustomSmartWalletRelayVerifier: ${deploymentResult.customSmartWalletRelayVerifierAddress}`); -} - -export function loadDeployment(workdir: string): DeploymentResult { - function getAddress(name: string): string { - return getAddressFromFile(path.join(workdir, name + '.json')); - } - - return { - relayHubAddress: getAddress('RelayHub'), - penalizerAddress: getAddress('Penalizer'), - smartWalletTemplateAddress: getAddress('SmartWallet'), - smartWalletFactoryAddress: getAddress('SmartWalletFactory'), - versionRegistryAddress: getAddress('VersionRegistry'), - relayVerifierAddress: getAddress('RelayVerifier'), - deployVerifierAddress: getAddress('DeployVerifier'), - customSmartWalletTemplateAddress: getAddress('CustomSmartWallet'), - customSmartWalletFactoryAddress: getAddress('CustomSmartWalletFactory'), - customSmartWalletDeployVerifierAddress: getAddress( - 'CustomSmartWalletDeployVerifier' - ), - customSmartWalletRelayVerifierAddress: getAddress( - 'CustomSmartWalletRelayVerifier' - ) - }; -} - -type EnvelopingOption = 'n' | 'f' | 'h' | 'm' | 'g'; - -export function envelopingCommander( - options: EnvelopingOption[] -): CommanderStatic { - options.forEach((option) => { - switch (option) { - case 'n': - commander.option( - '-n, --network ', - 'network name or URL to an RSK node', - 'localhost' - ); - break; - case 'f': - commander.option( - '-f, --from
', - 'account to send transactions from (default: the first account with balance)' - ); - break; - case 'h': - commander.option( - '-h, --hub
', - 'address of the hub contract (default: the address from build/enveloping/RelayHub.json if exists)' - ); - break; - case 'm': - commander.option( - '-m, --mnemonic ', - "mnemonic file to generate private key for account 'from' (default: empty)" - ); - break; - case 'g': - commander.option( - '-g, --gasPrice ', - 'gas price to give to the transaction. Defaults to 1 gwei.', - '1000000000' - ); - break; - } - }); - return commander; -} diff --git a/test-to-migrate/VersionRegistry.test.ts b/test-to-migrate/VersionRegistry.test.ts deleted file mode 100644 index 83cf2a60..00000000 --- a/test-to-migrate/VersionRegistry.test.ts +++ /dev/null @@ -1,294 +0,0 @@ -import { VersionRegistryInstance } from '@rsksmart/rif-relay-contracts/types/truffle-contracts'; -import { expectRevert } from '@openzeppelin/test-helpers'; -import { increaseTime, getTestingEnvironment } from './TestUtils'; -import { - VersionRegistry, - string32, - isRsk, - Environment -} from '@rsksmart/rif-relay-common'; -import chai from 'chai'; -import chaiAsPromised from 'chai-as-promised'; - -const { expect, assert } = chai.use(chaiAsPromised); - -//@ts-ignore -import sourceMapSupport from 'source-map-support'; -//@ts-ignore -sourceMapSupport.install({ errorFormatterForce: true }); - -const VersionRegistryContract = artifacts.require('VersionRegistry'); - -contract('VersionRegistry', ([account]) => { - let now: number; - let registryContract: VersionRegistryInstance; - let jsRegistry: VersionRegistry; - let env: Environment; - - before('create registry', async () => { - registryContract = await VersionRegistryContract.new(); - jsRegistry = new VersionRegistry( - web3.currentProvider, - registryContract.address, - { from: account } - ); - await jsRegistry.addVersion('id', 'ver', 'value'); - await jsRegistry.addVersion('another', 'ver', 'anothervalue'); - }); - context('contract param validations', () => { - it('should fail to add without id', async () => { - await expectRevert( - registryContract.addVersion( - string32(''), - string32(''), - 'value' - ), - 'missing id' - ); - }); - it('should fail to add without version', async () => { - await expectRevert( - registryContract.addVersion( - string32('id'), - string32(''), - 'value' - ), - 'missing version' - ); - }); - }); - context('javascript param validations', () => { - it('should reject adding the same version again', async () => { - await expect( - jsRegistry.addVersion('id', 'ver', 'changevalue') - ).to.eventually.be.rejectedWith('version already exists'); - }); - it('should rejecting canceling non-existent version', async () => { - await expect( - jsRegistry.cancelVersion('nosuchid', 'ver', 'changevalue') - ).to.eventually.be.rejectedWith('version does not exist'); - }); - }); - - context('basic getAllVersions', () => { - it('should return nothing for unknown id', async () => { - assert.deepEqual(await jsRegistry.getAllVersions('nosuchid'), []); - }); - it('should get version of specific id', async () => { - const versions = await jsRegistry.getAllVersions('id'); - assert.deepInclude(versions[0], { - version: 'ver', - value: 'value', - canceled: false - }); - }); - }); - - context('with more versions', () => { - before(async () => { - env = await getTestingEnvironment(); - - await increaseTime(100); - await jsRegistry.addVersion('id', 'ver2', 'value2'); - // evm_increaseTime with Automine enabled RSKJ works a bit different in RSKJ - await increaseTime(isRsk(env) ? 200 : 100); - await jsRegistry.addVersion('id', 'ver3', 'value3'); - // evm_increaseTime with Automine enabled RSKJ works a bit different in RSKJ - await increaseTime(isRsk(env) ? 300 : 100); - - // at this point: - // ver1 - 300 sec old - // ver2 - 200 sec old - // ver3 - 100 sec old - - now = parseInt( - (await web3.eth.getBlock('latest')).timestamp.toString() - ); - }); - context('#getAllVersions', () => { - it('should return all versions', async () => { - const versions = await jsRegistry.getAllVersions('id'); - - assert.equal(versions.length, 3); - assert.deepInclude(versions[0], { - version: 'ver3', - value: 'value3', - canceled: false - }); - assert.deepInclude(versions[1], { - version: 'ver2', - value: 'value2', - canceled: false - }); - assert.deepInclude(versions[2], { - version: 'ver', - value: 'value', - canceled: false - }); - - assert.closeTo( - now - versions[0].time, - 100, - isRsk(env) ? 10 : 2 - ); - assert.closeTo( - now - versions[1].time, - 200, - isRsk(env) ? 10 : 2 - ); - assert.closeTo( - now - versions[2].time, - 300, - isRsk(env) ? 10 : 2 - ); - }); - - it("should ignore repeated added version (can't modify history: only adding to it)", async () => { - // note that the javascript class reject such double-adding. we add directly through the contract API: - await registryContract.addVersion( - string32('id'), - string32('ver2'), - 'new-value2' - ); - const versions = await jsRegistry.getAllVersions('id'); - - assert.equal(versions.length, 3); - assert.deepInclude(versions[0], { - version: 'ver3', - value: 'value3', - canceled: false - }); - assert.deepInclude(versions[1], { - version: 'ver2', - value: 'value2', - canceled: false - }); - assert.deepInclude(versions[2], { - version: 'ver', - value: 'value', - canceled: false - }); - }); - }); - - describe('#getVersion', () => { - it('should revert if has no version', async () => { - await expect( - jsRegistry.getVersion('nosuchid', 1) - ).to.eventually.rejectedWith('no version found'); - }); - - it('should revert if no version is mature', async () => { - try { - await jsRegistry.getVersion('id', 10000); - } catch (e) { - assert.include(e.toString(), 'no version found'); - return; - } - assert.fail('should revert'); - }); - - it('should return latest version', async () => { - const { version, value, time } = await jsRegistry.getVersion( - 'id', - 1 - ); - assert.deepEqual( - { version, value }, - { version: 'ver3', value: 'value3' } - ); - assert.closeTo(time, now - 100, 2); - }); - - it('should return latest "mature" version', async () => { - // ignore entries in the past 150 seconds - const { version, value } = await jsRegistry.getVersion( - 'id', - 150 - ); - - assert.deepEqual( - { version, value }, - { version: 'ver2', value: 'value2' } - ); - }); - - it('should return "young" version if opted-in', async () => { - // ignore entries in the past 150 seconds (unless explicitly opted-in) - const { version, value } = await jsRegistry.getVersion( - 'id', - 150, - 'ver3' - ); - - assert.deepEqual( - { version, value }, - { version: 'ver3', value: 'value3' } - ); - }); - - it('should ignore opt-in if later version exists', async () => { - // ignore entries in the past 150 seconds - const { version, value } = await jsRegistry.getVersion( - 'id', - 150, - 'ver1' - ); - - assert.deepEqual( - { version, value }, - { version: 'ver2', value: 'value2' } - ); - }); - }); - - describe('with canceled version', () => { - before(async () => { - await registryContract.cancelVersion( - string32('id'), - string32('ver2'), - 'reason' - ); - // at this point: - // ver1 - 300 sec old - // ver2 - 200 sec old - canceled - // ver3 - 100 sec old - }); - - it('getVersion should ignore canceled version', async () => { - // ignore entries in the past 150 seconds - const { version, value } = await jsRegistry.getVersion( - 'id', - 150 - ); - assert.deepEqual( - { version, value }, - { version: 'ver', value: 'value' } - ); - }); - it('getAllVersions should return also canceled versions', async () => { - const versions = await jsRegistry.getAllVersions('id'); - - assert.equal(versions.length, 3); - assert.deepInclude(versions[0], { - version: 'ver3', - value: 'value3', - canceled: false, - cancelReason: undefined - }); - assert.deepInclude(versions[1], { - version: 'ver2', - value: 'value2', - canceled: true, - cancelReason: 'reason' - }); - assert.deepInclude(versions[2], { - version: 'ver', - value: 'value', - canceled: false, - cancelReason: undefined - }); - }); - }); - }); -}); diff --git a/test-to-migrate/WalletFactory.test.ts b/test-to-migrate/WalletFactory.test.ts deleted file mode 100644 index 22327803..00000000 --- a/test-to-migrate/WalletFactory.test.ts +++ /dev/null @@ -1,1663 +0,0 @@ -import { - SmartWalletInstance, - TestTokenInstance, - CustomSmartWalletFactoryInstance, - CustomSmartWalletInstance, - SmartWalletFactoryInstance -} from '@rsksmart/rif-relay-contracts/types/truffle-contracts'; -// @ts-ignore -import { signTypedData_v4, TypedDataUtils } from 'eth-sig-util'; -import { bufferToHex, privateToAddress, toBuffer } from 'ethereumjs-util'; -import { expectRevert, expectEvent } from '@openzeppelin/test-helpers'; -import { toChecksumAddress, soliditySha3Raw } from 'web3-utils'; -import { ethers } from 'ethers'; -import chai from 'chai'; -import { bytes32, getTestingEnvironment, stripHex } from './TestUtils'; -import { Environment, constants } from '@rsksmart/rif-relay-common'; -import { - DeployRequest, - DeployRequestDataType, - TypedDeployRequestData -} from '@rsksmart/rif-relay-contracts'; -import { PERSONAL_SIGN_PREFIX } from './Utils'; - -const keccak256 = web3.utils.keccak256; - -const TestToken = artifacts.require('TestToken'); - -contract('CustomSmartWalletFactory', ([from]) => { - const CustomSmartWallet = artifacts.require('CustomSmartWallet'); - const CustomSmartWalletFactory = artifacts.require( - 'CustomSmartWalletFactory' - ); - let fwd: CustomSmartWalletInstance; - let token: TestTokenInstance; - let factory: CustomSmartWalletFactoryInstance; - const ownerPrivateKey = toBuffer(bytes32(1)); - let ownerAddress: string; - const recipientPrivateKey = toBuffer(bytes32(1)); - let recipientAddress: string; - let env: Environment; - - const request: DeployRequest = { - request: { - relayHub: constants.ZERO_ADDRESS, - from: constants.ZERO_ADDRESS, - to: constants.ZERO_ADDRESS, - value: '0', - nonce: '0', - data: '0x', - tokenContract: constants.ZERO_ADDRESS, - tokenAmount: '0', - tokenGas: '60000', - recoverer: constants.ZERO_ADDRESS, - index: '0', - validUntilTime: '0' - }, - relayData: { - gasPrice: '1', - feesReceiver: constants.ZERO_ADDRESS, - callForwarder: constants.ZERO_ADDRESS, - callVerifier: constants.ZERO_ADDRESS - } - }; - - before(async () => { - ownerAddress = bufferToHex( - privateToAddress(ownerPrivateKey) - ).toLowerCase(); - recipientAddress = bufferToHex( - privateToAddress(recipientPrivateKey) - ).toLowerCase(); - request.request.from = ownerAddress; - env = await getTestingEnvironment(); - fwd = await CustomSmartWallet.new(); - }); - - beforeEach(async () => { - // A new factory for new create2 addresses each - factory = await CustomSmartWalletFactory.new(fwd.address); - request.relayData.callForwarder = factory.address; - }); - - describe('#getCreationBytecode', () => { - it('should return the expected bytecode', async () => { - const expectedCode = - '0x602D3D8160093D39F3363D3D373D3D3D3D363D73' + - stripHex(fwd.address) + - '5AF43D923D90803E602B57FD5BF3'; - - const code = await factory.getCreationBytecode(); - chai.expect(web3.utils.toBN(expectedCode)).to.be.bignumber.equal( - web3.utils.toBN(code) - ); - }); - }); - - describe('#getRuntimeCodeHash', () => { - it('should return the expected code hash', async () => { - const expectedCode = - '0x363D3D373D3D3D3D363D73' + - stripHex(fwd.address) + - '5AF43D923D90803E602B57FD5BF3'; - const expectedCodeHash = keccak256(expectedCode); - - const code = await factory.runtimeCodeHash(); - chai.expect( - web3.utils.toBN(expectedCodeHash) - ).to.be.bignumber.equal(web3.utils.toBN(code)); - }); - }); - - describe('#getSmartWalletAddress', () => { - it('should create the correct create2 Address', async () => { - const logicAddress = constants.ZERO_ADDRESS; - const initParamsHash = constants.SHA3_NULL_S; - const recoverer = constants.ZERO_ADDRESS; - const index = '0'; - const create2Address = await factory.getSmartWalletAddress( - ownerAddress, - recoverer, - logicAddress, - initParamsHash, - index - ); - const creationByteCode = await factory.getCreationBytecode(); - - const salt: string = - web3.utils.soliditySha3( - { t: 'address', v: ownerAddress }, - { t: 'address', v: recoverer }, - { t: 'address', v: logicAddress }, - { t: 'bytes32', v: initParamsHash }, - { t: 'uint256', v: index } - ) ?? ''; - - const bytecodeHash: string = - web3.utils.soliditySha3({ t: 'bytes', v: creationByteCode }) ?? - ''; - - const _data: string = - web3.utils.soliditySha3( - { t: 'bytes1', v: '0xff' }, - { t: 'address', v: factory.address }, - { t: 'bytes32', v: salt }, - { t: 'bytes32', v: bytecodeHash } - ) ?? ''; - - const expectedAddress = toChecksumAddress( - '0x' + _data.slice(26, _data.length), - env.chainId - ); - assert.equal(create2Address, expectedAddress); - }); - }); - - describe('#createUserSmartWallet', () => { - it('should create the Smart Wallet in the expected address', async () => { - const logicAddress = constants.ZERO_ADDRESS; - const initParams = '0x'; - const recoverer = constants.ZERO_ADDRESS; - const index = '0'; - - const message: string = - web3.utils.soliditySha3( - { t: 'address', v: factory.address }, - { t: 'address', v: ownerAddress }, - { t: 'address', v: recoverer }, - { t: 'address', v: logicAddress }, - { t: 'uint256', v: index }, - { t: 'bytes', v: initParams } // Init params is empty - ) ?? ''; - - const toSign: string = - web3.utils.soliditySha3( - { - t: 'string', - v: - PERSONAL_SIGN_PREFIX + - web3.utils.hexToBytes(message).length - }, - { t: 'bytes32', v: message } - ) ?? ''; - - const toSignAsBinaryArray = ethers.utils.arrayify(toSign); - const signingKey = new ethers.utils.SigningKey(ownerPrivateKey); - const signature = signingKey.signDigest(toSignAsBinaryArray); - const signatureCollapsed = ethers.utils.joinSignature(signature); - - const { logs } = await factory.createUserSmartWallet( - ownerAddress, - recoverer, - logicAddress, - index, - initParams, - signatureCollapsed - ); - - const expectedAddress = await factory.getSmartWalletAddress( - ownerAddress, - recoverer, - logicAddress, - soliditySha3Raw({ t: 'bytes', v: initParams }), - index - ); - - const salt = - web3.utils.soliditySha3( - { t: 'address', v: ownerAddress }, - { t: 'address', v: recoverer }, - { t: 'address', v: logicAddress }, - { - t: 'bytes32', - v: soliditySha3Raw({ t: 'bytes', v: initParams }) - }, - { t: 'uint256', v: index } - ) ?? ''; - - const expectedSalt = web3.utils.toBN(salt).toString(); - - expectEvent.inLogs(logs, 'Deployed', { - addr: expectedAddress, - salt: expectedSalt - }); - }); - - it('should create the Smart Wallet with the expected proxy code', async () => { - const logicAddress = constants.ZERO_ADDRESS; - const initParams = '0x'; - const recoverer = constants.ZERO_ADDRESS; - const index = '0'; - - const expectedAddress = await factory.getSmartWalletAddress( - ownerAddress, - recoverer, - logicAddress, - soliditySha3Raw({ t: 'bytes', v: initParams }), - index - ); - - const message: string = - web3.utils.soliditySha3( - { t: 'address', v: factory.address }, - { t: 'address', v: ownerAddress }, - { t: 'address', v: recoverer }, - { t: 'address', v: logicAddress }, - { t: 'uint256', v: index }, - { t: 'bytes', v: initParams } - ) ?? ''; - - const toSign: string = - web3.utils.soliditySha3( - { - t: 'string', - v: - PERSONAL_SIGN_PREFIX + - web3.utils.hexToBytes(message).length - }, - { t: 'bytes32', v: message } - ) ?? ''; - - const toSignAsBinaryArray = ethers.utils.arrayify(toSign); - const signingKey = new ethers.utils.SigningKey(ownerPrivateKey); - const signature = signingKey.signDigest(toSignAsBinaryArray); - const signatureCollapsed = ethers.utils.joinSignature(signature); - - // expectedCode = runtime code only - let expectedCode = await factory.getCreationBytecode(); - expectedCode = '0x' + expectedCode.slice(20, expectedCode.length); - - const { logs } = await factory.createUserSmartWallet( - ownerAddress, - recoverer, - logicAddress, - index, - initParams, - signatureCollapsed - ); - - const code = await web3.eth.getCode( - expectedAddress, - logs[0].blockNumber - ); - - chai.expect(web3.utils.toBN(expectedCode)).to.be.bignumber.equal( - web3.utils.toBN(code) - ); - }); - - it('should revert for an invalid signature', async () => { - const logicAddress = constants.ZERO_ADDRESS; - const initParams = '0x00'; - const recoverer = constants.ZERO_ADDRESS; - const index = '0'; - - const message: string = - web3.utils.soliditySha3( - { t: 'address', v: factory.address }, - { t: 'address', v: ownerAddress }, - { t: 'address', v: recoverer }, - { t: 'address', v: logicAddress }, - { t: 'uint256', v: index }, - { t: 'bytes', v: initParams } - ) ?? ''; - - const toSign: string = - web3.utils.soliditySha3( - { - t: 'string', - v: - PERSONAL_SIGN_PREFIX + - web3.utils.hexToBytes(message).length - }, - { t: 'bytes32', v: message } - ) ?? ''; - - const toSignAsBinaryArray = ethers.utils.arrayify(toSign); - const signingKey = new ethers.utils.SigningKey(ownerPrivateKey); - const signature = signingKey.signDigest(toSignAsBinaryArray); - let signatureCollapsed: string = - ethers.utils.joinSignature(signature); - - signatureCollapsed = signatureCollapsed - .substr(0, signatureCollapsed.length - 1) - .concat('0'); - - await expectRevert( - factory.createUserSmartWallet( - ownerAddress, - recoverer, - logicAddress, - index, - initParams, - signatureCollapsed - ), - 'invalid signature' - ); - }); - - it('should not initialize if a second initialize() call to the Smart Wallet is attempted', async () => { - const logicAddress = constants.ZERO_ADDRESS; - const initParams = '0x'; - const recoverer = constants.ZERO_ADDRESS; - const index = '0'; - - const expectedAddress = await factory.getSmartWalletAddress( - ownerAddress, - recoverer, - logicAddress, - soliditySha3Raw({ t: 'bytes', v: initParams }), - index - ); - - const message: string = - web3.utils.soliditySha3( - { t: 'address', v: factory.address }, - { t: 'address', v: ownerAddress }, - { t: 'address', v: recoverer }, - { t: 'address', v: logicAddress }, - { t: 'uint256', v: index }, - { t: 'bytes', v: initParams } - ) ?? ''; - - const toSign: string = - web3.utils.soliditySha3( - { - t: 'string', - v: - PERSONAL_SIGN_PREFIX + - web3.utils.hexToBytes(message).length - }, - { t: 'bytes32', v: message } - ) ?? ''; - - const toSignAsBinaryArray = ethers.utils.arrayify(toSign); - const signingKey = new ethers.utils.SigningKey(ownerPrivateKey); - const signature = signingKey.signDigest(toSignAsBinaryArray); - const signatureCollapsed = ethers.utils.joinSignature(signature); - - const { logs } = await factory.createUserSmartWallet( - ownerAddress, - recoverer, - logicAddress, - index, - initParams, - signatureCollapsed - ); - - const salt = - web3.utils.soliditySha3( - { t: 'address', v: ownerAddress }, - { t: 'address', v: recoverer }, - { t: 'address', v: logicAddress }, - { - t: 'bytes32', - v: soliditySha3Raw({ t: 'bytes', v: initParams }) - }, - { t: 'uint256', v: index } - ) ?? ''; - - const expectedSalt = web3.utils.toBN(salt).toString(); - - // Check the emitted event - expectEvent.inLogs(logs, 'Deployed', { - addr: expectedAddress, - salt: expectedSalt - }); - - const isInitializedFunc = web3.eth.abi.encodeFunctionCall( - { - name: 'isInitialized', - type: 'function', - inputs: [] - }, - [] - ); - - const trx = await web3.eth.getTransaction(logs[0].transactionHash); - - const newTrx = { - from: trx.from, - gas: trx.gas, - to: expectedAddress, - gasPrice: trx.gasPrice, - value: trx.value, - data: isInitializedFunc - }; - - // Call the isInitialized function - let result = await web3.eth.call(newTrx); - let resultStr = result as string; - - // It should be initialized - chai.expect(web3.utils.toBN(1)).to.be.bignumber.equal( - web3.utils.toBN(resultStr) - ); - - const initFunc = await web3.eth.abi.encodeFunctionCall( - { - name: 'initialize', - type: 'function', - inputs: [ - { - type: 'address', - name: 'owner' - }, - { - type: 'address', - name: 'logic' - }, - { - type: 'address', - name: 'tokenAddr' - }, - { - type: 'address', - name: 'tokenRecipient' - }, - { - type: 'uint256', - name: 'tokenAmount' - }, - { - type: 'uint256', - name: 'tokenGas' - }, - { - type: 'bytes', - name: 'initParams' - } - ] - }, - [ - ownerAddress, - logicAddress, - constants.ZERO_ADDRESS, - constants.ZERO_ADDRESS, - '0x00', - '0x00', - initParams - ] - ); - - newTrx.data = initFunc; - - // Trying to manually call the initialize function again (it was called during deploy) - await expectRevert( - web3.eth.sendTransaction(newTrx), - 'already initialized' - ); - - newTrx.data = isInitializedFunc; - result = await web3.eth.call(newTrx); - resultStr = result as string; - - // The smart wallet should be still initialized - chai.expect(web3.utils.toBN(1)).to.be.bignumber.equal( - web3.utils.toBN(resultStr) - ); - }); - }); - - describe('#relayedUserSmartWalletCreation', () => { - it('should create the Smart Wallet in the expected address', async () => { - const logicAddress = constants.ZERO_ADDRESS; - const initParams = '0x'; - const deployPrice = '0x01'; // 1 token - const recoverer = constants.ZERO_ADDRESS; - const index = '0'; - - const expectedAddress = await factory.getSmartWalletAddress( - ownerAddress, - recoverer, - logicAddress, - soliditySha3Raw({ t: 'bytes', v: initParams }), - index - ); - - token = await TestToken.new(); - await token.mint('200', expectedAddress); - - const originalBalance = await token.balanceOf(expectedAddress); - - const req: DeployRequest = { - request: { - ...request.request, - tokenContract: token.address, - tokenAmount: deployPrice - }, - relayData: { - ...request.relayData - } - }; - - req.request.relayHub = from; - - const dataToSign = new TypedDeployRequestData( - env.chainId, - factory.address, - req - ); - - const sig = signTypedData_v4(ownerPrivateKey, { data: dataToSign }); - - // relayData information - const suffixData = bufferToHex( - TypedDataUtils.encodeData( - dataToSign.primaryType, - dataToSign.message, - dataToSign.types - ).slice((1 + DeployRequestDataType.length) * 32) - ); - - const { logs } = await factory.relayedUserSmartWalletCreation( - req.request, - suffixData, - from, - sig - ); - - const salt = - web3.utils.soliditySha3( - { t: 'address', v: ownerAddress }, - { t: 'address', v: recoverer }, - { t: 'address', v: logicAddress }, - { - t: 'bytes32', - v: soliditySha3Raw({ t: 'bytes', v: initParams }) - }, - { t: 'uint256', v: index } - ) ?? ''; - - const expectedSalt = web3.utils.toBN(salt).toString(); - - // Check the emitted event - expectEvent.inLogs(logs, 'Deployed', { - addr: expectedAddress, - salt: expectedSalt - }); - - // The Smart Wallet should have been charged for the deploy - const newBalance = await token.balanceOf(expectedAddress); - const expectedBalance = originalBalance.sub( - web3.utils.toBN(deployPrice) - ); - chai.expect(expectedBalance).to.be.bignumber.equal(newBalance); - }); - - it('should create the Smart Wallet with the expected proxy code', async () => { - const logicAddress = constants.ZERO_ADDRESS; - const initParams = '0x'; - const deployPrice = '0x01'; // 1 token - const recoverer = constants.ZERO_ADDRESS; - const index = '0'; - - const expectedAddress = await factory.getSmartWalletAddress( - ownerAddress, - recoverer, - logicAddress, - soliditySha3Raw({ t: 'bytes', v: initParams }), - index - ); - - token = await TestToken.new(); - await token.mint('200', expectedAddress); - - const req: DeployRequest = { - request: { - ...request.request, - tokenContract: token.address, - tokenAmount: deployPrice, - relayHub: from - }, - relayData: { - ...request.relayData - } - }; - - const dataToSign = new TypedDeployRequestData( - env.chainId, - factory.address, - req - ); - - const sig = signTypedData_v4(ownerPrivateKey, { data: dataToSign }); - - // expectedCode = runtime code only - let expectedCode = await factory.getCreationBytecode(); - expectedCode = '0x' + expectedCode.slice(20, expectedCode.length); - - const suffixData = bufferToHex( - TypedDataUtils.encodeData( - dataToSign.primaryType, - dataToSign.message, - dataToSign.types - ).slice((1 + DeployRequestDataType.length) * 32) - ); - const { logs } = await factory.relayedUserSmartWalletCreation( - req.request, - suffixData, - from, - sig, - { - from - } - ); - - const code = await web3.eth.getCode( - expectedAddress, - logs[0].blockNumber - ); - - chai.expect(web3.utils.toBN(expectedCode)).to.be.bignumber.equal( - web3.utils.toBN(code) - ); - }); - - it('should revert for an invalid signature', async () => { - const logicAddress = constants.ZERO_ADDRESS; - const initParams = '0x'; - const deployPrice = '0x01'; // 1 token - const recoverer = constants.ZERO_ADDRESS; - const index = '0'; - - const expectedAddress = await factory.getSmartWalletAddress( - ownerAddress, - recoverer, - logicAddress, - soliditySha3Raw({ t: 'bytes', v: initParams }), - index - ); - - const originalBalance = await token.balanceOf(expectedAddress); - - const req: DeployRequest = { - request: { - ...request.request, - tokenContract: token.address, - tokenAmount: deployPrice - }, - relayData: { - ...request.relayData - } - }; - req.request.relayHub = from; - - const dataToSign = new TypedDeployRequestData( - env.chainId, - factory.address, - req - ); - - const sig = signTypedData_v4(ownerPrivateKey, { data: dataToSign }); - - req.request.tokenAmount = '9'; - - const suffixData = bufferToHex( - TypedDataUtils.encodeData( - dataToSign.primaryType, - dataToSign.message, - dataToSign.types - ).slice((1 + DeployRequestDataType.length) * 32) - ); - - await expectRevert( - factory.relayedUserSmartWalletCreation( - req.request, - suffixData, - constants.ZERO_ADDRESS, - sig - ), - 'Signature mismatch' - ); - - const newBalance = await token.balanceOf(expectedAddress); - chai.expect(originalBalance).to.be.bignumber.equal(newBalance); - }); - - it('should not initialize if a second initialize() call to the Smart Wallet is attempted', async () => { - const logicAddress = constants.ZERO_ADDRESS; - const initParams = '0x'; - const deployPrice = '0x01'; // 1 token - const recoverer = constants.ZERO_ADDRESS; - const index = '0'; - - const expectedAddress = await factory.getSmartWalletAddress( - ownerAddress, - recoverer, - logicAddress, - soliditySha3Raw({ t: 'bytes', v: initParams }), - index - ); - - token = await TestToken.new(); - await token.mint('200', expectedAddress); - - const originalBalance = await token.balanceOf(expectedAddress); - - const req: DeployRequest = { - request: { - ...request.request, - tokenContract: token.address, - tokenAmount: deployPrice - }, - relayData: { - ...request.relayData - } - }; - req.request.relayHub = from; - - const dataToSign = new TypedDeployRequestData( - env.chainId, - factory.address, - req - ); - - const sig = signTypedData_v4(ownerPrivateKey, { data: dataToSign }); - const suffixData = bufferToHex( - TypedDataUtils.encodeData( - dataToSign.primaryType, - dataToSign.message, - dataToSign.types - ).slice((1 + DeployRequestDataType.length) * 32) - ); - - const { logs } = await factory.relayedUserSmartWalletCreation( - req.request, - suffixData, - from, - sig - ); - - const salt = - web3.utils.soliditySha3( - { t: 'address', v: ownerAddress }, - { t: 'address', v: recoverer }, - { t: 'address', v: logicAddress }, - { - t: 'bytes32', - v: soliditySha3Raw({ t: 'bytes', v: initParams }) - }, - { t: 'uint256', v: index } - ) ?? ''; - - const expectedSalt = web3.utils.toBN(salt).toString(); - - // Check the emitted event - expectEvent.inLogs(logs, 'Deployed', { - addr: expectedAddress, - salt: expectedSalt - }); - - // The Smart Wallet should have been charged for the deploy - const newBalance = await token.balanceOf(expectedAddress); - const expectedBalance = originalBalance.sub( - web3.utils.toBN(deployPrice) - ); - chai.expect(expectedBalance).to.be.bignumber.equal(newBalance); - - const isInitializedFunc = web3.eth.abi.encodeFunctionCall( - { - name: 'isInitialized', - type: 'function', - inputs: [] - }, - [] - ); - - const trx = await web3.eth.getTransaction(logs[0].transactionHash); - - const newTrx = { - from: trx.from, - gas: trx.gas, - to: expectedAddress, - gasPrice: trx.gasPrice, - value: trx.value, - data: isInitializedFunc - }; - - // Call the isInitialized function - let result = await web3.eth.call(newTrx); - - let resultStr = result as string; - - // It should be initialized - chai.expect(web3.utils.toBN(1)).to.be.bignumber.equal( - web3.utils.toBN(resultStr) - ); - - const initFunc = web3.eth.abi.encodeFunctionCall( - { - name: 'initialize', - type: 'function', - inputs: [ - { - type: 'address', - name: 'owner' - }, - { - type: 'address', - name: 'logic' - }, - { - type: 'address', - name: 'tokenAddr' - }, - { - type: 'address', - name: 'tokenRecipient' - }, - { - type: 'uint256', - name: 'tokenAmount' - }, - { - type: 'uint256', - name: 'tokenGas' - }, - { - type: 'bytes', - name: 'initParams' - } - ] - }, - [ - ownerAddress, - logicAddress, - token.address, - recipientAddress, - deployPrice, - '0xD6D8', - initParams - ] - ); - - newTrx.data = initFunc; - - // Trying to manually call the initialize function again (it was called during deploy) - await expectRevert( - web3.eth.sendTransaction(newTrx), - 'already initialized' - ); - - newTrx.data = isInitializedFunc; - - result = await web3.eth.call(newTrx); - resultStr = result as string; - - // The smart wallet should be still initialized - chai.expect(web3.utils.toBN(1)).to.be.bignumber.equal( - web3.utils.toBN(resultStr) - ); - }); - }); -}); - -contract('SmartWalletFactory', ([from]) => { - let fwd: SmartWalletInstance; - let token: TestTokenInstance; - let factory: SmartWalletFactoryInstance; - const ownerPrivateKey = toBuffer(bytes32(1)); - let ownerAddress: string; - const recipientPrivateKey = toBuffer(bytes32(1)); - let recipientAddress: string; - const SmartWallet = artifacts.require('SmartWallet'); - const SmartWalletFactory = artifacts.require('SmartWalletFactory'); - let env: Environment; - - const request: DeployRequest = { - request: { - relayHub: from, - from: constants.ZERO_ADDRESS, - to: constants.ZERO_ADDRESS, - value: '0', - nonce: '0', - data: '0x', - tokenContract: constants.ZERO_ADDRESS, - tokenAmount: '1', - tokenGas: '50000', - recoverer: constants.ZERO_ADDRESS, - index: '0', - validUntilTime: '0' - }, - relayData: { - gasPrice: '1', - feesReceiver: constants.ZERO_ADDRESS, - callForwarder: constants.ZERO_ADDRESS, - callVerifier: constants.ZERO_ADDRESS - } - }; - - before(async () => { - ownerAddress = bufferToHex( - privateToAddress(ownerPrivateKey) - ).toLowerCase(); - recipientAddress = bufferToHex( - privateToAddress(recipientPrivateKey) - ).toLowerCase(); - request.request.from = ownerAddress; - env = await getTestingEnvironment(); - fwd = await SmartWallet.new(); - }); - - beforeEach(async () => { - // A new factory for new create2 addresses each - factory = await SmartWalletFactory.new(fwd.address); - request.relayData.callForwarder = factory.address; - }); - - describe('#getCreationBytecode', () => { - it('should return the expected bytecode', async () => { - const expectedCode = - '0x602D3D8160093D39F3363D3D373D3D3D3D363D73' + - stripHex(fwd.address) + - '5AF43D923D90803E602B57FD5BF3'; - - const code = await factory.getCreationBytecode(); - chai.expect(web3.utils.toBN(expectedCode)).to.be.bignumber.equal( - web3.utils.toBN(code) - ); - }); - }); - - describe('#getRuntimeCodeHash', () => { - it('should return the expected code hash', async () => { - const expectedCode = - '0x363D3D373D3D3D3D363D73' + - stripHex(fwd.address) + - '5AF43D923D90803E602B57FD5BF3'; - const expectedCodeHash = keccak256(expectedCode); - - const code = await factory.runtimeCodeHash(); - chai.expect( - web3.utils.toBN(expectedCodeHash) - ).to.be.bignumber.equal(web3.utils.toBN(code)); - }); - }); - - describe('#getSmartWalletAddress', () => { - it('should create the correct create2 Address', async () => { - const recoverer = constants.ZERO_ADDRESS; - const index = '0'; - const create2Address = await factory.getSmartWalletAddress( - ownerAddress, - recoverer, - index - ); - const creationByteCode = await factory.getCreationBytecode(); - - const salt: string = - web3.utils.soliditySha3( - { t: 'address', v: ownerAddress }, - { t: 'address', v: recoverer }, - { t: 'uint256', v: index } - ) ?? ''; - - const bytecodeHash: string = - web3.utils.soliditySha3({ t: 'bytes', v: creationByteCode }) ?? - ''; - - const _data: string = - web3.utils.soliditySha3( - { t: 'bytes1', v: '0xff' }, - { t: 'address', v: factory.address }, - { t: 'bytes32', v: salt }, - { t: 'bytes32', v: bytecodeHash } - ) ?? ''; - - const expectedAddress = toChecksumAddress( - '0x' + _data.slice(26, _data.length), - env.chainId - ); - assert.equal(create2Address, expectedAddress); - }); - }); - - describe('#createUserSmartWallet', () => { - it('should create the Smart Wallet in the expected address', async () => { - const recoverer = constants.ZERO_ADDRESS; - const index = '0'; - - const message: string = - web3.utils.soliditySha3( - { t: 'address', v: factory.address }, - { t: 'address', v: ownerAddress }, - { t: 'address', v: recoverer }, - { t: 'uint256', v: index } - ) ?? ''; - - const toSign: string = - web3.utils.soliditySha3( - { - t: 'string', - v: - PERSONAL_SIGN_PREFIX + - web3.utils.hexToBytes(message).length - }, - { t: 'bytes32', v: message } - ) ?? ''; - - const toSignAsBinaryArray = ethers.utils.arrayify(toSign); - const signingKey = new ethers.utils.SigningKey(ownerPrivateKey); - const signature = signingKey.signDigest(toSignAsBinaryArray); - const signatureCollapsed = ethers.utils.joinSignature(signature); - - const { logs } = await factory.createUserSmartWallet( - ownerAddress, - recoverer, - index, - signatureCollapsed - ); - - const expectedAddress = await factory.getSmartWalletAddress( - ownerAddress, - recoverer, - index - ); - - const salt = - web3.utils.soliditySha3( - { t: 'address', v: ownerAddress }, - { t: 'address', v: recoverer }, - { t: 'uint256', v: index } - ) ?? ''; - - const expectedSalt = web3.utils.toBN(salt).toString(); - - expectEvent.inLogs(logs, 'Deployed', { - addr: expectedAddress, - salt: expectedSalt - }); - }); - - it('should create the Smart Wallet with the expected proxy code', async () => { - const recoverer = constants.ZERO_ADDRESS; - const index = '0'; - - const expectedAddress = await factory.getSmartWalletAddress( - ownerAddress, - recoverer, - index - ); - - const message: string = - web3.utils.soliditySha3( - { t: 'address', v: factory.address }, - { t: 'address', v: ownerAddress }, - { t: 'address', v: recoverer }, - { t: 'uint256', v: index } - ) ?? ''; - - const toSign: string = - web3.utils.soliditySha3( - { - t: 'string', - v: - PERSONAL_SIGN_PREFIX + - web3.utils.hexToBytes(message).length - }, - { t: 'bytes32', v: message } - ) ?? ''; - - const toSignAsBinaryArray = ethers.utils.arrayify(toSign); - const signingKey = new ethers.utils.SigningKey(ownerPrivateKey); - const signature = signingKey.signDigest(toSignAsBinaryArray); - const signatureCollapsed = ethers.utils.joinSignature(signature); - - // expectedCode = runtime code only - let expectedCode = await factory.getCreationBytecode(); - expectedCode = '0x' + expectedCode.slice(20, expectedCode.length); - - const { logs } = await factory.createUserSmartWallet( - ownerAddress, - recoverer, - index, - signatureCollapsed - ); - - const code = await web3.eth.getCode( - expectedAddress, - logs[0].blockNumber - ); - - chai.expect(web3.utils.toBN(expectedCode)).to.be.bignumber.equal( - web3.utils.toBN(code) - ); - }); - - it('should revert for an invalid signature', async () => { - const recoverer = constants.ZERO_ADDRESS; - const index = '0'; - - const message: string = - web3.utils.soliditySha3( - { t: 'address', v: factory.address }, - { t: 'address', v: ownerAddress }, - { t: 'address', v: recoverer }, - { t: 'uint256', v: index } - ) ?? ''; - - const toSign: string = - web3.utils.soliditySha3( - { - t: 'string', - v: - PERSONAL_SIGN_PREFIX + - web3.utils.hexToBytes(message).length - }, - { t: 'bytes32', v: message } - ) ?? ''; - - const toSignAsBinaryArray = ethers.utils.arrayify(toSign); - const signingKey = new ethers.utils.SigningKey(ownerPrivateKey); - const signature = signingKey.signDigest(toSignAsBinaryArray); - let signatureCollapsed: string = - ethers.utils.joinSignature(signature); - - signatureCollapsed = signatureCollapsed - .substr(0, signatureCollapsed.length - 1) - .concat('0'); - - await expectRevert( - factory.createUserSmartWallet( - ownerAddress, - recoverer, - index, - signatureCollapsed - ), - 'invalid signature' - ); - }); - - it('should not initialize if a second initialize() call to the Smart Wallet is attempted', async () => { - const recoverer = constants.ZERO_ADDRESS; - const index = '0'; - - const expectedAddress = await factory.getSmartWalletAddress( - ownerAddress, - recoverer, - index - ); - - const message: string = - web3.utils.soliditySha3( - { t: 'address', v: factory.address }, - { t: 'address', v: ownerAddress }, - { t: 'address', v: recoverer }, - { t: 'uint256', v: index } - ) ?? ''; - - const toSign: string = - web3.utils.soliditySha3( - { - t: 'string', - v: - PERSONAL_SIGN_PREFIX + - web3.utils.hexToBytes(message).length - }, - { t: 'bytes32', v: message } - ) ?? ''; - - const toSignAsBinaryArray = ethers.utils.arrayify(toSign); - const signingKey = new ethers.utils.SigningKey(ownerPrivateKey); - const signature = signingKey.signDigest(toSignAsBinaryArray); - const signatureCollapsed = ethers.utils.joinSignature(signature); - - const { logs } = await factory.createUserSmartWallet( - ownerAddress, - recoverer, - index, - signatureCollapsed - ); - - const salt = - web3.utils.soliditySha3( - { t: 'address', v: ownerAddress }, - { t: 'address', v: recoverer }, - { t: 'uint256', v: index } - ) ?? ''; - - const expectedSalt = web3.utils.toBN(salt).toString(); - - // Check the emitted event - expectEvent.inLogs(logs, 'Deployed', { - addr: expectedAddress, - salt: expectedSalt - }); - - const isInitializedFunc = web3.eth.abi.encodeFunctionCall( - { - name: 'isInitialized', - type: 'function', - inputs: [] - }, - [] - ); - - const trx = await web3.eth.getTransaction(logs[0].transactionHash); - - const newTrx = { - from: trx.from, - gas: trx.gas, - to: expectedAddress, - gasPrice: trx.gasPrice, - value: trx.value, - data: isInitializedFunc - }; - - // Call the isInitialized function - let result = await web3.eth.call(newTrx); - - let resultStr = result as string; - - // It should be initialized - chai.expect(web3.utils.toBN(1)).to.be.bignumber.equal( - web3.utils.toBN(resultStr) - ); - - const initFunc = await web3.eth.abi.encodeFunctionCall( - { - name: 'initialize', - type: 'function', - inputs: [ - { - type: 'address', - name: 'owner' - }, - { - type: 'address', - name: 'tokenAddr' - }, - { - type: 'address', - name: 'tokenRecipient' - }, - { - type: 'uint256', - name: 'tokenAmount' - }, - { - type: 'uint256', - name: 'tokenGas' - } - ] - }, - [ - ownerAddress, - constants.ZERO_ADDRESS, - constants.ZERO_ADDRESS, - '0x00', - '0x00' - ] - ); - - newTrx.data = initFunc; - - // Trying to manually call the initialize function again (it was called during deploy) - await expectRevert( - web3.eth.sendTransaction(newTrx), - 'already initialized' - ); - - newTrx.data = isInitializedFunc; - - result = await web3.eth.call(newTrx); - resultStr = result as string; - - // The smart wallet should be still initialized - chai.expect(web3.utils.toBN(1)).to.be.bignumber.equal( - web3.utils.toBN(resultStr) - ); - }); - }); - - describe('#relayedUserSmartWalletCreation', () => { - it('should create the Smart Wallet in the expected address', async () => { - const deployPrice = '0x01'; // 1 token - const recoverer = constants.ZERO_ADDRESS; - const index = '0'; - - const expectedAddress = await factory.getSmartWalletAddress( - ownerAddress, - recoverer, - index - ); - - token = await TestToken.new(); - await token.mint('200', expectedAddress); - - const originalBalance = await token.balanceOf(expectedAddress); - - const req: DeployRequest = { - request: { - ...request.request, - tokenContract: token.address, - tokenAmount: deployPrice - }, - relayData: { - ...request.relayData - } - }; - - const dataToSign = new TypedDeployRequestData( - env.chainId, - factory.address, - req - ); - - const sig = signTypedData_v4(ownerPrivateKey, { data: dataToSign }); - - // relayData information - const suffixData = bufferToHex( - TypedDataUtils.encodeData( - dataToSign.primaryType, - dataToSign.message, - dataToSign.types - ).slice((1 + DeployRequestDataType.length) * 32) - ); - - const { logs } = await factory.relayedUserSmartWalletCreation( - req.request, - suffixData, - from, - sig - ); - - const salt = - web3.utils.soliditySha3( - { t: 'address', v: ownerAddress }, - { t: 'address', v: recoverer }, - { t: 'uint256', v: index } - ) ?? ''; - - const expectedSalt = web3.utils.toBN(salt).toString(); - - // Check the emitted event - expectEvent.inLogs(logs, 'Deployed', { - addr: expectedAddress, - salt: expectedSalt - }); - - // The Smart Wallet should have been charged for the deploy - const newBalance = await token.balanceOf(expectedAddress); - const expectedBalance = originalBalance.sub( - web3.utils.toBN(deployPrice) - ); - chai.expect(expectedBalance).to.be.bignumber.equal(newBalance); - }); - - it('should create the Smart Wallet with the expected proxy code', async () => { - const deployPrice = '0x01'; // 1 token - const recoverer = constants.ZERO_ADDRESS; - const index = '0'; - - const expectedAddress = await factory.getSmartWalletAddress( - ownerAddress, - recoverer, - index - ); - - token = await TestToken.new(); - await token.mint('200', expectedAddress); - - const req: DeployRequest = { - request: { - ...request.request, - tokenContract: token.address, - tokenAmount: deployPrice - }, - relayData: { - ...request.relayData - } - }; - - const dataToSign = new TypedDeployRequestData( - env.chainId, - factory.address, - req - ); - - const sig = signTypedData_v4(ownerPrivateKey, { data: dataToSign }); - - // expectedCode = runtime code only - let expectedCode = await factory.getCreationBytecode(); - expectedCode = '0x' + expectedCode.slice(20, expectedCode.length); - - const suffixData = bufferToHex( - TypedDataUtils.encodeData( - dataToSign.primaryType, - dataToSign.message, - dataToSign.types - ).slice((1 + DeployRequestDataType.length) * 32) - ); - const { logs } = await factory.relayedUserSmartWalletCreation( - req.request, - suffixData, - from, - sig - ); - - const code = await web3.eth.getCode( - expectedAddress, - logs[0].blockNumber - ); - - chai.expect(web3.utils.toBN(expectedCode)).to.be.bignumber.equal( - web3.utils.toBN(code) - ); - }); - - it('should revert for an invalid signature', async () => { - const deployPrice = '0x01'; // 1 token - const recoverer = constants.ZERO_ADDRESS; - const index = '0'; - - const expectedAddress = await factory.getSmartWalletAddress( - ownerAddress, - recoverer, - index - ); - - const originalBalance = await token.balanceOf(expectedAddress); - - const req: DeployRequest = { - request: { - ...request.request, - tokenContract: token.address, - tokenAmount: deployPrice - }, - relayData: { - ...request.relayData - } - }; - req.request.relayHub = from; - - const dataToSign = new TypedDeployRequestData( - env.chainId, - factory.address, - req - ); - - const sig = signTypedData_v4(ownerPrivateKey, { data: dataToSign }); - - req.request.tokenAmount = '8'; // change data after signature - - const suffixData = bufferToHex( - TypedDataUtils.encodeData( - dataToSign.primaryType, - dataToSign.message, - dataToSign.types - ).slice((1 + DeployRequestDataType.length) * 32) - ); - - await expectRevert( - factory.relayedUserSmartWalletCreation( - req.request, - suffixData, - constants.ZERO_ADDRESS, - sig - ), - 'Signature mismatch' - ); - - const newBalance = await token.balanceOf(expectedAddress); - chai.expect(originalBalance).to.be.bignumber.equal(newBalance); - }); - - it('should not initialize if a second initialize() call to the Smart Wallet is attempted', async () => { - const deployPrice = '0x01'; // 1 token - const recoverer = constants.ZERO_ADDRESS; - const index = '0'; - - const expectedAddress = await factory.getSmartWalletAddress( - ownerAddress, - recoverer, - index - ); - - token = await TestToken.new(); - await token.mint('200', expectedAddress); - - const originalBalance = await token.balanceOf(expectedAddress); - - const req: DeployRequest = { - request: { - ...request.request, - tokenContract: token.address, - tokenAmount: deployPrice - }, - relayData: { - ...request.relayData - } - }; - - const dataToSign = new TypedDeployRequestData( - env.chainId, - factory.address, - req - ); - - const sig = signTypedData_v4(ownerPrivateKey, { data: dataToSign }); - const suffixData = bufferToHex( - TypedDataUtils.encodeData( - dataToSign.primaryType, - dataToSign.message, - dataToSign.types - ).slice((1 + DeployRequestDataType.length) * 32) - ); - - const { logs } = await factory.relayedUserSmartWalletCreation( - req.request, - suffixData, - from, - sig - ); - - const salt = - web3.utils.soliditySha3( - { t: 'address', v: ownerAddress }, - { t: 'address', v: recoverer }, - { t: 'uint256', v: index } - ) ?? ''; - - const expectedSalt = web3.utils.toBN(salt).toString(); - - // Check the emitted event - expectEvent.inLogs(logs, 'Deployed', { - addr: expectedAddress, - salt: expectedSalt - }); - - // The Smart Wallet should have been charged for the deploy - const newBalance = await token.balanceOf(expectedAddress); - const expectedBalance = originalBalance.sub( - web3.utils.toBN(deployPrice) - ); - chai.expect(expectedBalance).to.be.bignumber.equal(newBalance); - - const isInitializedFunc = web3.eth.abi.encodeFunctionCall( - { - name: 'isInitialized', - type: 'function', - inputs: [] - }, - [] - ); - - const trx = await web3.eth.getTransaction(logs[0].transactionHash); - - const newTrx = { - from: trx.from, - gas: trx.gas, - to: expectedAddress, - gasPrice: trx.gasPrice, - value: trx.value, - data: isInitializedFunc - }; - - // Call the isInitialized function - let result = await web3.eth.call(newTrx); - - let resultStr = result as string; - - // It should be initialized - chai.expect(web3.utils.toBN(1)).to.be.bignumber.equal( - web3.utils.toBN(resultStr) - ); - - const initFunc = await web3.eth.abi.encodeFunctionCall( - { - name: 'initialize', - type: 'function', - inputs: [ - { - type: 'address', - name: 'owner' - }, - { - type: 'address', - name: 'tokenAddr' - }, - { - type: 'address', - name: 'tokenRecipient' - }, - { - type: 'uint256', - name: 'tokenAmount' - }, - { - type: 'uint256', - name: 'tokenGas' - } - ] - }, - [ - ownerAddress, - token.address, - recipientAddress, - deployPrice, - '0xD6D8' - ] - ); - - newTrx.data = initFunc; - - // Trying to manually call the initialize function again (it was called during deploy) - await expectRevert( - web3.eth.sendTransaction(newTrx), - 'already initialized' - ); - - newTrx.data = isInitializedFunc; - - result = await web3.eth.call(newTrx); - resultStr = result as string; - - // The smart wallet should be still initialized - chai.expect(web3.utils.toBN(1)).to.be.bignumber.equal( - web3.utils.toBN(resultStr) - ); - }); - }); -}); diff --git a/test-to-migrate/common/VersionManager.test.ts b/test-to-migrate/common/VersionManager.test.ts deleted file mode 100644 index b32cd622..00000000 --- a/test-to-migrate/common/VersionManager.test.ts +++ /dev/null @@ -1,36 +0,0 @@ -/* eslint-disable no-new */ -import { VersionsManager } from '@rsksmart/rif-relay-common'; - -describe('VersionManager', function () { - context('constructor', function () { - it('should throw on invalid semver string', function () { - expect(function () { - new VersionsManager('v.1.0'); - }).to.throw('Component version is not valid'); - }); - it('should not throw on valid semver string', function () { - new VersionsManager('2.0.1-beta.1+enveloping.something'); - }); - }); - - context('#isMinorSameOrNewer()', function () { - const manager = new VersionsManager('1.2.3'); - it('should return true if version is same or newer', function () { - const isNewerSame = manager.isMinorSameOrNewer('1.2.4'); - const isNewerPatch = manager.isMinorSameOrNewer('1.2.4'); - const isNewerMinor = manager.isMinorSameOrNewer('1.2.4'); - assert.isTrue(isNewerSame); - assert.isTrue(isNewerPatch); - assert.isTrue(isNewerMinor); - - const isNewerMajor = manager.isMinorSameOrNewer('2.3.4'); - const isNewerPatchFalse = manager.isMinorSameOrNewer('1.2.0'); - const isNewerMinorFalse = manager.isMinorSameOrNewer('1.1.0'); - const isNewerMajorFalse = manager.isMinorSameOrNewer('0.2.3'); - assert.isFalse(isNewerMajor); - assert.isFalse(isNewerPatchFalse); - assert.isFalse(isNewerMinorFalse); - assert.isFalse(isNewerMajorFalse); - }); - }); -}); diff --git a/test-to-migrate/dummies/BadContractInteractor.ts b/test-to-migrate/dummies/BadContractInteractor.ts deleted file mode 100644 index d0aa5467..00000000 --- a/test-to-migrate/dummies/BadContractInteractor.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { - ContractInteractor, - Web3Provider, - EnvelopingConfig -} from '@rsksmart/rif-relay-common'; -import { RelayRequest } from '@rsksmart/rif-relay-contracts'; -import { TransactionReceipt } from 'web3-core'; - -export default class BadContractInteractor extends ContractInteractor { - static readonly message = 'This is not the contract you are looking for'; - static readonly wrongNonceMessage = "the tx doesn't have the correct nonce"; - - private readonly failValidateARC: boolean; - - constructor( - provider: Web3Provider, - config: EnvelopingConfig, - failValidateARC: boolean - ) { - super(provider, config); - this.failValidateARC = failValidateARC; - } - - async validateAcceptRelayCall( - relayRequest: RelayRequest, - signature: string, - workerAddress: string - ): Promise<{ - verifierAccepted: boolean; - returnValue: string; - reverted: boolean; - revertedInDestination: boolean; - }> { - if (this.failValidateARC) { - return { - verifierAccepted: false, - reverted: true, - returnValue: BadContractInteractor.message, - revertedInDestination: false - }; - } - return await super.validateAcceptRelayCall( - relayRequest, - signature, - workerAddress - ); - } - - // eslint-disable-next-line @typescript-eslint/require-await - async sendSignedTransaction(rawTx: string): Promise { - console.debug('rawTx', rawTx); - throw new Error(BadContractInteractor.wrongNonceMessage); - } -} diff --git a/test-to-migrate/dummies/BadHttpClient.ts b/test-to-migrate/dummies/BadHttpClient.ts deleted file mode 100644 index cbad0c8b..00000000 --- a/test-to-migrate/dummies/BadHttpClient.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { PrefixedHexString } from 'ethereumjs-tx'; -import { HttpClient, HttpWrapper } from '@rsksmart/rif-relay-client'; -import { - PingResponse, - EnvelopingConfig, - RelayTransactionRequest -} from '@rsksmart/rif-relay-common'; - -export default class BadHttpClient extends HttpClient { - static readonly message = 'This is not the relay you are looking for'; - - private readonly failRelay: boolean; - private readonly failPing: boolean; - private readonly timeoutRelay: boolean; - private readonly stubRelay: string | undefined; - private readonly stubPing: PingResponse | undefined; - - constructor( - config: EnvelopingConfig, - failPing: boolean, - failRelay: boolean, - timeoutRelay: boolean, - stubPing?: PingResponse, - stubRelay?: string - ) { - super(new HttpWrapper(), config); - this.failPing = failPing; - this.failRelay = failRelay; - this.timeoutRelay = timeoutRelay; - this.stubRelay = stubRelay; - this.stubPing = stubPing; - } - - async getPingResponse( - relayUrl: string, - verifier?: string - ): Promise { - if (this.failPing) { - throw new Error(BadHttpClient.message); - } - if (this.stubPing != null) { - return this.stubPing; - } - return await super.getPingResponse(relayUrl, verifier); - } - - async relayTransaction( - relayUrl: string, - request: RelayTransactionRequest - ): Promise { - if (this.failRelay) { - throw new Error(BadHttpClient.message); - } - if (this.timeoutRelay) { - throw new Error( - 'some error describing how timeout occurred somewhere' - ); - } - if (this.stubRelay != null) { - return this.stubRelay; - } - return await super.relayTransaction(relayUrl, request); - } -} diff --git a/test-to-migrate/dummies/BadRelayClient.ts b/test-to-migrate/dummies/BadRelayClient.ts deleted file mode 100644 index 73b8df2e..00000000 --- a/test-to-migrate/dummies/BadRelayClient.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { RelayClient, RelayingResult } from '@rsksmart/rif-relay-client'; -import { - EnvelopingTransactionDetails, - EnvelopingConfig -} from '@rsksmart/rif-relay-common'; -import { HttpProvider } from 'web3-core'; - -export default class BadRelayClient extends RelayClient { - static readonly message = 'This is not the transaction you are looking for'; - - private readonly failRelay: boolean; - private readonly returnUndefindedTransaction: boolean; - - constructor( - failRelay: boolean, - returnNullTransaction: boolean, - provider: HttpProvider, - config: EnvelopingConfig - ) { - super(provider, config); - this.failRelay = failRelay; - this.returnUndefindedTransaction = returnNullTransaction; - } - - async relayTransaction( - transactionDetails: EnvelopingTransactionDetails - ): Promise { - if (this.failRelay) { - throw new Error(BadRelayClient.message); - } - if (this.returnUndefindedTransaction) { - return { - transaction: undefined, - pingErrors: new Map(), - relayingErrors: new Map() - }; - } - return await super.relayTransaction(transactionDetails); - } -} diff --git a/test-to-migrate/dummies/BadRelayedTransactionValidator.ts b/test-to-migrate/dummies/BadRelayedTransactionValidator.ts deleted file mode 100644 index f9b64821..00000000 --- a/test-to-migrate/dummies/BadRelayedTransactionValidator.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { RelayedTransactionValidator } from '@rsksmart/rif-relay-client'; -import { - ContractInteractor, - EnvelopingConfig, - RelayTransactionRequest -} from '@rsksmart/rif-relay-common'; - -export default class BadRelayedTransactionValidator extends RelayedTransactionValidator { - private readonly failValidation: boolean; - - constructor( - failValidation: boolean, - contractInteractor: ContractInteractor, - config: EnvelopingConfig - ) { - super(contractInteractor, config); - this.failValidation = failValidation; - } - - validateRelayResponse( - transactionJsonRequest: RelayTransactionRequest, - returnedTx: string, - relayWorker: string - ): boolean { - if (this.failValidation) { - return false; - } - return super.validateRelayResponse( - transactionJsonRequest, - returnedTx, - relayWorker - ); - } -} diff --git a/test-to-migrate/regressions/PayableWithEmit.test.ts b/test-to-migrate/regressions/PayableWithEmit.test.ts deleted file mode 100644 index 815a173c..00000000 --- a/test-to-migrate/regressions/PayableWithEmit.test.ts +++ /dev/null @@ -1,19 +0,0 @@ -const PayableWithEmit = artifacts.require('PayableWithEmit'); - -contract('PayableWithEmit', () => { - let sender: any; - let receiver: any; - - before(async () => { - receiver = await PayableWithEmit.new(); - sender = await PayableWithEmit.new(); - }); - it('payable that uses _msgSender()', async () => { - const ret = await sender.doSend(receiver.address, { value: 1e18 }); - // console.log({ gasUsed: ret.receipt.gasUsed, log: getLogs(ret) }) - assert.equal( - ret.logs.find((e: any) => e.event === 'GasUsed').args.success, - true - ); - }); -}); diff --git a/test-to-migrate/relayclient/AccountManager.test.ts b/test-to-migrate/relayclient/AccountManager.test.ts deleted file mode 100644 index 77b9b1b7..00000000 --- a/test-to-migrate/relayclient/AccountManager.test.ts +++ /dev/null @@ -1,237 +0,0 @@ -import { - AccountManager, - AccountKeypair, - configure -} from '@rsksmart/rif-relay-client'; -import { - defaultEnvironment, - isSameAddress, - constants -} from '@rsksmart/rif-relay-common'; -import { RelayRequest, TypedRequestData } from '@rsksmart/rif-relay-contracts'; -import { HttpProvider } from 'web3-core'; -import sinon from 'sinon'; -import sigUtil from 'eth-sig-util'; -import chai from 'chai'; -import sinonChai from 'sinon-chai'; -import chaiAsPromised from 'chai-as-promised'; -import { getGaslessAccount } from '../TestUtils'; - -const { expect, assert } = chai.use(chaiAsPromised); - -chai.use(sinonChai); - -contract('AccountManager', function () { - const address = '0x982a8CbE734cb8c29A6a7E02a3B0e4512148F6F9'; - const keypair = { - privateKey: Buffer.from( - 'd353907ab062133759f149a3afcb951f0f746a65a60f351ba05a3ebf26b67f5c', - 'hex' - ), - address - }; - const config = configure({ - methodSuffix: '', - jsonStringifyRequest: false - }); - - let shouldThrow = false; - - describe('#addAccount()', function () { - it('should save the provided keypair internally', async function () { - const account = await getGaslessAccount(); - const accountManager = new AccountManager( - web3.currentProvider as HttpProvider, - defaultEnvironment.chainId, - config, - async (signedData: any): Promise => { - if (shouldThrow) { - throw new Error('Fail of testing'); - } - // @ts-ignore - return sigUtil.signTypedData_v4(account.privateKey, { - data: signedData - }); - } - ) as any; - sinon.spy(accountManager); - - accountManager.addAccount(keypair); - assert.equal( - accountManager.accounts[0].privateKey.toString(), - keypair.privateKey.toString() - ); - assert.equal(accountManager.accounts[0].address, keypair.address); - }); - - it('should throw if the provided keypair is not valid', async function () { - const account = await getGaslessAccount(); - const accountManager = new AccountManager( - web3.currentProvider as HttpProvider, - defaultEnvironment.chainId, - config, - async (signedData: any): Promise => { - if (shouldThrow) { - throw new Error('Fail of testing'); - } - // @ts-ignore - return sigUtil.signTypedData_v4(account.privateKey, { - data: signedData - }); - } - ); - // @ts-ignore - sinon.spy(accountManager); - - const keypair = { - privateKey: Buffer.from( - 'AAAAAAAAAAAAA6a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d', - 'hex' - ), - address - }; - - expect(() => { - accountManager.addAccount(keypair); - }).to.throw('invalid keypair'); - }); - }); - describe('#newAccount()', function () { - it('should create a new keypair, return it and save it internally', async function () { - const account = await getGaslessAccount(); - const accountManager = new AccountManager( - web3.currentProvider as HttpProvider, - defaultEnvironment.chainId, - config, - async (signedData: any): Promise => { - if (shouldThrow) { - throw new Error('Fail of testing'); - } - // @ts-ignore - return sigUtil.signTypedData_v4(account.privateKey, { - data: signedData - }); - } - ) as any; - sinon.spy(accountManager); - const keypair = accountManager.newAccount(); - assert.equal( - accountManager.accounts[0].privateKey.toString(), - keypair.privateKey.toString() - ); - assert.equal(accountManager.getAccounts()[0], keypair.address); - }); - }); - - describe('#sign()', function () { - let account: AccountKeypair; - let accountManager: AccountManager; - shouldThrow = false; - - const relayRequest: RelayRequest = { - request: { - relayHub: constants.ZERO_ADDRESS, - to: constants.ZERO_ADDRESS, - data: '0x123', - from: '', - nonce: '1', - value: '0', - gas: '1', - tokenContract: constants.ZERO_ADDRESS, - tokenAmount: '0', - tokenGas: '0', - validUntilTime: '0' - }, - relayData: { - gasPrice: '1', - feesReceiver: constants.ZERO_ADDRESS, - callForwarder: constants.ZERO_ADDRESS, - callVerifier: constants.ZERO_ADDRESS - } - }; - beforeEach(async function () { - account = await getGaslessAccount(); - accountManager = new AccountManager( - web3.currentProvider as HttpProvider, - defaultEnvironment.chainId, - config, - async (signedData: any): Promise => { - if (shouldThrow) { - throw new Error('Fail of testing'); - } - // @ts-ignore - return sigUtil.signTypedData_v4(account.privateKey, { - data: signedData - }); - } - ); - // @ts-ignore - sinon.spy(accountManager); - accountManager.addAccount(keypair); - sinon.resetHistory(); - }); - - function relayRequestWithoutExtraData( - relayRequest: RelayRequest - ): RelayRequest { - const cloneRequest = { ...relayRequest }; - return cloneRequest; - } - - it('should use internally controlled keypair for signing if available', async function () { - relayRequest.request.from = address; - const signedData = new TypedRequestData( - defaultEnvironment.chainId, - constants.ZERO_ADDRESS, - relayRequestWithoutExtraData(relayRequest) - ); - const signature = await accountManager.sign(relayRequest); - // @ts-ignore - const rec = sigUtil.recoverTypedSignature_v4({ - data: signedData, - sig: signature - }); - assert.ok( - isSameAddress(relayRequest.request.from.toLowerCase(), rec) - ); - expect( - accountManager._signWithControlledKey - ).to.have.been.calledWith(keypair, signedData); - expect(accountManager._signWithProvider).to.have.not.been.called; - }); - it('should ask provider to sign if key is not controlled', async function () { - relayRequest.request.from = account.address; - - const signedData = new TypedRequestData( - defaultEnvironment.chainId, - constants.ZERO_ADDRESS, - relayRequestWithoutExtraData(relayRequest) - ); - - const signature = await accountManager.sign(relayRequest); - // @ts-ignore - const rec = sigUtil.recoverTypedSignature_v4({ - data: signedData, - sig: signature - }); - assert.ok( - isSameAddress(relayRequest.request.from.toLowerCase(), rec) - ); - expect(accountManager._signWithProvider).to.have.been.calledWith( - signedData - ); - expect( - accountManager._signWithControlledKey - ).to.have.not.been.called; - }); - it('should throw if web3 fails to sign with requested address', async function () { - shouldThrow = true; - relayRequest.request.from = - '0x4cfb3f70bf6a80397c2e634e5bdd85bc0bb189ee'; - const promise = accountManager.sign(relayRequest); - await expect(promise).to.be.eventually.rejectedWith( - 'Failed to sign relayed transaction for 0x4cfb3f70bf6a80397c2e634e5bdd85bc0bb189ee' - ); - }); - }); -}); diff --git a/test-to-migrate/relayclient/Configurator.test.ts b/test-to-migrate/relayclient/Configurator.test.ts deleted file mode 100644 index 5c5bf8e9..00000000 --- a/test-to-migrate/relayclient/Configurator.test.ts +++ /dev/null @@ -1,66 +0,0 @@ -// test possible client errors - -import { TestEnvironment } from '../TestEnvironment'; -import chai from 'chai'; -import chaiAsPromised from 'chai-as-promised'; -import { resolveConfiguration } from '@rsksmart/rif-relay-client'; -import { HttpProvider } from 'web3-core'; - -const { assert, expect } = chai.use(chaiAsPromised); - -contract('client-configuration', () => { - before(async () => { - const host = (web3.currentProvider as HttpProvider).host; - await TestEnvironment.start(host, 0.6e18); - }); - describe('#resolveConfiguration', () => { - describe('failures', () => { - it('should fail with no params', async () => { - // @ts-ignore - await expect(resolveConfiguration()).to.eventually.rejectedWith( - /First param is not a web3 provider/ - ); - }); - - it('should throw if the first arg not provider', async () => { - await expect( - resolveConfiguration({} as any, {}) - ).to.eventually.rejectedWith( - /First param is not a web3 provider/ - ); - }); - }); - - describe('with successful resolveConfiguration', () => { - it('should set metamask defaults', async () => { - const metamaskProvider = { - isMetaMask: true, - send: (options: any, cb: any) => { - (web3.currentProvider as any).send(options, cb); - } - } as any; - const config = await resolveConfiguration(metamaskProvider, {}); - assert.equal(config.methodSuffix, '_v4'); - assert.equal(config.jsonStringifyRequest, true); - }); - - it('should allow to override metamask defaults', async () => { - const metamaskProvider = { - isMetaMask: true, - send: (options: any, cb: any) => { - (web3.currentProvider as any).send(options, cb); - } - } as any; - - // note: to check boolean override, we explicitly set it to something that - // is not in the defaults.. - const config = await resolveConfiguration(metamaskProvider, { - methodSuffix: 'suffix', - jsonStringifyRequest: 5 as unknown as boolean - }); - assert.equal(config.methodSuffix, 'suffix'); - assert.equal(config.jsonStringifyRequest as any, 5); - }); - }); - }); -}); diff --git a/test-to-migrate/relayclient/ContractInteractor.test.ts b/test-to-migrate/relayclient/ContractInteractor.test.ts deleted file mode 100644 index 89967e73..00000000 --- a/test-to-migrate/relayclient/ContractInteractor.test.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { RelayClient, configure } from '@rsksmart/rif-relay-client'; -import { HttpProvider } from 'web3-core'; -import { - ProfilingProvider, - ContractInteractor, - constants, - isRsk, - Environment -} from '@rsksmart/rif-relay-common'; -import { PrefixedHexString } from 'ethereumjs-tx'; -import Transaction from 'ethereumjs-tx/dist/transaction'; -import { getTestingEnvironment } from '../TestUtils'; - -contract('ContractInteractor', function () { - // TODO: these tests create an entire instance of the client to test one method. - context('#_validateCompatibility()', function () { - it('should not throw if the hub address is not configured', async function () { - const relayClient = new RelayClient( - web3.currentProvider as HttpProvider, - { logLevel: 5 } - ); - await relayClient._init(); - }); - }); - - context('#broadcastTransaction()', function () { - let provider: ProfilingProvider; - let contractInteractor: ContractInteractor; - let sampleTransactionHash: PrefixedHexString; - let sampleTransactionData: PrefixedHexString; - - before(async function () { - const env: Environment = await getTestingEnvironment(); - let pk = - '46e6ef4a356fa3fa3929bf4b59e6b3eb9d0521ea660fd2879c67bd501002ac2b'; - let address = '0xb473D6BE09D0d6a23e1832046dBE258cF6E8635B'; - let gasPrice = 0; - let txOpts = {}; - - if (isRsk(env)) { - pk = - 'c85ef7d79691fe79573b1a7064c19c1a9819ebdbd1faaab1a8ec92344438aaf4'; - address = '0xcd2a3d9f938e13cd947ec05abc7fe734df8dd826'; - gasPrice = 1; - txOpts = { chainId: env.chainId }; - } - - provider = new ProfilingProvider( - web3.currentProvider as HttpProvider - ); - contractInteractor = new ContractInteractor( - provider, - configure({}) - ); - const nonce = await web3.eth.getTransactionCount(address); - - const transaction = new Transaction( - { - to: constants.ZERO_ADDRESS, - gasLimit: '0x5208', - gasPrice, - nonce - }, - txOpts - ); - - transaction.sign(Buffer.from(pk, 'hex')); - sampleTransactionData = - '0x' + transaction.serialize().toString('hex'); - sampleTransactionHash = - '0x' + transaction.hash(true).toString('hex'); - }); - - it('should sent the transaction to the blockchain directly', async function () { - const txHash = await contractInteractor.broadcastTransaction( - sampleTransactionData - ); - assert.equal(txHash, sampleTransactionHash); - assert.equal(provider.methodsCount.size, 1); - assert.equal( - provider.methodsCount.get('eth_sendRawTransaction'), - 1 - ); - }); - }); -}); diff --git a/test-to-migrate/relayclient/KnownRelaysManager.test.ts b/test-to-migrate/relayclient/KnownRelaysManager.test.ts deleted file mode 100644 index fbe24c38..00000000 --- a/test-to-migrate/relayclient/KnownRelaysManager.test.ts +++ /dev/null @@ -1,615 +0,0 @@ -import { ether } from '@openzeppelin/test-helpers'; -import { HttpProvider } from 'web3-core'; -import { - AccountKeypair, - configure, - DefaultRelayScore, - KnownRelaysManager -} from '@rsksmart/rif-relay-client'; -import { - RelayHubInstance, - SmartWalletFactoryInstance, - SmartWalletInstance, - TestRecipientInstance, - TestTokenInstance, - TestVerifierConfigurableMisbehaviorInstance -} from '@rsksmart/rif-relay-contracts/types/truffle-contracts'; -import { - createSmartWallet, - createSmartWalletFactory, - deployHub, - evmMineMany, - getGaslessAccount, - getTestingEnvironment, - prepareTransaction, - startRelay, - stopRelay -} from '../TestUtils'; -import sinon from 'sinon'; -import { ChildProcessWithoutNullStreams } from 'child_process'; -import { - constants, - ContractInteractor, - EnvelopingConfig, - EnvelopingTransactionDetails, - Environment -} from '@rsksmart/rif-relay-common'; -import { RelayManagerData } from '@rsksmart/rif-relay-contracts'; -import { RIF_RELAY_URL } from '../Utils'; - -const TestVerifierConfigurableMisbehavior = artifacts.require( - 'TestVerifierConfigurableMisbehavior' -); -const TestRecipient = artifacts.require('TestRecipient'); -const SmartWallet = artifacts.require('SmartWallet'); - -export async function stake( - relayHub: RelayHubInstance, - manager: string, - owner: string -): Promise { - await relayHub.stakeForAddress(manager, 1000, { - value: ether('1'), - from: owner - }); -} - -export async function register( - relayHub: RelayHubInstance, - manager: string, - worker: string, - url: string -): Promise { - await relayHub.addRelayWorkers([worker], { from: manager }); - await relayHub.registerRelayServer(url, { from: manager }); -} - -contract( - 'KnownRelaysManager', - function ([ - activeRelayWorkersAdded, - activeRelayServerRegistered, - activeVerifierRejected, - activeTransactionRelayed, - notActiveRelay, - workerVerifierRejected, - workerTransactionRelayed, - owner - ]) { - const relayLookupWindowBlocks = 100; - - describe('#_fetchRecentlyActiveRelayManagers()', function () { - let config: EnvelopingConfig; - let contractInteractor: ContractInteractor; - let relayHub: RelayHubInstance; - let testRecipient: TestRecipientInstance; - let verifier: TestVerifierConfigurableMisbehaviorInstance; - let workerRelayWorkersAdded; - let workerRelayServerRegistered; - let workerNotActive; - const gas = 4e6; - let factory: SmartWalletFactoryInstance; - let sWalletTemplate: SmartWalletInstance; - let smartWallet: SmartWalletInstance; - let env: Environment; - let token: TestTokenInstance; - - before(async function () { - env = await getTestingEnvironment(); - workerRelayWorkersAdded = await web3.eth.personal.newAccount( - 'password' - ); - workerRelayServerRegistered = - await web3.eth.personal.newAccount('password'); - workerNotActive = await web3.eth.personal.newAccount( - 'password' - ); - relayHub = await deployHub(constants.ZERO_ADDRESS); - config = configure({ - relayHubAddress: relayHub.address, - relayLookupWindowBlocks, - chainId: env.chainId - }); - - const tTokenArtifact = artifacts.require('TestToken'); - token = await tTokenArtifact.new(); - - contractInteractor = new ContractInteractor( - web3.currentProvider as HttpProvider, - config - ); - const senderAddress: AccountKeypair = await getGaslessAccount(); - - await contractInteractor.init(); - - testRecipient = await TestRecipient.new(); - sWalletTemplate = await SmartWallet.new(); - factory = await createSmartWalletFactory(sWalletTemplate); - smartWallet = await createSmartWallet( - activeRelayWorkersAdded, - senderAddress.address, - factory, - senderAddress.privateKey, - env.chainId - ); - await token.mint('1000', smartWallet.address); - - // register hub's RelayRequest with forwarder, if not already done. - - verifier = await TestVerifierConfigurableMisbehavior.new(); - // await verifier.setTrustedForwarder(smartWallet.address)//TODO REMOVE - await stake(relayHub, activeRelayWorkersAdded, owner); - await stake(relayHub, activeRelayServerRegistered, owner); - await stake(relayHub, activeVerifierRejected, owner); - await stake(relayHub, activeTransactionRelayed, owner); - await stake(relayHub, notActiveRelay, owner); - - let nextNonce = (await smartWallet.nonce()).toString(); - const txTransactionRelayed = await prepareTransaction( - relayHub.address, - testRecipient, - senderAddress, - workerTransactionRelayed, - verifier.address, - nextNonce, - smartWallet.address, - token.address, - '1' - ); - - /** events that are not supposed to be visible to the manager */ - await relayHub.addRelayWorkers([workerRelayServerRegistered], { - from: activeRelayServerRegistered - }); - await relayHub.addRelayWorkers([workerNotActive], { - from: notActiveRelay - }); - await relayHub.addRelayWorkers([workerTransactionRelayed], { - from: activeTransactionRelayed - }); - await relayHub.addRelayWorkers([workerVerifierRejected], { - from: activeVerifierRejected - }); - await relayHub.registerRelayServer('', { - from: activeTransactionRelayed - }); - await relayHub.registerRelayServer('', { - from: activeVerifierRejected - }); - - await evmMineMany(relayLookupWindowBlocks); - /** events that are supposed to be visible to the manager */ - await relayHub.registerRelayServer('', { - from: activeRelayServerRegistered - }); - await relayHub.addRelayWorkers([workerRelayWorkersAdded], { - from: activeRelayWorkersAdded - }); - await relayHub.relayCall( - txTransactionRelayed.relayRequest, - txTransactionRelayed.signature, - { - from: workerTransactionRelayed, - gas, - gasPrice: - txTransactionRelayed.relayRequest.relayData.gasPrice - } - ); - await verifier.setReturnInvalidErrorCode(true); - - nextNonce = (await smartWallet.nonce()).toString(); - const txVerifierRejected = await prepareTransaction( - relayHub.address, - testRecipient, - senderAddress, - workerVerifierRejected, - verifier.address, - nextNonce, - smartWallet.address, - token.address, - '1' - ); - - await relayHub.relayCall( - txVerifierRejected.relayRequest, - txVerifierRejected.signature, - { - from: workerVerifierRejected, - gas, - gasPrice: - txVerifierRejected.relayRequest.relayData.gasPrice - } - ); - }); - - it("should contain all relay managers only if their workers were active in the last 'relayLookupWindowBlocks' blocks", async function () { - const knownRelaysManager = new KnownRelaysManager( - contractInteractor, - config - ); - const res = - await knownRelaysManager._fetchRecentlyActiveRelayManagers(); - const actual = Array.from(res.values()); - assert.equal(actual.length, 4); - assert.equal(actual[0], activeRelayServerRegistered); - assert.equal(actual[1], activeRelayWorkersAdded); - assert.equal(actual[2], activeTransactionRelayed); - assert.equal(actual[3], activeVerifierRejected); - }); - }); - } -); - -contract('KnownRelaysManager 2', function (accounts) { - let contractInteractor: ContractInteractor; - const transactionDetails: EnvelopingTransactionDetails = { - gas: '0x10000', - gasPrice: '0x300000', - from: '', - data: '', - to: '', - callForwarder: '', - callVerifier: '', - tokenAmount: '', - tokenGas: '', - tokenContract: '', - isSmartWalletDeploy: false - }; - - before(async function () { - const env = await getTestingEnvironment(); - contractInteractor = new ContractInteractor( - web3.currentProvider as HttpProvider, - configure({ chainId: env.chainId }) - ); - await contractInteractor.init(); - }); - - describe('#refresh()', function () { - let relayProcess: ChildProcessWithoutNullStreams; - let knownRelaysManager: KnownRelaysManager; - let contractInteractor: ContractInteractor; - let relayHub: RelayHubInstance; - let config: EnvelopingConfig; - let env: Environment; - - before(async function () { - env = await getTestingEnvironment(); - relayHub = await deployHub(constants.ZERO_ADDRESS); - config = configure({ - preferredRelays: [RIF_RELAY_URL], - relayHubAddress: relayHub.address, - chainId: env.chainId - }); - relayProcess = ( - await startRelay(relayHub, { - stake: 1e18, - url: 'asd', - relayOwner: accounts[1], - rskNodeUrl: (web3.currentProvider as HttpProvider).host - }) - ).proc; - - contractInteractor = new ContractInteractor( - web3.currentProvider as HttpProvider, - config - ); - await contractInteractor.init(); - knownRelaysManager = new KnownRelaysManager( - contractInteractor, - config - ); - await stake(relayHub, accounts[1], accounts[0]); - await stake(relayHub, accounts[2], accounts[0]); - await stake(relayHub, accounts[3], accounts[0]); - await register( - relayHub, - accounts[1], - accounts[6], - 'stakeAndAuthorization1' - ); - await register( - relayHub, - accounts[2], - accounts[7], - 'stakeAndAuthorization2' - ); - await register(relayHub, accounts[3], accounts[8], 'stakeUnlocked'); - - await relayHub.unlockStake(accounts[3]); - }); - - after(async function () { - await stopRelay(relayProcess); - }); - - it('should consider all relay managers with stake and authorization as active', async function () { - await knownRelaysManager.refresh(); - const preferredRelays = knownRelaysManager.preferredRelayers; - const activeRelays = knownRelaysManager.allRelayers; - assert.equal(preferredRelays.length, 1); - assert.equal(preferredRelays[0].url, RIF_RELAY_URL); - assert.equal(activeRelays.length, 3); - assert.equal(activeRelays[0].url, RIF_RELAY_URL); - assert.equal(activeRelays[1].url, 'stakeAndAuthorization1'); - assert.equal(activeRelays[2].url, 'stakeAndAuthorization2'); - }); - - it("should use 'relayFilter' to remove unsuitable relays", async function () { - const relayFilter = ( - registeredEventInfo: RelayManagerData - ): boolean => { - return registeredEventInfo.url.includes('2'); - }; - const knownRelaysManagerWithFilter = new KnownRelaysManager( - contractInteractor, - config, - relayFilter - ); - await knownRelaysManagerWithFilter.refresh(); - const relays = knownRelaysManagerWithFilter.allRelayers; - assert.equal(relays.length, 1); - assert.equal(relays[0].url, 'stakeAndAuthorization2'); - }); - }); - - // eslint-disable-next-line @typescript-eslint/no-misused-promises - describe('#getRelaysSortedForTransaction()', function () { - const relayData: RelayManagerData = Object.assign({} as any, { - manager: accounts[0], - url: 'url' - }); - - describe('#_refreshFailures()', function () { - let knownRelaysManager: KnownRelaysManager; - let lastErrorTime: number; - - before(async function () { - const env = await getTestingEnvironment(); - knownRelaysManager = new KnownRelaysManager( - contractInteractor, - configure({ chainId: env.chainId }) - ); - knownRelaysManager.saveRelayFailure(100, 'rm1', 'url1'); - knownRelaysManager.saveRelayFailure(500, 'rm2', 'url2'); - lastErrorTime = Date.now(); - knownRelaysManager.saveRelayFailure( - lastErrorTime, - 'rm3', - 'url3' - ); - }); - - it("should remove the failures that occurred more than 'relayTimeoutGrace' seconds ago", function () { - // @ts-ignore - knownRelaysManager.relayFailures.forEach((failures) => { - assert.equal(failures.length, 1); - }); - knownRelaysManager._refreshFailures(); - assert.equal( - // @ts-ignore - knownRelaysManager.relayFailures.get('url1').length, - 0 - ); - assert.equal( - // @ts-ignore - knownRelaysManager.relayFailures.get('url2').length, - 0 - ); - // @ts-ignore - assert.deepEqual(knownRelaysManager.relayFailures.get('url3'), [ - { - lastErrorTime, - relayManager: 'rm3', - relayUrl: 'url3' - } - ]); - }); - }); - - describe('#splitRange', () => { - let knownRelaysManager: KnownRelaysManager; - let lastErrorTime: number; - - before(async function () { - const env = await getTestingEnvironment(); - knownRelaysManager = new KnownRelaysManager( - contractInteractor, - configure({ chainId: env.chainId }) - ); - knownRelaysManager.saveRelayFailure(100, 'rm1', 'url1'); - knownRelaysManager.saveRelayFailure(500, 'rm2', 'url2'); - lastErrorTime = Date.now(); - knownRelaysManager.saveRelayFailure( - lastErrorTime, - 'rm3', - 'url3' - ); - }); - - it('split 1', () => { - assert.deepEqual(knownRelaysManager.splitRange(1, 6, 1), [ - { fromBlock: 1, toBlock: 6 } - ]); - }); - it('split 2', () => { - assert.deepEqual(knownRelaysManager.splitRange(1, 6, 2), [ - { fromBlock: 1, toBlock: 3 }, - { fromBlock: 4, toBlock: 6 } - ]); - }); - it('split 2 odd', () => { - assert.deepEqual(knownRelaysManager.splitRange(1, 7, 2), [ - { fromBlock: 1, toBlock: 4 }, - { fromBlock: 5, toBlock: 7 } - ]); - }); - it('split 3', () => { - assert.deepEqual(knownRelaysManager.splitRange(1, 9, 3), [ - { fromBlock: 1, toBlock: 3 }, - { fromBlock: 4, toBlock: 6 }, - { fromBlock: 7, toBlock: 9 } - ]); - }); - - it('split 3 odd', () => { - assert.deepEqual(knownRelaysManager.splitRange(1, 10, 3), [ - { fromBlock: 1, toBlock: 4 }, - { fromBlock: 5, toBlock: 8 }, - { fromBlock: 9, toBlock: 10 } - ]); - }); - }); - - describe('#getPastEventsForHub', () => { - let saveContractInteractor: any; - let knownRelaysManager: KnownRelaysManager; - let lastErrorTime: number; - before(async () => { - const env = await getTestingEnvironment(); - knownRelaysManager = new KnownRelaysManager( - contractInteractor, - configure({ chainId: env.chainId }) - ); - knownRelaysManager.saveRelayFailure(100, 'rm1', 'url1'); - knownRelaysManager.saveRelayFailure(500, 'rm2', 'url2'); - lastErrorTime = Date.now(); - knownRelaysManager.saveRelayFailure( - lastErrorTime, - 'rm3', - 'url3' - ); - - saveContractInteractor = (knownRelaysManager as any) - .contractInteractor; - (knownRelaysManager as any).contractInteractor = { - async getPastEventsForHub( - extra: any, - options: { fromBlock: number; toBlock: number } - ) { - if (options.toBlock - options.fromBlock > 100) { - throw new Error( - 'query returned more than 100 events' - ); - } - const ret: any[] = []; - for ( - let b = options.fromBlock; - b <= options.toBlock; - b++ - ) { - ret.push({ - event: `event${b}-${options.fromBlock}-${options.toBlock}` - }); - } - return ret; - } - }; - }); - after(() => { - (knownRelaysManager as any).contractInteractor = - saveContractInteractor; - }); - - it('should break large request into multiple chunks', async () => { - (knownRelaysManager as any).relayLookupWindowParts = 1; - const ret = await knownRelaysManager.getPastEventsForHub( - 1, - 300 - ); - - assert.equal( - (knownRelaysManager as any).relayLookupWindowParts, - 4 - ); - assert.equal(ret.length, 300); - assert.equal(ret[0].event, 'event1-1-75'); - assert.equal(ret[299].event, 'event300-226-300'); - }); - }); - - describe('DefaultRelayScore', function () { - const failure = { - lastErrorTime: 100, - relayManager: 'rm3', - relayUrl: 'url3' - }; - it('should subtract penalty from a relay for each known failure', async function () { - const relayScoreNoFailures = await DefaultRelayScore( - relayData, - transactionDetails, - [] - ); - const relayScoreOneFailure = await DefaultRelayScore( - relayData, - transactionDetails, - [failure] - ); - const relayScoreTenFailures = await DefaultRelayScore( - relayData, - transactionDetails, - Array(10).fill(failure) - ); - - assert.isAbove(relayScoreNoFailures, relayScoreOneFailure); - assert.isAbove(relayScoreOneFailure, relayScoreTenFailures); - }); - }); - }); - - // eslint-disable-next-line @typescript-eslint/no-misused-promises - describe('getRelaysSortedForTransaction', function () { - const biasedRelayScore = async function ( - relay: RelayManagerData - ): Promise { - if (relay.url === 'alex') { - return await Promise.resolve(1000); - } else { - return await Promise.resolve(100); - } - }; - let knownRelaysManager: KnownRelaysManager; - - before(async function () { - const env = await getTestingEnvironment(); - knownRelaysManager = new KnownRelaysManager( - contractInteractor, - configure({ chainId: env.chainId }), - undefined, - biasedRelayScore - ); - const activeRelays: RelayManagerData[] = [ - { - manager: accounts[0], - url: 'alex', - currentlyStaked: true, - registered: true - }, - { - manager: accounts[0], - url: 'joe', - currentlyStaked: true, - registered: true - }, - { - manager: accounts[1], - url: 'joe', - currentlyStaked: true, - registered: true - } - ]; - sinon.stub(knownRelaysManager, 'allRelayers').value(activeRelays); - }); - - it('should use provided score calculation method to sort the known relays', async function () { - const sortedRelays = - await knownRelaysManager.getRelaysSortedForTransaction( - transactionDetails - ); - assert.equal(sortedRelays[1][0].url, 'alex'); - // checking the relayers are sorted AND they cannot overshadow each other's url - assert.equal(sortedRelays[1][1].url, 'joe'); - assert.equal(sortedRelays[1][2].url, 'joe'); - }); - }); -}); diff --git a/test-to-migrate/relayclient/RelayClient.test.ts b/test-to-migrate/relayclient/RelayClient.test.ts deleted file mode 100644 index 0c6a1aac..00000000 --- a/test-to-migrate/relayclient/RelayClient.test.ts +++ /dev/null @@ -1,1558 +0,0 @@ -import Transaction from 'ethereumjs-tx/dist/transaction'; -import Web3 from 'web3'; -import chai, { assert, expect } from 'chai'; -import chaiAsPromised from 'chai-as-promised'; -import sinon from 'sinon'; -import sinonChai from 'sinon-chai'; -import { ChildProcessWithoutNullStreams } from 'child_process'; -import { HttpProvider } from 'web3-core'; -import express from 'express'; -import axios from 'axios'; -// @ts-ignore -import abiDecoder from 'abi-decoder'; - -import { - RelayHubInstance, - TestRecipientInstance, - SmartWalletInstance, - SmartWalletFactoryInstance, - TestTokenInstance, - TestVerifierEverythingAcceptedInstance, - TestDeployVerifierEverythingAcceptedInstance -} from '@rsksmart/rif-relay-contracts/types/truffle-contracts'; - -import { - EnvelopingConfig, - replaceErrors, - EnvelopingTransactionDetails, - PingResponse, - Web3Provider, - RelayTransactionRequest, - constants, - ContractInteractor -} from '@rsksmart/rif-relay-common'; -import { - DeployRequest, - TypedDeployRequestData -} from '@rsksmart/rif-relay-contracts'; -import { - _dumpRelayingResult, - RelayClient, - configure, - getDependencies, - RelayInfo, - RelayEvent, - HttpClient, - HttpWrapper, - AccountKeypair -} from '@rsksmart/rif-relay-client'; -import { PrefixedHexString } from 'ethereumjs-tx'; -import BadHttpClient from '../dummies/BadHttpClient'; -import BadContractInteractor from '../dummies/BadContractInteractor'; -import BadRelayedTransactionValidator from '../dummies/BadRelayedTransactionValidator'; -import { - stripHex, - deployHub, - startRelay, - stopRelay, - getTestingEnvironment, - createSmartWalletFactory, - createSmartWallet, - getGaslessAccount, - snapshot, - revert -} from '../TestUtils'; -import bodyParser from 'body-parser'; -import { Server } from 'http'; -import { toBN, toHex } from 'web3-utils'; -import { ether } from '@openzeppelin/test-helpers'; -import { RIF_RELAY_PORT, RIF_RELAY_URL } from '../Utils'; - -const TestRecipient = artifacts.require('TestRecipient'); -const TestRelayVerifier = artifacts.require('TestVerifierEverythingAccepted'); -const TestDeployVerifier = artifacts.require( - 'TestDeployVerifierEverythingAccepted' -); -const SmartWallet = artifacts.require('SmartWallet'); -const TestToken = artifacts.require('TestToken'); -const RelayHub = artifacts.require('RelayHub'); -const SmartWalletFactory = artifacts.require('SmartWalletFactory'); -// @ts-ignore -abiDecoder.addABI(RelayHub.abi); -// @ts-ignore -abiDecoder.addABI(SmartWalletFactory.abi); - -chai.use(sinonChai); -chai.use(chaiAsPromised); - -const localhostOne = RIF_RELAY_URL; -const cheapRelayerUrl = 'http://localhost:54321'; -const underlyingProvider = web3.currentProvider as HttpProvider; -class MockHttpClient extends HttpClient { - constructor( - readonly mockPort: number, - httpWrapper: HttpWrapper, - config: Partial - ) { - super(httpWrapper, config); - } - - async relayTransaction( - relayUrl: string, - request: RelayTransactionRequest - ): Promise { - return await super.relayTransaction(this.mapUrl(relayUrl), request); - } - - private mapUrl(relayUrl: string): string { - return relayUrl.replace(`:${RIF_RELAY_PORT}`, `:${this.mockPort}`); - } -} - -const gasOptions = [ - { - title: 'with gas estimation', - estimateGas: true - }, - { - title: 'with hardcoded gas', - estimateGas: false - } -]; - -gasOptions.forEach((gasOption) => { - contract(`RelayClient with ${gasOption.title}`, function (accounts) { - let web3: Web3; - let relayHub: RelayHubInstance; - let testRecipient: TestRecipientInstance; - let relayVerifier: TestVerifierEverythingAcceptedInstance; - let deployVerifier: TestDeployVerifierEverythingAcceptedInstance; - let relayProcess: ChildProcessWithoutNullStreams; - let relayClient: RelayClient; - let config: Partial; - let options: EnvelopingTransactionDetails; - let to: string; - let from: string; - let data: PrefixedHexString; - let relayEvents: RelayEvent[] = []; - let factory: SmartWalletFactoryInstance; - let sWalletTemplate: SmartWalletInstance; - let smartWallet: SmartWalletInstance; - let token: TestTokenInstance; - let gaslessAccount: AccountKeypair; - let relayWorker: string; - - async function registerRelayer( - relayHub: RelayHubInstance - ): Promise { - const relayWorker = '0x'.padEnd(42, '2'); - const relayOwner = accounts[3]; - const relayManager = accounts[4]; - await relayHub.stakeForAddress(relayManager, 1000, { - value: ether('2'), - from: relayOwner - }); - - await relayHub.addRelayWorkers([relayWorker], { - from: relayManager - }); - await relayHub.registerRelayServer(cheapRelayerUrl, { - from: relayManager - }); - } - - before(async function () { - web3 = new Web3(underlyingProvider); - relayHub = await deployHub(); - testRecipient = await TestRecipient.new(); - sWalletTemplate = await SmartWallet.new(); - token = await TestToken.new(); - const env = await getTestingEnvironment(); - - gaslessAccount = await getGaslessAccount(); - factory = await createSmartWalletFactory(sWalletTemplate); - smartWallet = await createSmartWallet( - accounts[0], - gaslessAccount.address, - factory, - gaslessAccount.privateKey, - env.chainId - ); - relayVerifier = await TestRelayVerifier.new(); - deployVerifier = await TestDeployVerifier.new(); - - const startRelayResult = await startRelay(relayHub, { - stake: 1e18, - relayOwner: accounts[1], - rskNodeUrl: underlyingProvider.host, - deployVerifierAddress: deployVerifier.address, - relayVerifierAddress: relayVerifier.address, - workerTargetBalance: 0.6e18 - }); - - relayWorker = accounts[2]; - const relayOwner = accounts[3]; - const relayManager = accounts[4]; - await relayHub.stakeForAddress(relayManager, 1000, { - value: ether('2'), - from: relayOwner - }); - - await relayHub.addRelayWorkers([relayWorker], { - from: relayManager - }); - // await relayHub.registerRelayServer(cheapRelayerUrl, { from: relayManager }) - - relayProcess = startRelayResult.proc; - - config = { - logLevel: 5, - relayHubAddress: relayHub.address, - chainId: env.chainId, - deployVerifierAddress: deployVerifier.address, - relayVerifierAddress: relayVerifier.address, - preferredRelays: ['http://localhost:8095'] - }; - - relayClient = new RelayClient(underlyingProvider, config); - - // register gasless account in RelayClient to avoid signing with RSKJ - relayClient.accountManager.addAccount(gaslessAccount); - - from = gaslessAccount.address; - to = testRecipient.address; - await token.mint('1000', smartWallet.address); - - data = testRecipient.contract.methods - .emitMessage('hello world') - .encodeABI(); - - options = { - from, - to, - data, - relayHub: relayHub.address, - callForwarder: smartWallet.address, - callVerifier: relayVerifier.address, - clientId: '1', - tokenContract: token.address, - tokenAmount: '1', - isSmartWalletDeploy: false - }; - - if (!gasOption.estimateGas) { - options.tokenGas = '50000'; - } - }); - - after(async function () { - await stopRelay(relayProcess); - }); - - describe('#relayTransaction()', function () { - it('should send transaction to a relay and receive a signed transaction in response', async function () { - const relayingResult = await relayClient.relayTransaction( - options - ); - const validTransaction = relayingResult.transaction; - - if (validTransaction == null) { - assert.fail( - `validTransaction is null: ${JSON.stringify( - relayingResult, - replaceErrors - )}` - ); - return; - } - const validTransactionHash: string = validTransaction - .hash(true) - .toString('hex'); - const txHash = `0x${validTransactionHash}`; - const res = await web3.eth.getTransactionReceipt(txHash); - - // validate we've got the "SampleRecipientEmitted" event - // TODO: use OZ test helpers - const topic: string = - web3.utils.sha3( - 'SampleRecipientEmitted(string,address,address,uint256,uint256)' - ) ?? ''; - assert(res.logs.find((log) => log.topics.includes(topic))); - - const destination: string = validTransaction.to.toString('hex'); - assert.equal( - `0x${destination}`, - relayHub.address.toString().toLowerCase() - ); - }); - - it('should skip timed-out server', async function () { - let server: Server | undefined; - try { - const pingResponse = await axios - .get(`${RIF_RELAY_URL}/getaddr`) - .then((res) => res.data); - const mockServer = express(); - mockServer.use(bodyParser.urlencoded({ extended: false })); - mockServer.use(bodyParser.json()); - - /* eslint-disable @typescript-eslint/no-misused-promises */ - mockServer.get('/getaddr', async (req, res) => { - console.log('=== got GET ping', req.query); - res.send(pingResponse); - }); - /* eslint-enable */ - - mockServer.post('/relay', () => { - console.log('== got relay.. ignoring'); - // don't answer... keeping client in limbo - }); - - await new Promise((resolve) => { - // @ts-ignore - server = mockServer.listen(0, resolve); - }); - const mockServerPort = (server as any).address().port; - - // MockHttpClient alter the server port, so the client "thinks" it works with relayUrl, but actually - // it uses the mockServer's port - const relayClient = new RelayClient( - underlyingProvider, - config, - { - httpClient: new MockHttpClient( - mockServerPort, - new HttpWrapper({ timeout: 100 }), - config - ) - } - ); - - // register gasless account in RelayClient to avoid signing with RSKJ - relayClient.accountManager.addAccount(gaslessAccount); - - // async relayTransaction (relayUrl: string, request: RelayTransactionRequest): Promise { - const relayingResult = await relayClient.relayTransaction( - options - ); - assert.match( - _dumpRelayingResult(relayingResult), - /timeout.*exceeded/ - ); - } finally { - server?.close(); - } - }); - - it('should use forceGasPrice if provided', async function () { - const forceGasPrice = '0x777777777'; - const optionsForceGas = Object.assign({}, options, { - forceGasPrice - }); - const { transaction, pingErrors, relayingErrors } = - await relayClient.relayTransaction(optionsForceGas); - assert.equal( - pingErrors.size, - 0, - 'Ping Errors list is not empty' - ); - assert.equal( - relayingErrors.size, - 0, - 'Relaying Errors list is not empy' - ); - assert.equal( - parseInt(transaction.gasPrice.toString('hex'), 16), - parseInt(forceGasPrice) - ); - }); - - it('should return errors encountered in ping', async function () { - const badHttpClient: any = new BadHttpClient( - configure(config), - true, - false, - false - ); - const relayClient = new RelayClient( - underlyingProvider, - config, - { httpClient: badHttpClient } - ); - const { transaction, relayingErrors, pingErrors } = - await relayClient.relayTransaction(options); - assert.isUndefined(transaction); - assert.equal(relayingErrors.size, 0); - assert.equal(pingErrors.size, 1); - assert.equal( - pingErrors.get(localhostOne).message, - BadHttpClient.message - ); - }); - - it('should return errors encountered in relaying', async function () { - const badHttpClient: any = new BadHttpClient( - configure(config), - false, - true, - false - ); - const relayClient = new RelayClient( - underlyingProvider, - config, - { httpClient: badHttpClient } - ); - - // register gasless account in RelayClient to avoid signing with RSKJ - relayClient.accountManager.addAccount(gaslessAccount); - - const { transaction, relayingErrors, pingErrors } = - await relayClient.relayTransaction(options); - assert.isUndefined(transaction); - assert.equal(pingErrors.size, 0); - assert.equal(relayingErrors.size, 1); - assert.equal( - relayingErrors.get(localhostOne).message, - BadHttpClient.message - ); - }); - - // TODO test other things, for example, if the smart wallet to deploy has no funds, etc - // Do we want to restrict to certnain factories? - - it('should calculate the estimatedGas for deploying a SmartWallet using the SmartWalletFactory', async function () { - const eoaWithoutSmartWalletAccount = await getGaslessAccount(); - // register eoaWithoutSmartWalletAccount account in RelayClient to avoid signing with RSKJ - relayClient.accountManager.addAccount( - eoaWithoutSmartWalletAccount - ); - const swAddress = await factory.getSmartWalletAddress( - eoaWithoutSmartWalletAccount.address, - constants.ZERO_ADDRESS, - '0' - ); - await token.mint('1000', swAddress); - - const details: EnvelopingTransactionDetails = { - from: eoaWithoutSmartWalletAccount.address, - to: constants.ZERO_ADDRESS, // No extra logic for the Smart Wallet - data: '0x', // No extra-logic init data - callForwarder: factory.address, - callVerifier: deployVerifier.address, - clientId: '1', - tokenContract: token.address, - tokenAmount: '1', - tokenGas: gasOption.estimateGas ? undefined : '50000', - recoverer: constants.ZERO_ADDRESS, - index: '0', - gasPrice: '1', - gas: '0x00', - value: '0', - isSmartWalletDeploy: true, - useEnveloping: true, - relayHub: relayHub.address, - smartWalletAddress: swAddress - }; - - const { feesReceiver } = await axios - .get(`${RIF_RELAY_URL}/getaddr`) - .then((res) => res.data); - - const tokenPaymentEstimate = - await relayClient.estimateTokenTransferGas(details); - const testRequest = - await relayClient._prepareFactoryGasEstimationRequest( - details, - feesReceiver - ); - const estimatedGasResultWithoutTokenPayment = - await relayClient.calculateDeployCallGas( - testRequest, - relayWorker - ); - - const originalBalance = await token.balanceOf(swAddress); - const senderNonce = await factory.nonce( - eoaWithoutSmartWalletAccount.address - ); - const chainId = (await getTestingEnvironment()).chainId; - - const request: DeployRequest = { - request: { - relayHub: relayHub.address, - from: eoaWithoutSmartWalletAccount.address, - to: constants.ZERO_ADDRESS, - value: '0', - nonce: senderNonce.toString(), - data: '0x', - tokenContract: token.address, - tokenAmount: '1', - tokenGas: tokenPaymentEstimate.toString(), - recoverer: constants.ZERO_ADDRESS, - index: '0', - validUntilTime: '0' - }, - relayData: { - gasPrice: '1', - feesReceiver: feesReceiver, - callForwarder: factory.address, - callVerifier: deployVerifier.address - } - }; - const dataToSign = new TypedDeployRequestData( - chainId, - factory.address, - request - ); - - const sig = relayClient.accountManager._signWithControlledKey( - eoaWithoutSmartWalletAccount, - dataToSign - ); - - const txResponse = await relayHub.deployCall(request, sig, { - from: relayWorker, - gasPrice: '1', - gas: 4e6 - }); - - const salt = - web3.utils.soliditySha3( - { - t: 'address', - v: eoaWithoutSmartWalletAccount.address - }, - { t: 'address', v: constants.ZERO_ADDRESS }, - { t: 'uint256', v: '0' } - ) ?? ''; - - const expectedSalt = web3.utils.toBN(salt).toString(); - - const actualGasUsed: number = - txResponse.receipt.cumulativeGasUsed; - const receipt = await web3.eth.getTransactionReceipt( - txResponse.tx - ); - - const logs = abiDecoder.decodeLogs(receipt.logs); - - const deployedEvent = logs.find( - (e: any) => e != null && e.name === 'Deployed' - ); - assert.equal( - swAddress.toLowerCase(), - deployedEvent.events[0].value.toLowerCase() - ); - assert.equal( - expectedSalt.toLowerCase(), - deployedEvent.events[1].value.toLowerCase() - ); - - // The Smart Wallet should have been charged for the deploy - const newBalance = await token.balanceOf(swAddress); - const expectedBalance = originalBalance.sub( - web3.utils.toBN('1') - ); - expect( - expectedBalance, - 'Deployment not paid' - ).to.be.bignumber.equal(newBalance); - - const tenPercertGasCushion = actualGasUsed * 0.1; - const highActual = actualGasUsed + tenPercertGasCushion; - const lowActual = actualGasUsed - tenPercertGasCushion; - - const estimatedGasResult = - estimatedGasResultWithoutTokenPayment + - tokenPaymentEstimate; - assert.isTrue( - estimatedGasResult === actualGasUsed || - (lowActual <= estimatedGasResult && - highActual >= estimatedGasResult), - 'Incorrect estimated gas' - ); - }); - - it('should relay properly with token transfer and relay gas estimations used', async function () { - const eoaWithoutSmartWalletAccount = await getGaslessAccount(); - - // register eoaWithoutSmartWallet account to avoid signing with RSKJ - relayClient.accountManager.addAccount( - eoaWithoutSmartWalletAccount - ); - const swAddress = await factory.getSmartWalletAddress( - eoaWithoutSmartWalletAccount.address, - constants.ZERO_ADDRESS, - '0' - ); - - const deployOptions: EnvelopingTransactionDetails = { - from: eoaWithoutSmartWalletAccount.address, - to: constants.ZERO_ADDRESS, // No extra logic for the Smart Wallet - data: '0x', // No extra-logic init data - gas: '0x1E8480', - relayHub: relayHub.address, - callForwarder: factory.address, - callVerifier: deployVerifier.address, - clientId: '1', - tokenContract: token.address, - tokenAmount: '1', - tokenGas: gasOption.estimateGas ? undefined : '50000', - isSmartWalletDeploy: true, - recoverer: constants.ZERO_ADDRESS, - smartWalletAddress: swAddress, - index: '0' - }; - - await token.mint('1000', swAddress); - - assert.equal( - await web3.eth.getCode(swAddress), - '0x', - 'SmartWallet not yet deployed, it must not have installed code' - ); - - const result = await relayClient.relayTransaction( - deployOptions - ); - const senderAddress = `0x${result.transaction - ?.getSenderAddress() - .toString('hex')}`; - const senderTokenInitialBalance = await token.balanceOf( - senderAddress - ); - assert.notEqual(senderAddress, constants.ZERO_ADDRESS); - assert.notEqual( - await web3.eth.getCode(swAddress), - '0x', - 'SmartWalletdeployed, it must have installed code' - ); - - const relayOptions = { - from: eoaWithoutSmartWalletAccount.address, - to: options.to, - data: options.data, - relayHub: options.relayHub, - callForwarder: swAddress, - callVerifier: options.callVerifier, - clientId: options.clientId, - tokenContract: options.tokenContract, // tokenGas is skipped, also smartWalletAddress is not needed since is the forwarder - tokenAmount: '1', - isSmartWalletDeploy: false - }; - - const relayingResult = await relayClient.relayTransaction( - relayOptions - ); - const senderAddressRelay = `0x${relayingResult.transaction - ?.getSenderAddress() - .toString('hex')}`; - const senderTokenFinalBalance = await token.balanceOf( - senderAddressRelay - ); - - assert.notEqual(senderAddressRelay, '0xundefined'); - assert.equal(senderAddress, senderAddressRelay); - assert.equal( - senderTokenInitialBalance.toString(), - senderTokenFinalBalance.sub(toBN('1')).toString() - ); - - const validTransaction = relayingResult.transaction; - - if (validTransaction == null) { - assert.fail( - `validTransaction is null: ${JSON.stringify( - relayingResult, - replaceErrors - )}` - ); - return; - } - const validTransactionHash: string = validTransaction - .hash(true) - .toString('hex'); - const txHash = `0x${validTransactionHash}`; - const res = await web3.eth.getTransactionReceipt(txHash); - - // validate we've got the "SampleRecipientEmitted" event - // TODO: use OZ test helpers - const topic: string = - web3.utils.sha3( - 'SampleRecipientEmitted(string,address,address,uint256,uint256)' - ) ?? ''; - assert(res.logs.find((log) => log.topics.includes(topic))); - - const destination: string = validTransaction.to.toString('hex'); - assert.equal( - `0x${destination}`, - relayHub.address.toString().toLowerCase() - ); - }); - - it('should fail if a deploy without tokenGas and smartwallet is attempted', async function () { - const eoaWithoutSmartWalletAccount = await getGaslessAccount(); - - // register eoaWithoutSmartWallet account to avoid signing with RSKJ - relayClient.accountManager.addAccount( - eoaWithoutSmartWalletAccount - ); - - const deployOptions: EnvelopingTransactionDetails = { - from: eoaWithoutSmartWalletAccount.address, - to: constants.ZERO_ADDRESS, // No extra logic for the Smart Wallet - data: '0x', // No extra-logic init data - gas: '0x1E8480', - relayHub: relayHub.address, - callForwarder: factory.address, - callVerifier: deployVerifier.address, - clientId: '1', - tokenContract: token.address, - tokenAmount: '1', - // tokenGas: '50000', omitted so it is calculated - isSmartWalletDeploy: true, - recoverer: constants.ZERO_ADDRESS, - index: '0' - }; - - const swAddress = await factory.getSmartWalletAddress( - eoaWithoutSmartWalletAccount.address, - constants.ZERO_ADDRESS, - '0' - ); - await token.mint('1000', swAddress); - // deployOptions.smartWalletAddress = swAddress --> The client cannot tell who is the token sender so it cannot estimate the token transfer. - // calculating the address in the client is not an option because the factory used is not in control of the client (a thus, the method to calculate it, which is not unique) - - assert.equal( - await web3.eth.getCode(swAddress), - '0x', - 'SmartWallet not yet deployed, it must not have installed code' - ); - - try { - await relayClient.relayTransaction(deployOptions); - } catch (error) { - assert.equal( - error.message, - 'In a deploy, if tokenGas is not defined, then the calculated SmartWallet address is needed to estimate the tokenGas value' - ); - } - }); - - it('should relay properly with full gas estimation used when token balance ends in zero', async function () { - const eoaWithoutSmartWalletAccount = await getGaslessAccount(); - - // register eoaWithoutSmartWallet account to avoid signing with RSKJ - relayClient.accountManager.addAccount( - eoaWithoutSmartWalletAccount - ); - - const swAddress = await factory.getSmartWalletAddress( - eoaWithoutSmartWalletAccount.address, - constants.ZERO_ADDRESS, - '0' - ); - - const deployOptions: EnvelopingTransactionDetails = { - from: eoaWithoutSmartWalletAccount.address, - to: constants.ZERO_ADDRESS, // No extra logic for the Smart Wallet - data: '0x', // No extra-logic init data - gas: '0x1E8480', - relayHub: relayHub.address, - callForwarder: factory.address, - callVerifier: deployVerifier.address, - clientId: '1', - tokenContract: token.address, - tokenAmount: '1', - tokenGas: gasOption.estimateGas ? undefined : '55000', - isSmartWalletDeploy: true, - recoverer: constants.ZERO_ADDRESS, - index: '0', - smartWalletAddress: swAddress - }; - - await token.mint('1000', swAddress); - - assert.equal( - await web3.eth.getCode(swAddress), - '0x', - 'SmartWallet not yet deployed, it must not have installed code' - ); - - const result = await relayClient.relayTransaction( - deployOptions - ); - const senderAddress = `0x${result.transaction - ?.getSenderAddress() - .toString('hex')}`; - const senderTokenInitialBalance = await token.balanceOf( - senderAddress - ); - assert.notEqual(senderAddress, constants.ZERO_ADDRESS); - assert.notEqual( - await web3.eth.getCode(swAddress), - '0x', - 'SmartWalletdeployed, it must have installed code' - ); - - const balanceToTransfer = await token.balanceOf(swAddress); - - const relayOptions = { - from: eoaWithoutSmartWalletAccount.address, - to: options.to, - data: options.data, - relayHub: options.relayHub, - callForwarder: swAddress, - callVerifier: options.callVerifier, - clientId: options.clientId, - tokenContract: options.tokenContract, // tokenGas is skipped, also smartWalletAddress is not needed since is the forwarder - tokenAmount: balanceToTransfer.toString(), - isSmartWalletDeploy: false - }; - - const relayingResult = await relayClient.relayTransaction( - relayOptions - ); - const senderAddressRelay = `0x${relayingResult.transaction - ?.getSenderAddress() - .toString('hex')}`; - const senderTokenFinalBalance = await token.balanceOf( - senderAddressRelay - ); - - assert.notEqual(senderAddressRelay, '0xundefined'); - assert.equal(senderAddress, senderAddressRelay); - assert.equal( - senderTokenInitialBalance.toString(), - senderTokenFinalBalance.sub(balanceToTransfer).toString() - ); - - const sWalletFinalBalance = await token.balanceOf(swAddress); - assert.isTrue( - sWalletFinalBalance.eq(toBN(0)), - 'SW Final balance must be zero' - ); - - const validTransaction = relayingResult.transaction; - - if (validTransaction == null) { - assert.fail( - `validTransaction is null: ${JSON.stringify( - relayingResult, - replaceErrors - )}` - ); - return; - } - const validTransactionHash: string = validTransaction - .hash(true) - .toString('hex'); - const txHash = `0x${validTransactionHash}`; - const res = await web3.eth.getTransactionReceipt(txHash); - - // validate we've got the "SampleRecipientEmitted" event - // TODO: use OZ test helpers - const topic: string = - web3.utils.sha3( - 'SampleRecipientEmitted(string,address,address,uint256,uint256)' - ) ?? ''; - assert(res.logs.find((log) => log.topics.includes(topic))); - - const destination: string = validTransaction.to.toString('hex'); - assert.equal( - `0x${destination}`, - relayHub.address.toString().toLowerCase() - ); - }); - it('should deploy properly with token transfer gas estimation used', async function () { - const eoaWithoutSmartWalletAccount = await getGaslessAccount(); - - // register eoaWithoutSmartWallet account to avoid signing with RSKJ - relayClient.accountManager.addAccount( - eoaWithoutSmartWalletAccount - ); - - const deployOptions: EnvelopingTransactionDetails = { - from: eoaWithoutSmartWalletAccount.address, - to: constants.ZERO_ADDRESS, // No extra logic for the Smart Wallet - data: '0x', // No extra-logic init data - gas: gasOption.estimateGas ? undefined : '0x1E8480', - relayHub: relayHub.address, - callForwarder: factory.address, - callVerifier: deployVerifier.address, - clientId: '1', - tokenContract: token.address, - tokenAmount: '1', - // tokenGas: '50000', omitted so it is calculated - isSmartWalletDeploy: true, - recoverer: constants.ZERO_ADDRESS, - index: '0' - }; - - const swAddress = ( - await factory.getSmartWalletAddress( - eoaWithoutSmartWalletAccount.address, - constants.ZERO_ADDRESS, - '0' - ) - ).toLowerCase(); - await token.mint('1000', swAddress); - deployOptions.smartWalletAddress = swAddress; - - assert.equal( - await web3.eth.getCode(swAddress), - '0x', - 'SmartWallet not yet deployed, it must not have installed code' - ); - - const relayingResult = await relayClient.relayTransaction( - deployOptions - ); - const validTransaction = relayingResult.transaction; - - if (validTransaction == null) { - assert.fail( - `validTransaction is null: ${JSON.stringify( - relayingResult, - replaceErrors - )}` - ); - return; - } - const validTransactionHash: string = validTransaction - .hash(true) - .toString('hex'); - const txHash = `0x${validTransactionHash}`; - const res = await web3.eth.getTransactionReceipt(txHash); - // validate we've got the "Deployed" event - - const topic: string = - web3.utils.sha3('Deployed(address,uint256)') ?? ''; - assert.notEqual(topic, '', 'error while calculating topic'); - - assert(res.logs.find((log) => log.topics.includes(topic))); - const eventIdx = res.logs.findIndex((log) => - log.topics.includes(topic) - ); - const loggedEvent = res.logs[eventIdx]; - - const strippedAddr = stripHex(swAddress); - assert( - loggedEvent.topics.find((data) => - data.slice(26, data.length).includes(strippedAddr) - ) - ); - let eventSWAddress = - loggedEvent.topics[ - loggedEvent.topics.findIndex((data) => - data.slice(26, data.length).includes(strippedAddr) - ) - ]; - eventSWAddress = '0x'.concat( - eventSWAddress - .slice(26, eventSWAddress.length) - .toLowerCase() - ); - - const saltSha = - web3.utils.soliditySha3( - { - t: 'address', - v: eoaWithoutSmartWalletAccount.address - }, - { t: 'address', v: constants.ZERO_ADDRESS }, - { t: 'uint256', v: '0' } - ) ?? ''; - - assert.notEqual(saltSha, '', 'error while calculating salt'); - - const expectedSalt = web3.utils.toBN(saltSha).toString(); - - const obtainedEventData = web3.eth.abi.decodeParameters( - [{ type: 'uint256', name: 'salt' }], - loggedEvent.data - ); - - assert.equal( - obtainedEventData.salt, - expectedSalt, - 'salt from Deployed event is not the expected one' - ); - assert.equal( - eventSWAddress, - swAddress, - 'SmartWallet address from the Deployed event is not the expected one' - ); - - const destination: string = validTransaction.to.toString('hex'); - assert.equal( - `0x${destination}`, - relayHub.address.toString().toLowerCase() - ); - - let expectedCode = await factory.getCreationBytecode(); - expectedCode = - '0x' + expectedCode.slice(20, expectedCode.length); // only runtime code - assert.equal( - await web3.eth.getCode(swAddress), - expectedCode, - 'The installed code is not the expected one' - ); - }); - - it('should send a SmartWallet create transaction to a relay and receive a signed transaction in response', async function () { - const eoaWithoutSmartWalletAccount = await getGaslessAccount(); - - // register eoaWithoutSmartWallet account to avoid signing with RSKJ - relayClient.accountManager.addAccount( - eoaWithoutSmartWalletAccount - ); - const swAddress = ( - await factory.getSmartWalletAddress( - eoaWithoutSmartWalletAccount.address, - constants.ZERO_ADDRESS, - '0' - ) - ).toLowerCase(); - - const deployOptions: EnvelopingTransactionDetails = { - from: eoaWithoutSmartWalletAccount.address, - to: constants.ZERO_ADDRESS, // No extra logic for the Smart Wallet - data: '0x', // No extra-logic init data - gas: gasOption.estimateGas ? undefined : '0x1E8480', - relayHub: relayHub.address, - callForwarder: factory.address, - callVerifier: deployVerifier.address, - clientId: '1', - tokenContract: token.address, - tokenAmount: '1', - tokenGas: gasOption.estimateGas ? undefined : '50000', - isSmartWalletDeploy: true, - recoverer: constants.ZERO_ADDRESS, - index: '0', - smartWalletAddress: swAddress - }; - - await token.mint('1000', swAddress); - - assert.equal( - await web3.eth.getCode(swAddress), - '0x', - 'SmartWallet not yet deployed, it must not have installed code' - ); - - const relayingResult = await relayClient.relayTransaction( - deployOptions - ); - const validTransaction = relayingResult.transaction; - - if (validTransaction == null) { - assert.fail( - `validTransaction is null: ${JSON.stringify( - relayingResult, - replaceErrors - )}` - ); - return; - } - const validTransactionHash: string = validTransaction - .hash(true) - .toString('hex'); - const txHash = `0x${validTransactionHash}`; - const res = await web3.eth.getTransactionReceipt(txHash); - // validate we've got the "Deployed" event - - const topic: string = - web3.utils.sha3('Deployed(address,uint256)') ?? ''; - assert.notEqual(topic, '', 'error while calculating topic'); - - assert(res.logs.find((log) => log.topics.includes(topic))); - const eventIdx = res.logs.findIndex((log) => - log.topics.includes(topic) - ); - const loggedEvent = res.logs[eventIdx]; - - const strippedAddr = stripHex(swAddress); - assert( - loggedEvent.topics.find((data) => - data.slice(26, data.length).includes(strippedAddr) - ) - ); - let eventSWAddress = - loggedEvent.topics[ - loggedEvent.topics.findIndex((data) => - data.slice(26, data.length).includes(strippedAddr) - ) - ]; - eventSWAddress = '0x'.concat( - eventSWAddress - .slice(26, eventSWAddress.length) - .toLowerCase() - ); - - const saltSha = - web3.utils.soliditySha3( - { - t: 'address', - v: eoaWithoutSmartWalletAccount.address - }, - { t: 'address', v: constants.ZERO_ADDRESS }, - { t: 'uint256', v: '0' } - ) ?? ''; - - assert.notEqual(saltSha, '', 'error while calculating salt'); - - const expectedSalt = web3.utils.toBN(saltSha).toString(); - - const obtainedEventData = web3.eth.abi.decodeParameters( - [{ type: 'uint256', name: 'salt' }], - loggedEvent.data - ); - - assert.equal( - obtainedEventData.salt, - expectedSalt, - 'salt from Deployed event is not the expected one' - ); - assert.equal( - eventSWAddress, - swAddress, - 'SmartWallet address from the Deployed event is not the expected one' - ); - - const destination: string = validTransaction.to.toString('hex'); - assert.equal( - `0x${destination}`, - relayHub.address.toString().toLowerCase() - ); - - let expectedCode = await factory.getCreationBytecode(); - expectedCode = - '0x' + expectedCode.slice(20, expectedCode.length); // only runtime code - assert.equal( - await web3.eth.getCode(swAddress), - expectedCode, - 'The installed code is not the expected one' - ); - }); - - describe('with events listener', () => { - function eventsHandler(e: RelayEvent): void { - relayEvents.push(e); - } - - before('registerEventsListener', () => { - relayClient = new RelayClient(underlyingProvider, config); - relayClient.registerEventListener(eventsHandler); - - // register gaslessAccount account to avoid signing with RSKJ - relayClient.accountManager.addAccount(gaslessAccount); - }); - it('should call events handler', async function () { - await relayClient.relayTransaction(options); - assert.equal(relayEvents.length, 8); - assert.equal(relayEvents[0].step, 0); - assert.equal(relayEvents[0].total, 8); - assert.equal(relayEvents[7].step, 7); - }); - describe('removing events listener', () => { - before('registerEventsListener', () => { - relayEvents = []; - relayClient.unregisterEventListener(eventsHandler); - }); - it('should call events handler', async function () { - await relayClient.relayTransaction(options); - assert.equal(relayEvents.length, 0); - }); - }); - }); - }); - - // eslint-disable-next-line @typescript-eslint/no-misused-promises - describe('#_calculateDefaultGasPrice()', function () { - it('should use minimum gas price if calculated is to low', async function () { - const minGasPrice = 1e18; - const config: Partial = { - logLevel: 5, - relayHubAddress: relayHub.address, - minGasPrice, - chainId: (await getTestingEnvironment()).chainId - }; - const relayClient = new RelayClient(underlyingProvider, config); - const calculatedGasPrice = - await relayClient._calculateGasPrice(); - assert.equal( - calculatedGasPrice, - `0x${minGasPrice.toString(16)}` - ); - }); - }); - - describe('#_attemptRelay()', function () { - const relayUrl = localhostOne; - const relayWorkerAddress = accounts[1]; - const relayManager = accounts[2]; - const relayOwner = accounts[3]; - let pingResponse: PingResponse; - let relayInfo: RelayInfo; - let optionsWithGas: EnvelopingTransactionDetails; - - before(async function () { - await relayHub.stakeForAddress(relayManager, 7 * 24 * 3600, { - from: relayOwner, - value: (2e18).toString() - }); - await relayHub.addRelayWorkers([relayWorkerAddress], { - from: relayManager - }); - await relayHub.registerRelayServer('url', { - from: relayManager - }); - pingResponse = { - relayWorkerAddress: relayWorkerAddress, - relayManagerAddress: relayManager, - relayHubAddress: relayManager, - feesReceiver: relayWorkerAddress, - minGasPrice: '', - ready: true, - version: '' - }; - relayInfo = { - relayInfo: { - manager: relayManager, - url: relayUrl, - currentlyStaked: true, - registered: false - }, - pingResponse - }; - - let gasToSend = await web3.eth.estimateGas({ - from: options.callForwarder, - to: options.to, - gasPrice: toHex('6000000000'), - data: options.data - }); - - gasToSend = - gasToSend > - constants.INTERNAL_TRANSACTION_ESTIMATE_CORRECTION - ? gasToSend - - constants.INTERNAL_TRANSACTION_ESTIMATE_CORRECTION - : gasToSend; - - optionsWithGas = Object.assign({}, options, { - gas: toHex(gasToSend), - gasPrice: toHex('6000000000') - }); - }); - - it("should return error if view call to 'relayCall()' fails", async function () { - const badContractInteractor = new BadContractInteractor( - web3.currentProvider as Web3Provider, - configure(config), - true - ); - const relayClient = new RelayClient( - underlyingProvider, - config, - { contractInteractor: badContractInteractor } - ); - await relayClient._init(); - - // register gasless account in RelayClient to avoid signing with RSKJ - relayClient.accountManager.addAccount(gaslessAccount); - const { transaction, error } = await relayClient._attemptRelay( - relayInfo, - optionsWithGas - ); - assert.isUndefined(transaction); - assert.equal( - error.message, - `local view call reverted: ${BadContractInteractor.message}` - ); - }); - - it('should report relays that timeout to the Known Relays Manager', async function () { - const badHttpClient: any = new BadHttpClient( - configure(config), - false, - false, - true - ); - const dependencyTree = getDependencies( - configure(config), - underlyingProvider, - { httpClient: badHttpClient } - ); - const relayClient = new RelayClient( - underlyingProvider, - config, - dependencyTree - ); - await relayClient._init(); - - // register gasless account in RelayClient to avoid signing with RSKJ - relayClient.accountManager.addAccount(gaslessAccount); - - // @ts-ignore (sinon allows spying on all methods of the object, but TypeScript does not seem to know that) - sinon.spy(dependencyTree.knownRelaysManager); - const attempt = await relayClient._attemptRelay(relayInfo, { - ...optionsWithGas - }); - assert.equal( - attempt.error?.message, - 'some error describing how timeout occurred somewhere' - ); - expect( - dependencyTree.knownRelaysManager.saveRelayFailure - ).to.have.been.calledWith( - sinon.match.any, - relayManager, - relayUrl - ); - }); - - it('should not report relays if error is not timeout', async function () { - const badHttpClient: any = new BadHttpClient( - configure(config), - false, - true, - false - ); - const dependencyTree = getDependencies( - configure(config), - underlyingProvider, - { httpClient: badHttpClient } - ); - dependencyTree.httpClient = badHttpClient; - const relayClient = new RelayClient( - underlyingProvider, - config, - dependencyTree - ); - await relayClient._init(); - - // register gasless account in RelayClient to avoid signing with RSKJ - relayClient.accountManager.addAccount(gaslessAccount); - - // @ts-ignore (sinon allows spying on all methods of the object, but TypeScript does not seem to know that) - sinon.spy(dependencyTree.knownRelaysManager); - await relayClient._attemptRelay(relayInfo, optionsWithGas); - expect( - dependencyTree.knownRelaysManager.saveRelayFailure - ).to.have.not.been.called; - }); - - it('should return error if transaction returned by a relay does not pass validation', async function () { - const badHttpClient: any = new BadHttpClient( - configure(config), - false, - false, - false, - pingResponse, - '0x123' - ); - let dependencyTree = getDependencies( - configure(config), - underlyingProvider - ); - const badTransactionValidator: any = - new BadRelayedTransactionValidator( - true, - dependencyTree.contractInteractor, - configure(config) - ); - dependencyTree = getDependencies( - configure(config), - underlyingProvider, - { - httpClient: badHttpClient, - transactionValidator: badTransactionValidator - } - ); - const relayClient = new RelayClient( - underlyingProvider, - config, - dependencyTree - ); - - await relayClient._init(); - - // register gasless account in RelayClient to avoid signing with RSKJ - relayClient.accountManager.addAccount(gaslessAccount); - - // @ts-ignore (sinon allows spying on all methods of the object, but TypeScript does not seem to know that) - sinon.spy(dependencyTree.knownRelaysManager); - const tokenGas = gasOption.estimateGas - ? ( - await relayClient.estimateTokenTransferGas(options) - ).toString() - : options.tokenGas; - const { transaction, error } = await relayClient._attemptRelay( - relayInfo, - { - ...optionsWithGas, - tokenGas - } - ); - assert.isUndefined(transaction); - assert.equal( - error.message, - 'Returned transaction did not pass validation' - ); - expect( - dependencyTree.knownRelaysManager.saveRelayFailure - ).to.have.been.calledWith( - sinon.match.any, - relayManager, - relayUrl - ); - }); - }); - - describe('#_broadcastRawTx()', function () { - // TODO: TBD: there has to be other behavior then that. Maybe query the transaction with the nonce somehow? - it("should return 'wrongNonce' if broadcast fails with nonce error", async function () { - const badContractInteractor = new BadContractInteractor( - underlyingProvider, - configure(config), - true - ); - const transaction = new Transaction('0x'); - const relayClient = new RelayClient( - underlyingProvider, - config, - { contractInteractor: badContractInteractor } - ); - const { hasReceipt, wrongNonce, broadcastError } = - await relayClient._broadcastRawTx(transaction); - assert.isFalse(hasReceipt); - assert.isTrue(wrongNonce); - assert.equal( - broadcastError?.message, - BadContractInteractor.wrongNonceMessage - ); - }); - }); - - describe('multiple relayers', () => { - let id: string; - before(async () => { - id = (await snapshot()).result; - await registerRelayer(relayHub); - }); - after(async () => { - await revert(id); - }); - - it('should succeed to relay, but report ping error', async () => { - relayClient = new RelayClient(underlyingProvider, { - ...config, - preferredRelays: [RIF_RELAY_URL, cheapRelayerUrl] - }); - const relayingResult = await relayClient.relayTransaction( - options - ); - assert.isNotNull(relayingResult.transaction); - assert.match( - relayingResult.pingErrors.get(cheapRelayerUrl)?.message, - /ECONNREFUSED/, - `relayResult: ${_dumpRelayingResult(relayingResult)}` - ); - }); - - it('use preferred relay if one is set', async () => { - relayClient = new RelayClient(underlyingProvider, { - preferredRelays: [RIF_RELAY_URL], - ...config - }); - - relayClient.accountManager.addAccount(gaslessAccount); - - const relayingResult = await relayClient.relayTransaction( - options - ); - assert.isNotNull(relayingResult.transaction); - assert.equal(relayingResult.pingErrors.size, 0); - - console.log(relayingResult); - }); - }); - - describe('#validateSmartWallet', () => { - it('should fail if is not the owner', async () => { - const notOwner = await getGaslessAccount(); - relayClient.accountManager.addAccount(notOwner); - const txDetails: EnvelopingTransactionDetails = { - from: notOwner.address, - to: constants.ZERO_ADDRESS, - callForwarder: options.callForwarder, - data: '0x' - }; - await assert.isRejected( - relayClient.validateSmartWallet(txDetails), - 'Returned error: VM Exception while processing transaction: revert Not the owner of the SmartWallet' - ); - }); - - it('should fail if smart wallet is not deployed', async () => { - const swAddress = await factory.getSmartWalletAddress( - gaslessAccount.address, - constants.ZERO_ADDRESS, - '1' - ); - const txDetails: EnvelopingTransactionDetails = { - from: gaslessAccount.address, - to: constants.ZERO_ADDRESS, - callForwarder: swAddress, - data: '0x' - }; - await assert.isRejected( - relayClient.validateSmartWallet(txDetails), - 'Cannot create instance of IForwarder; no code at address' - ); - }); - - it('should fail if nonce mismatch', async () => { - const contractInteractor = new ContractInteractor( - web3.currentProvider as Web3Provider, - configure(config) - ); - sinon - .stub(contractInteractor, 'getSenderNonce') - .returns(Promise.resolve('500')); - const relayClient = new RelayClient( - underlyingProvider, - config, - { contractInteractor: contractInteractor } - ); - await relayClient._init(); - relayClient.accountManager.addAccount(gaslessAccount); - const txDetails: EnvelopingTransactionDetails = { - from: gaslessAccount.address, - to: constants.ZERO_ADDRESS, - callForwarder: options.callForwarder, - data: '0x' - }; - await assert.isRejected( - relayClient.validateSmartWallet(txDetails), - 'Returned error: VM Exception while processing transaction: revert nonce mismatch' - ); - }); - - it('should succeed the validation and call once resolveForwarder', async () => { - const spy = sinon.spy(relayClient, 'resolveForwarder'); - const txDetails: EnvelopingTransactionDetails = { - from: gaslessAccount.address, - to: constants.ZERO_ADDRESS, - callForwarder: options.callForwarder, - data: '0x' - }; - await relayClient.validateSmartWallet(txDetails); - assert.isTrue(spy.calledOnce); - }); - }); - }); -}); diff --git a/test-to-migrate/relayclient/RelayProvider.test.ts b/test-to-migrate/relayclient/RelayProvider.test.ts deleted file mode 100644 index c1c7cc61..00000000 --- a/test-to-migrate/relayclient/RelayProvider.test.ts +++ /dev/null @@ -1,1174 +0,0 @@ -import { ether, expectEvent, expectRevert } from '@openzeppelin/test-helpers'; -import { HttpProvider, WebsocketProvider } from 'web3-core'; -import { ChildProcessWithoutNullStreams } from 'child_process'; -import { JsonRpcPayload, JsonRpcResponse } from 'web3-core-helpers'; -import chaiAsPromised from 'chai-as-promised'; -import Web3 from 'web3'; -import { toBN, toChecksumAddress } from 'web3-utils'; -// @ts-ignore -import abiDecoder from 'abi-decoder'; -import { IWalletFactory } from '@rsksmart/rif-relay-contracts'; -import { - RelayHubInstance, - SmartWalletFactoryInstance, - SmartWalletInstance, - TestDeployVerifierConfigurableMisbehaviorInstance, - TestRecipientContract, - TestRecipientInstance, - TestTokenInstance, - TestVerifierConfigurableMisbehaviorInstance -} from '@rsksmart/rif-relay-contracts/types/truffle-contracts'; -import { - constants, - EnvelopingConfig, - EnvelopingTransactionDetails, - isRsk -} from '@rsksmart/rif-relay-common'; -import { - createSmartWallet, - createSmartWalletFactory, - deployHub, - getGaslessAccount, - getTestingEnvironment, - getWebSocketUrl, - prepareTransaction, - RelayServerData, - startRelay, - stopRelay -} from '../TestUtils'; -import BadRelayClient from '../dummies/BadRelayClient'; -import { - AccountKeypair, - configure, - RelayingResult, - RelayProvider -} from '@rsksmart/rif-relay-client'; - -import * as chai from 'chai'; - -const { expect, assert } = chai.use(chaiAsPromised); - -const SmartWallet = artifacts.require('SmartWallet'); -const TestToken = artifacts.require('TestToken'); -const TestVerifierConfigurableMisbehavior = artifacts.require( - 'TestVerifierConfigurableMisbehavior' -); -const TestDeployVerifierConfigurableMisbehavior = artifacts.require( - 'TestDeployVerifierConfigurableMisbehavior' -); - -const underlyingProvider = web3.currentProvider as HttpProvider; -const revertReasonEnabled = true; // Enable when the RSK node supports revert reason codes -abiDecoder.addABI(IWalletFactory.abi); - -contract('RelayProvider', function (accounts) { - let web3: Web3; - let relayHub: RelayHubInstance; - let verifierInstance: TestVerifierConfigurableMisbehaviorInstance; - let deployVerifierInstance: TestDeployVerifierConfigurableMisbehaviorInstance; - let relayProcess: ChildProcessWithoutNullStreams; - let relayProvider: RelayProvider; - let factory: SmartWalletFactoryInstance; - let sWalletTemplate: SmartWalletInstance; - let smartWallet: SmartWalletInstance; - let sender: string; - let token: TestTokenInstance; - let gaslessAccount: AccountKeypair; - let relayServerData: RelayServerData; - before(async function () { - sender = accounts[0]; - gaslessAccount = await getGaslessAccount(); - web3 = new Web3(underlyingProvider); - relayHub = await deployHub(constants.ZERO_ADDRESS); - - sWalletTemplate = await SmartWallet.new(); - const env = await getTestingEnvironment(); - factory = await createSmartWalletFactory(sWalletTemplate); - smartWallet = await createSmartWallet( - accounts[0], - gaslessAccount.address, - factory, - gaslessAccount.privateKey, - env.chainId - ); - token = await TestToken.new(); - await token.mint('1000', smartWallet.address); - - verifierInstance = await TestVerifierConfigurableMisbehavior.new(); - deployVerifierInstance = - await TestDeployVerifierConfigurableMisbehavior.new(); - - relayServerData = await startRelay(relayHub, { - relaylog: process.env.relaylog, - stake: 1e18, - url: 'asd', - relayOwner: accounts[1], - rskNodeUrl: underlyingProvider.host, - deployVerifierAddress: deployVerifierInstance.address, - relayVerifierAddress: verifierInstance.address, - workerMinBalance: 0.1e18, - workerTargetBalance: 0.3e18, - managerMinBalance: 0.1e18, - managerTargetBalance: 0.3e18, - minHubWithdrawalBalance: 0.1e18 - }); - - relayProcess = relayServerData.proc; - }); - - after(async function () { - await stopRelay(relayProcess); - }); - - describe('Use Provider to relay transparently', () => { - let testRecipient: TestRecipientInstance; - let testRecipient2: TestRecipientInstance; - before(async () => { - const env = await getTestingEnvironment(); - const TestRecipient = artifacts.require('TestRecipient'); - testRecipient = await TestRecipient.new(); - testRecipient2 = await TestRecipient.new(); - const config = configure({ - logLevel: 5, - relayHubAddress: relayHub.address, - chainId: env.chainId, - forwarderAddress: smartWallet.address, - relayVerifierAddress: verifierInstance.address, - deployVerifierAddress: deployVerifierInstance.address, - preferredRelays: ['http://localhost:8095'] - }); - - let websocketProvider: WebsocketProvider; - - if (isRsk(await getTestingEnvironment())) { - websocketProvider = new Web3.providers.WebsocketProvider( - getWebSocketUrl() - ); - } else { - websocketProvider = new Web3.providers.WebsocketProvider( - underlyingProvider.host - ); - } - - relayProvider = new RelayProvider(websocketProvider as any, config); - - // NOTE: in real application its enough to set the provider in web3. - // however, in Truffle, all contracts are built BEFORE the test have started, and COPIED the web3, - // so changing the global one is not enough. - // @ts-ignore - TestRecipient.web3.setProvider(relayProvider); - relayProvider.addAccount(gaslessAccount); - }); - - it('should relay transparently', async function () { - const res = await testRecipient.emitMessage('hello world', { - from: gaslessAccount.address, - value: '0', - callVerifier: verifierInstance.address - }); - - expectEvent.inLogs(res.logs, 'SampleRecipientEmitted', { - message: 'hello world', - msgValue: '0', - balance: '0' - }); - }); - - it('should relay transparently using forceGas', async function () { - const res = await testRecipient.emitMessage('hello world', { - from: gaslessAccount.address, - value: '0', - callVerifier: verifierInstance.address, - forceGas: '6000' - }); - - expectEvent.inLogs(res.logs, 'SampleRecipientEmitted', { - message: 'hello world', - msgValue: '0', - balance: '0' - }); - }); - - it('should fail to relay using a lower-than-required forceGas', async function () { - await expectRevert( - testRecipient.emitMessage('hello world', { - from: gaslessAccount.address, - value: '0', - callVerifier: verifierInstance.address, - forceGas: '4000' - }), - 'Destination contract method reverted in local view call' - ); - }); - - it('should fail to relay if forceGas gas is much greater than what is required ', async function () { - // Putting a much higher gas may pass in the relayclient, but it will fail in the local view call of the - // relay server, because the relayServer estimates the maximum gas to send by estimating it using a node, - // whereas the relay client (to avoid another call to a node), estimates the maxGas using a linear fit function - // obtained from a simulation - - // It reverts in the server, but in a local view call - // await expectRevert(testRecipient.emitMessage('hello world', { - // from: gaslessAccount.address, - // value: '0', - // callVerifier: verifierInstance.address, - // forceGas: '196000' - // }), 'local view call reverted: view call to \'relayCall\' reverted in client: Not enough gas left') - try { - await testRecipient.emitMessage('hello world', { - from: gaslessAccount.address, - value: '0', - callVerifier: verifierInstance.address, - forceGas: '196000' - }); - } catch (error) { - const err: string = - error instanceof Error - ? error.message - : JSON.stringify(error); - assert.isTrue( - err.includes( - "local view call reverted: view call to 'relayCall' reverted in client: Returned error: VM Exception while processing transaction: revert Not enough gas left" - ) - ); - } - }); - - it('should send a transaction when useEnveloping is false', async function () { - const res = await testRecipient.emitMessage( - 'hello world, not using enveloping', - { - from: accounts[0], - useEnveloping: false - } - ); - - expectEvent.inLogs(res.logs, 'SampleRecipientEmitted', { - message: 'hello world, not using enveloping', - msgValue: '0', - balance: '0' - }); - }); - - it('should relay transparently, with Token Payment included', async function () { - await token.mint('1', smartWallet.address); - const initialSwBalance = await token.balanceOf(smartWallet.address); - const workerInitialBalance = await token.balanceOf( - relayServerData.worker - ); - - const res = await testRecipient.emitMessage('hello world', { - from: gaslessAccount.address, - value: '0', - callVerifier: verifierInstance.address, - tokenAmount: '1', - tokenContract: token.address - }); - - const finalSwBalance = await token.balanceOf(smartWallet.address); - const workerFinalBalance = await token.balanceOf( - relayServerData.worker - ); - - expectEvent.inLogs(res.logs, 'SampleRecipientEmitted', { - message: 'hello world', - msgValue: '0', - balance: '0' - }); - - assert.isTrue( - finalSwBalance.add(toBN(1)).eq(initialSwBalance), - 'Token Payment did not occurr' - ); - assert.isTrue( - workerInitialBalance.add(toBN(1)).eq(workerFinalBalance), - 'Worker did not get the payment' - ); - }); - - it('should relay transparently using forceGas, with Token Payment included', async function () { - await token.mint('1', smartWallet.address); - const initialSwBalance = await token.balanceOf(smartWallet.address); - const workerInitialBalance = await token.balanceOf( - relayServerData.worker - ); - - const res = await testRecipient.emitMessage('hello world', { - from: gaslessAccount.address, - value: '0', - callVerifier: verifierInstance.address, - tokenAmount: '1', - tokenContract: token.address, - forceGas: '6000' - }); - - const finalSwBalance = await token.balanceOf(smartWallet.address); - const workerFinalBalance = await token.balanceOf( - relayServerData.worker - ); - - expectEvent.inLogs(res.logs, 'SampleRecipientEmitted', { - message: 'hello world', - msgValue: '0', - balance: '0' - }); - - assert.isTrue( - finalSwBalance.add(toBN(1)).eq(initialSwBalance), - 'Token Payment did not occurr' - ); - assert.isTrue( - workerInitialBalance.add(toBN(1)).eq(workerFinalBalance), - 'Worker did not get the payment' - ); - }); - - it('should fail to relay using a lower-than-required forceGas, with Token Payment included', async function () { - await token.mint('1', smartWallet.address); - const initialSwBalance = await token.balanceOf(smartWallet.address); - const workerInitialBalance = await token.balanceOf( - relayServerData.worker - ); - - await expectRevert( - testRecipient.emitMessage('hello world', { - from: gaslessAccount.address, - value: '0', - callVerifier: verifierInstance.address, - tokenAmount: '1', - tokenContract: token.address, - forceGas: '4000' - }), - 'Destination contract method reverted in local view call' - ); - - const finalSwBalance = await token.balanceOf(smartWallet.address); - const workerFinalBalance = await token.balanceOf( - relayServerData.worker - ); - - assert.isTrue( - finalSwBalance.eq(initialSwBalance), - 'Token Payment did occurr' - ); - assert.isTrue( - workerInitialBalance.eq(workerFinalBalance), - 'Worker did get the payment' - ); - }); - - it('should fail to relay if forceGas gas is much greater than what is required, with Token Payment included ', async function () { - // Putting a much higher gas may pass in the relayclient, but it will fail in the local view call of the - // relay server, because the relayServer estimates the maximum gas to send by estimating it using a node, - // whereas the relay client (to avoid another call to a node), estimates the maxGas using a linear fit function - // obtained from a simulation - - await token.mint('1', smartWallet.address); - const initialSwBalance = await token.balanceOf(smartWallet.address); - const workerInitialBalance = await token.balanceOf( - relayServerData.worker - ); - await expectRevert( - testRecipient.emitMessage('hello world', { - from: gaslessAccount.address, - value: '0', - callVerifier: verifierInstance.address, - tokenAmount: '1', - tokenContract: token.address, - forceGas: '196000' - }), - 'Not enough gas left' - ); - - const finalSwBalance = await token.balanceOf(smartWallet.address); - const workerFinalBalance = await token.balanceOf( - relayServerData.worker - ); - - assert.isTrue( - finalSwBalance.eq(initialSwBalance), - 'Token Payment did occurr' - ); - assert.isTrue( - workerInitialBalance.eq(workerFinalBalance), - 'Worker did get the payment' - ); - }); - - it('should fail to relay transparently with Token Payment included, but when token Balance is not enough', async function () { - const initialSwBalance = await token.balanceOf(smartWallet.address); - - await expectRevert( - testRecipient.emitMessage('hello world', { - from: gaslessAccount.address, - value: '0', - callVerifier: verifierInstance.address, - tokenAmount: initialSwBalance.add(toBN(1)).toString(), - tokenContract: token.address - }), - 'Unable to pay for relay' - ); - - const finalSwBalance = await token.balanceOf(smartWallet.address); - assert.isTrue( - finalSwBalance.eq(initialSwBalance), - 'Token Payment did occurr' - ); - }); - - it('should relay transparently with gasPrice forced', async function () { - const res = await testRecipient.emitMessage('hello world', { - from: gaslessAccount.address, - forceGasPrice: '0x51f4d5c00', - value: '0', - callVerifier: verifierInstance.address - }); - - expectEvent.inLogs(res.logs, 'SampleRecipientEmitted', { - message: 'hello world', - msgValue: '0', - balance: '0' - }); - }); - - it('should relay transparently with value', async function () { - const value = (1e18).toString(); - // note: this test only validates we process the "value" parameter of the request properly. - // a real use-case should have a verifier to transfer the value into the forwarder, - // probably by swapping user's tokens into eth. - - await web3.eth.sendTransaction({ - from: sender, - to: smartWallet.address, - value - }); - - const res = await testRecipient.emitMessage('hello world', { - from: gaslessAccount.address, - forceGasPrice: '0x51f4d5c00', - value, - gas: '100000', - callVerifier: verifierInstance.address - }); - - expectEvent.inLogs(res.logs, 'SampleRecipientEmitted', { - message: 'hello world', - msgValue: value, - balance: value - }); - }); - - it('should revert if the sender is not the owner of the smart wallet', async function () { - const differentSender = await getGaslessAccount(); - relayProvider.addAccount(differentSender); - - await expectRevert( - testRecipient.emitMessage('hello world', { - from: differentSender.address, // different sender - value: '0', - callVerifier: verifierInstance.address - }), - 'Not the owner of the SmartWallet' - ); - }); - - it('should calculate the correct smart wallet address', async function () { - assert.isTrue(relayProvider != null); - const env = await getTestingEnvironment(); - const config = configure({ - relayHubAddress: relayHub.address, - logLevel: 5, - chainId: env.chainId - }); - config.forwarderAddress = constants.ZERO_ADDRESS; - const recoverer = constants.ZERO_ADDRESS; - const walletIndex = 0; - const bytecodeHash = web3.utils.keccak256( - await factory.getCreationBytecode() - ); - - const rProvider = new RelayProvider(underlyingProvider, config); - const swAddress = rProvider.calculateSmartWalletAddress( - factory.address, - gaslessAccount.address, - recoverer, - walletIndex, - bytecodeHash - ); - - const expectedAddress = await factory.getSmartWalletAddress( - gaslessAccount.address, - recoverer, - walletIndex - ); - - assert.equal(swAddress, expectedAddress); - }); - - it('should fail to deploy the smart wallet due to insufficient token balance', async function () { - const ownerEOA = await getGaslessAccount(); - const recoverer = constants.ZERO_ADDRESS; - const customLogic = constants.ZERO_ADDRESS; - const logicData = '0x'; - const walletIndex = 0; - const env = await getTestingEnvironment(); - const config = configure({ - relayHubAddress: relayHub.address, - logLevel: 5, - chainId: env.chainId, - relayVerifierAddress: verifierInstance.address, - deployVerifierAddress: deployVerifierInstance.address, - preferredRelays: ['http://localhost:8095'] - }); - assert.isTrue(relayProvider != null); - config.forwarderAddress = constants.ZERO_ADDRESS; - const rProvider = new RelayProvider(underlyingProvider, config); - rProvider.addAccount(ownerEOA); - const bytecodeHash = web3.utils.keccak256( - await factory.getCreationBytecode() - ); - const swAddress = rProvider.calculateSmartWalletAddress( - factory.address, - ownerEOA.address, - recoverer, - walletIndex, - bytecodeHash - ); - - assert.isTrue( - (await token.balanceOf(swAddress)).toNumber() < 10, - 'Account must have insufficient funds' - ); - - const expectedCode = await web3.eth.getCode(swAddress); - assert.equal('0x', expectedCode); - - const trxData: EnvelopingTransactionDetails = { - from: ownerEOA.address, - to: customLogic, - data: logicData, - tokenContract: token.address, - tokenAmount: '10', - // tokenGas: '50000', - recoverer: recoverer, - callForwarder: factory.address, - index: walletIndex.toString(), - isSmartWalletDeploy: true, - callVerifier: deployVerifierInstance.address, - smartWalletAddress: swAddress - }; - - try { - await rProvider.deploySmartWallet(trxData); - assert.fail(); - } catch (error) { - assert.include( - error.message, - "view call to 'deployCall' reverted in client" - ); - } - }); - - it('should correctly deploy the smart wallet', async function () { - const ownerEOA = await getGaslessAccount(); - const recoverer = constants.ZERO_ADDRESS; - const customLogic = constants.ZERO_ADDRESS; - const logicData = '0x'; - const walletIndex = 0; - const env = await getTestingEnvironment(); - const config = configure({ - relayHubAddress: relayHub.address, - logLevel: 5, - chainId: env.chainId, - deployVerifierAddress: deployVerifierInstance.address, - relayVerifierAddress: verifierInstance.address - }); - assert.isTrue(relayProvider != null); - config.forwarderAddress = constants.ZERO_ADDRESS; - const rProvider = new RelayProvider(underlyingProvider, config); - rProvider.addAccount(ownerEOA); - const bytecodeHash = web3.utils.keccak256( - await factory.getCreationBytecode() - ); - const swAddress = rProvider.calculateSmartWalletAddress( - factory.address, - ownerEOA.address, - recoverer, - walletIndex, - bytecodeHash - ); - await token.mint('10000', swAddress); - - let expectedCode = await web3.eth.getCode(swAddress); - assert.equal('0x', expectedCode); - - const trxData: EnvelopingTransactionDetails = { - from: ownerEOA.address, - to: customLogic, - data: logicData, - // gas: toHex('400000'), - tokenContract: token.address, - tokenAmount: '10', - tokenGas: '50000', - recoverer: recoverer, - index: walletIndex.toString(), - callVerifier: deployVerifierInstance.address, - callForwarder: factory.address, - isSmartWalletDeploy: true - }; - - const relayingResult: RelayingResult = - await rProvider.deploySmartWallet(trxData); - const txHash: string = - '0x' + relayingResult.transaction.hash(true).toString('hex'); - const trx = await web3.eth.getTransactionReceipt(txHash); - - const logs = abiDecoder.decodeLogs(trx.logs); - const deployedEvent = logs.find( - (e: any) => e != null && e.name === 'Deployed' - ); - const event = deployedEvent.events[0]; - assert.equal(event.name, 'addr'); - const generatedSWAddress = toChecksumAddress( - event.value, - env.chainId - ); - - assert.equal(generatedSWAddress, swAddress); - const deployedCode = await web3.eth.getCode(generatedSWAddress); - expectedCode = await factory.getCreationBytecode(); - expectedCode = '0x' + expectedCode.slice(20, expectedCode.length); - assert.equal(deployedCode, expectedCode); - }); - - it('should correclty deploy the smart wallet when tokenGas is not defined', async function () { - const ownerEOA = await getGaslessAccount(); - const recoverer = constants.ZERO_ADDRESS; - const customLogic = constants.ZERO_ADDRESS; - const logicData = '0x'; - const walletIndex = 0; - const env = await getTestingEnvironment(); - const config = configure({ - relayHubAddress: relayHub.address, - logLevel: 5, - chainId: env.chainId, - deployVerifierAddress: deployVerifierInstance.address, - relayVerifierAddress: verifierInstance.address, - preferredRelays: ['http://localhost:8095'] - }); - assert.isTrue(relayProvider != null); - config.forwarderAddress = constants.ZERO_ADDRESS; - const rProvider = new RelayProvider(underlyingProvider, config); - rProvider.addAccount(ownerEOA); - const bytecodeHash = web3.utils.keccak256( - await factory.getCreationBytecode() - ); - const swAddress = rProvider.calculateSmartWalletAddress( - factory.address, - ownerEOA.address, - recoverer, - walletIndex, - bytecodeHash - ); - await token.mint('10000', swAddress); - - let expectedCode = await web3.eth.getCode(swAddress); - assert.equal('0x', expectedCode); - - const trxData: EnvelopingTransactionDetails = { - from: ownerEOA.address, - to: customLogic, - data: logicData, - // gas: toHex('400000'), - tokenContract: token.address, - tokenAmount: '10', - // tokenGas: '50000', - recoverer: recoverer, - index: walletIndex.toString(), - callVerifier: deployVerifierInstance.address, - callForwarder: factory.address, - isSmartWalletDeploy: true, - smartWalletAddress: swAddress // so the client knows how to estimate tokenGas - }; - - const relayingResult: RelayingResult = - await rProvider.deploySmartWallet(trxData); - const txHash: string = - '0x' + relayingResult.transaction.hash(true).toString('hex'); - const trx = await web3.eth.getTransactionReceipt(txHash); - - const logs = abiDecoder.decodeLogs(trx.logs); - const deployedEvent = logs.find( - (e: any) => e != null && e.name === 'Deployed' - ); - const event = deployedEvent.events[0]; - assert.equal(event.name, 'addr'); - const generatedSWAddress = toChecksumAddress( - event.value, - env.chainId - ); - - assert.equal(generatedSWAddress, swAddress); - const deployedCode = await web3.eth.getCode(generatedSWAddress); - expectedCode = await factory.getCreationBytecode(); - expectedCode = '0x' + expectedCode.slice(20, expectedCode.length); - assert.equal(deployedCode, expectedCode); - }); - - it('should fail to deploy the smart wallet is tokenGas and smartWalletAddress are not defined', async function () { - const ownerEOA = await getGaslessAccount(); - const recoverer = constants.ZERO_ADDRESS; - const customLogic = constants.ZERO_ADDRESS; - const logicData = '0x'; - const walletIndex = 0; - const env = await getTestingEnvironment(); - const config = configure({ - relayHubAddress: relayHub.address, - logLevel: 5, - chainId: env.chainId, - deployVerifierAddress: deployVerifierInstance.address, - relayVerifierAddress: verifierInstance.address, - preferredRelays: ['http://localhost:8095'] - }); - assert.isTrue(relayProvider != null); - config.forwarderAddress = constants.ZERO_ADDRESS; - const rProvider = new RelayProvider(underlyingProvider, config); - rProvider.addAccount(ownerEOA); - const bytecodeHash = web3.utils.keccak256( - await factory.getCreationBytecode() - ); - const swAddress = rProvider.calculateSmartWalletAddress( - factory.address, - ownerEOA.address, - recoverer, - walletIndex, - bytecodeHash - ); - await token.mint('10000', swAddress); - - const expectedCode = await web3.eth.getCode(swAddress); - assert.equal('0x', expectedCode); - - const trxData: EnvelopingTransactionDetails = { - from: ownerEOA.address, - to: customLogic, - data: logicData, - // gas: toHex('400000'), - tokenContract: token.address, - tokenAmount: '10', - // tokenGas: '50000', - recoverer: recoverer, - index: walletIndex.toString(), - callVerifier: deployVerifierInstance.address, - callForwarder: factory.address, - isSmartWalletDeploy: true, - smartWalletAddress: swAddress - }; - - try { - await rProvider.deploySmartWallet(trxData); - } catch (error) { - assert.equal( - error.message, - 'In a deploy, if tokenGas is not defined, then the calculated SmartWallet address is needed to estimate the tokenGas value' - ); - } - }); - - it('should subscribe to events', async () => { - const block = await web3.eth.getBlockNumber(); - - const eventPromise = new Promise((resolve, reject) => { - // @ts-ignore - testRecipient2.contract.once( - 'SampleRecipientEmitted', - { fromBlock: block }, - (err: any, ev: any) => { - if (err !== null) { - reject(err); - } else { - resolve(ev); - } - } - ); - }); - - await testRecipient2.emitMessage('hello again', { - from: gaslessAccount.address, - gas: '100000', - callVerifier: verifierInstance.address - }); - const log: any = await eventPromise; - - assert.equal(log.returnValues.message, 'hello again'); - }); - - // note that the revert reason here was discovered via some truffle/ganache magic (see truffle/reason.js) - // this is not the way the revert reason is being reported by Enveloping solidity contracts - it('should fail if transaction failed', async () => { - await expectRevert( - testRecipient.testRevert({ - from: gaslessAccount.address, - callVerifier: verifierInstance.address - }), - 'Destination contract method reverted in local view call' - ); - }); - }); - - describe('_ethSendTransaction', function () { - const id = 777; - let testRecipient: TestRecipientInstance; - let config: EnvelopingConfig; - let jsonRpcPayload: JsonRpcPayload; - - before(async function () { - const TestRecipient = artifacts.require('TestRecipient'); - testRecipient = await TestRecipient.new(); - - const env = await getTestingEnvironment(); - config = configure({ - relayHubAddress: relayHub.address, - logLevel: 5, - chainId: env.chainId - }); - config.forwarderAddress = smartWallet.address; - - // call to emitMessage('hello world') - jsonRpcPayload = { - jsonrpc: '2.0', - id, - method: 'eth_sendTransaction', - params: [ - { - from: gaslessAccount.address, - gas: '0x186a0', - gasPrice: '0x4a817c800', - forceGasPrice: '0x51f4d5c00', - callVerifier: verifierInstance.address, - to: testRecipient.address, - data: testRecipient.contract.methods - .emitMessage('hello world') - .encodeABI() - } - ] - }; - }); - - it('should call callback with error if relayTransaction throws', async function () { - const badRelayClient: any = new BadRelayClient( - true, - false, - underlyingProvider, - config - ); - const relayProvider = new RelayProvider( - underlyingProvider, - config, - {}, - badRelayClient - ); - const promisified = new Promise((resolve, reject) => - relayProvider._ethSendTransaction( - jsonRpcPayload, - (error: Error | null): void => { - reject(error); - } - ) - ); - await expect(promisified).to.be.eventually.rejectedWith( - `Rejected relayTransaction call - Reason: ${BadRelayClient.message}` - ); - }); - - it('should call callback with error containing relaying results dump if relayTransaction does not return a transaction object', async function () { - const badRelayClient: any = new BadRelayClient( - false, - true, - underlyingProvider, - config - ); - const relayProvider = new RelayProvider( - underlyingProvider, - config, - {}, - badRelayClient - ); - const promisified = new Promise((resolve, reject) => - relayProvider._ethSendTransaction( - jsonRpcPayload, - (error: Error | null): void => { - reject(error); - } - ) - ); - await expect(promisified).to.be.eventually.rejectedWith( - 'Failed to relay call. Results:' - ); - }); - - it('should convert a returned transaction to a compatible rpc transaction hash response', async function () { - const env = await getTestingEnvironment(); - const config = configure({ - logLevel: 5, - relayHubAddress: relayHub.address, - chainId: env.chainId, - relayVerifierAddress: verifierInstance.address, - deployVerifierAddress: deployVerifierInstance.address - }); - config.forwarderAddress = smartWallet.address; - - const relayProvider = new RelayProvider(underlyingProvider, config); - relayProvider.addAccount(gaslessAccount); - const response: JsonRpcResponse = await new Promise( - (resolve, reject) => - relayProvider._ethSendTransaction( - jsonRpcPayload, - ( - error: Error | null, - result?: JsonRpcResponse - ): void => { - if (error != null) { - reject(error); - } else { - if (result !== undefined) { - resolve(result); - } else throw new Error('Result is undefined'); - } - } - ) - ); - assert.equal(id, response.id); - assert.equal('2.0', response.jsonrpc); - // I don't want to hard-code tx hash, so for now just checking it is there - assert.equal(66, response.result.length); - }); - }); - - // TODO: most of this code is copy-pasted from the RelayHub.test.ts. Maybe extract better utils? - describe('_getRelayStatus', function () { - let relayProvider: RelayProvider; - let testRecipient: TestRecipientInstance; - const gas = toBN(3e6).toString(); - - // It is not strictly necessary to make this test against actual tx receipt, but I prefer to do it anyway - before(async function () { - const TestRecipient = artifacts.require('TestRecipient'); - testRecipient = await TestRecipient.new(); - const env = await getTestingEnvironment(); - const config = configure({ - relayHubAddress: relayHub.address, - logLevel: 5, - chainId: env.chainId, - relayVerifierAddress: verifierInstance.address, - deployVerifierAddress: verifierInstance.address, - forwarderAddress: smartWallet.address - }); - - // @ts-ignore - Object.keys(TestRecipient.events).forEach(function (topic) { - // @ts-ignore - relayHub.constructor.network.events[topic] = - TestRecipient.events[topic]; - }); - relayProvider = new RelayProvider(underlyingProvider, config); - relayProvider.addAccount(gaslessAccount); - // @ts-ignore - TestRecipient.web3.setProvider(relayProvider); - // add accounts[0], accounts[1] and accounts[2] as worker, manager and owner - await relayHub.stakeForAddress(accounts[1], 1000, { - value: ether('1'), - from: accounts[2] - }); - await relayHub.addRelayWorkers([accounts[0]], { - from: accounts[1] - }); - - // create desired transactions - const nonceToUse = await smartWallet.nonce(); - const { relayRequest, signature } = await prepareTransaction( - relayHub.address, - testRecipient, - gaslessAccount, - accounts[0], - verifierInstance.address, - nonceToUse.toString(), - smartWallet.address, - token.address, - '1' - ); - - await verifierInstance.setReturnInvalidErrorCode(false); - await verifierInstance.setRevertPreRelayCall(false); - await verifierInstance.setOverspendAcceptGas(false); - - const innerTxSuccessReceiptTruffle = await relayHub.relayCall( - relayRequest, - signature, - { - from: accounts[0], - gas, - gasPrice: '1' - } - ); - - expectEvent.inLogs( - innerTxSuccessReceiptTruffle.logs, - 'TransactionRelayed' - ); - expectEvent.inLogs( - innerTxSuccessReceiptTruffle.logs, - 'SampleRecipientEmitted' - ); - - const notRelayedTxReceiptTruffle = await testRecipient.emitMessage( - 'hello world with gas', - { - from: gaslessAccount.address, - gas: '100000', - gasPrice: '1', - callVerifier: verifierInstance.address - } - ); - assert.equal(notRelayedTxReceiptTruffle.logs.length, 1); - expectEvent.inLogs( - notRelayedTxReceiptTruffle.logs, - 'SampleRecipientEmitted' - ); - }); - - it('should fail to send transaction if verifier reverts in local execution', async function () { - await verifierInstance.setReturnInvalidErrorCode(false); - await verifierInstance.setRevertPreRelayCall(true); - await verifierInstance.setOverspendAcceptGas(false); - - try { - await testRecipient.emitMessage('hello again', { - from: gaslessAccount.address, - gas: '100000', - gasPrice: '1', - callVerifier: verifierInstance.address - }); - } catch (error) { - const err: string = - error instanceof Error - ? error.message - : JSON.stringify(error); - if (revertReasonEnabled) { - assert.isTrue( - err.includes( - "verifier rejected in local view call : view call to 'relayCall' reverted in verifier" - ) - ); - assert.isTrue( - err.includes('revertPreRelayCall: Reverting') - ); - } - return; - } - - assert.fail('It should have thrown an exception'); - }); - - it('should fail to send transaction if verifier fails in local execution', async function () { - await verifierInstance.setReturnInvalidErrorCode(true); - await verifierInstance.setRevertPreRelayCall(false); - await verifierInstance.setOverspendAcceptGas(false); - - try { - await testRecipient.emitMessage('hello again', { - from: gaslessAccount.address, - gas: '100000', - gasPrice: '1', - callVerifier: verifierInstance.address - }); - } catch (error) { - const err = String(error); - if (revertReasonEnabled) { - assert.isTrue( - err.includes( - "verifier rejected in local view call : view call to 'relayCall' reverted in verifier" - ) - ); - assert.isTrue(err.includes('invalid code')); - } - return; - } - - assert.fail('It should have thrown an exception'); - }); - }); - - describe('_getAccounts', function () { - it('should append ephemeral accounts to the ones from the underlying provider', async function () { - const relayProvider = new RelayProvider(underlyingProvider, { - logLevel: 5 - }); - const web3 = new Web3(relayProvider); - const accountsBefore = await web3.eth.getAccounts(); - const newAccount = relayProvider.newAccount(); - const address = '0x982a8cbe734cb8c29a6a7e02a3b0e4512148f6f9'; - relayProvider.addAccount({ - privateKey: Buffer.from( - 'd353907ab062133759f149a3afcb951f0f746a65a60f351ba05a3ebf26b67f5c', - 'hex' - ), - address - }); - const accountsAfter = await web3.eth.getAccounts(); - const newAccounts = accountsAfter - .filter((value) => !accountsBefore.includes(value)) - .map((it) => it.toLowerCase()); - assert.equal(newAccounts.length, 2); - assert.include(newAccounts, address); - assert.include(newAccounts, newAccount.address); - }); - }); - - describe('new contract deployment', function () { - let TestRecipient: TestRecipientContract; - before(async function () { - TestRecipient = artifacts.require('TestRecipient'); - - const config = configure({ - logLevel: 5, - relayHubAddress: relayHub.address, - chainId: (await getTestingEnvironment()).chainId - }); - config.forwarderAddress = smartWallet.address; - - let websocketProvider: WebsocketProvider; - - if (isRsk(await getTestingEnvironment())) { - websocketProvider = new Web3.providers.WebsocketProvider( - getWebSocketUrl() - ); - } else { - websocketProvider = new Web3.providers.WebsocketProvider( - underlyingProvider.host - ); - } - - relayProvider = new RelayProvider(websocketProvider as any, config); - // @ts-ignore - TestRecipient.web3.setProvider(relayProvider); - }); - - it('should throw on calling .new without useEnveloping: false', async function () { - await expect(TestRecipient.new()).to.be.eventually.rejectedWith( - 'Enveloping cannot relay contract deployment transactions. Add {from: accountWithRBTC, useEnveloping: false}.' - ); - }); - - it('should deploy a contract without Enveloping on calling .new with useEnveloping: false', async function () { - const testRecipient = await TestRecipient.new({ - from: accounts[0], - useEnveloping: false - }); - const receipt = await web3.eth.getTransactionReceipt( - testRecipient.transactionHash - ); - assert.equal(receipt.from.toLowerCase(), accounts[0].toLowerCase()); - }); - }); -}); diff --git a/test-to-migrate/relayclient/RelaySelectionManager.test.ts b/test-to-migrate/relayclient/RelaySelectionManager.test.ts deleted file mode 100644 index 4964dc29..00000000 --- a/test-to-migrate/relayclient/RelaySelectionManager.test.ts +++ /dev/null @@ -1,523 +0,0 @@ -import chaiAsPromised from 'chai-as-promised'; -import sinon, { SinonStub } from 'sinon'; -import { HttpProvider } from 'web3-core'; -import { - configure, - EnvelopingDependencies, - GasPricePingFilter, - getDependencies, - PartialRelayInfo, - PingFilter, - RelaySelectionManager -} from '@rsksmart/rif-relay-client'; -import { register, stake } from './KnownRelaysManager.test'; -import { - constants, - EnvelopingTransactionDetails, - PingResponse -} from '@rsksmart/rif-relay-common'; -import { RelayManagerData } from '@rsksmart/rif-relay-contracts'; -import { deployHub, getTestingEnvironment } from '../TestUtils'; - -import * as chai from 'chai'; - -const { expect, assert } = chai.use(chaiAsPromised); -// eslint-disable-next-line @typescript-eslint/no-misused-promises -contract('RelaySelectionManager', async function (accounts) { - const sliceSize = 3; - const dependencyTree = getDependencies( - configure({}), - web3.currentProvider as HttpProvider - ); - const stubGetRelaysSorted = sinon.stub( - dependencyTree.knownRelaysManager, - 'getRelaysSortedForTransaction' - ); - const stubGetActiveRelays = sinon.stub( - dependencyTree.contractInteractor, - 'getActiveRelayInfo' - ); - const stubGetRelayData = sinon.stub( - dependencyTree.contractInteractor, - 'getRelayInfo' - ); - const errors = new Map(); - const config = configure({ - sliceSize, - chainId: (await getTestingEnvironment()).chainId - }); - const relayInfo = { - manager: '', - url: '', - currentlyStaked: false, - registered: false - }; - const pingResponse: PingResponse = { - relayWorkerAddress: '', - relayManagerAddress: '', - relayHubAddress: '', - minGasPrice: '1', - ready: true, - version: '1', - feesReceiver: '' - }; - const winner = { - pingResponse, - relayInfo - }; - const transactionDetails: EnvelopingTransactionDetails = { - from: '', - data: '', - to: '', - callForwarder: '', - callVerifier: '', - tokenAmount: '', - tokenGas: '', - tokenContract: '', - isSmartWalletDeploy: false - }; - - let stubPingResponse: SinonStub; - describe('#selectNextRelay()', function () { - let relaySelectionManager: RelaySelectionManager; - let stubRaceToSuccess: SinonStub; - let stubGetNextSlice: SinonStub; - - before(async function () { - stubGetActiveRelays.returns(Promise.resolve([relayInfo])); - stubGetRelayData.returns(Promise.resolve([relayInfo])); - stubGetRelaysSorted.returns(Promise.resolve([[relayInfo]])); - relaySelectionManager = await new RelaySelectionManager( - transactionDetails, - dependencyTree.knownRelaysManager, - dependencyTree.httpClient, - GasPricePingFilter, - config - ).init(); - stubRaceToSuccess = sinon.stub( - relaySelectionManager, - '_raceToSuccess' - ); - stubGetNextSlice = sinon.stub( - relaySelectionManager, - '_getNextSlice' - ); - // unless this is stubbed, promises will not be handled and exception will be thrown somewhere - // @ts-ignore - sinon - .stub(relaySelectionManager, '_getRelayAddressPing') - .returns(Promise.resolve(winner)); - }); - - afterEach(function () { - stubGetNextSlice.reset(); - stubRaceToSuccess.reset(); - }); - - it('should return the first relay to ping', async function () { - stubGetNextSlice.returns([relayInfo]); - stubRaceToSuccess - .onFirstCall() - .returns(Promise.resolve({ errors })) - .onSecondCall() - .returns( - Promise.resolve({ - winner, - errors - }) - ); - const nextRelay = await relaySelectionManager.selectNextRelay(); - assert.deepStrictEqual(nextRelay, winner); - }); - - describe('with preferred relay URL', function () { - const preferredRelayUrl = 'preferredRelayUrl'; - const relayManager = accounts[1]; - let relaySelectionManager: RelaySelectionManager; - let stubRaceToSuccess: SinonStub; - let stubGetNextSlice: SinonStub; - let relayHub: any; - let dependencyTree: EnvelopingDependencies; - let chainId: number; - - before(async function () { - chainId = (await getTestingEnvironment()).chainId; - relayHub = await deployHub(constants.ZERO_ADDRESS); - await stake(relayHub, relayManager, accounts[0]); - await register( - relayHub, - relayManager, - accounts[2], - preferredRelayUrl - ); - - const config = configure({ - relayHubAddress: relayHub.address, - chainId - }); - dependencyTree = getDependencies( - config, - web3.currentProvider as HttpProvider - ); - await dependencyTree.contractInteractor.init(); - - relaySelectionManager = await new RelaySelectionManager( - transactionDetails, - dependencyTree.knownRelaysManager, - dependencyTree.httpClient, - GasPricePingFilter, - config - ).init(); - stubRaceToSuccess = sinon.stub( - relaySelectionManager, - '_raceToSuccess' - ); - stubGetNextSlice = sinon.stub( - relaySelectionManager, - '_getNextSlice' - ); - }); - - it('should fill in the details if the relay was known only by URL', async function () { - const relayInfo: RelayManagerData = Object.assign({} as any, { - url: preferredRelayUrl - }); - const pingResponse: PingResponse = { - relayWorkerAddress: relayManager, - relayManagerAddress: relayManager, - relayHubAddress: relayManager, - feesReceiver: relayManager, - minGasPrice: '1', - ready: true, - version: '' - }; - const winner: PartialRelayInfo = { - pingResponse, - relayInfo - }; - - stubGetNextSlice.returns([relayInfo]); - stubRaceToSuccess.returns( - Promise.resolve({ - winner, - errors - }) - ); - stubGetRelaysSorted.returns(Promise.resolve([[relayInfo]])); - const nextRelay = await relaySelectionManager.selectNextRelay(); - assert.equal(nextRelay.relayInfo.url, preferredRelayUrl); - assert.equal(nextRelay.relayInfo.manager, relayManager); - }); - }); - - it('should return null if no relay could ping', async function () { - stubGetNextSlice - .onFirstCall() - .returns([relayInfo]) - .onSecondCall() - .returns([]); - stubRaceToSuccess.returns(Promise.resolve({ errors })); - const nextRelay = await relaySelectionManager.selectNextRelay(); - assert.isUndefined(nextRelay); - }); - }); - - describe('#_getNextSlice()', function () { - it("should return 'relaySliceSize' relays if available on the highest priority level", async function () { - stubGetRelaysSorted.returns( - Promise.resolve([ - [ - winner.relayInfo, - winner.relayInfo, - winner.relayInfo, - winner.relayInfo, - winner.relayInfo - ] - ]) - ); - for (let i = 1; i < 5; i++) { - const rsm = await new RelaySelectionManager( - transactionDetails, - dependencyTree.knownRelaysManager, - dependencyTree.httpClient, - GasPricePingFilter, - configure({ - sliceSize: i, - chainId: (await getTestingEnvironment()).chainId - }) - ).init(); - const returned = await rsm._getNextSlice(); - assert.equal(returned.length, i); - } - }); - - it("should return all remaining relays if less then 'relaySliceSize' remains on current priority level", async function () { - const relaysLeft = [[winner.relayInfo, winner.relayInfo]]; - stubGetRelaysSorted.returns(Promise.resolve(relaysLeft)); - const rsm = await new RelaySelectionManager( - transactionDetails, - dependencyTree.knownRelaysManager, - dependencyTree.httpClient, - GasPricePingFilter, - configure({ - sliceSize: 7, - chainId: (await getTestingEnvironment()).chainId - }) - ).init(); - const returned = await rsm._getNextSlice(); - assert.deepEqual(returned, relaysLeft[0]); - }); - - it('should start returning relays from lower priority level if higher level is empty', async function () { - // Create stub array of distinct relay URLs (URL is used as mapping key) - const relayInfoGenerator = ( - e: RelayManagerData, - i: number, - a: RelayManagerData[] - ): RelayManagerData => { - return { - ...e, - url: `relay ${i} of ${a.length}` - }; - }; - - const relaysLeft = [ - Array(2).fill(winner).map(relayInfoGenerator), - Array(3).fill(winner).map(relayInfoGenerator) - ]; - stubGetRelaysSorted.returns(Promise.resolve(relaysLeft)); - const rsm = await new RelaySelectionManager( - transactionDetails, - dependencyTree.knownRelaysManager, - dependencyTree.httpClient, - GasPricePingFilter, - configure({ - sliceSize: 7, - chainId: (await getTestingEnvironment()).chainId - }) - ).init(); - // Initial request only returns the top preference relays - const returned1 = await rsm._getNextSlice(); - assert.equal(returned1.length, 2); - // Pretend all relays failed to ping - let errors = new Map( - returned1.map((info) => [info.url, new Error('fake error')]) - ); - rsm._handleRaceResults({ errors }); - const returned2 = await rsm._getNextSlice(); - assert.equal(returned2.length, 3); - errors = new Map( - returned2.map((info) => [info.url, new Error('fake error')]) - ); - rsm._handleRaceResults({ errors }); - const returned3 = await rsm._getNextSlice(); - assert.equal(returned3.length, 0); - }); - }); - - describe('#_getRelayAddressPing()', function () { - // eslint-disable-next-line @typescript-eslint/no-empty-function - const emptyFilter: PingFilter = (): void => {}; - - before(function () { - stubPingResponse = sinon.stub( - dependencyTree.httpClient, - 'getPingResponse' - ); - }); - - it('should throw if the relay is not ready', async function () { - stubPingResponse.returns( - Promise.resolve( - Object.assign({}, pingResponse, { ready: false }) - ) - ); - const rsm = new RelaySelectionManager( - transactionDetails, - dependencyTree.knownRelaysManager, - dependencyTree.httpClient, - emptyFilter, - config - ); - const promise = rsm._getRelayAddressPing(relayInfo); - await expect(promise).to.be.eventually.rejectedWith( - 'Relay not ready' - ); - }); - - // TODO: change the way filtering is implemented - it('should call filter and not catch exceptions in it', async function () { - const message = 'Filter Error Message'; - const filter: PingFilter = (): void => { - throw new Error(message); - }; - stubPingResponse.returns(Promise.resolve(pingResponse)); - const rsm = new RelaySelectionManager( - transactionDetails, - dependencyTree.knownRelaysManager, - dependencyTree.httpClient, - filter, - config - ); - const promise = rsm._getRelayAddressPing(relayInfo); - await expect(promise).to.be.eventually.rejectedWith(message); - }); - - it('should return the relay info if it pinged as ready and passed filter successfully', async function () { - stubPingResponse.returns(Promise.resolve(pingResponse)); - const rsm = new RelaySelectionManager( - transactionDetails, - dependencyTree.knownRelaysManager, - dependencyTree.httpClient, - emptyFilter, - config - ); - const info = await rsm._getRelayAddressPing(relayInfo); - assert.deepEqual(info, winner); - }); - }); - - describe('#_raceToSuccess()', function () { - // Note that promises must be created and passed to the 'raceToSuccess' in the same, synchronous block. - // Otherwise, rejections will not be handled and mocha will crash. - it('only first to resolve and all that rejected by that time', async function () { - const slowRelay = { - pingResponse, - relayData: Object.assign({}, relayInfo, { url: 'slowRelay' }) - }; - const fastRelay = { - pingResponse, - relayData: Object.assign({}, relayInfo, { url: 'fastRelay' }) - }; - const fastFailRelay = { - pingResponse, - relayData: Object.assign({}, relayInfo, { - url: 'fastFailRelay' - }) - }; - const slowFailRelay = { - pingResponse, - relayData: Object.assign({}, relayInfo, { - url: 'slowFailRelay' - }) - }; - const slowPromise = new Promise((resolve) => { - setTimeout(() => { - resolve(pingResponse); - }, 1500); - }); - const fastPromise = new Promise((resolve) => { - setTimeout(() => { - resolve(pingResponse); - }, 300); - }); - const fastFailPromise = new Promise( - (resolve, reject) => { - setTimeout(() => { - reject(new Error(fastFailedMessage)); - }, 180); - } - ); - const slowFailPromise = new Promise( - (resolve, reject) => { - setTimeout(() => { - reject(new Error(slowFailedMessage)); - }, 1800); - } - ); - const fastFailedMessage = 'Fast Failed Promise'; - const slowFailedMessage = 'Slow Failed Promise'; - const relays = [ - slowRelay.relayData, - fastRelay.relayData, - slowFailRelay.relayData, - fastFailRelay.relayData - ]; - stubPingResponse.callsFake( - async (relayUrl: string): Promise => { - switch (relayUrl) { - case slowRelay.relayData.url: - return await slowPromise; - case fastRelay.relayData.url: - return await fastPromise; - case slowFailRelay.relayData.url: - return await slowFailPromise; - case fastFailRelay.relayData.url: - return await fastFailPromise; - } - throw new Error('Non test relay pinged'); - } - ); - const rsm = new RelaySelectionManager( - transactionDetails, - dependencyTree.knownRelaysManager, - dependencyTree.httpClient, - GasPricePingFilter, - config - ); - const raceResults = await rsm._raceToSuccess(relays); - assert.equal(raceResults.winner?.relayInfo.url, 'fastRelay'); - assert.equal(raceResults.errors.size, 1); - assert.equal( - raceResults.errors.get('fastFailRelay')?.message, - fastFailedMessage - ); - }); - }); - - describe('#_handleRaceResults()', function () { - const winnerRelayUrl = 'winnerRelayUrl'; - const failureRelayUrl = 'failureRelayUrl'; - const otherRelayUrl = 'otherRelayUrl'; - const winner = { - pingResponse, - relayInfo: Object.assign({}, relayInfo, { url: winnerRelayUrl }) - }; - const message = 'some failure message'; - const failureRelayEventInfo = Object.assign({}, relayInfo, { - url: failureRelayUrl - }); - const otherRelayEventInfo = Object.assign({}, relayInfo, { - url: otherRelayUrl - }); - it('should remove all relays featured in race results', async function () { - sinon.stub(dependencyTree.knownRelaysManager, 'refresh'); - stubGetRelaysSorted.returns( - Promise.resolve([ - [ - winner.relayInfo, - failureRelayEventInfo, - otherRelayEventInfo - ] - ]) - ); - const rsm = await new RelaySelectionManager( - transactionDetails, - dependencyTree.knownRelaysManager, - dependencyTree.httpClient, - GasPricePingFilter, - config - ).init(); - // initialize 'remainingRelays' field by calling '_getNextSlice' - await rsm._getNextSlice(); - const errors = new Map(); - errors.set(failureRelayUrl, new Error(message)); - const raceResults = { - winner, - errors - }; - // @ts-ignore - let remainingRelays = rsm.remainingRelays; - assert.equal(remainingRelays?.length, 1); - assert.equal(remainingRelays[0].length, 3); - assert.equal(remainingRelays[0][0].url, winnerRelayUrl); - assert.equal(remainingRelays[0][1].url, failureRelayUrl); - assert.equal(remainingRelays[0][2].url, otherRelayUrl); - rsm._handleRaceResults(raceResults); - // @ts-ignore - remainingRelays = rsm.remainingRelays; - assert.equal(remainingRelays?.length, 1); - assert.equal(remainingRelays[0][0].url, otherRelayUrl); - }); - }); -}); diff --git a/test-to-migrate/relayclient/SmartWalletDiscovery.test.ts b/test-to-migrate/relayclient/SmartWalletDiscovery.test.ts deleted file mode 100644 index 050be235..00000000 --- a/test-to-migrate/relayclient/SmartWalletDiscovery.test.ts +++ /dev/null @@ -1,2118 +0,0 @@ -import Web3 from 'web3'; -import { hdkey as EthereumHDKey } from 'ethereumjs-wallet'; -import { ethers } from 'ethers'; - -import { - SmartWalletInstance, - SmartWalletFactoryInstance, - TestTokenInstance, - CustomSmartWalletFactoryInstance, - CustomSmartWalletInstance -} from '@rsksmart/rif-relay-contracts/types/truffle-contracts'; - -import { - DiscoveryConfig, - SmartWalletDiscovery -} from '@rsksmart/rif-relay-client'; - -import { toChecksumAddress } from 'web3-utils'; -import { constants } from '@rsksmart/rif-relay-common'; -import { WebsocketProvider } from 'web3-core'; -import { PERSONAL_SIGN_PREFIX } from '../Utils'; -import { getWebSocketUrl } from '../TestUtils'; - -const CustomSmartWallet = artifacts.require('CustomSmartWallet'); -const SmartWallet = artifacts.require('SmartWallet'); -const TestToken = artifacts.require('TestToken'); -const CustomSmartWalletFactory = artifacts.require('CustomSmartWalletFactory'); -const SmartWalletFactory = artifacts.require('SmartWalletFactory'); - -const options = [ - { - title: 'SmartWallet', - isCustom: false - }, - { - title: 'CustomSmartWallet', - isCustom: true - } -]; - -contract('SmartWalletDiscovery', function (accounts) { - let chainId: number; - let token: TestTokenInstance; - let factory: SmartWalletFactoryInstance | CustomSmartWalletFactoryInstance; - let byteCodeHash: string; - let socketProvider: WebsocketProvider; - let sWalletTemplate: SmartWalletInstance | CustomSmartWalletInstance; - const mnemonic = - 'figure arrow make ginger educate drip thing theory champion faint vendor push'; - let currentWeb3: Web3; - - for (let optionIdx = 0; optionIdx < options.length; optionIdx++) { - const params = options[optionIdx]; - - describe(`${params.title} - #smartWalletDiscovery`, function () { - let swd: SmartWalletDiscovery; - let discoverableAccounts: Set; - - before(async function () { - await init(params.isCustom); - swd = new SmartWalletDiscovery(socketProvider); - discoverableAccounts = new Set(); - - const rootKey: EthereumHDKey = - SmartWalletDiscovery.getRootExtKeyFromMnemonic( - mnemonic, - `${params.title}_0` - ); - const firstAccountRoot = - rootKey.derivePath("m/44'/37310'/0'/0"); - - // Fill 20 accounts with balance and for each one 20 swallets with balance - for (let i = 0; i < 20; i++) { - const account = firstAccountRoot - .deriveChild(i) - .getWallet() - .getAddressString(); - await currentWeb3.eth.sendTransaction({ - from: accounts[0], - to: account, - value: 1 - }); - discoverableAccounts.add(account); - - for (let j = 0; j < 20; j++) { - const swAddress = params.isCustom - ? calculateCustomSmartWalletAddress( - factory.address, - account, - constants.ZERO_ADDRESS, - constants.ZERO_ADDRESS, - j, - byteCodeHash, - constants.SHA3_NULL_S - ) - : calculateSmartWalletAddress( - factory.address, - account, - constants.ZERO_ADDRESS, - j, - byteCodeHash - ); - await currentWeb3.eth.sendTransaction({ - from: accounts[0], - to: swAddress, - value: 1 - }); - discoverableAccounts.add(swAddress); - } - } - }); - - after(function () { - closeSocket(); - }); - - it('should discover all the user accounts - using only native crypto balance', async function () { - const config = new DiscoveryConfig({ - isTestNet: true, - factory: factory.address, - isCustomWallet: params.isCustom - }); - - await swd.discoverAccountsFromMnemonic( - config, - mnemonic, - `${params.title}_0` - ); - - assert.equal( - swd.accounts.length, - 20, - 'incorrect eoa accounts discovered' - ); - - for (let i = 0; i < swd.accounts.length; i++) { - assert.equal( - swd.accounts[i].swAccounts.length, - 20, - `incorrect sw accounts discovered for address ${swd.accounts[i].eoaAccount}` - ); - const account = swd.accounts[i].eoaAccount; - assert.isTrue( - discoverableAccounts.has(account), - 'Discovered Account not part of Discoverable Set' - ); - discoverableAccounts.delete(account); - - for (let j = 0; j < 20; j++) { - const swAccount = swd.accounts[i].swAccounts[j]; - assert.isTrue( - discoverableAccounts.has(swAccount), - 'Discovered SWAccount not part of Discoverable Set' - ); - discoverableAccounts.delete(swAccount); - } - } - - assert.isTrue( - discoverableAccounts.size === 0, - 'Some Discoverable Accounts were not found' - ); - }); - }); - - describe(`${params.title} - #smartWalletDiscovery 1`, function () { - let swd: SmartWalletDiscovery; - let discoverableAccounts: Set; - - before(async function () { - await init(params.isCustom); - discoverableAccounts = new Set(); - swd = new SmartWalletDiscovery(socketProvider, [token.address]); - - // new rootkey - const rootKey = SmartWalletDiscovery.getRootExtKeyFromMnemonic( - mnemonic, - `${params.title}_1` - ); - const firstAccountRoot = - rootKey.derivePath("m/44'/37310'/0'/0"); - - // Fill 20 accounts with balance and for each one fill a token to 20 smart wallets - for (let i = 0; i < 20; i++) { - const accountWallet = firstAccountRoot - .deriveChild(i) - .getWallet(); - const account = accountWallet.getAddressString(); - discoverableAccounts.add(account); - - for (let j = 0; j < 20; j++) { - const swAddress = params.isCustom - ? calculateCustomSmartWalletAddress( - factory.address, - account, - constants.ZERO_ADDRESS, - constants.ZERO_ADDRESS, - j, - byteCodeHash, - constants.SHA3_NULL_S - ) - : calculateSmartWalletAddress( - factory.address, - account, - constants.ZERO_ADDRESS, - j, - byteCodeHash - ); - await token.mint('1', swAddress); - discoverableAccounts.add(swAddress); - } - } - }); - - after(function () { - closeSocket(); - }); - it('should discover all the user accounts - using only token balance', async function () { - const config = new DiscoveryConfig({ - isTestNet: true, - factory: factory.address, - isCustomWallet: params.isCustom - }); - - await swd.discoverAccountsFromMnemonic( - config, - mnemonic, - `${params.title}_1` - ); - - assert.equal( - swd.accounts.length, - 20, - 'incorrect eoa accounts discovered' - ); - - for (let i = 0; i < swd.accounts.length; i++) { - assert.equal( - swd.accounts[i].swAccounts.length, - 20, - `incorrect sw accounts discovered for address ${swd.accounts[i].eoaAccount}` - ); - const account = swd.accounts[i].eoaAccount; - assert.isTrue( - discoverableAccounts.has(account), - 'Discovered Account not part of Discoverable Set' - ); - discoverableAccounts.delete(account); - - for (let j = 0; j < 20; j++) { - const swAccount = swd.accounts[i].swAccounts[j]; - assert.isTrue( - discoverableAccounts.has(swAccount), - 'Discovered SWAccount not part of Discoverable Set' - ); - discoverableAccounts.delete(swAccount); - } - } - assert.isTrue( - discoverableAccounts.size === 0, - 'Some Discoverable Accounts were not found' - ); - }); - }); - - describe(`${params.title} - #smartWalletDiscovery 2`, function () { - let swd: SmartWalletDiscovery; - let discoverableAccounts: Set; - - before(async function () { - await init(params.isCustom); - discoverableAccounts = new Set(); - swd = new SmartWalletDiscovery(socketProvider, [token.address]); - // new rootkey - const rootKey = SmartWalletDiscovery.getRootExtKeyFromMnemonic( - mnemonic, - `${params.title}_2` - ); - const firstAccountRoot = - rootKey.derivePath("m/44'/37310'/0'/0"); - - // Fill 20 accounts with balance and for each one fill a token to 20 smart wallets - for (let i = 0; i < 20; i++) { - const account = firstAccountRoot - .deriveChild(i) - .getWallet() - .getAddressString(); - await currentWeb3.eth.sendTransaction({ - from: accounts[0], - to: account, - value: 1 - }); - discoverableAccounts.add(account); - - for (let j = 0; j < 20; j++) { - const swAddress = params.isCustom - ? calculateCustomSmartWalletAddress( - factory.address, - account, - constants.ZERO_ADDRESS, - constants.ZERO_ADDRESS, - j, - byteCodeHash, - constants.SHA3_NULL_S - ) - : calculateSmartWalletAddress( - factory.address, - account, - constants.ZERO_ADDRESS, - j, - byteCodeHash - ); - await token.mint('1', swAddress); - discoverableAccounts.add(swAddress); - } - } - }); - - after(function () { - closeSocket(); - }); - - it('should discover all the user accounts - using balance and token balance', async function () { - const config = new DiscoveryConfig({ - isTestNet: true, - factory: factory.address, - isCustomWallet: params.isCustom - }); - - await swd.discoverAccountsFromMnemonic( - config, - mnemonic, - `${params.title}_2` - ); - - assert.equal( - swd.accounts.length, - 20, - 'incorrect eoa accounts discovered' - ); - - for (let i = 0; i < swd.accounts.length; i++) { - assert.equal( - swd.accounts[i].swAccounts.length, - 20, - `incorrect sw accounts discovered for address ${swd.accounts[i].eoaAccount}` - ); - const account = swd.accounts[i].eoaAccount; - assert.isTrue( - discoverableAccounts.has(account), - 'Discovered Account not part of Discoverable Set' - ); - discoverableAccounts.delete(account); - - for (let j = 0; j < 20; j++) { - const swAccount = swd.accounts[i].swAccounts[j]; - assert.isTrue( - discoverableAccounts.has(swAccount), - 'Discovered SWAccount not part of Discoverable Set' - ); - discoverableAccounts.delete(swAccount); - } - } - - assert.isTrue( - discoverableAccounts.size === 0, - 'Some Discoverable Accounts were not found' - ); - }); - }); - - describe(`${params.title} - #smartWalletDiscovery 3`, function () { - let swd: SmartWalletDiscovery; - let discoverableAccounts: Set; - - before(async function () { - await init(params.isCustom); - discoverableAccounts = new Set(); - - swd = new SmartWalletDiscovery(socketProvider, [token.address]); - // new rootkey - const rootKey = SmartWalletDiscovery.getRootExtKeyFromMnemonic( - mnemonic, - `${params.title}_3` - ); - const firstAccountRoot = - rootKey.derivePath("m/44'/37310'/0'/0"); - - for (let i = 0; i < 20; i++) { - const accountWallet = firstAccountRoot - .deriveChild(i) - .getWallet(); - const account = accountWallet.getAddressString(); - - await currentWeb3.eth.sendTransaction({ - from: accounts[0], - to: account, - value: 1 - }); - discoverableAccounts.add(account); - - for (let j = 0; j < 20; j++) { - const swAddress = params.isCustom - ? calculateCustomSmartWalletAddress( - factory.address, - account, - constants.ZERO_ADDRESS, - constants.ZERO_ADDRESS, - j, - byteCodeHash, - constants.SHA3_NULL_S - ) - : calculateSmartWalletAddress( - factory.address, - account, - constants.ZERO_ADDRESS, - j, - byteCodeHash - ); - - discoverableAccounts.add(swAddress); - - const message: string = params.isCustom - ? currentWeb3.utils.soliditySha3( - { t: 'address', v: factory.address }, - { t: 'address', v: account }, - { t: 'address', v: constants.ZERO_ADDRESS }, - { t: 'address', v: constants.ZERO_ADDRESS }, - { t: 'uint256', v: j }, - { t: 'bytes', v: '0x' } - ) ?? '' - : currentWeb3.utils.soliditySha3( - { t: 'address', v: factory.address }, - { t: 'address', v: account }, - { t: 'address', v: constants.ZERO_ADDRESS }, - { t: 'uint256', v: j } - ) ?? ''; - - const toSign: string = - web3.utils.soliditySha3( - { - t: 'string', - v: - PERSONAL_SIGN_PREFIX + - web3.utils.hexToBytes(message).length - }, - { t: 'bytes32', v: message } - ) ?? ''; - - const toSignAsBinaryArray = - ethers.utils.arrayify(toSign); - const signingKey = new ethers.utils.SigningKey( - accountWallet.getPrivateKey() - ); - const signature = - signingKey.signDigest(toSignAsBinaryArray); - const signatureCollapsed = - ethers.utils.joinSignature(signature); - - params.isCustom - ? await ( - factory as CustomSmartWalletFactoryInstance - ).createUserSmartWallet( - account, - constants.ZERO_ADDRESS, - constants.ZERO_ADDRESS, - j, - '0x', - signatureCollapsed - ) - : await ( - factory as SmartWalletFactoryInstance - ).createUserSmartWallet( - account, - constants.ZERO_ADDRESS, - j, - signatureCollapsed - ); - } - } - }); - - after(function () { - closeSocket(); - }); - - it('should discover all the user accounts - using deploy and native crypto balance', async function () { - const config = new DiscoveryConfig({ - isTestNet: true, - factory: factory.address, - isCustomWallet: params.isCustom - }); - - await swd.discoverAccountsFromMnemonic( - config, - mnemonic, - `${params.title}_3` - ); - - assert.equal( - swd.accounts.length, - 20, - 'incorrect eoa accounts discovered' - ); - - for (let i = 0; i < swd.accounts.length; i++) { - assert.equal( - swd.accounts[i].swAccounts.length, - 20, - `incorrect sw accounts discovered for address ${swd.accounts[i].eoaAccount}` - ); - const account = swd.accounts[i].eoaAccount; - assert.isTrue( - discoverableAccounts.has(account), - 'Discovered Account not part of Discoverable Set' - ); - discoverableAccounts.delete(account); - - for (let j = 0; j < 20; j++) { - const swAccount = swd.accounts[i].swAccounts[j]; - assert.isTrue( - discoverableAccounts.has(swAccount), - 'Discovered SWAccount not part of Discoverable Set' - ); - discoverableAccounts.delete(swAccount); - } - } - - assert.isTrue( - discoverableAccounts.size === 0, - 'Some Discoverable Accounts were not found' - ); - }); - }); - - describe(`${params.title} - #smartWalletDiscovery 4`, function () { - let swd: SmartWalletDiscovery; - let discoverableAccounts: Set; - - before(async function () { - await init(params.isCustom); - discoverableAccounts = new Set(); - swd = new SmartWalletDiscovery(socketProvider, [token.address]); - - // new rootkey - const rootKey = SmartWalletDiscovery.getRootExtKeyFromMnemonic( - mnemonic, - `${params.title}_4` - ); - const firstAccountRoot = - rootKey.derivePath("m/44'/37310'/0'/0"); - - // Fill 20 accounts with balance and for each one fill a token to 20 smart wallets - for (let i = 0; i < 20; i++) { - const accountWallet = firstAccountRoot - .deriveChild(i) - .getWallet(); - const account = accountWallet.getAddressString(); - discoverableAccounts.add(account); - - for (let j = 0; j < 20; j++) { - const swAddress = params.isCustom - ? calculateCustomSmartWalletAddress( - factory.address, - account, - constants.ZERO_ADDRESS, - constants.ZERO_ADDRESS, - j, - byteCodeHash, - constants.SHA3_NULL_S - ) - : calculateSmartWalletAddress( - factory.address, - account, - constants.ZERO_ADDRESS, - j, - byteCodeHash - ); - discoverableAccounts.add(swAddress); - - const message: string = params.isCustom - ? currentWeb3.utils.soliditySha3( - { t: 'address', v: factory.address }, - { t: 'address', v: account }, - { t: 'address', v: constants.ZERO_ADDRESS }, - { t: 'address', v: constants.ZERO_ADDRESS }, - { t: 'uint256', v: j }, - { t: 'bytes', v: '0x' } - ) ?? '' - : currentWeb3.utils.soliditySha3( - { t: 'address', v: factory.address }, - { t: 'address', v: account }, - { t: 'address', v: constants.ZERO_ADDRESS }, - { t: 'uint256', v: j } - ) ?? ''; - - const toSign: string = - web3.utils.soliditySha3( - { - t: 'string', - v: - PERSONAL_SIGN_PREFIX + - web3.utils.hexToBytes(message).length - }, - { t: 'bytes32', v: message } - ) ?? ''; - - const toSignAsBinaryArray = - ethers.utils.arrayify(toSign); - const signingKey = new ethers.utils.SigningKey( - accountWallet.getPrivateKey() - ); - const signature = - signingKey.signDigest(toSignAsBinaryArray); - const signatureCollapsed = - ethers.utils.joinSignature(signature); - - params.isCustom - ? await ( - factory as CustomSmartWalletFactoryInstance - ).createUserSmartWallet( - account, - constants.ZERO_ADDRESS, - constants.ZERO_ADDRESS, - j, - '0x', - signatureCollapsed - ) - : await ( - factory as SmartWalletFactoryInstance - ).createUserSmartWallet( - account, - constants.ZERO_ADDRESS, - j, - signatureCollapsed - ); - } - } - }); - - after(function () { - closeSocket(); - }); - - it('should discover all the user accounts - using wallet Deploy only', async function () { - const config = new DiscoveryConfig({ - isTestNet: true, - factory: factory.address, - isCustomWallet: params.isCustom - }); - - await swd.discoverAccountsFromMnemonic( - config, - mnemonic, - `${params.title}_4` - ); - - assert.equal( - swd.accounts.length, - 20, - 'incorrect eoa accounts discovered' - ); - - for (let i = 0; i < swd.accounts.length; i++) { - assert.equal( - swd.accounts[i].swAccounts.length, - 20, - `incorrect sw accounts discovered for address ${swd.accounts[i].eoaAccount}` - ); - const account = swd.accounts[i].eoaAccount; - assert.isTrue( - discoverableAccounts.has(account), - 'Discovered Account not part of Discoverable Set' - ); - discoverableAccounts.delete(account); - - for (let j = 0; j < 20; j++) { - const swAccount = swd.accounts[i].swAccounts[j]; - assert.isTrue( - discoverableAccounts.has(swAccount), - 'Discovered SWAccount not part of Discoverable Set' - ); - discoverableAccounts.delete(swAccount); - } - } - - assert.isTrue( - discoverableAccounts.size === 0, - 'Some Discoverable Accounts were not found' - ); - }); - }); - - describe(`${params.title} - #smartWalletDiscovery From External Keys`, function () { - let swd: SmartWalletDiscovery; - let discoverableAccounts: Set; - const usedPublicKeys: string[] = []; - - before(async function () { - await init(params.isCustom); - swd = new SmartWalletDiscovery(socketProvider); - discoverableAccounts = new Set(); - - const rootKey: EthereumHDKey = - SmartWalletDiscovery.getRootExtKeyFromMnemonic( - mnemonic, - `${params.title}_Ext_0` - ); - - for (let accIdx = 0; accIdx < 2; accIdx++) { - const firstAccountRoot = rootKey.derivePath( - `m/44'/37310'/${accIdx}'/0` - ); - - usedPublicKeys.push( - firstAccountRoot.publicExtendedKey().toString() - ); - - // Fill 20 accounts with balance and for each one 20 swallets with balance - for (let i = 0; i < 20; i++) { - const account = firstAccountRoot - .deriveChild(i) - .getWallet() - .getAddressString(); - await currentWeb3.eth.sendTransaction({ - from: accounts[0], - to: account, - value: 1 - }); - discoverableAccounts.add(account); - - for (let j = 0; j < 20; j++) { - const swAddress = params.isCustom - ? calculateCustomSmartWalletAddress( - factory.address, - account, - constants.ZERO_ADDRESS, - constants.ZERO_ADDRESS, - j, - byteCodeHash, - constants.SHA3_NULL_S - ) - : calculateSmartWalletAddress( - factory.address, - account, - constants.ZERO_ADDRESS, - j, - byteCodeHash - ); - await currentWeb3.eth.sendTransaction({ - from: accounts[0], - to: swAddress, - value: 1 - }); - discoverableAccounts.add(swAddress); - } - } - } - }); - - after(function () { - closeSocket(); - }); - - it('should discover all the user accounts - using only native crypto balance', async function () { - const config = new DiscoveryConfig({ - isTestNet: true, - factory: factory.address, - isCustomWallet: params.isCustom - }); - - await swd.discoverAccountsFromExtendedPublicKeys( - config, - usedPublicKeys - ); - - assert.equal( - swd.accounts.length, - 40, - 'incorrect eoa accounts discovered' - ); - - for (let i = 0; i < swd.accounts.length; i++) { - assert.equal( - swd.accounts[i].swAccounts.length, - 20, - `incorrect sw accounts discovered for address ${swd.accounts[i].eoaAccount}` - ); - const account = swd.accounts[i].eoaAccount; - assert.isTrue( - discoverableAccounts.has(account), - 'Discovered Account not part of Discoverable Set' - ); - discoverableAccounts.delete(account); - - for (let j = 0; j < 20; j++) { - const swAccount = swd.accounts[i].swAccounts[j]; - assert.isTrue( - discoverableAccounts.has(swAccount), - 'Discovered SWAccount not part of Discoverable Set' - ); - discoverableAccounts.delete(swAccount); - } - } - - assert.isTrue( - discoverableAccounts.size === 0, - 'Some Discoverable Accounts were not found' - ); - }); - }); - - describe(`${params.title} - #smartWalletDiscovery From External Keys 1`, function () { - let swd: SmartWalletDiscovery; - let discoverableAccounts: Set; - const usedPublicKeys: string[] = []; - - before(async function () { - await init(params.isCustom); - discoverableAccounts = new Set(); - swd = new SmartWalletDiscovery(socketProvider, [token.address]); - - // new rootkey - const rootKey = SmartWalletDiscovery.getRootExtKeyFromMnemonic( - mnemonic, - `${params.title}_Ext_1` - ); - const firstAccountRoot = - rootKey.derivePath("m/44'/37310'/0'/0"); - usedPublicKeys.push( - firstAccountRoot.publicExtendedKey().toString() - ); - - // Fill 20 accounts with balance and for each one fill a token to 20 smart wallets - for (let i = 0; i < 20; i++) { - const accountWallet = firstAccountRoot - .deriveChild(i) - .getWallet(); - const account = accountWallet.getAddressString(); - discoverableAccounts.add(account); - - for (let j = 0; j < 20; j++) { - const swAddress = params.isCustom - ? calculateCustomSmartWalletAddress( - factory.address, - account, - constants.ZERO_ADDRESS, - constants.ZERO_ADDRESS, - j, - byteCodeHash, - constants.SHA3_NULL_S - ) - : calculateSmartWalletAddress( - factory.address, - account, - constants.ZERO_ADDRESS, - j, - byteCodeHash - ); - await token.mint('1', swAddress); - discoverableAccounts.add(swAddress); - } - } - }); - - after(function () { - closeSocket(); - }); - - it('should discover all the user accounts - using only token balance', async function () { - const config = new DiscoveryConfig({ - isTestNet: true, - factory: factory.address, - isCustomWallet: params.isCustom - }); - - await swd.discoverAccountsFromExtendedPublicKeys( - config, - usedPublicKeys - ); - - assert.equal( - swd.accounts.length, - 20, - 'incorrect eoa accounts discovered' - ); - - for (let i = 0; i < swd.accounts.length; i++) { - assert.equal( - swd.accounts[i].swAccounts.length, - 20, - `incorrect sw accounts discovered for address ${swd.accounts[i].eoaAccount}` - ); - const account = swd.accounts[i].eoaAccount; - assert.isTrue( - discoverableAccounts.has(account), - 'Discovered Account not part of Discoverable Set' - ); - discoverableAccounts.delete(account); - - for (let j = 0; j < 20; j++) { - const swAccount = swd.accounts[i].swAccounts[j]; - assert.isTrue( - discoverableAccounts.has(swAccount), - 'Discovered SWAccount not part of Discoverable Set' - ); - discoverableAccounts.delete(swAccount); - } - } - - assert.isTrue( - discoverableAccounts.size === 0, - 'Some Discoverable Accounts were not found' - ); - }); - }); - - describe(`${params.title} - #smartWalletDiscovery From External Keys 2`, function () { - let swd: SmartWalletDiscovery; - const usedPublicKeys: string[] = []; - let discoverableAccounts: Set; - - before(async function () { - await init(params.isCustom); - discoverableAccounts = new Set(); - swd = new SmartWalletDiscovery(socketProvider, [token.address]); - // new rootkey - const rootKey = SmartWalletDiscovery.getRootExtKeyFromMnemonic( - mnemonic, - `${params.title}_Ext_2` - ); - const firstAccountRoot = - rootKey.derivePath("m/44'/37310'/0'/0"); - usedPublicKeys.push( - firstAccountRoot.publicExtendedKey().toString() - ); - - // Fill 20 accounts with balance and for each one fill a token to 20 smart wallets - for (let i = 0; i < 20; i++) { - const account = firstAccountRoot - .deriveChild(i) - .getWallet() - .getAddressString(); - await currentWeb3.eth.sendTransaction({ - from: accounts[0], - to: account, - value: 1 - }); - discoverableAccounts.add(account); - - for (let j = 0; j < 20; j++) { - const swAddress = params.isCustom - ? calculateCustomSmartWalletAddress( - factory.address, - account, - constants.ZERO_ADDRESS, - constants.ZERO_ADDRESS, - j, - byteCodeHash, - constants.SHA3_NULL_S - ) - : calculateSmartWalletAddress( - factory.address, - account, - constants.ZERO_ADDRESS, - j, - byteCodeHash - ); - await token.mint('1', swAddress); - discoverableAccounts.add(swAddress); - } - } - }); - - after(function () { - closeSocket(); - }); - - it('should discover all the user accounts - using balance and token balance', async function () { - const config = new DiscoveryConfig({ - isTestNet: true, - factory: factory.address, - isCustomWallet: params.isCustom - }); - - await swd.discoverAccountsFromExtendedPublicKeys( - config, - usedPublicKeys - ); - - assert.equal( - swd.accounts.length, - 20, - 'incorrect eoa accounts discovered' - ); - - for (let i = 0; i < swd.accounts.length; i++) { - assert.equal( - swd.accounts[i].swAccounts.length, - 20, - `incorrect sw accounts discovered for address ${swd.accounts[i].eoaAccount}` - ); - const account = swd.accounts[i].eoaAccount; - assert.isTrue( - discoverableAccounts.has(account), - 'Discovered Account not part of Discoverable Set' - ); - discoverableAccounts.delete(account); - - for (let j = 0; j < 20; j++) { - const swAccount = swd.accounts[i].swAccounts[j]; - assert.isTrue( - discoverableAccounts.has(swAccount), - 'Discovered SWAccount not part of Discoverable Set' - ); - discoverableAccounts.delete(swAccount); - } - } - - assert.isTrue( - discoverableAccounts.size === 0, - 'Some Discoverable Accounts were not found' - ); - }); - }); - - describe(`${params.title} - #smartWalletDiscovery From External Keys 3`, function () { - let swd: SmartWalletDiscovery; - const usedPublicKeys: string[] = []; - let discoverableAccounts: Set; - - before(async function () { - await init(params.isCustom); - discoverableAccounts = new Set(); - - swd = new SmartWalletDiscovery(socketProvider, [token.address]); - // new rootkey - const rootKey = SmartWalletDiscovery.getRootExtKeyFromMnemonic( - mnemonic, - `${params.title}_Ext_3` - ); - const firstAccountRoot = - rootKey.derivePath("m/44'/37310'/0'/0"); - usedPublicKeys.push( - firstAccountRoot.publicExtendedKey().toString() - ); - - for (let i = 0; i < 20; i++) { - const accountWallet = firstAccountRoot - .deriveChild(i) - .getWallet(); - const account = accountWallet.getAddressString(); - - await currentWeb3.eth.sendTransaction({ - from: accounts[0], - to: account, - value: 1 - }); - discoverableAccounts.add(account); - - for (let j = 0; j < 20; j++) { - const swAddress = params.isCustom - ? calculateCustomSmartWalletAddress( - factory.address, - account, - constants.ZERO_ADDRESS, - constants.ZERO_ADDRESS, - j, - byteCodeHash, - constants.SHA3_NULL_S - ) - : calculateSmartWalletAddress( - factory.address, - account, - constants.ZERO_ADDRESS, - j, - byteCodeHash - ); - - discoverableAccounts.add(swAddress); - - const message: string = params.isCustom - ? currentWeb3.utils.soliditySha3( - { t: 'address', v: factory.address }, - { t: 'address', v: account }, - { t: 'address', v: constants.ZERO_ADDRESS }, - { t: 'address', v: constants.ZERO_ADDRESS }, - { t: 'uint256', v: j }, - { t: 'bytes', v: '0x' } - ) ?? '' - : currentWeb3.utils.soliditySha3( - { t: 'address', v: factory.address }, - { t: 'address', v: account }, - { t: 'address', v: constants.ZERO_ADDRESS }, - { t: 'uint256', v: j } - ) ?? ''; - - const toSign: string = - web3.utils.soliditySha3( - { - t: 'string', - v: - PERSONAL_SIGN_PREFIX + - web3.utils.hexToBytes(message).length - }, - { t: 'bytes32', v: message } - ) ?? ''; - - const toSignAsBinaryArray = - ethers.utils.arrayify(toSign); - const signingKey = new ethers.utils.SigningKey( - accountWallet.getPrivateKey() - ); - const signature = - signingKey.signDigest(toSignAsBinaryArray); - const signatureCollapsed = - ethers.utils.joinSignature(signature); - - params.isCustom - ? await ( - factory as CustomSmartWalletFactoryInstance - ).createUserSmartWallet( - account, - constants.ZERO_ADDRESS, - constants.ZERO_ADDRESS, - j, - '0x', - signatureCollapsed - ) - : await ( - factory as SmartWalletFactoryInstance - ).createUserSmartWallet( - account, - constants.ZERO_ADDRESS, - j, - signatureCollapsed - ); - } - } - }); - - after(function () { - closeSocket(); - }); - - it('should discover all the user accounts - using deploy and native crypto balance', async function () { - const config = new DiscoveryConfig({ - isTestNet: true, - factory: factory.address, - isCustomWallet: params.isCustom - }); - - await swd.discoverAccountsFromExtendedPublicKeys( - config, - usedPublicKeys - ); - - assert.equal( - swd.accounts.length, - 20, - 'incorrect eoa accounts discovered' - ); - - for (let i = 0; i < swd.accounts.length; i++) { - assert.equal( - swd.accounts[i].swAccounts.length, - 20, - `incorrect sw accounts discovered for address ${swd.accounts[i].eoaAccount}` - ); - const account = swd.accounts[i].eoaAccount; - assert.isTrue( - discoverableAccounts.has(account), - 'Discovered Account not part of Discoverable Set' - ); - discoverableAccounts.delete(account); - - for (let j = 0; j < 20; j++) { - const swAccount = swd.accounts[i].swAccounts[j]; - assert.isTrue( - discoverableAccounts.has(swAccount), - 'Discovered SWAccount not part of Discoverable Set' - ); - discoverableAccounts.delete(swAccount); - } - } - - assert.isTrue( - discoverableAccounts.size === 0, - 'Some Discoverable Accounts were not found' - ); - }); - }); - - describe(`${params.title} - #smartWalletDiscovery From External Keys 4`, function () { - let swd: SmartWalletDiscovery; - let discoverableAccounts: Set; - const usedPublicKeys: string[] = []; - - before(async function () { - await init(params.isCustom); - discoverableAccounts = new Set(); - swd = new SmartWalletDiscovery(socketProvider, [token.address]); - - // new rootkey - const rootKey = SmartWalletDiscovery.getRootExtKeyFromMnemonic( - mnemonic, - `${params.title}_Ext_4` - ); - const firstAccountRoot = - rootKey.derivePath("m/44'/37310'/0'/0"); - usedPublicKeys.push( - firstAccountRoot.publicExtendedKey().toString() - ); - - // Fill 20 accounts with balance and for each one fill a token to 20 smart wallets - for (let i = 0; i < 20; i++) { - const accountWallet = firstAccountRoot - .deriveChild(i) - .getWallet(); - const account = accountWallet.getAddressString(); - discoverableAccounts.add(account); - - for (let j = 0; j < 20; j++) { - const swAddress = params.isCustom - ? calculateCustomSmartWalletAddress( - factory.address, - account, - constants.ZERO_ADDRESS, - constants.ZERO_ADDRESS, - j, - byteCodeHash, - constants.SHA3_NULL_S - ) - : calculateSmartWalletAddress( - factory.address, - account, - constants.ZERO_ADDRESS, - j, - byteCodeHash - ); - discoverableAccounts.add(swAddress); - - const message: string = params.isCustom - ? currentWeb3.utils.soliditySha3( - { t: 'address', v: factory.address }, - { t: 'address', v: account }, - { t: 'address', v: constants.ZERO_ADDRESS }, - { t: 'address', v: constants.ZERO_ADDRESS }, - { t: 'uint256', v: j }, - { t: 'bytes', v: '0x' } - ) ?? '' - : currentWeb3.utils.soliditySha3( - { t: 'address', v: factory.address }, - { t: 'address', v: account }, - { t: 'address', v: constants.ZERO_ADDRESS }, - { t: 'uint256', v: j } - ) ?? ''; - - const toSign: string = - web3.utils.soliditySha3( - { - t: 'string', - v: - PERSONAL_SIGN_PREFIX + - web3.utils.hexToBytes(message).length - }, - { t: 'bytes32', v: message } - ) ?? ''; - - const toSignAsBinaryArray = - ethers.utils.arrayify(toSign); - const signingKey = new ethers.utils.SigningKey( - accountWallet.getPrivateKey() - ); - const signature = - signingKey.signDigest(toSignAsBinaryArray); - const signatureCollapsed = - ethers.utils.joinSignature(signature); - - params.isCustom - ? await ( - factory as CustomSmartWalletFactoryInstance - ).createUserSmartWallet( - account, - constants.ZERO_ADDRESS, - constants.ZERO_ADDRESS, - j, - '0x', - signatureCollapsed - ) - : await ( - factory as SmartWalletFactoryInstance - ).createUserSmartWallet( - account, - constants.ZERO_ADDRESS, - j, - signatureCollapsed - ); - } - } - }); - - after(function () { - closeSocket(); - }); - - it('should discover all the user accounts - using wallet Deploy only', async function () { - const config = new DiscoveryConfig({ - isTestNet: true, - factory: factory.address, - isCustomWallet: params.isCustom - }); - - await swd.discoverAccountsFromExtendedPublicKeys( - config, - usedPublicKeys - ); - - assert.equal( - swd.accounts.length, - 20, - 'incorrect eoa accounts discovered' - ); - - for (let i = 0; i < swd.accounts.length; i++) { - assert.equal( - swd.accounts[i].swAccounts.length, - 20, - `incorrect sw accounts discovered for address ${swd.accounts[i].eoaAccount}` - ); - const account = swd.accounts[i].eoaAccount; - assert.isTrue( - discoverableAccounts.has(account), - 'Discovered Account not part of Discoverable Set' - ); - discoverableAccounts.delete(account); - - for (let j = 0; j < 20; j++) { - const swAccount = swd.accounts[i].swAccounts[j]; - assert.isTrue( - discoverableAccounts.has(swAccount), - 'Discovered SWAccount not part of Discoverable Set' - ); - discoverableAccounts.delete(swAccount); - } - } - - assert.isTrue( - discoverableAccounts.size === 0, - 'Some Discoverable Accounts were not found' - ); - }); - }); - - describe(`${params.title} - #smartWalletDiscovery from Template method `, function () { - let swd: SmartWalletDiscovery; - let discoverableAccounts: Set; - const usedPublicKeys: string[] = []; - - async function getUsedPubKey( - accountIdx: number - ): Promise { - return accountIdx < usedPublicKeys.length - ? usedPublicKeys[accountIdx] - : undefined; - } - - before(async function () { - await init(params.isCustom); - discoverableAccounts = new Set(); - swd = new SmartWalletDiscovery(socketProvider); - - const rootKey: EthereumHDKey = - SmartWalletDiscovery.getRootExtKeyFromMnemonic( - mnemonic, - `${params.title}_Template_0` - ); - const firstAccountRoot = - rootKey.derivePath("m/44'/37310'/0'/0"); - usedPublicKeys.push( - firstAccountRoot.publicExtendedKey().toString() - ); - - // Fill 20 accounts with balance and for each one 20 swallets with balance - for (let i = 0; i < 20; i++) { - const account = firstAccountRoot - .deriveChild(i) - .getWallet() - .getAddressString(); - await currentWeb3.eth.sendTransaction({ - from: accounts[0], - to: account, - value: 1 - }); - discoverableAccounts.add(account); - - for (let j = 0; j < 20; j++) { - const swAddress = params.isCustom - ? calculateCustomSmartWalletAddress( - factory.address, - account, - constants.ZERO_ADDRESS, - constants.ZERO_ADDRESS, - j, - byteCodeHash, - constants.SHA3_NULL_S - ) - : calculateSmartWalletAddress( - factory.address, - account, - constants.ZERO_ADDRESS, - j, - byteCodeHash - ); - await currentWeb3.eth.sendTransaction({ - from: accounts[0], - to: swAddress, - value: 1 - }); - discoverableAccounts.add(swAddress); - } - } - }); - - after(function () { - closeSocket(); - }); - - it('should discover all the user accounts - using only native crypto balance', async function () { - const config = new DiscoveryConfig({ - isTestNet: true, - factory: factory.address, - isCustomWallet: params.isCustom - }); - - await swd.discoverAccounts(config, getUsedPubKey); - - assert.equal( - swd.accounts.length, - 20, - 'incorrect eoa accounts discovered' - ); - - for (let i = 0; i < swd.accounts.length; i++) { - assert.equal( - swd.accounts[i].swAccounts.length, - 20, - `incorrect sw accounts discovered for address ${swd.accounts[i].eoaAccount}` - ); - const account = swd.accounts[i].eoaAccount; - assert.isTrue( - discoverableAccounts.has(account), - 'Discovered Account not part of Discoverable Set' - ); - discoverableAccounts.delete(account); - - for (let j = 0; j < 20; j++) { - const swAccount = swd.accounts[i].swAccounts[j]; - assert.isTrue( - discoverableAccounts.has(swAccount), - 'Discovered SWAccount not part of Discoverable Set' - ); - discoverableAccounts.delete(swAccount); - } - } - - assert.isTrue( - discoverableAccounts.size === 0, - 'Some Discoverable Accounts were not found' - ); - }); - }); - - describe(`${params.title} - #smartWalletDiscovery from Template method 1`, function () { - let swd: SmartWalletDiscovery; - let discoverableAccounts: Set; - const usedPublicKeys: string[] = []; - - async function getUsedPubKey( - accountIdx: number - ): Promise { - return usedPublicKeys[accountIdx]; - } - - before(async function () { - await init(params.isCustom); - discoverableAccounts = new Set(); - swd = new SmartWalletDiscovery(socketProvider, [token.address]); - - // new rootkey - const rootKey = SmartWalletDiscovery.getRootExtKeyFromMnemonic( - mnemonic, - `${params.title}_Template_1` - ); - const firstAccountRoot = - rootKey.derivePath("m/44'/37310'/0'/0"); - usedPublicKeys.push( - firstAccountRoot.publicExtendedKey().toString() - ); - - // Fill 20 accounts with balance and for each one fill a token to 20 smart wallets - for (let i = 0; i < 20; i++) { - const accountWallet = firstAccountRoot - .deriveChild(i) - .getWallet(); - const account = accountWallet.getAddressString(); - discoverableAccounts.add(account); - - for (let j = 0; j < 20; j++) { - const swAddress = params.isCustom - ? calculateCustomSmartWalletAddress( - factory.address, - account, - constants.ZERO_ADDRESS, - constants.ZERO_ADDRESS, - j, - byteCodeHash, - constants.SHA3_NULL_S - ) - : calculateSmartWalletAddress( - factory.address, - account, - constants.ZERO_ADDRESS, - j, - byteCodeHash - ); - await token.mint('1', swAddress); - discoverableAccounts.add(swAddress); - } - } - }); - - after(function () { - closeSocket(); - }); - - it('should discover all the user accounts - using only token balance', async function () { - const config = new DiscoveryConfig({ - isTestNet: true, - factory: factory.address, - isCustomWallet: params.isCustom - }); - - await swd.discoverAccounts(config, getUsedPubKey); - - assert.equal( - swd.accounts.length, - 20, - 'incorrect eoa accounts discovered' - ); - - for (let i = 0; i < swd.accounts.length; i++) { - assert.equal( - swd.accounts[i].swAccounts.length, - 20, - `incorrect sw accounts discovered for address ${swd.accounts[i].eoaAccount}` - ); - const account = swd.accounts[i].eoaAccount; - assert.isTrue( - discoverableAccounts.has(account), - 'Discovered Account not part of Discoverable Set' - ); - discoverableAccounts.delete(account); - - for (let j = 0; j < 20; j++) { - const swAccount = swd.accounts[i].swAccounts[j]; - assert.isTrue( - discoverableAccounts.has(swAccount), - 'Discovered SWAccount not part of Discoverable Set' - ); - discoverableAccounts.delete(swAccount); - } - } - - assert.isTrue( - discoverableAccounts.size === 0, - 'Some Discoverable Accounts were not found' - ); - }); - }); - - describe(`${params.title} - #smartWalletDiscovery from Template method 2`, function () { - let swd: SmartWalletDiscovery; - const usedPublicKeys: string[] = []; - let discoverableAccounts: Set; - - async function getUsedPubKey( - accountIdx: number - ): Promise { - return usedPublicKeys[accountIdx]; - } - - before(async function () { - await init(params.isCustom); - discoverableAccounts = new Set(); - swd = new SmartWalletDiscovery(socketProvider, [token.address]); - // new rootkey - const rootKey = SmartWalletDiscovery.getRootExtKeyFromMnemonic( - mnemonic, - `${params.title}_Template_2` - ); - const firstAccountRoot = - rootKey.derivePath("m/44'/37310'/0'/0"); - usedPublicKeys.push( - firstAccountRoot.publicExtendedKey().toString() - ); - - // Fill 20 accounts with balance and for each one fill a token to 20 smart wallets - for (let i = 0; i < 20; i++) { - const account = firstAccountRoot - .deriveChild(i) - .getWallet() - .getAddressString(); - await currentWeb3.eth.sendTransaction({ - from: accounts[0], - to: account, - value: 1 - }); - discoverableAccounts.add(account); - - for (let j = 0; j < 20; j++) { - const swAddress = params.isCustom - ? calculateCustomSmartWalletAddress( - factory.address, - account, - constants.ZERO_ADDRESS, - constants.ZERO_ADDRESS, - j, - byteCodeHash, - constants.SHA3_NULL_S - ) - : calculateSmartWalletAddress( - factory.address, - account, - constants.ZERO_ADDRESS, - j, - byteCodeHash - ); - await token.mint('1', swAddress); - discoverableAccounts.add(swAddress); - } - } - }); - - after(function () { - closeSocket(); - }); - - it('should discover all the user accounts - using balance and token balance', async function () { - const config = new DiscoveryConfig({ - isTestNet: true, - factory: factory.address, - isCustomWallet: params.isCustom - }); - - await swd.discoverAccounts(config, getUsedPubKey); - - assert.equal( - swd.accounts.length, - 20, - 'incorrect eoa accounts discovered' - ); - - for (let i = 0; i < swd.accounts.length; i++) { - assert.equal( - swd.accounts[i].swAccounts.length, - 20, - `incorrect sw accounts discovered for address ${swd.accounts[i].eoaAccount}` - ); - const account = swd.accounts[i].eoaAccount; - assert.isTrue( - discoverableAccounts.has(account), - 'Discovered Account not part of Discoverable Set' - ); - discoverableAccounts.delete(account); - - for (let j = 0; j < 20; j++) { - const swAccount = swd.accounts[i].swAccounts[j]; - assert.isTrue( - discoverableAccounts.has(swAccount), - 'Discovered SWAccount not part of Discoverable Set' - ); - discoverableAccounts.delete(swAccount); - } - } - - assert.isTrue( - discoverableAccounts.size === 0, - 'Some Discoverable Accounts were not found' - ); - }); - }); - - describe(`${params.title} - #smartWalletDiscovery from Template method 3`, function () { - let swd: SmartWalletDiscovery; - const usedPublicKeys: string[] = []; - let discoverableAccounts: Set; - - async function getUsedPubKey( - accountIdx: number - ): Promise { - return usedPublicKeys[accountIdx]; - } - - before(async function () { - await init(params.isCustom); - discoverableAccounts = new Set(); - swd = new SmartWalletDiscovery(socketProvider, [token.address]); - // new rootkey - const rootKey = SmartWalletDiscovery.getRootExtKeyFromMnemonic( - mnemonic, - `${params.title}_Template_3` - ); - const firstAccountRoot = - rootKey.derivePath("m/44'/37310'/0'/0"); - usedPublicKeys.push( - firstAccountRoot.publicExtendedKey().toString() - ); - - for (let i = 0; i < 20; i++) { - const accountWallet = firstAccountRoot - .deriveChild(i) - .getWallet(); - const account = accountWallet.getAddressString(); - - await currentWeb3.eth.sendTransaction({ - from: accounts[0], - to: account, - value: 1 - }); - discoverableAccounts.add(account); - - for (let j = 0; j < 20; j++) { - const swAddress = params.isCustom - ? calculateCustomSmartWalletAddress( - factory.address, - account, - constants.ZERO_ADDRESS, - constants.ZERO_ADDRESS, - j, - byteCodeHash, - constants.SHA3_NULL_S - ) - : calculateSmartWalletAddress( - factory.address, - account, - constants.ZERO_ADDRESS, - j, - byteCodeHash - ); - - discoverableAccounts.add(swAddress); - - const message: string = params.isCustom - ? currentWeb3.utils.soliditySha3( - { t: 'address', v: factory.address }, - { t: 'address', v: account }, - { t: 'address', v: constants.ZERO_ADDRESS }, - { t: 'address', v: constants.ZERO_ADDRESS }, - { t: 'uint256', v: j }, - { t: 'bytes', v: '0x' } - ) ?? '' - : currentWeb3.utils.soliditySha3( - { t: 'address', v: factory.address }, - { t: 'address', v: account }, - { t: 'address', v: constants.ZERO_ADDRESS }, - { t: 'uint256', v: j } - ) ?? ''; - - const toSign: string = - web3.utils.soliditySha3( - { - t: 'string', - v: - PERSONAL_SIGN_PREFIX + - web3.utils.hexToBytes(message).length - }, - { t: 'bytes32', v: message } - ) ?? ''; - - const toSignAsBinaryArray = - ethers.utils.arrayify(toSign); - const signingKey = new ethers.utils.SigningKey( - accountWallet.getPrivateKey() - ); - const signature = - signingKey.signDigest(toSignAsBinaryArray); - const signatureCollapsed = - ethers.utils.joinSignature(signature); - - params.isCustom - ? await ( - factory as CustomSmartWalletFactoryInstance - ).createUserSmartWallet( - account, - constants.ZERO_ADDRESS, - constants.ZERO_ADDRESS, - j, - '0x', - signatureCollapsed - ) - : await ( - factory as SmartWalletFactoryInstance - ).createUserSmartWallet( - account, - constants.ZERO_ADDRESS, - j, - signatureCollapsed - ); - } - } - }); - - after(function () { - closeSocket(); - }); - - it('should discover all the user accounts - using deploy and native crypto balance', async function () { - const config = new DiscoveryConfig({ - isTestNet: true, - factory: factory.address, - isCustomWallet: params.isCustom - }); - - await swd.discoverAccounts(config, getUsedPubKey); - - assert.equal( - swd.accounts.length, - 20, - 'incorrect eoa accounts discovered' - ); - - for (let i = 0; i < swd.accounts.length; i++) { - assert.equal( - swd.accounts[i].swAccounts.length, - 20, - `incorrect sw accounts discovered for address ${swd.accounts[i].eoaAccount}` - ); - const account = swd.accounts[i].eoaAccount; - assert.isTrue( - discoverableAccounts.has(account), - 'Discovered Account not part of Discoverable Set' - ); - discoverableAccounts.delete(account); - - for (let j = 0; j < 20; j++) { - const swAccount = swd.accounts[i].swAccounts[j]; - assert.isTrue( - discoverableAccounts.has(swAccount), - 'Discovered SWAccount not part of Discoverable Set' - ); - discoverableAccounts.delete(swAccount); - } - } - - assert.isTrue( - discoverableAccounts.size === 0, - 'Some Discoverable Accounts were not found' - ); - }); - }); - - describe(`${params.title} - #smartWalletDiscovery from Template method 4`, function () { - let swd: SmartWalletDiscovery; - let discoverableAccounts: Set; - const usedPublicKeys: string[] = []; - - async function getUsedPubKey( - accountIdx: number - ): Promise { - return usedPublicKeys[accountIdx]; - } - - before(async function () { - await init(params.isCustom); - discoverableAccounts = new Set(); - swd = new SmartWalletDiscovery(socketProvider, [token.address]); - - // new rootkey - const rootKey = SmartWalletDiscovery.getRootExtKeyFromMnemonic( - mnemonic, - `${params.title}_Template_4` - ); - const firstAccountRoot = - rootKey.derivePath("m/44'/37310'/0'/0"); - usedPublicKeys.push( - firstAccountRoot.publicExtendedKey().toString() - ); - - // Fill 20 accounts with balance and for each one fill a token to 20 smart wallets - for (let i = 0; i < 20; i++) { - const accountWallet = firstAccountRoot - .deriveChild(i) - .getWallet(); - const account = accountWallet.getAddressString(); - discoverableAccounts.add(account); - - for (let j = 0; j < 20; j++) { - const swAddress = params.isCustom - ? calculateCustomSmartWalletAddress( - factory.address, - account, - constants.ZERO_ADDRESS, - constants.ZERO_ADDRESS, - j, - byteCodeHash, - constants.SHA3_NULL_S - ) - : calculateSmartWalletAddress( - factory.address, - account, - constants.ZERO_ADDRESS, - j, - byteCodeHash - ); - discoverableAccounts.add(swAddress); - - const message: string = params.isCustom - ? currentWeb3.utils.soliditySha3( - { t: 'address', v: factory.address }, - { t: 'address', v: account }, - { t: 'address', v: constants.ZERO_ADDRESS }, - { t: 'address', v: constants.ZERO_ADDRESS }, - { t: 'uint256', v: j }, - { t: 'bytes', v: '0x' } - ) ?? '' - : currentWeb3.utils.soliditySha3( - { t: 'address', v: factory.address }, - { t: 'address', v: account }, - { t: 'address', v: constants.ZERO_ADDRESS }, - { t: 'uint256', v: j } - ) ?? ''; - - const toSign: string = - web3.utils.soliditySha3( - { - t: 'string', - v: - PERSONAL_SIGN_PREFIX + - web3.utils.hexToBytes(message).length - }, - { t: 'bytes32', v: message } - ) ?? ''; - - const toSignAsBinaryArray = - ethers.utils.arrayify(toSign); - const signingKey = new ethers.utils.SigningKey( - accountWallet.getPrivateKey() - ); - const signature = - signingKey.signDigest(toSignAsBinaryArray); - const signatureCollapsed = - ethers.utils.joinSignature(signature); - - params.isCustom - ? await ( - factory as CustomSmartWalletFactoryInstance - ).createUserSmartWallet( - account, - constants.ZERO_ADDRESS, - constants.ZERO_ADDRESS, - j, - '0x', - signatureCollapsed - ) - : await ( - factory as SmartWalletFactoryInstance - ).createUserSmartWallet( - account, - constants.ZERO_ADDRESS, - j, - signatureCollapsed - ); - } - } - }); - - after(function () { - closeSocket(); - }); - - it('should discover all the user accounts - using wallet Deploy only', async function () { - const config = new DiscoveryConfig({ - isTestNet: true, - factory: factory.address, - isCustomWallet: params.isCustom - }); - - await swd.discoverAccounts(config, getUsedPubKey); - - assert.equal( - swd.accounts.length, - 20, - 'incorrect eoa accounts discovered' - ); - - for (let i = 0; i < swd.accounts.length; i++) { - assert.equal( - swd.accounts[i].swAccounts.length, - 20, - `incorrect sw accounts discovered for address ${swd.accounts[i].eoaAccount}` - ); - const account = swd.accounts[i].eoaAccount; - assert.isTrue( - discoverableAccounts.has(account), - 'Discovered Account not part of Discoverable Set' - ); - discoverableAccounts.delete(account); - - for (let j = 0; j < 20; j++) { - const swAccount = swd.accounts[i].swAccounts[j]; - assert.isTrue( - discoverableAccounts.has(swAccount), - 'Discovered SWAccount not part of Discoverable Set' - ); - discoverableAccounts.delete(swAccount); - } - } - - assert.isTrue( - discoverableAccounts.size === 0, - 'Some Discoverable Accounts were not found' - ); - }); - }); - } - - function calculateSmartWalletAddress( - factory: string, - ownerEOA: string, - recoverer: string, - walletIndex: number, - bytecodeHash: string - ): string { - const salt: string = - web3.utils.soliditySha3( - { t: 'address', v: ownerEOA }, - { t: 'address', v: recoverer }, - { t: 'uint256', v: walletIndex } - ) ?? ''; - - const _data: string = - web3.utils.soliditySha3( - { t: 'bytes1', v: '0xff' }, - { t: 'address', v: factory }, - { t: 'bytes32', v: salt }, - { t: 'bytes32', v: bytecodeHash } - ) ?? ''; - - return toChecksumAddress('0x' + _data.slice(26, _data.length), chainId); - } - - async function init(isCustom: boolean): Promise { - socketProvider = new Web3.providers.WebsocketProvider( - getWebSocketUrl() - ); - - CustomSmartWalletFactory.setProvider(socketProvider, undefined); - SmartWalletFactory.setProvider(socketProvider, undefined); - CustomSmartWallet.setProvider(socketProvider, undefined); - SmartWallet.setProvider(socketProvider, undefined); - TestToken.setProvider(socketProvider, undefined); - - currentWeb3 = new Web3(socketProvider); - chainId = await currentWeb3.eth.getChainId(); - sWalletTemplate = isCustom - ? await CustomSmartWallet.new() - : await SmartWallet.new(); - factory = isCustom - ? await CustomSmartWalletFactory.new(sWalletTemplate.address) - : await SmartWalletFactory.new(sWalletTemplate.address); - byteCodeHash = currentWeb3.utils.keccak256( - await factory.getCreationBytecode() - ); - token = await TestToken.new(); - } - - function closeSocket(): void { - const socketProvider = currentWeb3.currentProvider as WebsocketProvider; - socketProvider.disconnect(0, ''); - assert.isFalse( - socketProvider.connected, - 'Socket connection did not end' - ); - } - - function calculateCustomSmartWalletAddress( - factory: string, - ownerEOA: string, - recoverer: string, - customLogic: string, - walletIndex: number, - bytecodeHash: string, - logicInitParamsHash?: string - ): string { - const salt: string = - web3.utils.soliditySha3( - { t: 'address', v: ownerEOA }, - { t: 'address', v: recoverer }, - { t: 'address', v: customLogic }, - { - t: 'bytes32', - v: logicInitParamsHash ?? constants.SHA3_NULL_S - }, - { t: 'uint256', v: walletIndex } - ) ?? ''; - - const _data: string = - web3.utils.soliditySha3( - { t: 'bytes1', v: '0xff' }, - { t: 'address', v: factory }, - { t: 'bytes32', v: salt }, - { t: 'bytes32', v: bytecodeHash } - ) ?? ''; - - return toChecksumAddress('0x' + _data.slice(26, _data.length), chainId); - } -}); diff --git a/test-to-migrate/relayserver/NetworkSimulation.test.ts b/test-to-migrate/relayserver/NetworkSimulation.test.ts deleted file mode 100644 index 03c483b6..00000000 --- a/test-to-migrate/relayserver/NetworkSimulation.test.ts +++ /dev/null @@ -1,108 +0,0 @@ -import { ServerTestEnvironment } from './ServerTestEnvironment'; -import { - NetworkSimulatingProvider, - EnvelopingConfig, - ContractInteractor, - EnvelopingTransactionDetails -} from '@rsksmart/rif-relay-common'; -import { HttpProvider } from 'web3-core'; -import { - getTestingEnvironment, - createSmartWalletFactory, - createSmartWallet, - getGaslessAccount -} from '../TestUtils'; -import { AccountKeypair, configure } from '@rsksmart/rif-relay-client'; - -contract('Network Simulation for Relay Server', function (accounts) { - let env: ServerTestEnvironment; - let provider: NetworkSimulatingProvider; - - before(async function () { - provider = new NetworkSimulatingProvider( - web3.currentProvider as HttpProvider - ); - const contractFactory = async function ( - partialConfig: Partial - ): Promise { - const contractInteractor = new ContractInteractor( - provider, - configure(partialConfig) - ); - await contractInteractor.init(); - return contractInteractor; - }; - env = new ServerTestEnvironment( - web3.currentProvider as HttpProvider, - accounts - ); - await env.init( - { chainId: (await getTestingEnvironment()).chainId }, - {}, - contractFactory - ); - await env.newServerInstance({ workerTargetBalance: 0.6e18 }); - provider.setDelayTransactions(true); - }); - - describe('without automated mining', function () { - beforeEach(async function () { - await env.clearServerStorage(); - }); - - it('should resolve once the transaction is broadcast', async function () { - assert.equal(provider.mempool.size, 0); - const { txHash, reqSigHash } = await env.relayTransaction(false); - assert.equal(provider.mempool.size, 1); - const receipt = await env.web3.eth.getTransactionReceipt(txHash); - assert.isNull(receipt); - await provider.mineTransaction(txHash); - assert.equal(provider.mempool.size, 0); - await env.assertTransactionRelayed(txHash, reqSigHash); - }); - - it('should broadcast multiple transactions at once', async function () { - const gaslessAccount: AccountKeypair = await getGaslessAccount(); - - const SmartWallet = artifacts.require('SmartWallet'); - const smartWalletTemplate = await SmartWallet.new(); - const factory = await createSmartWalletFactory(smartWalletTemplate); - const smartWallet = await createSmartWallet( - accounts[0], - gaslessAccount.address, - factory, - gaslessAccount.privateKey, - ( - await getTestingEnvironment() - ).chainId - ); - - env.relayClient.accountManager.addAccount(gaslessAccount); - - assert.equal(provider.mempool.size, 0); - // cannot use the same sender as it will create same request with same forwarder nonce, etc - const overrideDetails: Partial = { - from: gaslessAccount.address, - callForwarder: smartWallet.address - }; - // noinspection ES6MissingAwait - done on purpose - const promises = [ - env.relayTransaction(false), - env.relayTransaction(false, overrideDetails) - ]; - const txs = await Promise.all(promises); - assert.equal(provider.mempool.size, 2); - await provider.mineTransaction(txs[0].txHash); - await provider.mineTransaction(txs[1].txHash); - await env.assertTransactionRelayed( - txs[0].txHash, - txs[0].reqSigHash - ); - await env.assertTransactionRelayed( - txs[1].txHash, - txs[1].reqSigHash, - overrideDetails - ); - }); - }); -}); diff --git a/test-to-migrate/relayserver/RegistrationManager.test.ts b/test-to-migrate/relayserver/RegistrationManager.test.ts deleted file mode 100644 index 4685290b..00000000 --- a/test-to-migrate/relayserver/RegistrationManager.test.ts +++ /dev/null @@ -1,602 +0,0 @@ -import Web3 from 'web3'; -import { HttpProvider } from 'web3-core'; -import { toBN } from 'web3-utils'; -import { ContractInteractor, constants } from '@rsksmart/rif-relay-common'; -import { RelayHubConfiguration } from '@rsksmart/rif-relay-contracts'; -import { - KeyManager, - TxStoreManager, - RegistrationManager, - RelayServer, - ServerAction, - ServerConfigParams, - ServerDependencies -} from '@rsksmart/rif-relay-server'; -import { configure } from '@rsksmart/rif-relay-client'; -import { evmMine, evmMineMany, revert, snapshot } from '../TestUtils'; -import { LocalhostOne, ServerTestEnvironment } from './ServerTestEnvironment'; -import { - assertRelayAdded, - getTemporaryWorkdirs, - getTotalTxCosts, - ServerWorkdirs -} from './ServerTestUtils'; -import { ether } from '@openzeppelin/test-helpers'; - -const { oneEther } = constants; - -const workerIndex = 0; - -const unstakeDelay = 50; - -const maxWorkerCount = 1; -const minimumEntryDepositValue = ether('1').toString(); -const minimumStake = ether('1').toString(); -const minimumUnstakeDelay = 50; - -const hubConfig: Partial = { - maxWorkerCount, - minimumEntryDepositValue, - minimumStake, - minimumUnstakeDelay -}; - -contract('RegistrationManager', function (accounts) { - const relayOwner = accounts[4]; - - let env: ServerTestEnvironment; - let relayServer: RelayServer; - let id: string; - let serverWorkdirs: ServerWorkdirs; - - before(async function () { - serverWorkdirs = getTemporaryWorkdirs(); - env = new ServerTestEnvironment( - web3.currentProvider as HttpProvider, - accounts - ); - - await env.init({}, hubConfig); - env.newServerInstanceNoFunding({}, serverWorkdirs); - await env.clearServerStorage(); - relayServer = env.relayServer; - }); - - // When running server before staking/funding it, or when balance gets too low - describe('multi-step server initialization', function () { - // TODO: It does not make sense for the '_worker' method to expose the reason it does not register - // This means these 2 tests cannot check what they used to and require refactoring. - it('should wait for balance', async function () { - let latestBlock = await env.web3.eth.getBlock('latest'); - let transactionHashes = await relayServer._worker( - latestBlock.number - ); - assert.equal(transactionHashes.length, 0); - const expectedBalance = env.web3.utils.toWei('2', 'ether'); - assert.notEqual( - (await relayServer.getManagerBalance()).cmp( - toBN(expectedBalance) - ), - 0 - ); - await env.web3.eth.sendTransaction({ - to: relayServer.managerAddress, - from: relayOwner, - value: expectedBalance - }); - latestBlock = await env.web3.eth.getBlock('latest'); - transactionHashes = await relayServer._worker(latestBlock.number); - assert.equal(transactionHashes.length, 0); - assert.equal( - relayServer.isReady(), - false, - 'relay should not be ready yet' - ); - assert.equal( - (await relayServer.getManagerBalance()).cmp( - toBN(expectedBalance) - ), - 0 - ); - await evmMine(); - }); - - it('should wait for stake and fund workers', async function () { - let latestBlock = await env.web3.eth.getBlock('latest'); - const transactionHashes = await relayServer._worker( - latestBlock.number - ); - assert.equal(transactionHashes.length, 0); - assert.equal( - relayServer.isReady(), - false, - 'relay should not be ready yet' - ); - const res = await env.relayHub.stakeForAddress( - relayServer.managerAddress, - unstakeDelay, - { - from: relayOwner, - value: oneEther - } - ); - assert.ok(res.receipt.status, 'stake failed'); - const workerBalanceBefore = await relayServer.getWorkerBalance( - workerIndex - ); - assert.equal(workerBalanceBefore.toString(), '0'); - latestBlock = await env.web3.eth.getBlock('latest'); - const receipts = await relayServer._worker(latestBlock.number); - await relayServer._worker(latestBlock.number + 1); - const workerBalanceAfter = await relayServer.getWorkerBalance( - workerIndex - ); - assert.equal(relayServer.lastScannedBlock, latestBlock.number + 1); - assert.isTrue( - relayServer.registrationManager.stakeRequired.currentValue.eq( - oneEther - ) - ); - assert.equal( - relayServer.registrationManager.ownerAddress, - relayOwner - ); - assert.equal( - workerBalanceAfter.toString(), - relayServer.config.workerTargetBalance.toString() - ); - assert.equal(relayServer.isReady(), true, 'relay not ready?'); - await assertRelayAdded(receipts, relayServer); - }); - - it('should start again after restarting process', async () => { - const managerKeyManager = new KeyManager( - 1, - serverWorkdirs.managerWorkdir - ); - const workersKeyManager = new KeyManager( - 1, - serverWorkdirs.workersWorkdir - ); - const txStoreManager = new TxStoreManager({ - workdir: serverWorkdirs.workdir - }); - const serverWeb3provider = new Web3.providers.HttpProvider( - (web3.currentProvider as HttpProvider).host - ); - const contractInteractor = new ContractInteractor( - serverWeb3provider, - configure({ - relayHubAddress: env.relayHub.address - }) - ); - await contractInteractor.init(); - const serverDependencies: ServerDependencies = { - txStoreManager, - managerKeyManager, - workersKeyManager, - contractInteractor - }; - const params: Partial = { - relayHubAddress: env.relayHub.address, - url: LocalhostOne, - logLevel: 5, - gasPriceFactor: 1, - checkInterval: 10 - }; - const newRelayServer = new RelayServer(params, serverDependencies); - await newRelayServer.init(); - const latestBlock = await env.web3.eth.getBlock('latest'); - await newRelayServer._worker(latestBlock.number); - assert.equal(relayServer.isReady(), true, 'relay not ready?'); - }); - }); - - // When running server after both staking & funding it - describe('single step server initialization', function () { - beforeEach(async function () { - id = (await snapshot()).result; - }); - - afterEach(async function () { - await revert(id); - }); - - let newServer: RelayServer; - it('should initialize relay after staking and funding it', async function () { - await env.newServerInstanceNoInit({}, undefined, unstakeDelay); - newServer = env.relayServer; - await newServer.init(); - assert.equal(newServer.registrationManager.ownerAddress, undefined); - await newServer.registrationManager.refreshStake(); - assert.isTrue( - newServer.registrationManager.stakeRequired.currentValue.eq( - oneEther - ) - ); - assert.equal( - newServer.registrationManager.ownerAddress, - relayOwner, - 'owner should be set after refreshing stake' - ); - - const expectedGasPrice = - parseInt(await env.web3.eth.getGasPrice()) * - newServer.config.gasPriceFactor; - assert.equal(newServer.isReady(), false); - assert.equal(newServer.lastScannedBlock, 0); - const workerBalanceBefore = await newServer.getWorkerBalance( - workerIndex - ); - assert.equal(workerBalanceBefore.toString(), '0'); - const latestBlock = await env.web3.eth.getBlock('latest'); - const receipts = await newServer._worker(latestBlock.number); - await newServer._worker(latestBlock.number + 1); - assert.equal(newServer.lastScannedBlock, latestBlock.number + 1); - assert.equal(newServer.gasPrice, expectedGasPrice); - assert.equal(newServer.isReady(), true, 'relay no ready?'); - const workerBalanceAfter = await newServer.getWorkerBalance( - workerIndex - ); - assert.isTrue( - newServer.registrationManager.stakeRequired.currentValue.eq( - oneEther - ) - ); - assert.equal( - newServer.registrationManager.ownerAddress, - relayOwner - ); - assert.equal( - workerBalanceAfter.toString(), - newServer.config.workerTargetBalance.toString() - ); - await assertRelayAdded(receipts, newServer); - }); - - after('txstore cleanup', async function () { - await newServer.transactionManager.txStoreManager.clearAll(); - assert.deepEqual( - [], - await newServer.transactionManager.txStoreManager.getAll() - ); - }); - }); - - describe('configuration change', function () { - let relayServer: RelayServer; - - before(async function () { - await env.newServerInstanceNoInit( - { refreshStateTimeoutBlocks: 1 }, - undefined, - unstakeDelay - ); - relayServer = env.relayServer; - }); - - // TODO: separate this into 2 unit tests for 'isRegistrationValid' and 1 test for 'handlePastEvents' - it('should re-register server with new configuration', async function () { - let latestBlock = await env.web3.eth.getBlock('latest'); - const receipts = await relayServer._worker(latestBlock.number); - await assertRelayAdded(receipts, relayServer); - await relayServer._worker(latestBlock.number + 1); - - let transactionHashes = - await relayServer.registrationManager.handlePastEvents( - [], - latestBlock.number, - 0, - false - ); - assert.equal( - transactionHashes.length, - 0, - 'should not re-register if already registered' - ); - - latestBlock = await env.web3.eth.getBlock('latest'); - await relayServer._worker(latestBlock.number); - - relayServer.config.url = 'fakeUrl'; - transactionHashes = - await relayServer.registrationManager.handlePastEvents( - [], - latestBlock.number, - 0, - false - ); - await assertRelayAdded(transactionHashes, relayServer, false); - }); - }); - - describe('event handlers', function () { - describe('Withdrawn event', function () { - async function assertSendBalancesToOwner( - server: RelayServer, - managerBalanceBefore: BN, - workerBalanceBefore: BN - ): Promise { - const gasPrice = await env.web3.eth.getGasPrice(); - const ownerBalanceBefore = toBN( - await env.web3.eth.getBalance( - newServer.registrationManager.ownerAddress - ) - ); - assert.equal( - newServer.registrationManager.stakeRequired.currentValue.toString(), - oneEther.toString() - ); - // TODO: assert on withdrawal block? - // assert.equal(newServer.config.withdrawBlock?.toString(), '0') - const latestBlock = await env.web3.eth.getBlock('latest'); - const receipts = await newServer._worker(latestBlock.number); - const totalTxCosts = await getTotalTxCosts(receipts, gasPrice); - const ownerBalanceAfter = toBN( - await env.web3.eth.getBalance( - newServer.registrationManager.ownerAddress - ) - ); - assert.equal( - ownerBalanceAfter.sub(ownerBalanceBefore).toString(), - managerBalanceBefore - .add(workerBalanceBefore) - .sub(totalTxCosts) - .toString(), - `ownerBalanceAfter(${ownerBalanceAfter.toString()}) - ownerBalanceBefore(${ownerBalanceBefore.toString()}) != - + managerBalanceBefore(${managerBalanceBefore.toString()}) + workerBalanceBefore(${workerBalanceBefore.toString()}) - - totalTxCosts(${totalTxCosts.toString()})` - ); - const managerBalanceAfter = await newServer.getManagerBalance(); - const workerBalanceAfter = await newServer.getWorkerBalance( - workerIndex - ); - assert.isTrue(managerBalanceAfter.eqn(0)); - assert.isTrue(workerBalanceAfter.eqn(0)); - // TODO - // assert.isTrue(newServer.withdrawBlock?.gtn(0)) - } - - let newServer: RelayServer; - beforeEach(async function () { - id = (await snapshot()).result; - await env.newServerInstanceNoInit( - { refreshStateTimeoutBlocks: 1 }, - undefined, - unstakeDelay - ); - newServer = env.relayServer; - const latestBlock = await env.web3.eth.getBlock('latest'); - await newServer._worker(latestBlock.number); - await newServer._worker(latestBlock.number + 1); - await env.relayHub.unlockStake(newServer.managerAddress, { - from: relayOwner - }); - await evmMineMany(unstakeDelay); - await env.relayHub.withdrawStake(newServer.managerAddress, { - from: relayOwner - }); - }); - - afterEach(async function () { - await revert(id); - }); - - it('send balances to owner when all balances > tx costs', async function () { - const managerBalanceBefore = - await newServer.getManagerBalance(); - const workerBalanceBefore = await newServer.getWorkerBalance( - workerIndex - ); - assert.isTrue(managerBalanceBefore.gtn(0)); - assert.isTrue(workerBalanceBefore.gtn(0)); - await assertSendBalancesToOwner( - newServer, - managerBalanceBefore, - workerBalanceBefore - ); - }); - }); - - describe('HubUnauthorized event', function () { - let newServer: RelayServer; - beforeEach(async function () { - id = (await snapshot()).result; - await env.newServerInstanceNoInit( - { refreshStateTimeoutBlocks: 1 }, - undefined, - unstakeDelay - ); - newServer = env.relayServer; - const latestBlock = await env.web3.eth.getBlock('latest'); - await newServer._worker(latestBlock.number); - await newServer._worker(latestBlock.number + 1); - }); - - afterEach(async function () { - await revert(id); - }); - - it('should not send balance immediately after unauthorize (before unstake delay)', async function () { - const workerBalanceBefore = await newServer.getWorkerBalance( - workerIndex - ); - - await evmMineMany(unstakeDelay - 3); - const latestBlock = await env.web3.eth.getBlock('latest'); - - const receipt = await newServer._worker(latestBlock.number); - const receipt2 = await newServer._worker( - latestBlock.number + 1 - ); - - assert.equal(receipt.length, 0); - assert.equal(receipt2.length, 0); - assert.equal( - workerBalanceBefore.toString(), - await newServer - .getWorkerBalance(workerIndex) - .then((b) => b.toString()) - ); - }); - - it("send only workers' balances to owner (not manager hub, rbtc balance) - after unstake delay", async function () { - await env.relayHub.unlockStake(newServer.managerAddress, { - from: relayOwner - }); - - const managerBalanceBefore = - await newServer.getManagerBalance(); - const workerBalanceBefore = await newServer.getWorkerBalance( - workerIndex - ); - assert.isTrue(managerBalanceBefore.gtn(0)); - assert.isTrue(workerBalanceBefore.gtn(0)); - - const ownerBalanceBefore = toBN( - await env.web3.eth.getBalance(relayOwner) - ); - - await evmMineMany(unstakeDelay); - - const latestBlock = await env.web3.eth.getBlock('latest'); - - const receipts = await newServer._worker(latestBlock.number); - - const gasPrice = await env.web3.eth.getGasPrice(); - - // TODO: these two hard-coded indexes are dependent on the order of operations in 'withdrawAllFunds' - const workerEthTxCost = await getTotalTxCosts( - [receipts[0]], - gasPrice - ); - - const ownerBalanceAfter = toBN( - await env.web3.eth.getBalance(relayOwner) - ); - - const managerBalanceAfter = await newServer.getManagerBalance(); - - const workerBalanceAfter = await newServer.getWorkerBalance( - workerIndex - ); - - assert.isTrue(workerBalanceAfter.eqn(0)); - assert.equal( - managerBalanceAfter.toString(), - managerBalanceBefore.toString() - ); - assert.equal( - ownerBalanceAfter.sub(ownerBalanceBefore).toString(), - workerBalanceBefore.sub(workerEthTxCost).toString(), - `ownerBalanceAfter(${ownerBalanceAfter.toString()}) - ownerBalanceBefore(${ownerBalanceBefore.toString()}) != - + workerBalanceBefore(${workerBalanceBefore.toString()}) - - workerEthTxCost(${workerEthTxCost.toString()})` - ); - }); - }); - - it('_handleStakedEvent'); - }); - - describe('#_extractDuePendingEvents', () => { - let rm: RegistrationManager; - let extracted: any[]; - - before(async () => { - // @ts-ignore - // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions - if (!relayServer.initialized) { - await relayServer.init(); - } - - rm = relayServer.registrationManager; - - (rm as any).delayedEvents = [ - { block: 1, eventData: 'event1' }, - { block: 2, eventData: 'event2' }, - { block: 3, eventData: 'event3' } - ]; - extracted = rm._extractDuePendingEvents(2) as any; - }); - it('should extract events which are due (lower or equal block number)', function () { - assert.deepEqual(extracted, ['event1', 'event2']); - }); - - it('should leave future events in the delayedEvents list', function () { - assert.deepEqual((rm as any).delayedEvents, [ - { block: 3, eventData: 'event3' } - ]); - }); - }); - - describe('#attemptRegistration()', function () { - let newServer: RelayServer; - - describe('without re-registration', function () { - beforeEach(async function () { - id = (await snapshot()).result; - await env.newServerInstanceNoInit({}, undefined, unstakeDelay); - await env.relayServer.init(); - newServer = env.relayServer; - // TODO: this is horrible!!! - newServer.registrationManager.isStakeLocked = true; - newServer.registrationManager.stakeRequired.requiredValue = - toBN(0); - newServer.registrationManager.balanceRequired.requiredValue = - toBN(0); - await newServer.registrationManager.refreshStake(); - assert.isTrue( - newServer.registrationManager.stakeRequired.currentValue.eq( - oneEther - ) - ); - assert.equal( - newServer.registrationManager.ownerAddress, - relayOwner, - 'owner should be set after refreshing stake' - ); - assert.equal(newServer.config.registrationBlockRate, 0); - }); - - afterEach(async function () { - await revert(id); - }); - - it('should register server and add workers', async function () { - assert.equal( - (await newServer.txStoreManager.getAll()).length, - 0 - ); - const receipts = - await newServer.registrationManager.attemptRegistration(0); - await assertRelayAdded(receipts, newServer); - const pendingTransactions = - await newServer.txStoreManager.getAll(); - assert.equal(pendingTransactions.length, 2); - assert.equal( - pendingTransactions[0].serverAction, - ServerAction.ADD_WORKER - ); - assert.equal( - pendingTransactions[1].serverAction, - ServerAction.REGISTER_SERVER - ); - }); - }); - }); - - // note: relies on first 'before' to initialize server - describe('#assertRegistered()', function () { - before(function () { - relayServer.registrationManager.stakeRequired._requiredValue = - toBN(1e20); - }); - - it('should return false if the stake requirement is not satisfied', async function () { - const isRegistered = - await relayServer.registrationManager.isRegistered(); - assert.isFalse(isRegistered); - }); - }); -}); diff --git a/test-to-migrate/relayserver/RelayServer.test.ts b/test-to-migrate/relayserver/RelayServer.test.ts deleted file mode 100644 index ac4af759..00000000 --- a/test-to-migrate/relayserver/RelayServer.test.ts +++ /dev/null @@ -1,1037 +0,0 @@ -/* global artifacts describe */ -// @ts-ignore -import { - constants, - defaultEnvironment, - EnvelopingConfig, - isRsk, - RelayTransactionRequest, - sleep, - TokenResponse -} from '@rsksmart/rif-relay-common'; -import { - TestDeployVerifierConfigurableMisbehaviorInstance, - TestRecipientInstance, - TestTokenInstance, - TestVerifierConfigurableMisbehaviorInstance -} from '@rsksmart/rif-relay-contracts/types/truffle-contracts'; -import { - RelayServer, - SendTransactionDetails, - ServerAction, - ServerConfigParams, - SignedTransactionDetails -} from '@rsksmart/rif-relay-server'; -import chai from 'chai'; -import chaiAsPromised from 'chai-as-promised'; -import { PrefixedHexString } from 'ethereumjs-tx'; -import sinon from 'sinon'; -import sinonChai from 'sinon-chai'; -import { HttpProvider } from 'web3-core'; -import { toBN, toHex } from 'web3-utils'; -import { - evmMineMany, - getTestingEnvironment, - INCORRECT_ECDSA_SIGNATURE, - revert, - snapshot -} from '../TestUtils'; -import { LocalhostOne, ServerTestEnvironment } from './ServerTestEnvironment'; -import { assertRelayAdded, getTotalTxCosts } from './ServerTestUtils'; - -const expect = chai.expect; -chai.use(chaiAsPromised).use(sinonChai); - -const TestToken = artifacts.require('TestToken'); -const TestVerifierConfigurableMisbehavior = artifacts.require( - 'TestVerifierConfigurableMisbehavior' -); -const TestDeployVerifierConfigurableMisbehavior = artifacts.require( - 'TestDeployVerifierConfigurableMisbehavior' -); - -const revertReasonSupported = true; -contract('RelayServer', function (accounts) { - const alertedBlockDelay = 0; - - let id: string; - let globalId: string; - let env: ServerTestEnvironment; - let token: TestTokenInstance; - - before(async function () { - globalId = (await snapshot()).result; - const relayClientConfig: Partial = { - preferredRelays: [LocalhostOne], - maxRelayNonceGap: 0, - chainId: (await getTestingEnvironment()).chainId - }; - - env = new ServerTestEnvironment( - web3.currentProvider as HttpProvider, - accounts - ); - await env.init(relayClientConfig); - const overrideParams: Partial = { - alertedBlockDelay, - workerTargetBalance: 0.6e18 - }; - await env.newServerInstance(overrideParams); - await env.clearServerStorage(); - token = await TestToken.new(); - await token.mint('1000', env.forwarder.address); - }); - - after(async function () { - await revert(globalId); - await env.clearServerStorage(); - }); - - describe('#init()', function () { - it('should initialize relay params (chainId, networkId, gasPrice)', async function () { - const env = new ServerTestEnvironment( - web3.currentProvider as HttpProvider, - accounts - ); - await env.init({}); - await env.newServerInstanceNoInit(); - const relayServerToInit = env.relayServer; - const chainId = await env.web3.eth.getChainId(); - const networkId = await env.web3.eth.net.getId(); - assert.notEqual(relayServerToInit.chainId, chainId); - assert.notEqual(relayServerToInit.networkId, networkId); - assert.equal(relayServerToInit.ready, false); - await relayServerToInit.init(); - assert.equal( - relayServerToInit.ready, - false, - 'relay should not be ready yet' - ); - assert.equal(relayServerToInit.chainId, chainId); - assert.equal(relayServerToInit.networkId, networkId); - }); - }); - - describe('validation', function () { - beforeEach(async function () { - await env.relayServer.txStoreManager.clearAll(); - }); - - describe('#validateInputTypes()', function () { - // skipped because error message changed here for no apparent reason - it.skip('should throw on undefined data', async function () { - const req = await env.createRelayHttpRequest(); - // @ts-ignore - req.relayRequest.request.data = undefined; - try { - env.relayServer.validateInputTypes(req); - assert.fail(); - } catch (e) { - assert.include( - e.message, - 'Expected argument to be of type `string` but received type `undefined`' - ); - } - }); - }); - - describe('#validateInput()', function () { - it('should fail to relay with wrong relay worker', async function () { - const req = await env.createRelayHttpRequest(); - req.relayRequest.relayData.feesReceiver = accounts[1]; - try { - env.relayServer.validateInput(req); - assert.fail(); - } catch (e) { - assert.include( - e.message, - `Wrong fees receiver address: ${accounts[1]}` - ); - } - }); - - it('should fail to relay with unacceptable gasPrice', async function () { - const wrongGasPrice = isRsk(await getTestingEnvironment()) - ? '0.5' - : '100'; - const req = await env.createRelayHttpRequest(); - req.relayRequest.relayData.gasPrice = wrongGasPrice; - try { - env.relayServer.validateInput(req); - assert.fail(); - } catch (e) { - assert.include( - e.message, - `Unacceptable gasPrice: relayServer's gasPrice:${env.relayServer.gasPrice} request's gasPrice: ${wrongGasPrice}` - ); - } - }); - - it('should fail to relay with wrong hub address', async function () { - const wrongHubAddress = '0xdeadface'; - const req = await env.createRelayHttpRequest(); - req.metadata.relayHubAddress = wrongHubAddress; - try { - env.relayServer.validateInput(req); - assert.fail(); - } catch (e) { - assert.include( - e.message, - `Wrong hub address.\nRelay server's hub address: ${env.relayServer.config.relayHubAddress}, request's hub address: ${wrongHubAddress}\n` - ); - } - }); - }); - - describe('#validateVerifier()', function () { - describe('with trusted forwarder', function () { - before(async function () { - await env.relayServer._initTrustedVerifiers([ - env.relayVerifier.address, - env.deployVerifier.address - ]); - }); - - after(async function () { - await env.relayServer._initTrustedVerifiers([]); - }); - - it('#isTrustedVerifier', function () { - assert.isFalse( - env.relayServer.isTrustedVerifier(accounts[1]), - 'identify untrusted verifier' - ); - assert.isTrue( - env.relayServer.isTrustedVerifier( - env.relayVerifier.address - ), - 'identify trusted verifier' - ); - assert.isTrue( - env.relayServer.isTrustedVerifier( - env.deployVerifier.address - ), - 'identify trusted verifier' - ); - }); - - it('#verifierHandler', async function () { - const relayVerifier = - env.relayVerifier.address.toLowerCase(); - const deployVerifier = - env.deployVerifier.address.toLowerCase(); - const trustedVerifiers = ( - await env.relayServer.verifierHandler() - ).trustedVerifiers; - assert.isTrue(trustedVerifiers.includes(relayVerifier)); - assert.isTrue(trustedVerifiers.includes(deployVerifier)); - assert.equal(trustedVerifiers.length, 2); - }); - }); - - describe('#validateMaxNonce()', function () { - before(async function () { - // this is a new worker account - create transaction - const latestBlock = (await env.web3.eth.getBlock('latest')) - .number; - await env.relayServer._worker(latestBlock); - const signer = env.relayServer.workerAddress; - - console.log(`THE BALANCE OF THE WORKER ${signer} is`); - console.log(await web3.eth.getBalance(signer)); - await env.relayServer.transactionManager.sendTransaction({ - signer, - serverAction: ServerAction.VALUE_TRANSFER, - gasLimit: defaultEnvironment.mintxgascost, - destination: accounts[0], - creationBlockNumber: 0 - }); - }); - - it('should not throw with relayMaxNonce above current nonce', async function () { - await env.relayServer.validateMaxNonce(1000); - }); - - it('should throw exception with relayMaxNonce below current nonce', async function () { - try { - await env.relayServer.validateMaxNonce(0); - assert.fail(); - } catch (e) { - assert.include( - e.message, - 'Unacceptable relayMaxNonce:' - ); - } - }); - }); - }); - - describe('#validateVerifierGasLimits()', function () { - it('should fail to relay with invalid verifier', async function () { - const req = await env.createRelayHttpRequest(); - req.relayRequest.relayData.callVerifier = accounts[1]; - try { - env.relayServer.validateVerifier(req); - assert.fail(); - } catch (e) { - assert.include( - e.message, - `Invalid verifier: ${accounts[1]}` - ); - } - }); - - describe('relay max exposure to verifier rejections', function () { - let rejectingVerifier: TestVerifierConfigurableMisbehaviorInstance; - let req: RelayTransactionRequest; - - before(async function () { - rejectingVerifier = - await TestVerifierConfigurableMisbehavior.new(); - req = await env.createRelayHttpRequest(); - req.relayRequest.relayData.callVerifier = - rejectingVerifier.address; - }); - - it('should accept a transaction from trusted verifier returning above configured max exposure', async function () { - const req = await env.createRelayHttpRequest(); - try { - await env.relayServer._initTrustedVerifiers([ - rejectingVerifier.address - ]); - env.relayServer.validateVerifier(req); - } finally { - await env.relayServer._initTrustedVerifiers([]); - } - }); - }); - }); - - describe('#findMaxPossibleGasWithViewCall()', function () { - // RelayHub contract - it('should fail to relay rejected transaction', async function () { - const req = await env.createRelayHttpRequest(); - - req.metadata.signature = INCORRECT_ECDSA_SIGNATURE; - const method = env.relayHub.contract.methods.relayCall( - req.relayRequest, - req.metadata.signature - ); - - try { - await env.relayServer.findMaxPossibleGasWithViewCall( - method, - req, - '2000000' - ); - assert.fail(); - } catch (e) { - if (revertReasonSupported) { - const message = e.message ?? e; - assert.include(message, 'Signature mismatch'); - } else { - assert.include( - e.message, - 'relayCall (local call) reverted in server: Returned error: VM execution error: transaction reverted' - ); - } - } - }); - - it('should estimate the transaction max gas properly for subsidized transactions', async function () { - let estimatedGas = await env.contractInteractor.estimateGas({ - from: env.forwarder.address, - to: env.recipient.address, - gasPrice: toHex(60000000), - data: env.encodedFunction - }); - - estimatedGas = - estimatedGas > - constants.INTERNAL_TRANSACTION_ESTIMATE_CORRECTION - ? estimatedGas - - constants.INTERNAL_TRANSACTION_ESTIMATE_CORRECTION - : estimatedGas; - - const req = await env.createRelayHttpRequest({ - gas: toHex(estimatedGas) - }); - - assert.equal( - (await env.relayServer.txStoreManager.getAll()).length, - 0 - ); - - const result = - await env.relayServer.validateRequestWithVerifier(req); - const txDetails: SignedTransactionDetails = - await env.relayServer.createRelayTransaction(req); - - const pendingTransactions = - await env.relayServer.txStoreManager.getAll(); - assert.equal(pendingTransactions.length, 1); - assert.equal( - pendingTransactions[0].serverAction, - ServerAction.RELAY_CALL - ); - - const receipt = await web3.eth.getTransactionReceipt( - txDetails.transactionHash - ); - - console.log( - 'Estimated gas is:', - result.maxPossibleGas.toNumber() - ); - console.log('Actual gas used is: ', receipt.cumulativeGasUsed); - assert.equal( - receipt.cumulativeGasUsed, - result.maxPossibleGas.toNumber() - ); - - const topic: string = - web3.utils.sha3( - 'SampleRecipientEmitted(string,address,address,uint256,uint256)' - ) ?? ''; - assert( - receipt.logs.find((log) => log.topics.includes(topic)), - 'SampleRecipientEmitted event not found' - ); - }); - - it('should estimate the transaction max gas properly', async function () { - let estimatedGas = await env.contractInteractor.estimateGas({ - from: env.forwarder.address, - to: env.recipient.address, - gasPrice: toHex(60000000), - data: env.encodedFunction - }); - - estimatedGas = - estimatedGas > - constants.INTERNAL_TRANSACTION_ESTIMATE_CORRECTION - ? estimatedGas - - constants.INTERNAL_TRANSACTION_ESTIMATE_CORRECTION - : estimatedGas; - - const encodedFunction = - env.contractInteractor.web3.eth.abi.encodeFunctionCall( - { - name: 'transfer', - type: 'function', - inputs: [ - { - type: 'address', - name: 'recipient' - }, - { - type: 'uint256', - name: 'amount' - } - ] - }, - [env.relayServer.workerAddress, '1'] - ); - - let tokenGasCost = await env.contractInteractor.estimateGas({ - from: env.forwarder.address, // token holder is the smart wallet - to: token.address, - gasPrice: toHex(60000000), - data: encodedFunction - }); - - tokenGasCost = - tokenGasCost > - constants.INTERNAL_TRANSACTION_ESTIMATE_CORRECTION - ? tokenGasCost - - constants.INTERNAL_TRANSACTION_ESTIMATE_CORRECTION - : tokenGasCost; - - const req = await env.createRelayHttpRequest({ - tokenContract: token.address, - tokenAmount: '1', - gas: toHex(estimatedGas), - tokenGas: toHex(tokenGasCost) - }); - - assert.equal( - (await env.relayServer.txStoreManager.getAll()).length, - 0 - ); - - const result = - await env.relayServer.validateRequestWithVerifier(req); - const txDetails: SignedTransactionDetails = - await env.relayServer.createRelayTransaction(req); - - const pendingTransactions = - await env.relayServer.txStoreManager.getAll(); - assert.equal(pendingTransactions.length, 1); - assert.equal( - pendingTransactions[0].serverAction, - ServerAction.RELAY_CALL - ); - - const receipt = await web3.eth.getTransactionReceipt( - txDetails.transactionHash - ); - - // console.log("Estimated gas is:", result.maxPossibleGas.toNumber()) - // console.log("Actual gas used is: ", receipt.cumulativeGasUsed) - assert.equal( - receipt.cumulativeGasUsed, - result.maxPossibleGas.toNumber() - ); - - const topic: string = - web3.utils.sha3( - 'SampleRecipientEmitted(string,address,address,uint256,uint256)' - ) ?? ''; - assert( - receipt.logs.find((log) => log.topics.includes(topic)), - 'SampleRecipientEmitted event not found' - ); - }); - }); - }); - - describe('#createRelayTransaction()', function () { - before(async function () { - await env.relayServer.txStoreManager.clearAll(); - }); - - it('should relay transaction', async function () { - const req = await env.createRelayHttpRequest(); - assert.equal( - (await env.relayServer.txStoreManager.getAll()).length, - 0 - ); - await env.relayServer.createRelayTransaction(req); - const pendingTransactions = - await env.relayServer.txStoreManager.getAll(); - assert.equal(pendingTransactions.length, 1); - assert.equal( - pendingTransactions[0].serverAction, - ServerAction.RELAY_CALL - ); - // TODO: add asserts here!!! - }); - }); - - describe('relay workers/manager rebalancing', function () { - let relayServer: RelayServer; - const workerIndex = 0; - const gasPrice = (1e9).toString(); - let beforeDescribeId: string; - const txCost = toBN( - defaultEnvironment.mintxgascost * parseInt(gasPrice) - ); - - // TODO: not needed, worker is not funded at this point! - before('deplete worker balance', async function () { - relayServer = env.relayServer; - beforeDescribeId = (await snapshot()).result; - await relayServer.transactionManager.sendTransaction({ - signer: relayServer.workerAddress, - serverAction: ServerAction.VALUE_TRANSFER, - destination: accounts[0], - gasLimit: defaultEnvironment.mintxgascost, - gasPrice: gasPrice, - creationBlockNumber: 0, - value: toHex( - ( - await relayServer.getWorkerBalance(workerIndex) - ).sub(txCost) - ) - }); - const workerBalanceAfter = await relayServer.getWorkerBalance( - workerIndex - ); - assert.isTrue( - workerBalanceAfter.lt( - toBN(relayServer.config.workerMinBalance) - ), - 'worker balance should be lower than min balance' - ); - }); - - after(async function () { - await revert(beforeDescribeId); - }); - - beforeEach(async function () { - id = (await snapshot()).result; - await relayServer.transactionManager.txStoreManager.clearAll(); - }); - - afterEach(async function () { - await revert(id); - relayServer.transactionManager._initNonces(); - await relayServer.transactionManager.txStoreManager.clearAll(); - }); - - it('should not replenish when all balances are sufficient', async function () { - await env.web3.eth.sendTransaction({ - from: accounts[0], - to: relayServer.managerAddress, - value: relayServer.config.managerTargetBalance - }); - await env.web3.eth.sendTransaction({ - from: accounts[0], - to: relayServer.workerAddress, - value: relayServer.config.workerTargetBalance - }); - const currentBlockNumber = await env.web3.eth.getBlockNumber(); - const receipts = await relayServer.replenishServer(workerIndex, 0); - assert.deepEqual(receipts, []); - assert.equal( - currentBlockNumber, - await env.web3.eth.getBlockNumber() - ); - }); - - it('should use RBTC balance to fund workers', async function () { - await relayServer.transactionManager.sendTransaction({ - signer: relayServer.managerAddress, - serverAction: ServerAction.VALUE_TRANSFER, - creationBlockNumber: 0, - destination: accounts[0], - gasLimit: defaultEnvironment.mintxgascost, - gasPrice: gasPrice, - value: toHex( - (await relayServer.getManagerBalance()).sub(txCost) - ) - }); - assert.equal( - (await relayServer.getManagerBalance()).toString(), - '0' - ); - const workerBalanceBefore = await relayServer.getWorkerBalance( - workerIndex - ); - const refill = toBN( - relayServer.config.workerTargetBalance.toString() - ).sub(workerBalanceBefore); - - await env.web3.eth.sendTransaction({ - from: accounts[0], - to: relayServer.managerAddress, - value: toBN(relayServer.config.managerTargetBalance).add( - refill - ), - gasPrice: 1 - }); - const managerEthBalanceBefore = - await relayServer.getManagerBalance(); - assert.isTrue( - managerEthBalanceBefore.gt( - toBN(relayServer.config.managerTargetBalance.toString()) - ), - 'manager RBTC balance should be greater than target' - ); - const receipts = await relayServer.replenishServer(workerIndex, 0); - const totalTxCosts = await getTotalTxCosts( - receipts, - await env.web3.eth.getGasPrice() - ); - const workerBalanceAfter = await relayServer.getWorkerBalance( - workerIndex - ); - assert.isTrue( - workerBalanceAfter.eq(workerBalanceBefore.add(refill)), - `workerBalanceAfter (${workerBalanceAfter.toString()}) != workerBalanceBefore (${workerBalanceBefore.toString()}) + refill (${refill.toString()})` - ); - const managerEthBalanceAfter = - await relayServer.getManagerBalance(); - assert.isTrue( - managerEthBalanceAfter.eq( - managerEthBalanceBefore.sub(refill).sub(totalTxCosts) - ), - 'manager RBTC balance should increase by hub balance minus txs costs' - ); - }); - - it('should fund from manager RBTC balance when balance is too low', async function () { - await env.web3.eth.sendTransaction({ - from: accounts[0], - to: relayServer.managerAddress, - value: 1e18 - }); - const managerEthBalance = await relayServer.getManagerBalance(); - const workerBalanceBefore = await relayServer.getWorkerBalance( - workerIndex - ); - const refill = toBN(relayServer.config.workerTargetBalance).sub( - workerBalanceBefore - ); - assert.isTrue( - managerEthBalance.gte(refill), - 'manager RBTC balance should be sufficient to replenish worker' - ); - await relayServer.replenishServer(workerIndex, 0); - const workerBalanceAfter = await relayServer.getWorkerBalance( - workerIndex - ); - assert.isTrue( - workerBalanceAfter.eq(workerBalanceBefore.add(refill)), - `workerBalanceAfter (${workerBalanceAfter.toString()}) != workerBalanceBefore (${workerBalanceBefore.toString()}) + refill (${refill.toString()}` - ); - }); - - it("should emit 'funding needed' when both rbtc and hub balances are too low", async function () { - await relayServer.transactionManager.sendTransaction({ - signer: relayServer.managerAddress, - serverAction: ServerAction.VALUE_TRANSFER, - creationBlockNumber: 0, - destination: accounts[0], - gasLimit: defaultEnvironment.mintxgascost, - gasPrice: gasPrice.toString(), - value: toHex( - (await relayServer.getManagerBalance()).sub(txCost) - ) - }); - const managerEthBalance = await relayServer.getManagerBalance(); - const workerBalanceBefore = await relayServer.getWorkerBalance( - workerIndex - ); - const refill = toBN(relayServer.config.workerTargetBalance).sub( - workerBalanceBefore - ); - assert.isTrue( - managerEthBalance.lt(refill), - 'manager RBTC balance should be insufficient to replenish worker' - ); - let fundingNeededEmitted = false; - relayServer.on('fundingNeeded', () => { - fundingNeededEmitted = true; - }); - await relayServer.replenishServer(workerIndex, 0); - assert.isTrue(fundingNeededEmitted, 'fundingNeeded not emitted'); - }); - }); - - describe('server keepalive re-registration', function () { - const registrationBlockRate = 100; - const refreshStateTimeoutBlocks = 1; - let relayServer: RelayServer; - - before(async function () { - await env.newServerInstance({ - registrationBlockRate, - refreshStateTimeoutBlocks, - workerTargetBalance: 0.6e18 - }); - relayServer = env.relayServer; - sinon.spy(relayServer.registrationManager, 'handlePastEvents'); - }); - - it('should re-register server only if registrationBlockRate passed from any tx', async function () { - let latestBlock = await env.web3.eth.getBlock('latest'); - let receipts = await relayServer._worker(latestBlock.number); - const receipts2 = await relayServer._worker(latestBlock.number + 1); - expect( - relayServer.registrationManager.handlePastEvents - ).to.have.been.calledWith( - sinon.match.any, - sinon.match.any, - sinon.match.any, - false - ); - assert.equal( - receipts.length, - 0, - 'should not re-register if already registered' - ); - assert.equal( - receipts2.length, - 0, - 'should not re-register if already registered' - ); - await evmMineMany(registrationBlockRate); - latestBlock = await env.web3.eth.getBlock('latest'); - receipts = await relayServer._worker(latestBlock.number); - expect( - relayServer.registrationManager.handlePastEvents - ).to.have.been.calledWith( - sinon.match.any, - sinon.match.any, - sinon.match.any, - true - ); - await assertRelayAdded(receipts, relayServer, false); - }); - }); - - describe('Function testing', function () { - let relayServer: RelayServer; - - before(function () { - relayServer = env.relayServer; - }); - it('_workerSemaphore', async function () { - assert.isFalse( - relayServer._workerSemaphoreOn, - '_workerSemaphoreOn should be false first' - ); - const workerOrig = relayServer._worker; - let shouldRun = true; - try { - relayServer._worker = async function (): Promise< - PrefixedHexString[] - > { - // eslint-disable-next-line no-unmodified-loop-condition - while (shouldRun) { - await sleep(200); - } - return []; - }; - const latestBlock = await env.web3.eth.getBlock('latest'); - // eslint-disable-next-line - relayServer._workerSemaphore(latestBlock.number); - assert.isTrue( - relayServer._workerSemaphoreOn, - '_workerSemaphoreOn should be true after' - ); - shouldRun = false; - await sleep(200); - assert.isFalse( - relayServer._workerSemaphoreOn, - '_workerSemaphoreOn should be false after' - ); - } finally { - relayServer._worker = workerOrig; - } - }); - }); - - describe('alerted state as griefing mitigation', function () { - const alertedBlockDelay = 100; - const refreshStateTimeoutBlocks = 1; - let rejectingVerifier: TestVerifierConfigurableMisbehaviorInstance; - let rejectingDeployVerifier: TestDeployVerifierConfigurableMisbehaviorInstance; - let newServer: RelayServer; - const TestRecipient = artifacts.require('TestRecipient'); - let recipient: TestRecipientInstance; - - beforeEach( - 'should enter an alerted state for a configured blocks delay after verifier rejecting an on-chain tx', - async function () { - id = (await snapshot()).result; - rejectingVerifier = - await TestVerifierConfigurableMisbehavior.new(); - rejectingDeployVerifier = - await TestDeployVerifierConfigurableMisbehavior.new(); - recipient = await TestRecipient.new(); - - await env.newServerInstance({ - alertedBlockDelay, - refreshStateTimeoutBlocks, - relayVerifierAddress: rejectingVerifier.address, - deployVerifierAddress: rejectingDeployVerifier.address, - workerTargetBalance: 0.6e18 - }); - newServer = env.relayServer; - await attackTheServer(newServer); - } - ); - afterEach(async function () { - await revert(id); - newServer.transactionManager._initNonces(); - }); - - async function attackTheServer(server: RelayServer): Promise { - const _sendTransactionOrig = - server.transactionManager.sendTransaction; - - server.transactionManager.sendTransaction = async function ({ - signer, - method, - destination, - value = '0x', - gasLimit, - gasPrice, - creationBlockNumber, - serverAction - }: SendTransactionDetails): Promise { - await recipient.setNextRevert(); - return await _sendTransactionOrig.call( - server.transactionManager, - { - signer, - method, - destination, - value, - gasLimit, - gasPrice, - creationBlockNumber, - serverAction - } - ); - }; - - const req = await env.createRelayHttpRequest({ - callVerifier: rejectingVerifier.address, - to: recipient.address, - data: recipient.contract.methods.testNextRevert().encodeABI() - }); - - await env.relayServer.createRelayTransaction(req); - const currentBlock = await env.web3.eth.getBlock('latest'); - - await server._worker(currentBlock.number); - assert.isTrue(server.alerted, 'server not alerted'); - assert.equal( - server.alertedBlock, - currentBlock.number, - 'server alerted block incorrect' - ); - } - - it('should delay transactions in alerted state', async function () { - newServer.config.minAlertedDelayMS = 300; - newServer.config.maxAlertedDelayMS = 350; - const timeBefore = Date.now(); - const req = await env.createRelayHttpRequest(); - await env.relayServer.createRelayTransaction(req); - // await relayTransaction(relayTransactionParams, options) - const timeAfter = Date.now(); - assert.isTrue( - timeAfter - timeBefore > 300, - 'checking that enough time passed' - ); - }); - - it('should exit alerted state after the configured blocks delay', async function () { - await evmMineMany(newServer.config.alertedBlockDelay - 1); - let latestBlock = await env.web3.eth.getBlock('latest'); - await newServer._worker(latestBlock.number); - assert.isTrue(newServer.alerted, 'server not alerted'); - await evmMineMany(2); - latestBlock = await env.web3.eth.getBlock('latest'); - await newServer._worker(latestBlock.number); - assert.isFalse(newServer.alerted, 'server alerted'); - }); - }); - - describe('Custom replenish function', function () { - let relayServer: RelayServer; - const workerIndex = 0; - - before(async function () { - await env.newServerInstanceNoInit({ - customReplenish: true - }); - relayServer = env.relayServer; - }); - // This test should be skipped in the case a custom replenish is implemented - it('should throw an errror if there is no custom replenish function', async function () { - try { - await relayServer.replenishServer(workerIndex, 0); - } catch (error) { - assert.equal( - error.message, - 'No custom replenish function found, to remove this error please add the custom replenish implementation here deleting this line.' - ); - } - }); - }); - - describe('acceptTokens', function () { - const testToken1 = String('0xAbCeBBc80e1a11bD4e2F692A75dFF73753aABF5f'); - const testToken2 = String('0x85d55E6228C9a6bA73567926f0A0EB3e5f191803'); - - afterEach(async function () { - // reset verifiers for each test - await env.relayServer.trustedVerifiers.clear(); - }); - - it('should return empty if there are no trusted verifiers', async function () { - const res = await env.relayServer.tokenHandler(); - assert.isEmpty(res); - }); - - it('should return error if verifier is not trusted', async function () { - // trust relay verifier, but query deploy verifier - env.relayServer.trustedVerifiers.add( - env.relayVerifier.address.toLowerCase() - ); - try { - await env.relayServer.tokenHandler(env.deployVerifier.address); - assert.fail(); // previous line should throw exception - } catch (error) { - assert.equal(error.message, 'supplied verifier is not trusted'); - } - }); - - it('should return no tokens for verifiers when none were allowed', async function () { - env.relayServer.trustedVerifiers.add( - env.relayVerifier.address.toLowerCase() - ); - - const exp: TokenResponse = {}; - exp[env.relayVerifier.address] = []; - const res = await env.relayServer.tokenHandler( - env.relayVerifier.address - ); - - assert.deepEqual(res, exp); - }); - - it('should return allowed tokens for one trusted verifier', async function () { - env.relayServer.trustedVerifiers.add( - env.deployVerifier.address.toLowerCase() - ); - - // add token 1 to deploy verifier - await env.deployVerifier.acceptToken(testToken1); - - const exp: TokenResponse = {}; - exp[env.deployVerifier.address] = [testToken1]; - let res = await env.relayServer.tokenHandler( - env.deployVerifier.address - ); - - assert.deepEqual(res, exp); - - // add token 2 to deploy verifier - await env.deployVerifier.acceptToken(testToken2); - - exp[env.deployVerifier.address].push(testToken2); - res = await env.relayServer.tokenHandler( - env.deployVerifier.address - ); - - assert.deepEqual(res, exp); - }); - - it('should return allowed tokens for all trusted verifiers', async function () { - env.relayServer.trustedVerifiers.add( - env.relayVerifier.address.toLowerCase() - ); - env.relayServer.trustedVerifiers.add( - env.deployVerifier.address.toLowerCase() - ); - - const exp: TokenResponse = {}; - exp[env.deployVerifier.address] = [testToken1, testToken2]; - exp[env.relayVerifier.address] = []; - let res = await env.relayServer.tokenHandler(); - - assert.deepEqual(res, exp); - - // add token 1 to relay verifier - await env.relayVerifier.acceptToken(testToken1); - - exp[env.relayVerifier.address] = [testToken1]; - res = await env.relayServer.tokenHandler(); - assert.deepEqual(res, exp); - - // add token 2 to relay verifier - await env.relayVerifier.acceptToken(testToken2); - - exp[env.relayVerifier.address].push(testToken2); - res = await env.relayServer.tokenHandler(); - assert.deepEqual(res, exp); - }); - }); -}); diff --git a/test-to-migrate/relayserver/RelayServerRequestsProfiling.test.ts b/test-to-migrate/relayserver/RelayServerRequestsProfiling.test.ts deleted file mode 100644 index 4828c8da..00000000 --- a/test-to-migrate/relayserver/RelayServerRequestsProfiling.test.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { RelayServer, ServerConfigParams } from '@rsksmart/rif-relay-server'; -import { evmMine, evmMineMany, getTestingEnvironment } from '../TestUtils'; -import { configure } from '@rsksmart/rif-relay-client'; -import { HttpProvider } from 'web3-core'; -import { ServerTestEnvironment } from './ServerTestEnvironment'; -import { - ProfilingProvider, - EnvelopingConfig, - ContractInteractor -} from '@rsksmart/rif-relay-common'; - -contract('RelayServerRequestsProfiling', function (accounts) { - const refreshStateTimeoutBlocks = 2; - const callsPerStateRefresh = 11; - const callsPerBlock = 0; - const callsPerTransaction = 17; - - let provider: ProfilingProvider; - let relayServer: RelayServer; - let env: ServerTestEnvironment; - - before(async function () { - const serverConfig: Partial = { - refreshStateTimeoutBlocks, - workerMinBalance: 0.1e18, - workerTargetBalance: 0.3e18, - managerMinBalance: 0.1e18, - managerTargetBalance: 0.3e18, - minHubWithdrawalBalance: 0.1e18 - }; - - provider = new ProfilingProvider(web3.currentProvider as HttpProvider); - const contractFactory = async function ( - partialConfig: Partial - ): Promise { - const contractInteractor = new ContractInteractor( - provider, - configure(partialConfig) - ); - await contractInteractor.init(); - return contractInteractor; - }; - env = new ServerTestEnvironment( - web3.currentProvider as HttpProvider, - accounts - ); - await env.init( - { chainId: (await getTestingEnvironment()).chainId }, - {}, - contractFactory - ); - await env.newServerInstance(serverConfig); - relayServer = env.relayServer; - const latestBlock = await web3.eth.getBlock('latest'); - await relayServer._worker(latestBlock.number); - }); - - beforeEach(async function () { - provider.reset(); - }); - - it('should make X requests per block callback when state must be refreshed', async function () { - await evmMineMany(5); - const latestBlock = await web3.eth.getBlock('latest'); - assert.isTrue(relayServer._shouldRefreshState(latestBlock.number)); - const receipts = await relayServer._worker(latestBlock.number); - assert.equal(receipts.length, 0); - provider.log(); - assert.equal(provider.requestsCount, callsPerStateRefresh); - }); - - it('should make X requests per block callback when nothing needs to be done', async function () { - await evmMine(); - const latestBlock = await web3.eth.getBlock('latest'); - assert.isFalse(relayServer._shouldRefreshState(latestBlock.number)); - const receipts = await relayServer._worker(latestBlock.number); - assert.equal(receipts.length, 0); - provider.log(); - assert.equal(provider.requestsCount, callsPerBlock); - }); - - describe('relay transaction', function () { - before(async function () { - provider.reset(); - }); - - it('should make X requests per relay transaction request', async function () { - await env.relayTransaction(); - provider.log(); - assert.equal(provider.requestsCount, callsPerTransaction); - }); - }); -}); diff --git a/test-to-migrate/relayserver/ServerConfigParams.test.ts b/test-to-migrate/relayserver/ServerConfigParams.test.ts deleted file mode 100644 index 3e23d224..00000000 --- a/test-to-migrate/relayserver/ServerConfigParams.test.ts +++ /dev/null @@ -1,246 +0,0 @@ -import { - entriesToObj, - filterMembers, - filterType, - parseServerConfig, - resolveServerConfig -} from '@rsksmart/rif-relay-server'; -import * as fs from 'fs'; -import { expectRevert } from '@openzeppelin/test-helpers'; -import { VersionRegistryInstance } from '@rsksmart/rif-relay-contracts/types/truffle-contracts'; -import { string32 } from '@rsksmart/rif-relay-common'; - -//@ts-ignore -import sourceMapSupport from 'source-map-support'; -//@ts-ignore -sourceMapSupport.install({ errorFormatterForce: true }); - -const VersionRegistryContract = artifacts.require('VersionRegistry'); - -function expectThrow(func: () => void, match: string): void { - try { - func(); - } catch (e) { - assert.include(e.toString(), match); - return; - } - assert.fail('expected to fail with: ' + match); -} - -function addr(n: number): string { - return '0x'.padEnd(42, n.toString()); -} - -context('#ServerConfigParams', () => { - context('utils', () => { - it('#filterType', () => { - assert.deepEqual( - filterType({ a: 'number', b: 'string', c: 'number' }, 'number'), - ['a', 'c'] - ); - }); - it('#entriesToObj', () => { - const a = { x: 1, y: 2, z: { a: 11, b: 22 } }; - assert.deepEqual(a, entriesToObj(Object.entries(a))); - }); - - it('#filterMembers', () => { - const a = { x: 1, y: 2, z: 3 }; - const config = { x: 'number', y: 'string' }; - - assert.deepEqual(filterMembers(a, config), { x: 1, y: 2 }); - }); - }); - - context('#parseServerConfig', () => { - const tmpConfigfile = '/tmp/test.configfile.tmp'; - after(() => { - if (fs.existsSync(tmpConfigfile)) { - fs.unlinkSync(tmpConfigfile); - } - }); - it('should parse command line params', function () { - assert.deepEqual( - parseServerConfig( - [ - '--devMode=true', - '--customReplenish=true', - '--relayHubAddress=123' - ], - {} - ), - { devMode: true, customReplenish: true, relayHubAddress: '123' } - ); - }); - - it('cmdline should override env, which should override file', async () => { - fs.writeFileSync( - tmpConfigfile, - JSON.stringify({ url: 'fileparam' }) - ); - const env = { url: 'envparam' }; - // just file - assert.deepInclude( - parseServerConfig(['--config', tmpConfigfile], {}), - { - url: 'fileparam' - } - ); - // file+env - assert.deepInclude( - parseServerConfig(['--config', tmpConfigfile], env), - { - url: 'envparam' - } - ); - // file+env+cmdline - assert.deepInclude( - parseServerConfig( - ['--config', tmpConfigfile, '--url', 'cmdparam'], - env - ), - { url: 'cmdparam' } - ); - }); - - it('should use env as defaults', function () { - assert.deepEqual( - parseServerConfig(['--devMode=true', '--relayHubAddress=123'], { - relayHubAddress: 'hubFromEnv', - url: 'urlFromEnv' - }), - { devMode: true, relayHubAddress: '123', url: 'urlFromEnv' } - ); - }); - - it('should throw on unknown cmdline param', function () { - expectThrow( - () => parseServerConfig(['--asdasd'], {}), - 'unexpected param asdasd' - ); - }); - - it('should throw on missing config file', function () { - expectThrow( - () => parseServerConfig(['--config=nosuchfile'], {}), - 'unable to read config file' - ); - }); - - it('should abort on invalid config file', function () { - fs.writeFileSync(tmpConfigfile, 'asdasd'); - expectThrow( - () => parseServerConfig(['--config', tmpConfigfile], {}), - 'SyntaxError' - ); - }); - - it('should abort on unknown param in config file', function () { - fs.writeFileSync(tmpConfigfile, '{"asd":123}'); - expectThrow( - () => parseServerConfig(['--config', tmpConfigfile], {}), - 'unexpected param asd' - ); - }); - - it('should read param from file if no commandline or env', function () { - fs.writeFileSync(tmpConfigfile, '{"port":345}'); - assert.deepEqual( - parseServerConfig( - ['--config', tmpConfigfile, '--port', '111'], - {} - ), - { config: tmpConfigfile, port: 111 } - ); - }); - }); - context('#resolveServerConfig', () => { - const provider = web3.currentProvider; - it('should fail on missing hub/oracle', async () => { - await expectRevert( - resolveServerConfig({}, provider), - 'missing param: must have either relayHubAddress or versionRegistryAddress' - ); - }); - - it('should fail on invalid relayhub address', async () => { - const config = { relayHubAddress: '123' }; - await expectRevert( - resolveServerConfig(config, provider), - 'invalid param: "relayHubAddress" is not a valid address: 123' - ); - }); - - it('should fail on no-contract relayhub address', async () => { - const config = { relayHubAddress: addr(1) }; - await expectRevert( - resolveServerConfig(config, provider), - 'RelayHub: no contract at address 0x1111111111111111111111111111111111111111' - ); - }); - - it('should fail on missing hubid for VersionRegistry', async () => { - const config = { versionRegistryAddress: addr(1) }; - await expectRevert( - resolveServerConfig(config, provider), - 'missing param: relayHubId to read from VersionRegistry' - ); - }); - - it('should fail on no-contract VersionRegistry address', async () => { - const config = { - versionRegistryAddress: addr(1), - relayHubId: 'hubid' - }; - await expectRevert( - resolveServerConfig(config, provider), - 'Invalid param versionRegistryAddress: no contract at address 0x1111111111111111111111111111111111111111' - ); - }); - - contract('with VersionRegistry', () => { - let oracle: VersionRegistryInstance; - - before(async () => { - oracle = await VersionRegistryContract.new(); - await oracle.addVersion( - string32('hub-invalidaddr'), - string32('1.0'), - 'garbagevalue' - ); - await oracle.addVersion( - string32('hub-nocontract'), - string32('1.0'), - addr(2) - ); - await oracle.addVersion( - string32('hub-wrongcontract'), - string32('1.0'), - oracle.address - ); - }); - - it('should fail on invalid hub address in oracle', async () => { - const config = { - versionRegistryAddress: oracle.address, - relayHubId: 'hub-invalidaddr' - }; - await expectRevert( - resolveServerConfig(config, provider), - 'Invalid param relayHubId hub-invalidaddr @ 1.0: not an address: garbagevalue' - ); - }); - - it('should fail on no contract at hub address in oracle', async () => { - const config = { - versionRegistryAddress: oracle.address, - relayHubId: 'hub-nocontract' - }; - await expectRevert( - resolveServerConfig(config, provider), - 'RelayHub: no contract at address 0x2222222222222222222222222222222222222222' - ); - }); - }); - }); -}); diff --git a/test-to-migrate/relayserver/ServerTestEnvironment.ts b/test-to-migrate/relayserver/ServerTestEnvironment.ts deleted file mode 100644 index 663e03e7..00000000 --- a/test-to-migrate/relayserver/ServerTestEnvironment.ts +++ /dev/null @@ -1,402 +0,0 @@ -// @ts-ignore -import abiDecoder from 'abi-decoder'; -import Web3 from 'web3'; -import crypto from 'crypto'; -import { HttpProvider } from 'web3-core'; -import { keccak256, toHex } from 'web3-utils'; -import * as ethUtils from 'ethereumjs-util'; -import * as fs from 'fs'; -import { - IDeployVerifier, - IRelayHub, - IRelayVerifier, - RelayManagerData -} from '@rsksmart/rif-relay-contracts'; -import { - IForwarderInstance, - IRelayHubInstance, - SmartWalletInstance, - TestDeployVerifierEverythingAcceptedInstance, - TestRecipientInstance, - TestVerifierEverythingAcceptedInstance -} from '@rsksmart/rif-relay-contracts/types/truffle-contracts'; -import { - assertRelayAdded, - getTemporaryWorkdirs, - ServerWorkdirs -} from './ServerTestUtils'; -import { - KeyManager, - RelayServer, - ServerConfigParams, - TxStoreManager -} from '@rsksmart/rif-relay-server'; -import { PrefixedHexString } from 'ethereumjs-tx'; -import { configure, RelayClient, RelayInfo } from '@rsksmart/rif-relay-client'; -import { - createSmartWallet, - createSmartWalletFactory, - deployHub, - getGaslessAccount, - getTestingEnvironment -} from '../TestUtils'; -import { - constants, - ContractInteractor, - EnvelopingConfig, - EnvelopingTransactionDetails, - PingResponse, - RelayTransactionRequest -} from '@rsksmart/rif-relay-common'; -import { RelayHubConfiguration } from '@rsksmart/rif-relay-contracts'; -import { ether } from '@openzeppelin/test-helpers'; -import { RIF_RELAY_URL } from '../Utils'; -const TestRecipient = artifacts.require('TestRecipient'); - -const TestVerifierEverythingAccepted = artifacts.require( - 'TestVerifierEverythingAccepted' -); -const TestDeployVerifierEverythingAccepted = artifacts.require( - 'TestDeployVerifierEverythingAccepted' -); -const SmartWallet = artifacts.require('SmartWallet'); - -abiDecoder.addABI(IRelayHub.abi); -abiDecoder.addABI(IRelayVerifier.abi); -abiDecoder.addABI(IDeployVerifier.abi); - -// @ts-ignore -abiDecoder.addABI(TestRecipient.abi); -// @ts-ignore -abiDecoder.addABI(TestVerifierEverythingAccepted.abi); -// @ts-ignore -abiDecoder.addABI(TestDeployVerifierEverythingAccepted.abi); - -export const LocalhostOne = RIF_RELAY_URL; - -export interface PrepareRelayRequestOption { - to: string; - from: string; - verifier: string; // TODO Change to relay and deploy verifiers -} - -export class ServerTestEnvironment { - relayHub!: IRelayHubInstance; - forwarder!: IForwarderInstance; - relayVerifier!: TestVerifierEverythingAcceptedInstance; - deployVerifier!: TestDeployVerifierEverythingAcceptedInstance; - - recipient!: TestRecipientInstance; - - relayOwner!: string; - gasLess!: string; - - encodedFunction!: PrefixedHexString; - - clientId!: string; - - options?: PrepareRelayRequestOption; - - /** - * Note: do not call methods of contract interactor inside Test Environment. It may affect Profiling Test. - */ - contractInteractor!: ContractInteractor; - - relayClient!: RelayClient; - provider: HttpProvider; - web3: Web3; - relayServer!: RelayServer; - - constructor(provider: HttpProvider, accounts: string[]) { - this.provider = provider; - this.web3 = new Web3(this.provider); - this.relayOwner = accounts[4]; - } - - /** - * @param clientConfig - * @param contractFactory - added for Profiling test, as it requires Test Environment to be using - * different provider from the contract interactor itself. - */ - async init( - clientConfig: Partial = {}, - relayHubConfig: Partial = {}, - contractFactory?: ( - clientConfig: Partial - ) => Promise - ): Promise { - this.relayHub = await deployHub(undefined, relayHubConfig); - this.recipient = await TestRecipient.new(); - this.relayVerifier = await TestVerifierEverythingAccepted.new(); - this.deployVerifier = await TestDeployVerifierEverythingAccepted.new(); - - this.encodedFunction = this.recipient.contract.methods - .emitMessage('hello world') - .encodeABI(); - - const gaslessAccount = await getGaslessAccount(); - this.gasLess = gaslessAccount.address; - - const sWalletTemplate = await SmartWallet.new(); - const factory = await createSmartWalletFactory(sWalletTemplate); - const chainId = - clientConfig.chainId ?? (await getTestingEnvironment()).chainId; - - const defaultAccount = - web3.defaultAccount ?? (await web3.eth.getAccounts())[0]; - const smartWallet: SmartWalletInstance = await createSmartWallet( - defaultAccount ?? constants.ZERO_ADDRESS, - this.gasLess, - factory, - gaslessAccount.privateKey, - chainId - ); - this.forwarder = smartWallet; - - const shared: Partial = { - logLevel: 5, - relayHubAddress: this.relayHub.address, - relayVerifierAddress: this.relayVerifier.address, - deployVerifierAddress: this.deployVerifier.address - }; - if (contractFactory == null) { - this.contractInteractor = new ContractInteractor( - this.provider, - configure(shared) - ); - await this.contractInteractor.init(); - } else { - this.contractInteractor = await contractFactory(shared); - } - const mergedConfig = Object.assign({}, shared, clientConfig); - this.relayClient = new RelayClient( - this.provider, - configure(mergedConfig) - ); - - // Regisgter gasless account to avoid signing with RSKJ - this.relayClient.accountManager.addAccount(gaslessAccount); - } - - async newServerInstance( - config: Partial = {}, - serverWorkdirs?: ServerWorkdirs - ): Promise { - await this.newServerInstanceNoInit(config, serverWorkdirs, undefined); - await this.relayServer.init(); - // initialize server - gas price, stake, owner, etc, whatever - const latestBlock = await this.web3.eth.getBlock('latest'); - const receipts = await this.relayServer._worker(latestBlock.number); - await assertRelayAdded(receipts, this.relayServer); // sanity check - await this.relayServer._worker(latestBlock.number + 1); - } - - _createKeyManager(workdir?: string): KeyManager { - if (workdir != null) { - return new KeyManager(1, workdir); - } else { - return new KeyManager(1, undefined, crypto.randomBytes(32)); - } - } - - async newServerInstanceNoInit( - config: Partial = {}, - serverWorkdirs?: ServerWorkdirs, - unstakeDelay = constants.weekInSec - ): Promise { - this.newServerInstanceNoFunding(config, serverWorkdirs); - await web3.eth.sendTransaction({ - to: this.relayServer.managerAddress, - from: this.relayOwner, - value: web3.utils.toWei('2', 'ether') - }); - - await this.relayHub.stakeForAddress( - this.relayServer.managerAddress, - unstakeDelay, - { - from: this.relayOwner, - value: ether('1') - } - ); - } - - newServerInstanceNoFunding( - config: Partial = {}, - serverWorkdirs?: ServerWorkdirs - ): void { - const managerKeyManager = this._createKeyManager( - serverWorkdirs?.managerWorkdir - ); - const workersKeyManager = this._createKeyManager( - serverWorkdirs?.workersWorkdir - ); - const workdir = - serverWorkdirs?.workdir ?? getTemporaryWorkdirs().workdir; - fs.mkdirSync(workdir, { recursive: true }); - fs.writeFileSync(workdir + '/txstore.db', ''); - const txStoreManager = new TxStoreManager({ workdir }); - const serverDependencies = { - contractInteractor: this.contractInteractor, - txStoreManager, - managerKeyManager, - workersKeyManager - }; - const shared: Partial = { - relayHubAddress: this.relayHub.address, - checkInterval: 10, - logLevel: 5, - relayVerifierAddress: this.relayVerifier.address, - deployVerifierAddress: this.deployVerifier.address - }; - const mergedConfig: Partial = Object.assign( - {}, - shared, - config - ); - - this.relayServer = new RelayServer(mergedConfig, serverDependencies); - - this.relayServer.on('error', (e) => { - console.log('newServer event', e.message); - }); - this.relayServer.config.trustedVerifiers.push( - this.relayVerifier.address - ); - this.relayServer.config.trustedVerifiers.push( - this.deployVerifier.address - ); - } - - async createRelayHttpRequest( - overrideDetails: Partial = {} - ): Promise { - const pingResponse = { - relayHubAddress: this.relayHub.address, - relayWorkerAddress: this.relayServer.workerAddress, - feesReceiver: this.relayServer.workerAddress - }; - const managerData: RelayManagerData = { - manager: '', - url: '', - currentlyStaked: false, - registered: false - }; - const relayInfo: RelayInfo = { - pingResponse: pingResponse as PingResponse, - relayInfo: managerData - }; - - let transactionDetails: EnvelopingTransactionDetails = { - from: this.gasLess, - to: this.recipient.address, - data: this.encodedFunction, - relayHub: this.relayHub.address, - callVerifier: this.relayVerifier.address, - callForwarder: this.forwarder.address, - gasPrice: toHex(60000000), - tokenAmount: toHex(0), - tokenGas: toHex(0), - tokenContract: constants.ZERO_ADDRESS, - isSmartWalletDeploy: false - }; - - transactionDetails = Object.assign( - {}, - transactionDetails, - overrideDetails - ); - - const destinationGas = - await this.contractInteractor.estimateDestinationContractCallGas( - this.relayClient.getEstimateGasParams(transactionDetails) - ); - transactionDetails.gas = toHex(destinationGas); - - return await this.relayClient._prepareRelayHttpRequest( - relayInfo, - transactionDetails - ); - } - - async relayTransaction( - assertRelayed = true, - overrideDetails: Partial = {} - ): Promise<{ - signedTx: PrefixedHexString; - txHash: PrefixedHexString; - reqSigHash: PrefixedHexString; - }> { - const req = await this.createRelayHttpRequest(overrideDetails); - const txDetails = await this.relayServer.createRelayTransaction(req); - const reqSigHash = ethUtils.bufferToHex( - ethUtils.keccak256(req.metadata.signature) - ); - if (assertRelayed) { - await this.assertTransactionRelayed( - txDetails.transactionHash, - keccak256(req.metadata.signature) - ); - } - return { - txHash: txDetails.transactionHash, - signedTx: txDetails.signedTx, - reqSigHash - }; - } - - async clearServerStorage(): Promise { - await this.relayServer.transactionManager.txStoreManager.clearAll(); - assert.deepEqual( - [], - await this.relayServer.transactionManager.txStoreManager.getAll() - ); - } - - async assertTransactionRelayed( - txHash: string, - reqSignatureHash: string, - overrideDetails: Partial = {} - ): Promise { - console.debug('overrideDetails', overrideDetails); - - const receipt = await web3.eth.getTransactionReceipt(txHash); - if (receipt == null) { - throw new Error('Transaction Receipt not found'); - } - const decodedLogs = abiDecoder - .decodeLogs(receipt.logs) - .map(this.relayServer.registrationManager._parseEvent); - const event1 = decodedLogs.find( - (e: { name: string }) => e.name === 'SampleRecipientEmitted' - ); - assert.exists( - event1, - 'SampleRecipientEmitted not found, maybe transaction was not relayed successfully' - ); - assert.equal(event1.args.message, 'hello world'); - const event2 = decodedLogs.find( - (e: { name: string }) => e.name === 'TransactionRelayed' - ); - assert.exists( - event2, - 'TransactionRelayed not found, maybe transaction was not relayed successfully' - ); - assert.equal(event2.name, 'TransactionRelayed'); - /** - * event TransactionRelayed( - address indexed relayManager, - address relayWorker, - bytes32 relayRequestSigHash); - */ - assert.equal( - event2.args.relayWorker.toLowerCase(), - this.relayServer.workerAddress.toLowerCase() - ); - assert.equal( - event2.args.relayManager.toLowerCase(), - this.relayServer.managerAddress.toLowerCase() - ); - assert.equal(event2.args.relayRequestSigHash, reqSignatureHash); - } -} diff --git a/test-to-migrate/relayserver/ServerTestUtils.ts b/test-to-migrate/relayserver/ServerTestUtils.ts deleted file mode 100644 index b7653f17..00000000 --- a/test-to-migrate/relayserver/ServerTestUtils.ts +++ /dev/null @@ -1,112 +0,0 @@ -// @ts-ignore -import abiDecoder from 'abi-decoder'; -import { TransactionReceipt } from 'web3-core'; -import { toBN } from 'web3-utils'; -import { - IRelayVerifier, - IDeployVerifier, - IRelayHub -} from '@rsksmart/rif-relay-contracts'; -import { RelayServer } from '@rsksmart/rif-relay-server'; -import { PrefixedHexString } from 'ethereumjs-tx'; - -const TestRecipient = artifacts.require('TestRecipient'); -const TestVerifierEverythingAccepted = artifacts.require( - 'TestVerifierEverythingAccepted' -); -const TestDeployVerifierEverythingAccepted = artifacts.require( - 'TestDeployVerifierEverythingAccepted' -); - -abiDecoder.addABI(IRelayHub.abi); -abiDecoder.addABI(IRelayVerifier.abi); -abiDecoder.addABI(IDeployVerifier.abi); - -// @ts-ignore -abiDecoder.addABI(TestRecipient.abi); -// @ts-ignore -abiDecoder.addABI(TestVerifierEverythingAccepted.abi); -// @ts-ignore -abiDecoder.addABI(TestDeployVerifierEverythingAccepted.abi); - -async function resolveAllReceipts( - transactionHashes: PrefixedHexString[] -): Promise { - // actually returns promise for '.all' - // eslint-disable-next-line @typescript-eslint/promise-function-async - return await Promise.all( - transactionHashes.map((transactionHash) => - web3.eth.getTransactionReceipt(transactionHash) - ) - ); -} - -export async function assertRelayAdded( - transactionHashes: PrefixedHexString[], - server: RelayServer, - checkWorkers = true -): Promise { - const receipts = await resolveAllReceipts(transactionHashes); - const registeredReceipt = receipts.find((r) => { - const decodedLogs = abiDecoder - .decodeLogs(r.logs) - .map(server.registrationManager._parseEvent); - return decodedLogs[0].name === 'RelayServerRegistered'; - }); - if (registeredReceipt == null) { - throw new Error('Registered Receipt not found'); - } - const registeredLogs = abiDecoder - .decodeLogs(registeredReceipt.logs) - .map(server.registrationManager._parseEvent); - assert.equal(registeredLogs.length, 1); - assert.equal(registeredLogs[0].name, 'RelayServerRegistered'); - assert.equal( - registeredLogs[0].args.relayManager.toLowerCase(), - server.managerAddress.toLowerCase() - ); - assert.equal(registeredLogs[0].args.relayUrl, server.config.url); - - if (checkWorkers) { - const workersAddedReceipt = receipts.find((r) => { - const decodedLogs = abiDecoder - .decodeLogs(r.logs) - .map(server.registrationManager._parseEvent); - return decodedLogs[0].name === 'RelayWorkersAdded'; - }); - const workersAddedLogs = abiDecoder - .decodeLogs(workersAddedReceipt.logs) - .map(server.registrationManager._parseEvent); - assert.equal(workersAddedLogs.length, 1); - assert.equal(workersAddedLogs[0].name, 'RelayWorkersAdded'); - } -} - -export async function getTotalTxCosts( - transactionHashes: PrefixedHexString[], - gasPrice: string -): Promise { - const receipts = await resolveAllReceipts(transactionHashes); - return receipts - .map((r) => toBN(r.gasUsed).mul(toBN(gasPrice))) - .reduce((previous, current) => previous.add(current), toBN(0)); -} - -export interface ServerWorkdirs { - workdir: string; - managerWorkdir: string; - workersWorkdir: string; -} - -export function getTemporaryWorkdirs(): ServerWorkdirs { - const workdir = - '/tmp/enveloping/test/relayserver/defunct' + Date.now().toString(); - const managerWorkdir = workdir + '/manager'; - const workersWorkdir = workdir + '/workers'; - - return { - workdir, - managerWorkdir, - workersWorkdir - }; -} diff --git a/test-to-migrate/relayserver/TransactionManager.test.ts b/test-to-migrate/relayserver/TransactionManager.test.ts deleted file mode 100644 index 78ff0775..00000000 --- a/test-to-migrate/relayserver/TransactionManager.test.ts +++ /dev/null @@ -1,107 +0,0 @@ -import { PrefixedHexString, Transaction } from 'ethereumjs-tx'; -import * as ethUtils from 'ethereumjs-util'; - -import { evmMineMany, getTestingEnvironment } from '../TestUtils'; -import { RelayServer } from '@rsksmart/rif-relay-server'; -import { HttpProvider } from 'web3-core'; -import { ServerTestEnvironment } from './ServerTestEnvironment'; - -contract('TransactionManager', function (accounts) { - const pendingTransactionTimeoutBlocks = 5; - const confirmationsNeeded = 12; - let relayServer: RelayServer; - let env: ServerTestEnvironment; - - before(async function () { - const chainId = (await getTestingEnvironment()).chainId; - env = new ServerTestEnvironment( - web3.currentProvider as HttpProvider, - accounts - ); - await env.init({ chainId }); - await env.newServerInstance({ - pendingTransactionTimeoutBlocks, - workerTargetBalance: 0.6e18 - }); - relayServer = env.relayServer; - }); - - describe('nonce counter asynchronous access protection', function () { - let _pollNonceOrig: (signer: string) => Promise; - let signTransactionOrig: ( - signer: string, - tx: Transaction - ) => PrefixedHexString; - before(function () { - _pollNonceOrig = relayServer.transactionManager.pollNonce; - relayServer.transactionManager.pollNonce = async function (signer) { - return await this.contractInteractor.getTransactionCount( - signer, - 'pending' - ); - }; - }); - after(function () { - relayServer.transactionManager.pollNonce = _pollNonceOrig; - }); - - it('should not deadlock if server returned error while locked', async function () { - try { - signTransactionOrig = - relayServer.transactionManager.workersKeyManager - .signTransaction; - relayServer.transactionManager.workersKeyManager.signTransaction = - function () { - throw new Error('no tx for you'); - }; - try { - await env.relayTransaction(); - } catch (e) { - assert.include(e.message, 'no tx for you'); - assert.isFalse( - relayServer.transactionManager.nonceMutex.isLocked(), - 'nonce mutex not released after exception' - ); - } - } finally { - relayServer.transactionManager.workersKeyManager.signTransaction = - signTransactionOrig; - } - }); - }); - - describe('local storage maintenance', function () { - let parsedTxHash: PrefixedHexString; - let latestBlock: number; - - before(async function () { - await relayServer.transactionManager.txStoreManager.clearAll(); - relayServer.transactionManager._initNonces(); - const { signedTx } = await env.relayTransaction(); - parsedTxHash = ethUtils.bufferToHex( - new Transaction( - signedTx, - relayServer.transactionManager.rawTxOptions - ).hash() - ); - latestBlock = (await env.web3.eth.getBlock('latest')).number; - }); - - it('should remove confirmed transactions from the recent transactions storage', async function () { - await relayServer.transactionManager.removeConfirmedTransactions( - latestBlock - ); - let storedTransactions = - await relayServer.transactionManager.txStoreManager.getAll(); - assert.equal(storedTransactions[0].txId, parsedTxHash); - await evmMineMany(confirmationsNeeded); - const newLatestBlock = await env.web3.eth.getBlock('latest'); - await relayServer.transactionManager.removeConfirmedTransactions( - newLatestBlock.number - ); - storedTransactions = - await relayServer.transactionManager.txStoreManager.getAll(); - assert.deepEqual([], storedTransactions); - }); - }); -}); diff --git a/test-to-migrate/server-config.json b/test-to-migrate/server-config.json deleted file mode 100644 index 34083343..00000000 --- a/test-to-migrate/server-config.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "url": "http://localhost:8095", - "port": 8095, - "relayHubAddress": "", - "gasPriceFactor": 1, - "rskNodeUrl": "http://127.0.0.1:4444", - "workdir": "", - "devMode": "true" -}