From ca756ac2f42f1571903b9887ab2e94eb27eb4685 Mon Sep 17 00:00:00 2001 From: yann300 Date: Thu, 14 Jan 2021 10:57:52 +0100 Subject: [PATCH 01/18] refactor_send_txs --- apps/remix-ide/src/app/tabs/network-module.js | 12 - .../src/app/tabs/runTab/model/recorder.js | 4 +- apps/remix-ide/src/blockchain/blockchain.js | 69 ++-- apps/remix-ide/src/blockchain/providers/vm.js | 8 +- .../src/blockchain/txResultHelper.js | 46 --- jest.config.js | 15 +- .../src/execution/execution-context.ts | 36 +- libs/remix-lib/src/execution/txExecution.ts | 10 +- libs/remix-lib/src/execution/txFormat.ts | 2 +- libs/remix-lib/src/execution/txListener.ts | 37 +- libs/remix-lib/src/execution/txRunner.ts | 252 +----------- libs/remix-lib/src/execution/txRunnerVM.ts | 120 ++++++ libs/remix-lib/src/execution/txRunnerWeb3.ts | 156 +++++++ libs/remix-lib/src/helpers/txResultHelper.ts | 8 +- libs/remix-lib/src/index.ts | 18 +- libs/remix-lib/src/universalDapp.ts | 379 ------------------ .../src/web3Provider/web3VmProvider.ts | 6 + libs/remix-lib/test/txFormat.ts | 4 +- libs/remix-lib/test/txResultHelper.ts | 37 +- libs/remix-simulator/src/genesis.ts | 8 +- libs/remix-simulator/src/index.ts | 2 +- libs/remix-simulator/src/methods/accounts.ts | 16 +- libs/remix-simulator/src/methods/blocks.ts | 18 +- libs/remix-simulator/src/methods/debug.ts | 12 +- libs/remix-simulator/src/methods/filters.ts | 24 +- .../src/methods/transactions.ts | 69 +++- libs/remix-simulator/src/methods/txProcess.ts | 25 +- libs/remix-simulator/src/provider.ts | 57 ++- libs/remix-simulator/src/vm-context.ts | 147 +++++++ libs/remix-tests/jest.config.js | 4 +- libs/remix-tests/src/deployer.ts | 2 +- libs/remix-tests/tests/testRunner.cli.spec.ts | 8 +- libs/remix-tests/tsconfig.json | 2 +- libs/remix-tests/tsconfig.lib.json | 27 +- libs/remix-tests/tsconfig.spec.json | 29 +- 35 files changed, 770 insertions(+), 899 deletions(-) delete mode 100644 apps/remix-ide/src/blockchain/txResultHelper.js create mode 100644 libs/remix-lib/src/execution/txRunnerVM.ts create mode 100644 libs/remix-lib/src/execution/txRunnerWeb3.ts delete mode 100644 libs/remix-lib/src/universalDapp.ts create mode 100644 libs/remix-simulator/src/vm-context.ts diff --git a/apps/remix-ide/src/app/tabs/network-module.js b/apps/remix-ide/src/app/tabs/network-module.js index fdae172c15..c1cc1e12b5 100644 --- a/apps/remix-ide/src/app/tabs/network-module.js +++ b/apps/remix-ide/src/app/tabs/network-module.js @@ -22,18 +22,6 @@ export class NetworkModule extends Plugin { this.blockchain.event.register('contextChanged', (provider) => { this.emit('providerChanged', provider) }) - /* - // Events that could be implemented later - executionContext.event.register('removeProvider', (provider) => { - this.events.emit('networkRemoved', provider) - }) - executionContext.event.register('addProvider', (provider) => { - this.events.emit('networkAdded', provider) - }) - executionContext.event.register('web3EndpointChanged', (provider) => { - this.events.emit('web3EndpointChanged', provider) - }) - */ } /** Return the current network provider (web3, vm, injected) */ diff --git a/apps/remix-ide/src/app/tabs/runTab/model/recorder.js b/apps/remix-ide/src/app/tabs/runTab/model/recorder.js index 126b354927..f9a5d9aa78 100644 --- a/apps/remix-ide/src/app/tabs/runTab/model/recorder.js +++ b/apps/remix-ide/src/app/tabs/runTab/model/recorder.js @@ -63,10 +63,10 @@ class Recorder { } }) - this.blockchain.event.register('transactionExecuted', (error, from, to, data, call, txResult, timestamp, _payload, rawAddress) => { + this.blockchain.event.register('transactionExecuted', (error, from, to, data, call, txResult, timestamp, _payload) => { if (error) return console.log(error) if (call) return - + const rawAddress = txResult.receipt.contractAddress if (!rawAddress) return // not a contract creation const address = helper.addressToString(rawAddress) // save back created addresses for the convertion from tokens to real adresses diff --git a/apps/remix-ide/src/blockchain/blockchain.js b/apps/remix-ide/src/blockchain/blockchain.js index 84062f7ac4..09ca8a05a2 100644 --- a/apps/remix-ide/src/blockchain/blockchain.js +++ b/apps/remix-ide/src/blockchain/blockchain.js @@ -3,7 +3,8 @@ const txFormat = remixLib.execution.txFormat const txExecution = remixLib.execution.txExecution const typeConversion = remixLib.execution.typeConversion const Txlistener = remixLib.execution.txListener -const TxRunner = remixLib.execution.txRunner +const TxRunner = remixLib.execution.TxRunner +const TxRunnerWeb3 = remixLib.execution.TxRunnerWeb3 const txHelper = remixLib.execution.txHelper const EventManager = remixLib.EventManager const executionContext = remixLib.execution.executionContext @@ -12,7 +13,7 @@ const Web3 = require('web3') const async = require('async') const { EventEmitter } = require('events') -const { resultToRemixTx } = require('./txResultHelper') +const { resultToRemixTx } = remixLib.helpers.txResultHelper const VMProvider = require('./providers/vm.js') const InjectedProvider = require('./providers/injected.js') @@ -26,8 +27,7 @@ class Blockchain { this.events = new EventEmitter() this.config = config - - this.txRunner = new TxRunner({}, { + const web3Runner = new TxRunnerWeb3({ config: config, detectNetwork: (cb) => { this.executionContext.detectNetwork(cb) @@ -35,7 +35,9 @@ class Blockchain { personalMode: () => { return this.getProvider() === 'web3' ? this.config.get('settings/personal-mode') : false } - }, this.executionContext) + }, _ => this.executionContext.web3(), _ => this.executionContext.currentblockGasLimit()) + this.txRunner = new TxRunner(web3Runner, { runAsync: true }) + this.executionContext.event.register('contextChanged', this.resetEnvironment.bind(this)) this.networkcallid = 0 @@ -123,7 +125,7 @@ class Blockchain { if (error) { return finalCb(`creation of ${selectedContract.name} errored: ${(error.message ? error.message : error)}`) } - if (txResult.result.status && txResult.result.status === '0x0') { + if (txResult.receipt.status === false || txResult.receipt.status === '0x0') { return finalCb(`creation of ${selectedContract.name} errored: transaction execution failed`) } finalCb(null, selectedContract, address) @@ -309,18 +311,17 @@ class Blockchain { resetEnvironment () { this.getCurrentProvider().resetEnvironment() // TODO: most params here can be refactored away in txRunner - // this.txRunner = new TxRunner(this.providers.vm.accounts, { - this.txRunner = new TxRunner(this.providers.vm.RemixSimulatorProvider.Accounts.accounts, { - // TODO: only used to check value of doNotShowTransactionConfirmationAgain property + const web3Runner = new TxRunnerWeb3({ config: this.config, - // TODO: to refactor, TxRunner already has access to executionContext detectNetwork: (cb) => { this.executionContext.detectNetwork(cb) }, personalMode: () => { return this.getProvider() === 'web3' ? this.config.get('settings/personal-mode') : false } - }, this.executionContext) + }, _ => this.executionContext.web3(), _ => this.executionContext.currentblockGasLimit()) + + this.txRunner = new TxRunner(web3Runner, { runAsync: true }) this.txRunner.event.register('transactionBroadcasted', (txhash) => { this.executionContext.detectNetwork((error, network) => { if (error || !network) return @@ -372,10 +373,11 @@ class Blockchain { (network, tx, gasEstimation, continueTxExecution, cancelCb) => { continueTxExecution() }, (error, continueTxExecution, cancelCb) => { if (error) { reject(error) } else { continueTxExecution() } }, (okCb, cancelCb) => { okCb() }, - (error, result) => { + async (error, result) => { if (error) return reject(error) try { - resolve(resultToRemixTx(result)) + const execResult = await this.web3().eth.getExecutionResultFromSimulator(result.transactionHash) + resolve(resultToRemixTx(result, execResult)) } catch (e) { reject(e) } @@ -429,19 +431,24 @@ class Blockchain { function runTransaction (fromAddress, value, gasLimit, next) { const tx = { to: args.to, data: args.data.dataHex, useCall: args.useCall, from: fromAddress, value: value, gasLimit: gasLimit, timestamp: args.data.timestamp } const payLoad = { funAbi: args.data.funAbi, funArgs: args.data.funArgs, contractBytecode: args.data.contractBytecode, contractName: args.data.contractName, contractABI: args.data.contractABI, linkReferences: args.data.linkReferences } - let timestamp = Date.now() - if (tx.timestamp) { - timestamp = tx.timestamp - } + if (!tx.timestamp) tx.timestamp = Date.now() + const timestamp = tx.timestamp self.event.trigger('initiatingTransaction', [timestamp, tx, payLoad]) self.txRunner.rawRun(tx, confirmationCb, continueCb, promptCb, - function (error, result) { + async (error, result) => { if (error) return next(error) - const rawAddress = self.executionContext.isVM() ? (result.result.createdAddress && result.result.createdAddress.toBuffer()) : result.result.contractAddress + const isVM = self.executionContext.isVM() + if (isVM && tx.useCall) { + try { + result.transactionHash = await self.web3().eth.getHashFromTagBySimulator(timestamp) + } catch (e) { + console.log('unable to retrieve back the "call" hash', e) + } + } const eventName = (tx.useCall ? 'callExecuted' : 'transactionExecuted') - self.event.trigger(eventName, [error, tx.from, tx.to, tx.data, tx.useCall, result, timestamp, payLoad, rawAddress]) + self.event.trigger(eventName, [error, tx.from, tx.to, tx.data, tx.useCall, result, timestamp, payLoad]) if (error && (typeof (error) !== 'string')) { if (error.message) error = error.message @@ -454,25 +461,29 @@ class Blockchain { ) } ], - (error, txResult) => { + async (error, txResult) => { if (error) { return cb(error) } const isVM = this.executionContext.isVM() + let execResult + let returnValue = null if (isVM) { - const vmError = txExecution.checkVMError(txResult) - if (vmError.error) { - return cb(vmError.message) + execResult = await this.web3().eth.getExecutionResultFromSimulator(txResult.transactionHash) + if (execResult) { + // if it's not the VM, we don't have return value. We only have the transaction, and it does not contain the return value. + returnValue = (execResult && isVM) ? execResult.returnValue : txResult + const vmError = txExecution.checkVMError(execResult) + if (vmError.error) { + return cb(vmError.message) + } } } let address = null - let returnValue = null - if (txResult && txResult.result) { - address = isVM ? (txResult.result.createdAddress && txResult.result.createdAddress.toBuffer()) : txResult.result.contractAddress - // if it's not the VM, we don't have return value. We only have the transaction, and it does not contain the return value. - returnValue = (txResult.result.execResult && isVM) ? txResult.result.execResult.returnValue : txResult.result + if (txResult && txResult.receipt) { + address = txResult.receipt.contractAddress } cb(error, txResult, address, returnValue) diff --git a/apps/remix-ide/src/blockchain/providers/vm.js b/apps/remix-ide/src/blockchain/providers/vm.js index 8ec6583ff7..c251743e2b 100644 --- a/apps/remix-ide/src/blockchain/providers/vm.js +++ b/apps/remix-ide/src/blockchain/providers/vm.js @@ -1,14 +1,16 @@ const Web3 = require('web3') -const { BN, privateToAddress, hashPersonalMessage } = require('ethereumjs-util') -const RemixSimulator = require('@remix-project/remix-simulator') +const { BN, privateToAddress, stripHexPrefix, hashPersonalMessage } = require('ethereumjs-util') +const { Provider, extend } = require('@remix-project/remix-simulator') class VMProvider { constructor (executionContext) { this.executionContext = executionContext - this.RemixSimulatorProvider = new RemixSimulator.Provider({ executionContext: this.executionContext }) + this.RemixSimulatorProvider = new Provider({}) this.RemixSimulatorProvider.init() this.web3 = new Web3(this.RemixSimulatorProvider) + extend(this.web3) this.accounts = {} + this.executionContext.setWeb3('vm', this.web3) } getAccounts (cb) { diff --git a/apps/remix-ide/src/blockchain/txResultHelper.js b/apps/remix-ide/src/blockchain/txResultHelper.js deleted file mode 100644 index f608324062..0000000000 --- a/apps/remix-ide/src/blockchain/txResultHelper.js +++ /dev/null @@ -1,46 +0,0 @@ -'use strict' -const { bufferToHex, isHexString } = require('ethereumjs-util') - -function convertToPrefixedHex (input) { - if (input === undefined || input === null || isHexString(input)) { - return input - } else if (Buffer.isBuffer(input)) { - return bufferToHex(input) - } - return '0x' + input.toString(16) -} - -/* - txResult.result can be 3 different things: - - VM call or tx: ethereumjs-vm result object - - Node transaction: object returned from eth.getTransactionReceipt() - - Node call: return value from function call (not an object) - - Also, VM results use BN and Buffers, Node results use hex strings/ints, - So we need to normalize the values to prefixed hex strings -*/ -function resultToRemixTx (txResult) { - const { result, transactionHash } = txResult - const { status, execResult, gasUsed, createdAddress, contractAddress } = result - let returnValue, errorMessage - - if (isHexString(result)) { - returnValue = result - } else if (execResult !== undefined) { - returnValue = execResult.returnValue - errorMessage = execResult.exceptionError - } - - return { - transactionHash, - status, - gasUsed: convertToPrefixedHex(gasUsed), - error: errorMessage, - return: convertToPrefixedHex(returnValue), - createdAddress: convertToPrefixedHex(createdAddress || contractAddress) - } -} - -module.exports = { - resultToRemixTx -} diff --git a/jest.config.js b/jest.config.js index 30b91f3cbe..4b90409756 100644 --- a/jest.config.js +++ b/jest.config.js @@ -5,5 +5,18 @@ module.exports = { }, resolver: '@nrwl/jest/plugins/resolver', moduleFileExtensions: ['ts', 'js', 'html'], - coverageReporters: ['html'] + coverageReporters: ['html'], + moduleNameMapper:{ + "@remix-project/remix-analyzer": "/../../dist/libs/remix-analyzer/index.js", + "@remix-project/remix-astwalker": "/../../dist/libs/remix-astwalker/index.js", + "@remix-project/remix-debug": "/../../dist/libs/remix-debug/src/index.js", + "@remix-project/remix-lib": "/../../dist/libs/remix-lib/src/index.js", + "@remix-project/remix-simulator": "/../../dist/libs/remix-simulator/src/index.js", + "@remix-project/remix-solidity": "/../../dist/libs/remix-solidity/index.js", + "@remix-project/remix-tests": "/../../dist/libs/remix-tests/src/index.js", + "@remix-project/remix-url-resolver": + "/../../dist/libs/remix-url-resolver/index.js" + , + "@remix-project/remixd": "/../../dist/libs/remixd/index.js" + } }; diff --git a/libs/remix-lib/src/execution/execution-context.ts b/libs/remix-lib/src/execution/execution-context.ts index 31212412ff..ba067e7035 100644 --- a/libs/remix-lib/src/execution/execution-context.ts +++ b/libs/remix-lib/src/execution/execution-context.ts @@ -4,7 +4,6 @@ import Web3 from 'web3' import { EventManager } from '../eventManager' import { rlp, keccak, bufferToHex } from 'ethereumjs-util' import { Web3VmProvider } from '../web3Provider/web3VmProvider' -import { LogsManager } from './logsManager' import VM from '@ethereumjs/vm' import Common from '@ethereumjs/common' import StateManager from '@ethereumjs/vm/dist/state/stateManager' @@ -100,22 +99,21 @@ class StateManagerCommonStorageDump extends StateManager { */ export class ExecutionContext { event - logsManager - blockGasLimitDefault - blockGasLimit + blockGasLimitDefault: number + blockGasLimit: number customNetWorks blocks latestBlockNumber txs - executionContext + executionContext: string listenOnLastBlockId currentFork: string vms mainNetGenesisHash: string + customWeb3: { [key: string]: Web3 } constructor () { this.event = new EventManager() - this.logsManager = new LogsManager() this.executionContext = null this.blockGasLimitDefault = 4300000 this.blockGasLimit = this.blockGasLimitDefault @@ -134,6 +132,7 @@ export class ExecutionContext { this.blocks = {} this.latestBlockNumber = 0 this.txs = {} + this.customWeb3 = {} // mapping between a context name and a web3.js instance } init (config) { @@ -172,7 +171,12 @@ export class ExecutionContext { return this.executionContext === 'vm' } + setWeb3 (context: string, web3: Web3) { + this.customWeb3[context] = web3 + } + web3 () { + if (this.customWeb3[this.executionContext]) return this.customWeb3[this.executionContext] return this.isVM() ? this.vms[this.currentFork].web3vm : web3 } @@ -339,23 +343,5 @@ export class ExecutionContext { if (transactionDetailsLinks[network]) { return transactionDetailsLinks[network] + hash } - } - - addBlock (block) { - let blockNumber = '0x' + block.header.number.toString('hex') - if (blockNumber === '0x') { - blockNumber = '0x0' - } - blockNumber = web3.utils.toHex(web3.utils.toBN(blockNumber)) - - this.blocks['0x' + block.hash().toString('hex')] = block - this.blocks[blockNumber] = block - this.latestBlockNumber = blockNumber - - this.logsManager.checkBlock(blockNumber, block, this.web3()) - } - - trackTx (tx, block) { - this.txs[tx] = block - } + } } diff --git a/libs/remix-lib/src/execution/txExecution.ts b/libs/remix-lib/src/execution/txExecution.ts index 84edfc1f8e..8eb1c927dd 100644 --- a/libs/remix-lib/src/execution/txExecution.ts +++ b/libs/remix-lib/src/execution/txExecution.ts @@ -53,10 +53,10 @@ export function callFunction (from, to, data, value, gasLimit, funAbi, txRunner, /** * check if the vm has errored * - * @param {Object} txResult - the value returned by the vm + * @param {Object} execResult - execution result given by the VM * @return {Object} - { error: true/false, message: DOMNode } */ -export function checkVMError (txResult) { +export function checkVMError (execResult) { const errorCode = { OUT_OF_GAS: 'out of gas', STACK_UNDERFLOW: 'stack underflow', @@ -74,10 +74,10 @@ export function checkVMError (txResult) { error: false, message: '' } - if (!txResult.result.execResult.exceptionError) { + if (!execResult.exceptionError) { return ret } - const exceptionError = txResult.result.execResult.exceptionError.error || '' + const exceptionError = execResult.exceptionError.error || '' const error = `VM error: ${exceptionError}.\n` let msg if (exceptionError === errorCode.INVALID_OPCODE) { @@ -87,7 +87,7 @@ export function checkVMError (txResult) { msg = '\tThe transaction ran out of gas. Please increase the Gas Limit.\n' ret.error = true } else if (exceptionError === errorCode.REVERT) { - const returnData = txResult.result.execResult.returnValue + const returnData = execResult.returnValue // It is the hash of Error(string) if (returnData && (returnData.slice(0, 4).toString('hex') === '08c379a0')) { const abiCoder = new ethers.utils.AbiCoder() diff --git a/libs/remix-lib/src/execution/txFormat.ts b/libs/remix-lib/src/execution/txFormat.ts index 7d3188a1d0..62f90be1a8 100644 --- a/libs/remix-lib/src/execution/txFormat.ts +++ b/libs/remix-lib/src/execution/txFormat.ts @@ -314,7 +314,7 @@ export function deployLibrary (libraryName, libraryShortName, library, contracts if (err) { return callback(err) } - const address = txResult.result.createdAddress || txResult.result.contractAddress + const address = txResult.receipt.contractAddress library.address = address callback(err, address) }) diff --git a/libs/remix-lib/src/execution/txListener.ts b/libs/remix-lib/src/execution/txListener.ts index fea5cdf270..967b8ee3a8 100644 --- a/libs/remix-lib/src/execution/txListener.ts +++ b/libs/remix-lib/src/execution/txListener.ts @@ -8,13 +8,13 @@ import { ExecutionContext } from './execution-context' import { decodeResponse } from './txFormat' import { getFunction, getReceiveInterface, getConstructorInterface, visitContracts, makeFullTypeDefinition } from './txHelper' -function addExecutionCosts (txResult, tx) { - if (txResult && txResult.result) { - if (txResult.result.execResult) { - tx.returnValue = txResult.result.execResult.returnValue - if (txResult.result.execResult.gasUsed) tx.executionCost = txResult.result.execResult.gasUsed.toString(10) +function addExecutionCosts (txResult, tx, execResult) { + if (txResult) { + if (execResult) { + tx.returnValue = execResult.returnValue + if (execResult.gasUsed) tx.executionCost = execResult.gasUsed.toString(10) } - if (txResult.result.gasUsed) tx.transactionCost = txResult.result.gasUsed.toString(10) + if (txResult.receipt && txResult.receipt.gasUsed) tx.transactionCost = txResult.receipt.gasUsed.toString(10) } } @@ -55,7 +55,7 @@ export class TxListener { } }) - opt.event.udapp.register('callExecuted', (error, from, to, data, lookupOnly, txResult) => { + opt.event.udapp.register('callExecuted', async (error, from, to, data, lookupOnly, txResult) => { if (error) return // we go for that case if // in VM mode @@ -63,17 +63,25 @@ export class TxListener { if (!this._isListening) return // we don't listen if (this._loopId && this.executionContext.getProvider() !== 'vm') return // we seems to already listen on a "web3" network + let returnValue + let execResult + if (this.executionContext.isVM()) { + execResult = await this.executionContext.web3().eth.getExecutionResultFromSimulator(txResult.transactionHash) + returnValue = execResult.returnValue + } else { + returnValue = toBuffer(txResult.result) + } const call = { from: from, to: to, input: data, hash: txResult.transactionHash ? txResult.transactionHash : 'call' + (from || '') + to + data, isCall: true, - returnValue: this.executionContext.isVM() ? txResult.result.execResult.returnValue : toBuffer(txResult.result), + returnValue, envMode: this.executionContext.getProvider() } - addExecutionCosts(txResult, call) + addExecutionCosts(txResult, call, execResult) this._resolveTx(call, call, (error, resolvedData) => { if (!error) { this.event.trigger('newCall', [call]) @@ -89,12 +97,17 @@ export class TxListener { // in web3 mode && listen remix txs only if (!this._isListening) return // we don't listen if (this._loopId && this.executionContext.getProvider() !== 'vm') return // we seems to already listen on a "web3" network - this.executionContext.web3().eth.getTransaction(txResult.transactionHash, (error, tx) => { + this.executionContext.web3().eth.getTransaction(txResult.transactionHash, async (error, tx) => { if (error) return console.log(error) - addExecutionCosts(txResult, tx) + let execResult + if (this.executionContext.isVM()) { + execResult = await this.executionContext.web3().eth.getExecutionResultFromSimulator(txResult.transactionHash) + } + + addExecutionCosts(txResult, tx, execResult) tx.envMode = this.executionContext.getProvider() - tx.status = txResult.result.status // 0x0 or 0x1 + tx.status = txResult.receipt.status // 0x0 or 0x1 this._resolve([tx], () => { }) }) diff --git a/libs/remix-lib/src/execution/txRunner.ts b/libs/remix-lib/src/execution/txRunner.ts index ddc2456fb5..254b4b0474 100644 --- a/libs/remix-lib/src/execution/txRunner.ts +++ b/libs/remix-lib/src/execution/txRunner.ts @@ -1,91 +1,26 @@ 'use strict' -import { Transaction } from '@ethereumjs/tx' -import { Block } from '@ethereumjs/block' -import { BN, bufferToHex, Address } from 'ethereumjs-util' -import { ExecutionContext } from './execution-context' import { EventManager } from '../eventManager' export class TxRunner { event - executionContext - _api - blockNumber runAsync pendingTxs - vmaccounts queusTxs - blocks - commonContext - - constructor (vmaccounts, api, executionContext) { + opt + internalRunner + constructor (internalRunner, opt) { + this.opt = opt || {} + this.internalRunner = internalRunner this.event = new EventManager() - // has a default for now for backwards compatability - this.executionContext = executionContext || new ExecutionContext() - this.commonContext = this.executionContext.vmObject().common - this._api = api - this.blockNumber = 0 - this.runAsync = true - if (this.executionContext.isVM()) { - // this.blockNumber = 1150000 // The VM is running in Homestead mode, which started at this block. - this.blockNumber = 0 // The VM is running in Homestead mode, which started at this block. - this.runAsync = false // We have to run like this cause the VM Event Manager does not support running multiple txs at the same time. - } + + this.runAsync = this.opt.runAsync || true // We have to run like this cause the VM Event Manager does not support running multiple txs at the same time. + this.pendingTxs = {} - this.vmaccounts = vmaccounts this.queusTxs = [] - this.blocks = [] } rawRun (args, confirmationCb, gasEstimationForceSend, promptCb, cb) { - let timestamp = Date.now() - if (args.timestamp) { - timestamp = args.timestamp - } - run(this, args, timestamp, confirmationCb, gasEstimationForceSend, promptCb, cb) - } - - _executeTx (tx, gasPrice, api, promptCb, callback) { - if (gasPrice) tx.gasPrice = this.executionContext.web3().utils.toHex(gasPrice) - if (api.personalMode()) { - promptCb( - (value) => { - this._sendTransaction(this.executionContext.web3().personal.sendTransaction, tx, value, callback) - }, - () => { - return callback('Canceled by user.') - } - ) - } else { - this._sendTransaction(this.executionContext.web3().eth.sendTransaction, tx, null, callback) - } - } - - _sendTransaction (sendTx, tx, pass, callback) { - const cb = (err, resp) => { - if (err) { - return callback(err, resp) - } - this.event.trigger('transactionBroadcasted', [resp]) - var listenOnResponse = () => { - // eslint-disable-next-line no-async-promise-executor - return new Promise(async (resolve, reject) => { - const result = await tryTillReceiptAvailable(resp, this.executionContext) - tx = await tryTillTxAvailable(resp, this.executionContext) - resolve({ - result, - tx, - transactionHash: result ? result['transactionHash'] : null - }) - }) - } - listenOnResponse().then((txData) => { callback(null, txData) }).catch((error) => { callback(error) }) - } - const args = pass !== null ? [tx, pass, cb] : [tx, cb] - try { - sendTx.apply({}, args) - } catch (e) { - return callback(`Send transaction failed: ${e.message} . if you use an injected provider, please check it is properly unlocked. `) - } + run(this, args, args.timestamp || Date.now(), confirmationCb, gasEstimationForceSend, promptCb, cb) } execute (args, confirmationCb, gasEstimationForceSend, promptCb, callback) { @@ -93,175 +28,10 @@ export class TxRunner { if (data.slice(0, 2) !== '0x') { data = '0x' + data } - - if (!this.executionContext.isVM()) { - return this.runInNode(args.from, args.to, data, args.value, args.gasLimit, args.useCall, confirmationCb, gasEstimationForceSend, promptCb, callback) - } - try { - this.runInVm(args.from, args.to, data, args.value, args.gasLimit, args.useCall, args.timestamp, callback) - } catch (e) { - callback(e, null) - } + this.internalRunner.execute(args, confirmationCb, gasEstimationForceSend, promptCb, callback) } - - runInVm (from, to, data, value, gasLimit, useCall, timestamp, callback) { - const self = this - const account = self.vmaccounts[from] - if (!account) { - return callback('Invalid account selected') - } - - if (Number.isInteger(gasLimit)) { - gasLimit = '0x' + gasLimit.toString(16) - } - - this.executionContext.vm().stateManager.getAccount(Address.fromString(from)).then((res) => { - // See https://github.com/ethereumjs/ethereumjs-tx/blob/master/docs/classes/transaction.md#constructor - // for initialization fields and their types - value = value ? parseInt(value) : 0 - const tx = Transaction.fromTxData({ - nonce: new BN(res.nonce), - gasPrice: '0x1', - gasLimit: gasLimit, - to: to, - value: value, - data: Buffer.from(data.slice(2), 'hex') - }, { common: this.commonContext }).sign(account.privateKey) - - const coinbases = ['0x0e9281e9c6a0808672eaba6bd1220e144c9bb07a', '0x8945a1288dc78a6d8952a92c77aee6730b414778', '0x94d76e24f818426ae84aa404140e8d5f60e10e7e'] - const difficulties = [new BN('69762765929000', 10), new BN('70762765929000', 10), new BN('71762765929000', 10)] - - var block = Block.fromBlockData({ - header: { - timestamp: timestamp || (new Date().getTime() / 1000 | 0), - number: self.blockNumber, - coinbase: coinbases[self.blockNumber % coinbases.length], - difficulty: difficulties[self.blockNumber % difficulties.length], - gasLimit: new BN(gasLimit.replace('0x', ''), 16).imuln(2) - }, - transactions: [tx] - }, { common: this.commonContext }) - - if (!useCall) { - ++self.blockNumber - this.runBlockInVm(tx, block, callback) - } else { - this.executionContext.vm().stateManager.checkpoint().then(() => { - this.runBlockInVm(tx, block, (err, result) => { - this.executionContext.vm().stateManager.revert().then(() => { - callback(err, result) - }) - }) - }) - } - }).catch((e) => { - callback(e) - }) - } - - runBlockInVm (tx, block, callback) { - this.executionContext.vm().runBlock({ block: block, generate: true, skipBlockValidation: true, skipBalance: false }).then((results) => { - const result = results.results[0] - if (result) { - const status = result.execResult.exceptionError ? 0 : 1 - result.status = `0x${status}` - } - this.executionContext.addBlock(block) - this.executionContext.trackTx('0x' + tx.hash().toString('hex'), block) - callback(null, { - result: result, - transactionHash: bufferToHex(Buffer.from(tx.hash())) - }) - }).catch((err) => { - callback(err) - }) - } - - runInNode (from, to, data, value, gasLimit, useCall, confirmCb, gasEstimationForceSend, promptCb, callback) { - const tx = { from: from, to: to, data: data, value: value } - - if (useCall) { - tx['gas'] = gasLimit - return this.executionContext.web3().eth.call(tx, function (error, result) { - callback(error, { - result: result, - transactionHash: result ? result.transactionHash : null - }) - }) - } - this.executionContext.web3().eth.estimateGas(tx, (err, gasEstimation) => { - if (err && err.message.indexOf('Invalid JSON RPC response') !== -1) { - // // @todo(#378) this should be removed when https://github.com/WalletConnect/walletconnect-monorepo/issues/334 is fixed - err = 'Gas estimation failed because of an unknown internal error. This may indicated that the transaction will fail.' - } - gasEstimationForceSend(err, () => { - // callback is called whenever no error - tx['gas'] = !gasEstimation ? gasLimit : gasEstimation - - if (this._api.config.getUnpersistedProperty('doNotShowTransactionConfirmationAgain')) { - return this._executeTx(tx, null, this._api, promptCb, callback) - } - - this._api.detectNetwork((err, network) => { - if (err) { - console.log(err) - return - } - - confirmCb(network, tx, tx['gas'], (gasPrice) => { - return this._executeTx(tx, gasPrice, this._api, promptCb, callback) - }, (error) => { - callback(error) - }) - }) - }, () => { - const blockGasLimit = this.executionContext.currentblockGasLimit() - // NOTE: estimateGas very likely will return a large limit if execution of the code failed - // we want to be able to run the code in order to debug and find the cause for the failure - if (err) return callback(err) - - let warnEstimation = ' An important gas estimation might also be the sign of a problem in the contract code. Please check loops and be sure you did not sent value to a non payable function (that\'s also the reason of strong gas estimation). ' - warnEstimation += ' ' + err - - if (gasEstimation > gasLimit) { - return callback('Gas required exceeds limit: ' + gasLimit + '. ' + warnEstimation) - } - if (gasEstimation > blockGasLimit) { - return callback('Gas required exceeds block gas limit: ' + gasLimit + '. ' + warnEstimation) - } - }) - }) - } -} - -async function tryTillReceiptAvailable (txhash, executionContext) { - return new Promise((resolve, reject) => { - executionContext.web3().eth.getTransactionReceipt(txhash, async (err, receipt) => { - if (err || !receipt) { - // Try again with a bit of delay if error or if result still null - await pause() - return resolve(await tryTillReceiptAvailable(txhash, executionContext)) - } - return resolve(receipt) - }) - }) } -async function tryTillTxAvailable (txhash, executionContext) { - return new Promise((resolve, reject) => { - executionContext.web3().eth.getTransaction(txhash, async (err, tx) => { - if (err || !tx) { - // Try again with a bit of delay if error or if result still null - await pause() - return resolve(await tryTillTxAvailable(txhash, executionContext)) - } - return resolve(tx) - }) - }) -} - -async function pause () { return new Promise((resolve, reject) => { setTimeout(resolve, 500) }) } - function run (self, tx, stamp, confirmationCb, gasEstimationForceSend = null, promptCb = null, callback = null) { if (!self.runAsync && Object.keys(self.pendingTxs).length) { return self.queusTxs.push({ tx, stamp, callback }) @@ -275,4 +45,4 @@ function run (self, tx, stamp, confirmationCb, gasEstimationForceSend = null, pr run(self, next.tx, next.stamp, next.callback) } }) -} +} \ No newline at end of file diff --git a/libs/remix-lib/src/execution/txRunnerVM.ts b/libs/remix-lib/src/execution/txRunnerVM.ts new file mode 100644 index 0000000000..3b221f8b75 --- /dev/null +++ b/libs/remix-lib/src/execution/txRunnerVM.ts @@ -0,0 +1,120 @@ +'use strict' +import { Transaction } from 'ethereumjs-tx' +import Block from 'ethereumjs-block' +import { BN, bufferToHex } from 'ethereumjs-util' +import { EventManager } from '../eventManager' +import { LogsManager } from './logsManager' + +export class TxRunnerVM { + event + _api + blockNumber + runAsync + pendingTxs + vmaccounts + queusTxs + blocks + txs + logsManager + getVM: () => any + + constructor (vmaccounts, api, getVM) { + this.event = new EventManager() + this.logsManager = new LogsManager() + // has a default for now for backwards compatability + this.getVM = getVM + this._api = api + this.blockNumber = 0 + this.runAsync = true + this.blockNumber = 0 // The VM is running in Homestead mode, which started at this block. + this.runAsync = false // We have to run like this cause the VM Event Manager does not support running multiple txs at the same time. + this.pendingTxs = {} + this.vmaccounts = vmaccounts + this.queusTxs = [] + this.blocks = [] + } + + execute (args, confirmationCb, gasEstimationForceSend, promptCb, callback) { + let data = args.data + if (data.slice(0, 2) !== '0x') { + data = '0x' + data + } + + try { + this.runInVm(args.from, args.to, data, args.value, args.gasLimit, args.useCall, args.timestamp, callback) + } catch (e) { + callback(e, null) + } + } + + runInVm (from, to, data, value, gasLimit, useCall, timestamp, callback) { + const self = this + const account = self.vmaccounts[from] + if (!account) { + return callback('Invalid account selected') + } + + this.getVM().stateManager.getAccount(Buffer.from(from.replace('0x', ''), 'hex'), (err, res) => { + if (err) { + callback('Account not found') + } else { + // See https://github.com/ethereumjs/ethereumjs-tx/blob/master/docs/classes/transaction.md#constructor + // for initialization fields and their types + value = value ? parseInt(value) : 0 + const tx = new Transaction({ + nonce: new BN(res.nonce), + gasPrice: '0x1', + gasLimit: gasLimit, + to: to, + value: value, + data: Buffer.from(data.slice(2), 'hex') + }) + tx.sign(account.privateKey) + const coinbases = ['0x0e9281e9c6a0808672eaba6bd1220e144c9bb07a', '0x8945a1288dc78a6d8952a92c77aee6730b414778', '0x94d76e24f818426ae84aa404140e8d5f60e10e7e'] + const difficulties = [new BN('69762765929000', 10), new BN('70762765929000', 10), new BN('71762765929000', 10)] + const block = new Block({ + header: { + timestamp: timestamp || (new Date().getTime() / 1000 | 0), + number: self.blockNumber, + coinbase: coinbases[self.blockNumber % coinbases.length], + difficulty: difficulties[self.blockNumber % difficulties.length], + gasLimit: new BN(gasLimit, 10).imuln(2) + }, + transactions: [tx], + uncleHeaders: [] + }) + if (!useCall) { + ++self.blockNumber + this.runBlockInVm(tx, block, callback) + } else { + this.getVM().stateManager.checkpoint(() => { + this.runBlockInVm(tx, block, (err, result) => { + this.getVM().stateManager.revert(() => { + callback(err, result) + }) + }) + }) + } + } + }) + } + + runBlockInVm (tx, block, callback) { + this.getVM().runBlock({ block: block, generate: true, skipBlockValidation: true, skipBalance: false }).then((results) => { + const result = results.results[0] + if (result) { + const status = result.execResult.exceptionError ? 0 : 1 + result.status = `0x${status}` + } + callback(null, { + result: result, + transactionHash: bufferToHex(Buffer.from(tx.hash())), + block, + tx, + }) + }).catch(function (err) { + callback(err) + }) + } +} + diff --git a/libs/remix-lib/src/execution/txRunnerWeb3.ts b/libs/remix-lib/src/execution/txRunnerWeb3.ts new file mode 100644 index 0000000000..f735b3401c --- /dev/null +++ b/libs/remix-lib/src/execution/txRunnerWeb3.ts @@ -0,0 +1,156 @@ +'use strict' +import { EventManager } from '../eventManager' +import Web3 from 'web3' + +export class TxRunnerWeb3 { + event + _api + getWeb3: () => Web3 + currentblockGasLimit: () => number + + constructor (api, getWeb3, currentblockGasLimit) { + this.event = new EventManager() + this.getWeb3 = getWeb3 + this.currentblockGasLimit = currentblockGasLimit + this._api = api + } + + _executeTx (tx, gasPrice, api, promptCb, callback) { + if (gasPrice) tx.gasPrice = this.getWeb3().utils.toHex(gasPrice) + if (api.personalMode()) { + promptCb( + (value) => { + this._sendTransaction((this.getWeb3() as any).personal.sendTransaction, tx, value, callback) + }, + () => { + return callback('Canceled by user.') + } + ) + } else { + this._sendTransaction(this.getWeb3().eth.sendTransaction, tx, null, callback) + } + } + + _sendTransaction (sendTx, tx, pass, callback) { + const cb = (err, resp) => { + if (err) { + return callback(err, resp) + } + this.event.trigger('transactionBroadcasted', [resp]) + var listenOnResponse = () => { + // eslint-disable-next-line no-async-promise-executor + return new Promise(async (resolve, reject) => { + const receipt = await tryTillReceiptAvailable(resp, this.getWeb3()) + tx = await tryTillTxAvailable(resp, this.getWeb3()) + resolve({ + receipt, + tx, + transactionHash: receipt ? receipt['transactionHash'] : null + }) + }) + } + listenOnResponse().then((txData) => { callback(null, txData) }).catch((error) => { callback(error) }) + } + const args = pass !== null ? [tx, pass, cb] : [tx, cb] + try { + sendTx.apply({}, args) + } catch (e) { + return callback(`Send transaction failed: ${e.message} . if you use an injected provider, please check it is properly unlocked. `) + } + } + + execute (args, confirmationCb, gasEstimationForceSend, promptCb, callback) { + let data = args.data + if (data.slice(0, 2) !== '0x') { + data = '0x' + data + } + + return this.runInNode(args.from, args.to, data, args.value, args.gasLimit, args.useCall, args.timestamp, confirmationCb, gasEstimationForceSend, promptCb, callback) + } + + runInNode (from, to, data, value, gasLimit, useCall, timestamp, confirmCb, gasEstimationForceSend, promptCb, callback) { + const tx = { from: from, to: to, data: data, value: value } + + if (useCall) { + const tag = Date.now() // for e2e reference + tx['gas'] = gasLimit + tx['timestamp'] = timestamp + return this.getWeb3().eth.call(tx, function (error, result: any) { + if (error) return callback(error) + callback(null, { + result: result + }) + }) + } + this.getWeb3().eth.estimateGas(tx, (err, gasEstimation) => { + if (err && err.message.indexOf('Invalid JSON RPC response') !== -1) { + // // @todo(#378) this should be removed when https://github.com/WalletConnect/walletconnect-monorepo/issues/334 is fixed + new Error('Gas estimation failed because of an unknown internal error. This may indicated that the transaction will fail.') + } + gasEstimationForceSend(err, () => { + // callback is called whenever no error + tx['gas'] = !gasEstimation ? gasLimit : gasEstimation + + if (this._api.config.getUnpersistedProperty('doNotShowTransactionConfirmationAgain')) { + return this._executeTx(tx, null, this._api, promptCb, callback) + } + + this._api.detectNetwork((err, network) => { + if (err) { + console.log(err) + return + } + + confirmCb(network, tx, tx['gas'], (gasPrice) => { + return this._executeTx(tx, gasPrice, this._api, promptCb, callback) + }, (error) => { + callback(error) + }) + }) + }, () => { + const blockGasLimit = this.currentblockGasLimit() + // NOTE: estimateGas very likely will return a large limit if execution of the code failed + // we want to be able to run the code in order to debug and find the cause for the failure + if (err) return callback(err) + + let warnEstimation = ' An important gas estimation might also be the sign of a problem in the contract code. Please check loops and be sure you did not sent value to a non payable function (that\'s also the reason of strong gas estimation). ' + warnEstimation += ' ' + err + + if (gasEstimation > gasLimit) { + return callback('Gas required exceeds limit: ' + gasLimit + '. ' + warnEstimation) + } + if (gasEstimation > blockGasLimit) { + return callback('Gas required exceeds block gas limit: ' + gasLimit + '. ' + warnEstimation) + } + }) + }) + } +} + +async function tryTillReceiptAvailable (txhash, web3) { + return new Promise((resolve, reject) => { + web3.eth.getTransactionReceipt(txhash, async (err, receipt) => { + if (err || !receipt) { + // Try again with a bit of delay if error or if result still null + await pause() + return resolve(await tryTillReceiptAvailable(txhash, web3)) + } + return resolve(receipt) + }) + }) +} + +async function tryTillTxAvailable (txhash, web3) { + return new Promise((resolve, reject) => { + web3.eth.getTransaction(txhash, async (err, tx) => { + if (err || !tx) { + // Try again with a bit of delay if error or if result still null + await pause() + return resolve(await tryTillTxAvailable(txhash, web3)) + } + return resolve(tx) + }) + }) +} + +async function pause () { return new Promise((resolve, reject) => { setTimeout(resolve, 500) }) } diff --git a/libs/remix-lib/src/helpers/txResultHelper.ts b/libs/remix-lib/src/helpers/txResultHelper.ts index 7aa7967f0d..3264e5b771 100644 --- a/libs/remix-lib/src/helpers/txResultHelper.ts +++ b/libs/remix-lib/src/helpers/txResultHelper.ts @@ -20,9 +20,9 @@ function convertToPrefixedHex (input) { Also, VM results use BN and Buffers, Node results use hex strings/ints, So we need to normalize the values to prefixed hex strings */ -export function resultToRemixTx (txResult) { - const { result, transactionHash } = txResult - const { status, execResult, gasUsed, createdAddress, contractAddress } = result +export function resultToRemixTx (txResult, execResult) { + const { receipt, transactionHash, result } = txResult + const { status, gasUsed, contractAddress } = receipt let returnValue, errorMessage if (isHexString(result)) { @@ -38,6 +38,6 @@ export function resultToRemixTx (txResult) { gasUsed: convertToPrefixedHex(gasUsed), error: errorMessage, return: convertToPrefixedHex(returnValue), - createdAddress: convertToPrefixedHex(createdAddress || contractAddress) + createdAddress: convertToPrefixedHex(contractAddress) } } diff --git a/libs/remix-lib/src/index.ts b/libs/remix-lib/src/index.ts index 5b1108ff70..afcd0beae6 100644 --- a/libs/remix-lib/src/index.ts +++ b/libs/remix-lib/src/index.ts @@ -12,9 +12,12 @@ import * as txHelper from './execution/txHelper' import * as txFormat from './execution/txFormat' import { TxListener } from './execution/txListener' import { TxRunner } from './execution/txRunner' +import { LogsManager } from './execution/logsManager' import { ExecutionContext } from './execution/execution-context' import * as typeConversion from './execution/typeConversion' -import { UniversalDApp } from './universalDapp' +import { TxRunnerVM } from './execution/txRunnerVM' +import { TxRunnerWeb3 } from './execution/txRunnerWeb3' +import * as txResultHelper from './helpers/txResultHelper' export = modules() @@ -23,7 +26,8 @@ function modules () { EventManager: EventManager, helpers: { ui: uiHelper, - compiler: compilerHelper + compiler: compilerHelper, + txResultHelper }, vm: { Web3Providers: Web3Providers, @@ -39,9 +43,11 @@ function modules () { executionContext: new ExecutionContext(), txFormat: txFormat, txListener: TxListener, - txRunner: TxRunner, - typeConversion: typeConversion - }, - UniversalDApp: UniversalDApp + TxRunner: TxRunner, + TxRunnerWeb3: TxRunnerWeb3, + TxRunnerVM: TxRunnerVM, + typeConversion: typeConversion, + LogsManager + } } } diff --git a/libs/remix-lib/src/universalDapp.ts b/libs/remix-lib/src/universalDapp.ts deleted file mode 100644 index 030a8da811..0000000000 --- a/libs/remix-lib/src/universalDapp.ts +++ /dev/null @@ -1,379 +0,0 @@ -import { waterfall } from 'async' -import { BN, privateToAddress, isValidPrivate, toChecksumAddress, Address } from 'ethereumjs-util' -import { randomBytes } from 'crypto' -import { EventEmitter } from 'events' -import { TxRunner } from './execution/txRunner' -import { sortAbiFunction, getFallbackInterface, getReceiveInterface, inputParametersDeclarationToString } from './execution/txHelper' -import { EventManager } from './eventManager' -import { ExecutionContext } from './execution/execution-context' -import { resultToRemixTx } from './helpers/txResultHelper' - -export class UniversalDApp { - events - event - executionContext - config - txRunner - accounts - transactionContextAPI - - constructor (config, executionContext) { - this.events = new EventEmitter() - this.event = new EventManager() - // has a default for now for backwards compatability - this.executionContext = executionContext || new ExecutionContext() - this.config = config - - this.txRunner = new TxRunner({}, { - config: config, - detectNetwork: (cb) => { - this.executionContext.detectNetwork(cb) - }, - personalMode: () => { - return this.executionContext.getProvider() === 'web3' ? this.config.get('settings/personal-mode') : false - } - }, this.executionContext) - this.accounts = {} - this.executionContext.event.register('contextChanged', this.resetEnvironment.bind(this)) - } - - // TODO : event should be triggered by Udapp instead of TxListener - /** Listen on New Transaction. (Cannot be done inside constructor because txlistener doesn't exist yet) */ - startListening (txlistener) { - txlistener.event.register('newTransaction', (tx) => { - this.events.emit('newTransaction', tx) - }) - } - - resetEnvironment () { - this.accounts = {} - if (this.executionContext.isVM()) { - this._addAccount('3cd7232cd6f3fc66a57a6bedc1a8ed6c228fff0a327e169c2bcc5e869ed49511', '0x56BC75E2D63100000') - this._addAccount('2ac6c190b09897cd8987869cc7b918cfea07ee82038d492abce033c75c1b1d0c', '0x56BC75E2D63100000') - this._addAccount('dae9801649ba2d95a21e688b56f77905e5667c44ce868ec83f82e838712a2c7a', '0x56BC75E2D63100000') - this._addAccount('d74aa6d18aa79a05f3473dd030a97d3305737cbc8337d940344345c1f6b72eea', '0x56BC75E2D63100000') - this._addAccount('71975fbf7fe448e004ac7ae54cad0a383c3906055a65468714156a07385e96ce', '0x56BC75E2D63100000') - } - // TODO: most params here can be refactored away in txRunner - this.txRunner = new TxRunner(this.accounts, { - // TODO: only used to check value of doNotShowTransactionConfirmationAgain property - config: this.config, - // TODO: to refactor, TxRunner already has access to executionContext - detectNetwork: (cb) => { - this.executionContext.detectNetwork(cb) - }, - personalMode: () => { - return this.executionContext.getProvider() === 'web3' ? this.config.get('settings/personal-mode') : false - } - }, this.executionContext) - this.txRunner.event.register('transactionBroadcasted', (txhash) => { - this.executionContext.detectNetwork((error, network) => { - if (error || !network) return - this.event.trigger('transactionBroadcasted', [txhash, network.name]) - }) - }) - } - - resetAPI (transactionContextAPI) { - this.transactionContextAPI = transactionContextAPI - } - - /** - * Create a VM Account - * @param {{privateKey: string, balance: string}} newAccount The new account to create - */ - createVMAccount (newAccount) { - const { privateKey, balance } = newAccount - if (this.executionContext.getProvider() !== 'vm') { - throw new Error('plugin API does not allow creating a new account through web3 connection. Only vm mode is allowed') - } - this._addAccount(privateKey, balance) - const privKey = Buffer.from(privateKey, 'hex') - return '0x' + privateToAddress(privKey).toString('hex') - } - - newAccount (password, passwordPromptCb, cb) { - if (!this.executionContext.isVM()) { - if (!this.config.get('settings/personal-mode')) { - return cb('Not running in personal mode') - } - return passwordPromptCb((passphrase) => { - this.executionContext.web3().personal.newAccount(passphrase, cb) - }) - } - let privateKey - do { - privateKey = randomBytes(32) - } while (!isValidPrivate(privateKey)) - this._addAccount(privateKey, '0x56BC75E2D63100000') - cb(null, '0x' + privateToAddress(privateKey).toString('hex')) - } - - /** Add an account to the list of account (only for Javascript VM) */ - _addAccount (privateKey, balance) { - if (!this.executionContext.isVM()) { - throw new Error('_addAccount() cannot be called in non-VM mode') - } - - if (!this.accounts) { - return - } - privateKey = Buffer.from(privateKey, 'hex') - const address = privateToAddress(privateKey) - - // FIXME: we don't care about the callback, but we should still make this proper - const stateManager = this.executionContext.vm().stateManager - stateManager.getAccount(address).then((account) => { - account.balance = new BN(balance.replace('0x', '') || 'f00000000000000001', 16) - stateManager.putAccount(address, account).catch((error) => { - console.log(error) - }) - }).catch((error) => { - console.log(error) - }) - - this.accounts[toChecksumAddress('0x' + address.toString('hex'))] = { privateKey, nonce: 0 } - } - - /** Return the list of accounts */ - getAccounts (cb) { - return new Promise((resolve, reject) => { - const provider = this.executionContext.getProvider() - switch (provider) { - case 'vm': - if (!this.accounts) { - if (cb) cb('No accounts?') - reject(new Error('No accounts?')) - return - } - if (cb) cb(null, Object.keys(this.accounts)) - resolve(Object.keys(this.accounts)) - break - case 'web3': - if (this.config.get('settings/personal-mode')) { - return this.executionContext.web3().personal.getListAccounts((error, accounts) => { - if (cb) cb(error, accounts) - if (error) return reject(error) - resolve(accounts) - }) - } else { - this.executionContext.web3().eth.getAccounts((error, accounts) => { - if (cb) cb(error, accounts) - if (error) return reject(error) - resolve(accounts) - }) - } - break - case 'injected': { - this.executionContext.web3().eth.getAccounts((error, accounts) => { - if (cb) cb(error, accounts) - if (error) return reject(error) - resolve(accounts) - }) - } - } - }) - } - - /** Get the balance of an address */ - getBalance (address, cb) { - if (!this.executionContext.isVM()) { - return this.executionContext.web3().eth.getBalance(address, (err, res) => { - if (err) { - return cb(err) - } - cb(null, res.toString(10)) - }) - } - if (!this.accounts) { - return cb('No accounts?') - } - - this.executionContext.vm().stateManager.getAccount(Address.fromString(address)).then((res) => { - cb(null, new BN(res.balance).toString(10)) - }).catch(() => { - cb('Account not found') - }) - } - - /** Get the balance of an address, and convert wei to ether */ - getBalanceInEther (address, callback) { - this.getBalance(address, (error, balance) => { - if (error) { - return callback(error) - } - callback(null, this.executionContext.web3().utils.fromWei(balance, 'ether')) - }) - } - - pendingTransactionsCount () { - return Object.keys(this.txRunner.pendingTxs).length - } - - /** - * deploy the given contract - * - * @param {String} data - data to send with the transaction ( return of txFormat.buildData(...) ). - * @param {Function} callback - callback. - */ - createContract (data, confirmationCb, continueCb, promptCb, callback) { - this.runTx({ data: data, useCall: false }, confirmationCb, continueCb, promptCb, callback) - } - - /** - * call the current given contract - * - * @param {String} to - address of the contract to call. - * @param {String} data - data to send with the transaction ( return of txFormat.buildData(...) ). - * @param {Object} funAbi - abi definition of the function to call. - * @param {Function} callback - callback. - */ - callFunction (to, data, funAbi, confirmationCb, continueCb, promptCb, callback) { - const useCall = funAbi.stateMutability === 'view' || funAbi.stateMutability === 'pure' - this.runTx({ to, data, useCall }, confirmationCb, continueCb, promptCb, callback) - } - - /** - * call the current given contract - * - * @param {String} to - address of the contract to call. - * @param {String} data - data to send with the transaction ( return of txFormat.buildData(...) ). - * @param {Function} callback - callback. - */ - sendRawTransaction (to, data, confirmationCb, continueCb, promptCb, callback) { - this.runTx({ to, data, useCall: false }, confirmationCb, continueCb, promptCb, callback) - } - - context () { - return (this.executionContext.isVM() ? 'memory' : 'blockchain') - } - - getABI (contract) { - return sortAbiFunction(contract.abi) - } - - getFallbackInterface (contractABI) { - return getFallbackInterface(contractABI) - } - - getReceiveInterface (contractABI) { - return getReceiveInterface(contractABI) - } - - getInputs (funABI) { - if (!funABI.inputs) { - return '' - } - return inputParametersDeclarationToString(funABI.inputs) - } - - /** - * This function send a tx only to javascript VM or testnet, will return an error for the mainnet - * SHOULD BE TAKEN CAREFULLY! - * - * @param {Object} tx - transaction. - */ - sendTransaction (tx) { - return new Promise((resolve, reject) => { - this.executionContext.detectNetwork((error, network) => { - if (error) return reject(error) - if (network.name === 'Main' && network.id === '1') { - return reject(new Error('It is not allowed to make this action against mainnet')) - } - this.silentRunTx(tx, (error, result) => { - if (error) return reject(error) - try { - resolve(resultToRemixTx(result)) - } catch (e) { - reject(e) - } - }) - }) - }) - } - - /** - * This function send a tx without alerting the user (if mainnet or if gas estimation too high). - * SHOULD BE TAKEN CAREFULLY! - * - * @param {Object} tx - transaction. - * @param {Function} callback - callback. - */ - silentRunTx (tx, cb) { - this.txRunner.rawRun( - tx, - (network, tx, gasEstimation, continueTxExecution, cancelCb) => { continueTxExecution() }, - (error, continueTxExecution, cancelCb) => { if (error) { cb(error) } else { continueTxExecution() } }, - (okCb, cancelCb) => { okCb() }, - cb - ) - } - - runTx (args, confirmationCb, continueCb, promptCb, cb) { - const self = this - waterfall([ - function getGasLimit (next) { - if (self.transactionContextAPI.getGasLimit) { - return self.transactionContextAPI.getGasLimit(next) - } - next(null, 3000000) - }, - function queryValue (gasLimit, next) { - if (args.value) { - return next(null, args.value, gasLimit) - } - if (args.useCall || !self.transactionContextAPI.getValue) { - return next(null, 0, gasLimit) - } - self.transactionContextAPI.getValue(function (err, value) { - next(err, value, gasLimit) - }) - }, - function getAccount (value, gasLimit, next) { - if (args.from) { - return next(null, args.from, value, gasLimit) - } - if (self.transactionContextAPI.getAddress) { - return self.transactionContextAPI.getAddress(function (err, address) { - next(err, address, value, gasLimit) - }) - } - self.getAccounts(function (err, accounts) { - const address = accounts[0] - - if (err) return next(err) - if (!address) return next('No accounts available') - if (self.executionContext.isVM() && !self.accounts[address]) { - return next('Invalid account selected') - } - next(null, address, value, gasLimit) - }) - }, - function runTransaction (fromAddress, value, gasLimit, next) { - const tx = { to: args.to, data: args.data.dataHex, useCall: args.useCall, from: fromAddress, value: value, gasLimit: gasLimit, timestamp: args.data.timestamp } - const payLoad = { funAbi: args.data.funAbi, funArgs: args.data.funArgs, contractBytecode: args.data.contractBytecode, contractName: args.data.contractName, contractABI: args.data.contractABI, linkReferences: args.data.linkReferences } - let timestamp = Date.now() - if (tx.timestamp) { - timestamp = tx.timestamp - } - - self.event.trigger('initiatingTransaction', [timestamp, tx, payLoad]) - self.txRunner.rawRun(tx, confirmationCb, continueCb, promptCb, - function (error, result) { - const eventName = (tx.useCall ? 'callExecuted' : 'transactionExecuted') - self.event.trigger(eventName, [error, tx.from, tx.to, tx.data, tx.useCall, result, timestamp, payLoad]) - - if (error && (typeof (error) !== 'string')) { - if (error.message) error = error.message - else { - // eslint-disable-next-line no-empty - try { error = 'error: ' + JSON.stringify(error) } catch (e) {} - } - } - next(error, result) - } - ) - } - ], cb) - } -} diff --git a/libs/remix-lib/src/web3Provider/web3VmProvider.ts b/libs/remix-lib/src/web3Provider/web3VmProvider.ts index 367370f928..361c2b7161 100644 --- a/libs/remix-lib/src/web3Provider/web3VmProvider.ts +++ b/libs/remix-lib/src/web3Provider/web3VmProvider.ts @@ -31,6 +31,9 @@ export class Web3VmProvider { toBigNumber isAddress utils + txsMapBlock + blocks + latestBlockNumber constructor () { this.web3 = new Web3() @@ -69,6 +72,9 @@ export class Web3VmProvider { this.toBigNumber = (...args) => this.web3.utils.toBN(...args) this.isAddress = (...args) => this.web3.utils.isAddress(...args) this.utils = Web3.utils || [] + this.txsMapBlock = {} + this.blocks = {} + this.latestBlockNumber } setVM (vm) { diff --git a/libs/remix-lib/test/txFormat.ts b/libs/remix-lib/test/txFormat.ts index d33760f9f9..2ab4a59b63 100644 --- a/libs/remix-lib/test/txFormat.ts +++ b/libs/remix-lib/test/txFormat.ts @@ -161,8 +161,8 @@ tape('ContractParameters - (TxFormat.buildData) - link Libraries', function (t) } const callbackDeployLibraries = (param, callback) => { callback(null, { - result: { - createdAddress: fakeDeployedContracts[param.data.contractName] + receipt: { + contractAddress: fakeDeployedContracts[param.data.contractName] } }) } // fake diff --git a/libs/remix-lib/test/txResultHelper.ts b/libs/remix-lib/test/txResultHelper.ts index 8fa2e5cdf2..67a136dc84 100644 --- a/libs/remix-lib/test/txResultHelper.ts +++ b/libs/remix-lib/test/txResultHelper.ts @@ -18,12 +18,13 @@ const GAS_USED_INT = 75427 const GAS_USED_HEX = '0x126a3' const NODE_CALL_RESULT = { + receipt: {}, result: RETURN_VALUE_HEX, transactionHash: undefined } const NODE_TX_RESULT = { - result: { + receipt: { blockHash: '0x380485a4e6372a42e36489783c7f7cb66257612133cd245859c206fd476e9c44', blockNumber: 5994, contractAddress: CONTRACT_ADDRESS_HEX, @@ -39,26 +40,31 @@ const NODE_TX_RESULT = { } const VM_RESULT = { - result: { + receipt: { amountSpent: new BN(1), - createdAddress: CONTRACT_ADDRESS_BUFFER, + contractAddress: CONTRACT_ADDRESS_BUFFER, gasRefund: new BN(0), gasUsed: new BN(GAS_USED_INT), status: STATUS_OK, - execResult: { - exceptionError: null, - gasRefund: new BN(0), - gasUsed: new BN(GAS_USED_INT), - returnValue: RETURN_VALUE_BUFFER - } }, transactionHash: TRANSACTION_HASH } +const EXEC_RESULT = { + exceptionError: null, + gasRefund: new BN(0), + gasUsed: new BN(GAS_USED_INT), + returnValue: RETURN_VALUE_BUFFER +} + +const EXEC_RESULT_ERROR = { + exceptionError: 'this is an error' +} + tape('converts node transaction result to RemixTx', function (t) { // contract creation let txResult = { ...NODE_TX_RESULT } - let remixTx = resultToRemixTx(txResult) + let remixTx = resultToRemixTx(txResult, {}) t.equal(remixTx.transactionHash, TRANSACTION_HASH) t.equal(remixTx.createdAddress, CONTRACT_ADDRESS_HEX) @@ -68,8 +74,8 @@ tape('converts node transaction result to RemixTx', function (t) { t.equal(remixTx.error, undefined) // contract method tx - txResult.result.contractAddress = null - remixTx = resultToRemixTx(txResult) + txResult.receipt.contractAddress = null + remixTx = resultToRemixTx(txResult, {}) t.equal(remixTx.createdAddress, null) t.end() @@ -77,7 +83,7 @@ tape('converts node transaction result to RemixTx', function (t) { tape('converts node call result to RemixTx', function (t) { let txResult = { ...NODE_CALL_RESULT } - let remixTx = resultToRemixTx(txResult) + let remixTx = resultToRemixTx(txResult, {}) t.equal(remixTx.transactionHash, undefined) t.equal(remixTx.createdAddress, undefined) @@ -91,7 +97,7 @@ tape('converts node call result to RemixTx', function (t) { tape('converts VM result to RemixTx', function (t) { let txResult = { ...VM_RESULT } - let remixTx = resultToRemixTx(txResult) + let remixTx = resultToRemixTx(txResult, EXEC_RESULT) t.equal(remixTx.transactionHash, TRANSACTION_HASH) @@ -101,8 +107,7 @@ tape('converts VM result to RemixTx', function (t) { t.equal(remixTx.return, RETURN_VALUE_HEX) t.equal(remixTx.error, null) - txResult.result.execResult.exceptionError = 'this is an error' - remixTx = resultToRemixTx(txResult) + remixTx = resultToRemixTx(VM_RESULT, EXEC_RESULT_ERROR) t.equal(remixTx.error, 'this is an error') t.end() diff --git a/libs/remix-simulator/src/genesis.ts b/libs/remix-simulator/src/genesis.ts index f3d7d0509e..839a0c4cb6 100644 --- a/libs/remix-simulator/src/genesis.ts +++ b/libs/remix-simulator/src/genesis.ts @@ -1,7 +1,7 @@ import { Block } from '@ethereumjs/block' import { BN } from 'ethereumjs-util' -export function generateBlock (executionContext) { +export function generateBlock (vmContext) { return new Promise((resolve, reject) => { const block: Block = Block.fromBlockData({ header: { @@ -11,10 +11,10 @@ export function generateBlock (executionContext) { difficulty: new BN('69762765929000', 10), gasLimit: new BN('8000000').imuln(1) } - }, { common: executionContext.vmObject().common }) + }, { common: vmContext.vmObject().common }) - executionContext.vm().runBlock({ block: block, generate: true, skipBlockValidation: true, skipBalance: false }).then(() => { - executionContext.addBlock(block) + vmContext.vm().runBlock({ block: block, generate: true, skipBlockValidation: true, skipBalance: false }).then(() => { + vmContext.addBlock(block) resolve({}) }).catch((e) => reject(e)) }) diff --git a/libs/remix-simulator/src/index.ts b/libs/remix-simulator/src/index.ts index fd73199808..19ca3ab802 100644 --- a/libs/remix-simulator/src/index.ts +++ b/libs/remix-simulator/src/index.ts @@ -1 +1 @@ -export { Provider } from './provider' +export { Provider, extend } from './provider' diff --git a/libs/remix-simulator/src/methods/accounts.ts b/libs/remix-simulator/src/methods/accounts.ts index 6fd359594b..8d18498afc 100644 --- a/libs/remix-simulator/src/methods/accounts.ts +++ b/libs/remix-simulator/src/methods/accounts.ts @@ -6,22 +6,20 @@ export class Accounts { web3 accounts: Record accountsKeys: Record - executionContext + vmContext - constructor (executionContext) { + constructor (vmContext) { this.web3 = new Web3() - this.executionContext = executionContext + this.vmContext = vmContext // TODO: make it random and/or use remix-libs this.accounts = {} this.accountsKeys = {} - this.executionContext.init({ get: () => { return true } }) } async resetAccounts (): Promise { - // TODO: setting this to {} breaks the app currently, unclear why still - // this.accounts = {} - // this.accountsKeys = {} + this.accounts = {} + this.accountsKeys = {} await this._addAccount('503f38a9c967ed597e47fe25643985f032b072db8075426a92110f82df48dfcb', '0x56BC75E2D63100000') await this._addAccount('7e5bfb82febc4c2c8529167104271ceec190eafdca277314912eaabdb67c6e5f', '0x56BC75E2D63100000') await this._addAccount('cc6d63f85de8fef05446ebdd3c537c72152d0fc437fd7aa62b3019b79bd1fdd4', '0x56BC75E2D63100000') @@ -47,7 +45,7 @@ export class Accounts { this.accounts[addressStr] = { privateKey, nonce: 0 } this.accountsKeys[addressStr] = '0x' + privateKey.toString('hex') - const stateManager = this.executionContext.vm().stateManager + const stateManager = this.vmContext.vm().stateManager stateManager.getAccount(Address.fromString(addressStr)).then((account) => { account.balance = new BN(balance.replace('0x', '') || 'f00000000000000001', 16) stateManager.putAccount(Address.fromString(addressStr), account).catch((error) => { @@ -85,7 +83,7 @@ export class Accounts { eth_getBalance (payload, cb) { const address = payload.params[0] - this.executionContext.vm().stateManager.getAccount(Address.fromString(address)).then((account) => { + this.vmContext.vm().stateManager.getAccount(Address.fromString(address)).then((account) => { cb(null, new BN(account.balance).toString(10)) }).catch((error) => { cb(error) diff --git a/libs/remix-simulator/src/methods/blocks.ts b/libs/remix-simulator/src/methods/blocks.ts index bfed840284..7ac7bc179f 100644 --- a/libs/remix-simulator/src/methods/blocks.ts +++ b/libs/remix-simulator/src/methods/blocks.ts @@ -1,10 +1,10 @@ export class Blocks { - executionContext + vmContext coinbase: string blockNumber: number - constructor (executionContext, _options) { - this.executionContext = executionContext + constructor (vmContext, _options) { + this.vmContext = vmContext const options = _options || {} this.coinbase = options.coinbase || '0x0000000000000000000000000000000000000000' this.blockNumber = 0 @@ -28,13 +28,13 @@ export class Blocks { eth_getBlockByNumber (payload, cb) { let blockIndex = payload.params[0] if (blockIndex === 'latest') { - blockIndex = this.executionContext.latestBlockNumber + blockIndex = this.vmContext.latestBlockNumber } if (Number.isInteger(blockIndex)) { blockIndex = '0x' + blockIndex.toString(16) } - const block = this.executionContext.blocks[blockIndex] + const block = this.vmContext.blocks[blockIndex] if (!block) { return cb(new Error('block not found')) @@ -70,7 +70,7 @@ export class Blocks { } eth_getBlockByHash (payload, cb) { - const block = this.executionContext.blocks[payload.params[0]] + const block = this.vmContext.blocks[payload.params[0]] const b = { number: this.toHex(block.header.number), @@ -109,13 +109,13 @@ export class Blocks { } eth_getBlockTransactionCountByHash (payload, cb) { - const block = this.executionContext.blocks[payload.params[0]] + const block = this.vmContext.blocks[payload.params[0]] cb(null, block.transactions.length) } eth_getBlockTransactionCountByNumber (payload, cb) { - const block = this.executionContext.blocks[payload.params[0]] + const block = this.vmContext.blocks[payload.params[0]] cb(null, block.transactions.length) } @@ -131,7 +131,7 @@ export class Blocks { eth_getStorageAt (payload, cb) { const [address, position, blockNumber] = payload.params - this.executionContext.web3().debug.storageRangeAt(blockNumber, 'latest', address.toLowerCase(), position, 1, (err, result) => { + this.vmContext.web3().debug.storageRangeAt(blockNumber, 'latest', address.toLowerCase(), position, 1, (err, result) => { if (err || (result.storage && Object.values(result.storage).length === 0)) { return cb(err, '') } diff --git a/libs/remix-simulator/src/methods/debug.ts b/libs/remix-simulator/src/methods/debug.ts index ae7f3067b8..b9bf886e50 100644 --- a/libs/remix-simulator/src/methods/debug.ts +++ b/libs/remix-simulator/src/methods/debug.ts @@ -1,8 +1,8 @@ export class Debug { - executionContext + vmContext - constructor (executionContext) { - this.executionContext = executionContext + constructor (vmContext) { + this.vmContext = vmContext } methods () { @@ -14,15 +14,15 @@ export class Debug { } debug_traceTransaction (payload, cb) { - this.executionContext.web3().debug.traceTransaction(payload.params[0], {}, cb) + this.vmContext.web3().debug.traceTransaction(payload.params[0], {}, cb) } debug_preimage (payload, cb) { - this.executionContext.web3().debug.preimage(payload.params[0], cb) + this.vmContext.web3().debug.preimage(payload.params[0], cb) } debug_storageRangeAt (payload, cb) { - this.executionContext.web3().debug.storageRangeAt( + this.vmContext.web3().debug.storageRangeAt( payload.params[0], payload.params[1], payload.params[2], diff --git a/libs/remix-simulator/src/methods/filters.ts b/libs/remix-simulator/src/methods/filters.ts index c27cc49bd6..43404a1c31 100644 --- a/libs/remix-simulator/src/methods/filters.ts +++ b/libs/remix-simulator/src/methods/filters.ts @@ -1,8 +1,8 @@ export class Filters { - executionContext + vmContext - constructor (executionContext) { - this.executionContext = executionContext + constructor (vmContext) { + this.vmContext = vmContext } methods () { @@ -14,49 +14,49 @@ export class Filters { } eth_getLogs (payload, cb) { - const results = this.executionContext.logsManager.getLogsFor(payload.params[0]) + const results = this.vmContext.logsManager.getLogsFor(payload.params[0]) cb(null, results) } eth_subscribe (payload, cb) { - const subscriptionId = this.executionContext.logsManager.subscribe(payload.params) + const subscriptionId = this.vmContext.logsManager.subscribe(payload.params) cb(null, subscriptionId) } eth_unsubscribe (payload, cb) { - this.executionContext.logsManager.unsubscribe(payload.params[0]) + this.vmContext.logsManager.unsubscribe(payload.params[0]) cb(null, true) } eth_newFilter (payload, cb) { - const filterId = this.executionContext.logsManager.newFilter('filter', payload.params[0]) + const filterId = this.vmContext.logsManager.newFilter('filter', payload.params[0]) cb(null, filterId) } eth_newBlockFilter (payload, cb) { - const filterId = this.executionContext.logsManager.newFilter('block') + const filterId = this.vmContext.logsManager.newFilter('block') cb(null, filterId) } eth_newPendingTransactionFilter (payload, cb) { - const filterId = this.executionContext.logsManager.newFilter('pendingTransactions') + const filterId = this.vmContext.logsManager.newFilter('pendingTransactions') cb(null, filterId) } eth_uninstallfilter (payload, cb) { - const result = this.executionContext.logsManager.uninstallFilter(payload.params[0]) + const result = this.vmContext.logsManager.uninstallFilter(payload.params[0]) cb(null, result) } eth_getFilterChanges (payload, cb) { const filterId = payload.params[0] - const results = this.executionContext.logsManager.getLogsForFilter(filterId) + const results = this.vmContext.logsManager.getLogsForFilter(filterId) cb(null, results) } eth_getFilterLogs (payload, cb) { const filterId = payload.params[0] - const results = this.executionContext.logsManager.getLogsForFilter(filterId, true) + const results = this.vmContext.logsManager.getLogsForFilter(filterId, true) cb(null, results) } } diff --git a/libs/remix-simulator/src/methods/transactions.ts b/libs/remix-simulator/src/methods/transactions.ts index f3e8ca3f43..04ef986403 100644 --- a/libs/remix-simulator/src/methods/transactions.ts +++ b/libs/remix-simulator/src/methods/transactions.ts @@ -3,11 +3,14 @@ import { toChecksumAddress, BN, Address } from 'ethereumjs-util' import { processTx } from './txProcess' export class Transactions { - executionContext + vmContext accounts + tags - constructor (executionContext) { - this.executionContext = executionContext + + constructor (vmContext) { + this.vmContext = vmContext + this.tags = {} } init (accounts) { @@ -24,7 +27,9 @@ export class Transactions { eth_getTransactionCount: this.eth_getTransactionCount.bind(this), eth_getTransactionByHash: this.eth_getTransactionByHash.bind(this), eth_getTransactionByBlockHashAndIndex: this.eth_getTransactionByBlockHashAndIndex.bind(this), - eth_getTransactionByBlockNumberAndIndex: this.eth_getTransactionByBlockNumberAndIndex.bind(this) + eth_getTransactionByBlockNumberAndIndex: this.eth_getTransactionByBlockNumberAndIndex.bind(this), + eth_getExecutionResultFromSimulator: this.eth_getExecutionResultFromSimulator.bind(this), + eth_getHashFromTagBySimulator: this.eth_getHashFromTagBySimulator.bind(this) } } @@ -33,16 +38,30 @@ export class Transactions { if (payload.params && payload.params.length > 0 && payload.params[0].from) { payload.params[0].from = toChecksumAddress(payload.params[0].from) } - processTx(this.executionContext, this.accounts, payload, false, cb) + processTx(this.vmContext, this.accounts, payload, false, (error, result) => { + if (!error && result) { + this.vmContext.addBlock(result.block) + const hash = '0x' + result.tx.hash().toString('hex') + this.vmContext.trackTx(hash, result.block) + this.vmContext.trackExecResult(hash, result.result.execResult) + return cb (null, result.transactionHash) + } + cb(error) + }) + } + + eth_getExecutionResultFromSimulator (payload, cb) { + const txHash = payload.params[0] + cb(null, this.vmContext.exeResults[txHash]) } eth_getTransactionReceipt (payload, cb) { - this.executionContext.web3().eth.getTransactionReceipt(payload.params[0], (error, receipt) => { + this.vmContext.web3().eth.getTransactionReceipt(payload.params[0], (error, receipt) => { if (error) { return cb(error) } - const txBlock = this.executionContext.txs[receipt.hash] + const txBlock = this.vmContext.txs[receipt.hash] const r: Record = { transactionHash: receipt.hash, @@ -72,7 +91,7 @@ export class Transactions { eth_getCode (payload, cb) { const address = payload.params[0] - this.executionContext.web3().eth.getCode(address, (error, result) => { + this.vmContext.web3().eth.getCode(address, (error, result) => { if (error) { console.dir('error getting code') console.dir(error) @@ -91,14 +110,32 @@ export class Transactions { } payload.params[0].value = undefined + + const tag = payload.params[0].timestamp // e2e reference + + processTx(this.vmContext, this.accounts, payload, true, (error, result) => { + if (!error && result) { + this.vmContext.addBlock(result.block) + const hash = '0x' + result.tx.hash().toString('hex') + this.vmContext.trackTx(hash, result.block) + this.vmContext.trackExecResult(hash, result.result.execResult) + this.tags[tag] = result.transactionHash + // calls are not supposed to return a transaction hash. we do this for keeping track of it and allowing debugging calls. + const returnValue = `0x${result.result.execResult.returnValue.toString('hex') || '0'}` + return cb (null, returnValue) + } + cb(error) + }) + } - processTx(this.executionContext, this.accounts, payload, true, cb) + eth_getHashFromTagBySimulator (payload, cb) { + return cb(null, this.tags[payload.params[0]]) } eth_getTransactionCount (payload, cb) { const address = payload.params[0] - this.executionContext.vm().stateManager.getAccount(Address.fromString(address)).then((account) => { + this.vmContext.vm().stateManager.getAccount(Address.fromString(address)).then((account) => { const nonce = new BN(account.nonce).toString(10) cb(null, nonce) }).catch((error) => { @@ -109,12 +146,12 @@ export class Transactions { eth_getTransactionByHash (payload, cb) { const address = payload.params[0] - this.executionContext.web3().eth.getTransactionReceipt(address, (error, receipt) => { + this.vmContext.web3().eth.getTransactionReceipt(address, (error, receipt) => { if (error) { return cb(error) } - const txBlock = this.executionContext.txs[receipt.transactionHash] + const txBlock = this.vmContext.txs[receipt.transactionHash] // TODO: params to add later const r: Record = { @@ -154,10 +191,10 @@ export class Transactions { eth_getTransactionByBlockHashAndIndex (payload, cb) { const txIndex = payload.params[1] - const txBlock = this.executionContext.blocks[payload.params[0]] + const txBlock = this.vmContext.blocks[payload.params[0]] const txHash = '0x' + txBlock.transactions[Web3.utils.toDecimal(txIndex)].hash().toString('hex') - this.executionContext.web3().eth.getTransactionReceipt(txHash, (error, receipt) => { + this.vmContext.web3().eth.getTransactionReceipt(txHash, (error, receipt) => { if (error) { return cb(error) } @@ -196,10 +233,10 @@ export class Transactions { eth_getTransactionByBlockNumberAndIndex (payload, cb) { const txIndex = payload.params[1] - const txBlock = this.executionContext.blocks[payload.params[0]] + const txBlock = this.vmContext.blocks[payload.params[0]] const txHash = '0x' + txBlock.transactions[Web3.utils.toDecimal(txIndex)].hash().toString('hex') - this.executionContext.web3().eth.getTransactionReceipt(txHash, (error, receipt) => { + this.vmContext.web3().eth.getTransactionReceipt(txHash, (error, receipt) => { if (error) { return cb(error) } diff --git a/libs/remix-simulator/src/methods/txProcess.ts b/libs/remix-simulator/src/methods/txProcess.ts index 8f8acb5b2f..821206eae8 100644 --- a/libs/remix-simulator/src/methods/txProcess.ts +++ b/libs/remix-simulator/src/methods/txProcess.ts @@ -1,15 +1,15 @@ import { execution } from '@remix-project/remix-lib' const TxExecution = execution.txExecution -const TxRunner = execution.txRunner +const TxRunnerVM = execution.TxRunnerVM +const TxRunner = execution.TxRunner + function runCall (payload, from, to, data, value, gasLimit, txRunner, callbacks, callback) { const finalCallback = function (err, result) { if (err) { return callback(err) - } - const returnValue = result.result.execResult.returnValue.toString('hex') - const toReturn = `0x${returnValue || '0'}` - return callback(null, toReturn) + } + return callback(null, result) } TxExecution.callFunction(from, to, data, value, gasLimit, { constant: true }, txRunner, callbacks, finalCallback) @@ -20,7 +20,7 @@ function runTx (payload, from, to, data, value, gasLimit, txRunner, callbacks, c if (err) { return callback(err) } - callback(null, result.transactionHash) + callback(null, result) } TxExecution.callFunction(from, to, data, value, gasLimit, { constant: false }, txRunner, callbacks, finalCallback) @@ -31,15 +31,16 @@ function createContract (payload, from, data, value, gasLimit, txRunner, callbac if (err) { return callback(err) } - callback(null, result.transactionHash) + callback(null, result) } TxExecution.createContract(from, data, value, gasLimit, txRunner, callbacks, finalCallback) } +let txRunnerVMInstance let txRunnerInstance -export function processTx (executionContext, accounts, payload, isCall, callback) { +export function processTx (vmContext, accounts, payload, isCall, callback) { const api = { logMessage: (msg) => { }, @@ -61,11 +62,11 @@ export function processTx (executionContext, accounts, payload, isCall, callback } } - executionContext.init(api.config) - - // let txRunner = new TxRunner(accounts, api) + if (!txRunnerVMInstance) { + txRunnerVMInstance = new TxRunnerVM(accounts, api, _ => vmContext.vm()) + } if (!txRunnerInstance) { - txRunnerInstance = new TxRunner(accounts, api, executionContext) + txRunnerInstance = new TxRunner(txRunnerVMInstance, { runAsync: false }) } txRunnerInstance.vmaccounts = accounts let { from, to, data, value, gas } = payload.params[0] diff --git a/libs/remix-simulator/src/provider.ts b/libs/remix-simulator/src/provider.ts index 8fdb5b5aba..5046660d7f 100644 --- a/libs/remix-simulator/src/provider.ts +++ b/libs/remix-simulator/src/provider.ts @@ -1,5 +1,4 @@ import { Blocks } from './methods/blocks' -import { execution } from '@remix-project/remix-lib' import { info } from './utils/logs' import merge from 'merge' @@ -11,11 +10,11 @@ import { methods as netMethods } from './methods/net' import { Transactions } from './methods/transactions' import { Debug } from './methods/debug' import { generateBlock } from './genesis' -const { executionContext } = execution +import { VMContext } from './vm-context' export class Provider { options: Record - executionContext + vmContext Accounts Transactions methods @@ -26,23 +25,23 @@ export class Provider { this.options = options this.host = host this.connected = true - // TODO: init executionContext here - this.executionContext = executionContext - this.Accounts = new Accounts(this.executionContext) - this.Transactions = new Transactions(this.executionContext) + this.vmContext = new VMContext() + + this.Accounts = new Accounts(this.vmContext) + this.Transactions = new Transactions(this.vmContext) this.methods = {} this.methods = merge(this.methods, this.Accounts.methods()) - this.methods = merge(this.methods, (new Blocks(this.executionContext, options)).methods()) + this.methods = merge(this.methods, (new Blocks(this.vmContext, options)).methods()) this.methods = merge(this.methods, miscMethods()) - this.methods = merge(this.methods, (new Filters(this.executionContext)).methods()) + this.methods = merge(this.methods, (new Filters(this.vmContext)).methods()) this.methods = merge(this.methods, netMethods()) this.methods = merge(this.methods, this.Transactions.methods()) - this.methods = merge(this.methods, (new Debug(this.executionContext)).methods()) + this.methods = merge(this.methods, (new Debug(this.vmContext)).methods()) } async init () { - await generateBlock(this.executionContext) + await generateBlock(this.vmContext) await this.Accounts.resetAccounts() this.Transactions.init(this.Accounts.accounts) } @@ -87,6 +86,40 @@ export class Provider { }; on (type, cb) { - this.executionContext.logsManager.addListener(type, cb) + this.vmContext.logsManager.addListener(type, cb) } } + +export function extend (web3) { + if (!web3.extend) { + return + } + // DEBUG + const methods = [] + if (!(web3.eth && web3.eth.getExecutionResultFromSimulator)) { + methods.push(new web3.extend.Method({ + name: 'getExecutionResultFromSimulator', + call: 'eth_getExecutionResultFromSimulator', + inputFormatter: [null], + params: 1 + })) + } + + if (!(web3.eth && web3.eth.getHashFromTagBySimulator)) { + methods.push(new web3.extend.Method({ + name: 'getHashFromTagBySimulator', + call: 'eth_getHashFromTagBySimulator', + inputFormatter: [null], + params: 1 + })) + } + + if (methods.length > 0) { + web3.extend({ + property: 'eth', + methods: methods, + properties: [] + }) + } +} + diff --git a/libs/remix-simulator/src/vm-context.ts b/libs/remix-simulator/src/vm-context.ts new file mode 100644 index 0000000000..481f5f4057 --- /dev/null +++ b/libs/remix-simulator/src/vm-context.ts @@ -0,0 +1,147 @@ +/* global ethereum */ +'use strict' +import Web3 from 'web3' +import { rlp, keccak, bufferToHex } from 'ethereumjs-util' +import { vm, execution } from '@remix-project/remix-lib' +const EthJSVM = require('ethereumjs-vm').default +const StateManager = require('ethereumjs-vm/dist/state/stateManager').default + +/* + extend vm state manager and instanciate VM +*/ + +class StateManagerCommonStorageDump extends StateManager { + constructor (arg) { + super(arg) + this.keyHashes = {} + } + + putContractStorage (address, key, value, cb) { + this.keyHashes[keccak(key).toString('hex')] = bufferToHex(key) + super.putContractStorage(address, key, value, cb) + } + + dumpStorage (address, cb) { + this._getStorageTrie(address, (err, trie) => { + if (err) { + return cb(err) + } + const storage = {} + const stream = trie.createReadStream() + stream.on('data', (val) => { + const value = rlp.decode(val.value) + storage['0x' + val.key.toString('hex')] = { + key: this.keyHashes[val.key.toString('hex')], + value: '0x' + value.toString('hex') + } + }) + stream.on('end', function () { + cb(storage) + }) + }) + } + + getStateRoot (cb) { + const checkpoint = this._checkpointCount + this._checkpointCount = 0 + super.getStateRoot((err, stateRoot) => { + this._checkpointCount = checkpoint + cb(err, stateRoot) + }) + } + + setStateRoot (stateRoot, cb) { + const checkpoint = this._checkpointCount + this._checkpointCount = 0 + super.setStateRoot(stateRoot, (err) => { + this._checkpointCount = checkpoint + cb(err) + }) + } +} + +/* + trigger contextChanged, web3EndpointChanged +*/ +export class VMContext { + currentFork: string + blockGasLimitDefault: number + blockGasLimit: number + customNetWorks + blocks + latestBlockNumber + txs + vms + web3vm + logsManager + exeResults + + constructor () { + this.blockGasLimitDefault = 4300000 + this.blockGasLimit = this.blockGasLimitDefault + this.currentFork = 'muirGlacier' + this.vms = { + /* + byzantium: createVm('byzantium'), + constantinople: createVm('constantinople'), + petersburg: createVm('petersburg'), + istanbul: createVm('istanbul'), + */ + muirGlacier: this.createVm('muirGlacier') + } + this.blocks = {} + this.latestBlockNumber = 0 + this.txs = {} + this.exeResults = {} + this.logsManager = new execution.LogsManager() + + } + + createVm (hardfork) { + const stateManager = new StateManagerCommonStorageDump({}) + stateManager.checkpoint(() => {}) + const ethvm = new EthJSVM({ + activatePrecompiles: true, + blockchain: stateManager.blockchain, + stateManager: stateManager, + hardfork: hardfork + }) + ethvm.blockchain.validate = false + this.web3vm = new vm.Web3VMProvider() + this.web3vm.setVM(ethvm) + return { vm: ethvm, web3vm: this.web3vm, stateManager } + } + + web3 () { + return this.web3vm + } + + blankWeb3 () { + return new Web3() + } + + vm () { + return this.vms[this.currentFork].vm + } + + addBlock (block) { + let blockNumber = '0x' + block.header.number.toString('hex') + if (blockNumber === '0x') { + blockNumber = '0x0' + } + + this.blocks['0x' + block.hash().toString('hex')] = block + this.blocks[blockNumber] = block + this.latestBlockNumber = blockNumber + + this.logsManager.checkBlock(blockNumber, block, this.web3vm) + } + + trackTx (tx, block) { + this.txs[tx] = block + } + + trackExecResult (tx, execReult) { + this.exeResults[tx] = execReult + } +} diff --git a/libs/remix-tests/jest.config.js b/libs/remix-tests/jest.config.js index 7923ddf044..dcbd823a5c 100644 --- a/libs/remix-tests/jest.config.js +++ b/libs/remix-tests/jest.config.js @@ -1,7 +1,7 @@ module.exports = { name: 'remix-tests', preset: '../../jest.config.js', - verbose: true, + verbose: false, silent: false, // Silent console messages, specially the 'remix-simulator' ones transform: { '^.+\\.[tj]sx?$': 'ts-jest', @@ -18,6 +18,6 @@ module.exports = { "!src/types.ts", "!src/logger.ts" ], - coverageDirectory: '../../coverage/libs/remix-tests', + coverageDirectory: '../../coverage/libs/remix-tests' }; \ No newline at end of file diff --git a/libs/remix-tests/src/deployer.ts b/libs/remix-tests/src/deployer.ts index c6e00456fa..1e3fcb8d88 100644 --- a/libs/remix-tests/src/deployer.ts +++ b/libs/remix-tests/src/deployer.ts @@ -79,7 +79,7 @@ export function deployAll (compileResult: compilationInterface, web3: Web3, with contracts[contractName] = contractObject contracts[contractName].filename = filename - callback(null, { result: { createdAddress: receipt.contractAddress } }) // TODO this will only work with JavaScriptV VM + callback(null, { receipt: { contractAddress: receipt.contractAddress } }) // TODO this will only work with JavaScriptV VM }).on('error', function (err) { console.error(err) callback(err) diff --git a/libs/remix-tests/tests/testRunner.cli.spec.ts b/libs/remix-tests/tests/testRunner.cli.spec.ts index 3d906095f3..a865bbc649 100644 --- a/libs/remix-tests/tests/testRunner.cli.spec.ts +++ b/libs/remix-tests/tests/testRunner.cli.spec.ts @@ -3,17 +3,23 @@ import { resolve } from 'path' describe('testRunner: remix-tests CLI', () => { // remix-tests binary, after build, is used as executable + const executablePath = resolve(__dirname + '/../../../dist/libs/remix-tests/bin/remix-tests') + const result = spawnSync('ls', { cwd: resolve(__dirname + '/../../../dist/libs/remix-tests') }) if(result) { const dirContent = result.stdout.toString() // Install dependencies if 'node_modules' is not already present - if(!dirContent.includes('node_modules')) execSync('npm install', { cwd: resolve(__dirname + '/../../../dist/libs/remix-tests') }) + if(!dirContent.includes('node_modules')) execSync( + 'ln -s ' + __dirname + '/../../../node_modules node_modules', + { cwd: resolve(__dirname + '/../../../dist/libs/remix-tests') }) } + describe('test various CLI options', () => { test('remix-tests version', () => { const res = spawnSync(executablePath, ['-V']) + console.log(res.stdout.toString()) expect(res.stdout.toString().trim()).toBe(require('../package.json').version) }) diff --git a/libs/remix-tests/tsconfig.json b/libs/remix-tests/tsconfig.json index 4422abf942..cf7076c1d1 100644 --- a/libs/remix-tests/tsconfig.json +++ b/libs/remix-tests/tsconfig.json @@ -3,9 +3,9 @@ "compilerOptions": { "types": ["node", "jest"], "module": "commonjs", + "esModuleInterop": true, "allowJs": true, "rootDir": "./", - "esModuleInterop": true }, "include": ["**/*.ts"] } \ No newline at end of file diff --git a/libs/remix-tests/tsconfig.lib.json b/libs/remix-tests/tsconfig.lib.json index ac117282b1..aec3a4c785 100644 --- a/libs/remix-tests/tsconfig.lib.json +++ b/libs/remix-tests/tsconfig.lib.json @@ -1,16 +1,15 @@ { - "extends": "./tsconfig.json", - "compilerOptions": { - "module": "commonjs", - "outDir": "../../dist/out-tsc", - "declaration": true, - "rootDir": "./", - "types": ["node"] - }, - "exclude": [ - "**/*.spec.ts", - "tests/" - ], - "include": ["**/*.ts"] + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "commonjs", + "outDir": "../../dist/out-tsc", + "declaration": true, + "rootDir": "./", + "types": ["node"] + }, + "exclude": [ + "**/*.spec.ts", + "test/" + ], + "include": ["**/*.ts"] } - \ No newline at end of file diff --git a/libs/remix-tests/tsconfig.spec.json b/libs/remix-tests/tsconfig.spec.json index 118a64f08e..559410b96a 100644 --- a/libs/remix-tests/tsconfig.spec.json +++ b/libs/remix-tests/tsconfig.spec.json @@ -1,16 +1,15 @@ { - "extends": "./tsconfig.json", - "compilerOptions": { - "outDir": "../../dist/out-tsc", - "module": "commonjs", - "types": ["jest", "node"] - }, - "include": [ - "**/*.spec.ts", - "**/*.spec.tsx", - "**/*.spec.js", - "**/*.spec.jsx", - "**/*.d.ts" - ] - } - \ No newline at end of file + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node"] + }, + "include": [ + "**/*.spec.ts", + "**/*.spec.tsx", + "**/*.spec.js", + "**/*.spec.jsx", + "**/*.d.ts" + ] +} From a1769914f1bc44fa29b29fcd538de601893bfda8 Mon Sep 17 00:00:00 2001 From: yann300 Date: Wed, 10 Mar 2021 12:57:39 +0100 Subject: [PATCH 02/18] linting --- .../src/execution/execution-context.ts | 2 +- libs/remix-lib/src/execution/txRunnerVM.ts | 7 ++--- libs/remix-lib/src/execution/txRunnerWeb3.ts | 10 +++---- .../src/web3Provider/web3VmProvider.ts | 2 +- .../src/methods/transactions.ts | 7 ++--- libs/remix-simulator/src/methods/txProcess.ts | 3 +- libs/remix-simulator/src/provider.ts | 9 +++--- libs/remix-simulator/src/vm-context.ts | 29 +++++++++---------- 8 files changed, 32 insertions(+), 37 deletions(-) diff --git a/libs/remix-lib/src/execution/execution-context.ts b/libs/remix-lib/src/execution/execution-context.ts index ba067e7035..7644081040 100644 --- a/libs/remix-lib/src/execution/execution-context.ts +++ b/libs/remix-lib/src/execution/execution-context.ts @@ -343,5 +343,5 @@ export class ExecutionContext { if (transactionDetailsLinks[network]) { return transactionDetailsLinks[network] + hash } - } + } } diff --git a/libs/remix-lib/src/execution/txRunnerVM.ts b/libs/remix-lib/src/execution/txRunnerVM.ts index 3b221f8b75..bc7e1403a7 100644 --- a/libs/remix-lib/src/execution/txRunnerVM.ts +++ b/libs/remix-lib/src/execution/txRunnerVM.ts @@ -27,7 +27,7 @@ export class TxRunnerVM { this.blockNumber = 0 this.runAsync = true this.blockNumber = 0 // The VM is running in Homestead mode, which started at this block. - this.runAsync = false // We have to run like this cause the VM Event Manager does not support running multiple txs at the same time. + this.runAsync = false // We have to run like this cause the VM Event Manager does not support running multiple txs at the same time. this.pendingTxs = {} this.vmaccounts = vmaccounts this.queusTxs = [] @@ -110,11 +110,10 @@ export class TxRunnerVM { result: result, transactionHash: bufferToHex(Buffer.from(tx.hash())), block, - tx, + tx }) }).catch(function (err) { callback(err) }) - } + } } - diff --git a/libs/remix-lib/src/execution/txRunnerWeb3.ts b/libs/remix-lib/src/execution/txRunnerWeb3.ts index f735b3401c..4554c396e8 100644 --- a/libs/remix-lib/src/execution/txRunnerWeb3.ts +++ b/libs/remix-lib/src/execution/txRunnerWeb3.ts @@ -65,8 +65,8 @@ export class TxRunnerWeb3 { data = '0x' + data } - return this.runInNode(args.from, args.to, data, args.value, args.gasLimit, args.useCall, args.timestamp, confirmationCb, gasEstimationForceSend, promptCb, callback) - } + return this.runInNode(args.from, args.to, data, args.value, args.gasLimit, args.useCall, args.timestamp, confirmationCb, gasEstimationForceSend, promptCb, callback) + } runInNode (from, to, data, value, gasLimit, useCall, timestamp, confirmCb, gasEstimationForceSend, promptCb, callback) { const tx = { from: from, to: to, data: data, value: value } @@ -76,16 +76,16 @@ export class TxRunnerWeb3 { tx['gas'] = gasLimit tx['timestamp'] = timestamp return this.getWeb3().eth.call(tx, function (error, result: any) { - if (error) return callback(error) + if (error) return callback(error) callback(null, { - result: result + result: result }) }) } this.getWeb3().eth.estimateGas(tx, (err, gasEstimation) => { if (err && err.message.indexOf('Invalid JSON RPC response') !== -1) { // // @todo(#378) this should be removed when https://github.com/WalletConnect/walletconnect-monorepo/issues/334 is fixed - new Error('Gas estimation failed because of an unknown internal error. This may indicated that the transaction will fail.') + callback(new Error('Gas estimation failed because of an unknown internal error. This may indicated that the transaction will fail.')) } gasEstimationForceSend(err, () => { // callback is called whenever no error diff --git a/libs/remix-lib/src/web3Provider/web3VmProvider.ts b/libs/remix-lib/src/web3Provider/web3VmProvider.ts index 361c2b7161..59b0e7e909 100644 --- a/libs/remix-lib/src/web3Provider/web3VmProvider.ts +++ b/libs/remix-lib/src/web3Provider/web3VmProvider.ts @@ -74,7 +74,7 @@ export class Web3VmProvider { this.utils = Web3.utils || [] this.txsMapBlock = {} this.blocks = {} - this.latestBlockNumber + this.latestBlockNumber = 0 } setVM (vm) { diff --git a/libs/remix-simulator/src/methods/transactions.ts b/libs/remix-simulator/src/methods/transactions.ts index 04ef986403..969eb0ff6f 100644 --- a/libs/remix-simulator/src/methods/transactions.ts +++ b/libs/remix-simulator/src/methods/transactions.ts @@ -7,7 +7,6 @@ export class Transactions { accounts tags - constructor (vmContext) { this.vmContext = vmContext this.tags = {} @@ -44,7 +43,7 @@ export class Transactions { const hash = '0x' + result.tx.hash().toString('hex') this.vmContext.trackTx(hash, result.block) this.vmContext.trackExecResult(hash, result.result.execResult) - return cb (null, result.transactionHash) + return cb(null, result.transactionHash) } cb(error) }) @@ -110,7 +109,7 @@ export class Transactions { } payload.params[0].value = undefined - + const tag = payload.params[0].timestamp // e2e reference processTx(this.vmContext, this.accounts, payload, true, (error, result) => { @@ -122,7 +121,7 @@ export class Transactions { this.tags[tag] = result.transactionHash // calls are not supposed to return a transaction hash. we do this for keeping track of it and allowing debugging calls. const returnValue = `0x${result.result.execResult.returnValue.toString('hex') || '0'}` - return cb (null, returnValue) + return cb(null, returnValue) } cb(error) }) diff --git a/libs/remix-simulator/src/methods/txProcess.ts b/libs/remix-simulator/src/methods/txProcess.ts index 821206eae8..d51bbf9c73 100644 --- a/libs/remix-simulator/src/methods/txProcess.ts +++ b/libs/remix-simulator/src/methods/txProcess.ts @@ -3,12 +3,11 @@ const TxExecution = execution.txExecution const TxRunnerVM = execution.TxRunnerVM const TxRunner = execution.TxRunner - function runCall (payload, from, to, data, value, gasLimit, txRunner, callbacks, callback) { const finalCallback = function (err, result) { if (err) { return callback(err) - } + } return callback(null, result) } diff --git a/libs/remix-simulator/src/provider.ts b/libs/remix-simulator/src/provider.ts index 5046660d7f..276b66ca15 100644 --- a/libs/remix-simulator/src/provider.ts +++ b/libs/remix-simulator/src/provider.ts @@ -26,7 +26,7 @@ export class Provider { this.host = host this.connected = true this.vmContext = new VMContext() - + this.Accounts = new Accounts(this.vmContext) this.Transactions = new Transactions(this.vmContext) @@ -102,7 +102,7 @@ export function extend (web3) { call: 'eth_getExecutionResultFromSimulator', inputFormatter: [null], params: 1 - })) + })) } if (!(web3.eth && web3.eth.getHashFromTagBySimulator)) { @@ -111,9 +111,9 @@ export function extend (web3) { call: 'eth_getHashFromTagBySimulator', inputFormatter: [null], params: 1 - })) + })) } - + if (methods.length > 0) { web3.extend({ property: 'eth', @@ -122,4 +122,3 @@ export function extend (web3) { }) } } - diff --git a/libs/remix-simulator/src/vm-context.ts b/libs/remix-simulator/src/vm-context.ts index 481f5f4057..e59eb68cdd 100644 --- a/libs/remix-simulator/src/vm-context.ts +++ b/libs/remix-simulator/src/vm-context.ts @@ -75,8 +75,8 @@ export class VMContext { web3vm logsManager exeResults - - constructor () { + + constructor () { this.blockGasLimitDefault = 4300000 this.blockGasLimit = this.blockGasLimitDefault this.currentFork = 'muirGlacier' @@ -94,7 +94,6 @@ export class VMContext { this.txs = {} this.exeResults = {} this.logsManager = new execution.LogsManager() - } createVm (hardfork) { @@ -124,20 +123,20 @@ export class VMContext { return this.vms[this.currentFork].vm } - addBlock (block) { - let blockNumber = '0x' + block.header.number.toString('hex') - if (blockNumber === '0x') { - blockNumber = '0x0' - } - - this.blocks['0x' + block.hash().toString('hex')] = block - this.blocks[blockNumber] = block - this.latestBlockNumber = blockNumber + addBlock (block) { + let blockNumber = '0x' + block.header.number.toString('hex') + if (blockNumber === '0x') { + blockNumber = '0x0' + } + + this.blocks['0x' + block.hash().toString('hex')] = block + this.blocks[blockNumber] = block + this.latestBlockNumber = blockNumber - this.logsManager.checkBlock(blockNumber, block, this.web3vm) - } + this.logsManager.checkBlock(blockNumber, block, this.web3vm) + } - trackTx (tx, block) { + trackTx (tx, block) { this.txs[tx] = block } From 2736a3e8dfa80854124c6a907fe41fc2539ea544 Mon Sep 17 00:00:00 2001 From: yann300 Date: Thu, 11 Mar 2021 13:02:39 +0100 Subject: [PATCH 03/18] fix tests --- .circleci/config.yml | 1 + libs/remix-simulator/package.json | 2 +- libs/remix-solidity/package.json | 2 +- libs/remix-tests/jest.config.js | 3 ++- libs/remix-tests/package.json | 6 +++--- libs/remix-tests/tests/testRunner.cli.spec.ts | 4 +--- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index ab5e15436f..49aada7eb0 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -51,6 +51,7 @@ jobs: - checkout - run: npm install - run: npm run build:libs + - run: export NODE_OPTIONS="--max_old_space_size=8192" - run: npm run test:libs remix-ide-chrome-1: diff --git a/libs/remix-simulator/package.json b/libs/remix-simulator/package.json index eb09b20106..b39deedf5a 100644 --- a/libs/remix-simulator/package.json +++ b/libs/remix-simulator/package.json @@ -14,7 +14,7 @@ ], "main": "src/index.js", "dependencies": { - "@remix-project/remix-lib": "^0.4.34", + "@remix-project/remix-lib": "../remix-lib", "ansi-gray": "^0.1.1", "async": "^3.1.0", "body-parser": "^1.18.2", diff --git a/libs/remix-solidity/package.json b/libs/remix-solidity/package.json index ddb834e951..7a54cfe8d7 100644 --- a/libs/remix-solidity/package.json +++ b/libs/remix-solidity/package.json @@ -15,7 +15,7 @@ } ], "dependencies": { - "@remix-project/remix-lib": "^0.4.34", + "@remix-project/remix-lib": "../remix-lib", "eslint-scope": "^5.0.0", "@ethereumjs/vm": "^5.3.2", "@ethereumjs/block": "^3.2.1", diff --git a/libs/remix-tests/jest.config.js b/libs/remix-tests/jest.config.js index dcbd823a5c..0f08d413b7 100644 --- a/libs/remix-tests/jest.config.js +++ b/libs/remix-tests/jest.config.js @@ -1,11 +1,12 @@ module.exports = { name: 'remix-tests', preset: '../../jest.config.js', - verbose: false, + verbose: true, silent: false, // Silent console messages, specially the 'remix-simulator' ones transform: { '^.+\\.[tj]sx?$': 'ts-jest', }, + transformIgnorePatterns: ["/node_modules/", "\\.pnp\\.[^\\\/]+$"], rootDir: "./", testTimeout: 40000, moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html', 'json'], diff --git a/libs/remix-tests/package.json b/libs/remix-tests/package.json index a2480bfa46..9adf364e2d 100644 --- a/libs/remix-tests/package.json +++ b/libs/remix-tests/package.json @@ -35,9 +35,9 @@ }, "homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-tests#readme", "dependencies": { - "@remix-project/remix-lib": "^0.4.34", - "@remix-project/remix-simulator": "^0.1.10-beta.0", - "@remix-project/remix-solidity": "^0.3.35", + "@remix-project/remix-lib": "../remix-lib", + "@remix-project/remix-simulator": "../remix-simulator", + "@remix-project/remix-solidity": "../remix-solidity", "ansi-gray": "^0.1.1", "async": "^2.6.0", "axios": ">=0.21.1", diff --git a/libs/remix-tests/tests/testRunner.cli.spec.ts b/libs/remix-tests/tests/testRunner.cli.spec.ts index a865bbc649..21ce34afdb 100644 --- a/libs/remix-tests/tests/testRunner.cli.spec.ts +++ b/libs/remix-tests/tests/testRunner.cli.spec.ts @@ -10,9 +10,7 @@ describe('testRunner: remix-tests CLI', () => { if(result) { const dirContent = result.stdout.toString() // Install dependencies if 'node_modules' is not already present - if(!dirContent.includes('node_modules')) execSync( - 'ln -s ' + __dirname + '/../../../node_modules node_modules', - { cwd: resolve(__dirname + '/../../../dist/libs/remix-tests') }) + if(!dirContent.includes('node_modules')) execSync('npm install', { cwd: resolve(__dirname + '/../../../dist/libs/remix-tests') }) } From d67fdca939bf8fb0cfed2a3707d276c51b4ac95a Mon Sep 17 00:00:00 2001 From: yann300 Date: Thu, 11 Mar 2021 14:17:47 +0100 Subject: [PATCH 04/18] linting --- .vscode/settings.json | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000000..2fb99dc19e --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "ethcode.keystore.keyStorePath": "/home/yann/.vscode/extensions/ethential.ethcode-0.2.2", + "ethcode.userConfig.defaultAccount": null +} \ No newline at end of file From 88713868605f91f8c652eaec9b6ecdc5eb5fc98e Mon Sep 17 00:00:00 2001 From: yann300 Date: Thu, 11 Mar 2021 14:42:28 +0100 Subject: [PATCH 05/18] fix e2e tests --- apps/remix-ide-e2e/src/tests/terminal.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/remix-ide-e2e/src/tests/terminal.test.ts b/apps/remix-ide-e2e/src/tests/terminal.test.ts index 448642f4b5..dc4fc9d462 100644 --- a/apps/remix-ide-e2e/src/tests/terminal.test.ts +++ b/apps/remix-ide-e2e/src/tests/terminal.test.ts @@ -62,7 +62,8 @@ module.exports = { 'Call web3.eth.getAccounts() using JavaScript VM': function (browser: NightwatchBrowser) { browser .executeScript('web3.eth.getAccounts()') - .waitForElementContainsText('*[data-id="terminalJournal"]', '[ "0x5B38Da6a701c568545dCfcB03FcB875f56beddC4", "0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2", "0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db", "0x78731D3Ca6b7E34aC0F824c42a7cC18A495cabaB", "0x617F2E2fD72FD9D5503197092aC168c91465E7f2", "0x17F6AD8Ef982297579C203069C1DbfFE4348c372", "0x5c6B0f7Bf3E7ce046039Bd8FABdfD3f9F5021678", "0x03C6FcED478cBbC9a4FAB34eF9f40767739D1Ff7", "0x1aE0EA34a72D944a8C7603FfB3eC30a6669E454C", "0x0A098Eda01Ce92ff4A4CCb7A4fFFb5A43EBC70DC", "0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c", "0x14723A09ACff6D2A60DcdF7aA4AFf308FDDC160C", "0x4B0897b0513fdC7C541B6d9D7E929C4e5364D2dB", "0x583031D1113aD414F02576BD6afaBfb302140225", "0xdD870fA1b7C4700F2BD7f44238821C26f7392148" ]', 60000) + .pause(2000) + .waitForElementContainsText('*[data-id="terminalJournal"]', '[ "0x5B38Da6a701c568545dCfcB03FcB875f56beddC4", "0x78731D3Ca6b7E34aC0F824c42a7cC18A495cabaB", "0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2", "0x617F2E2fD72FD9D5503197092aC168c91465E7f2", "0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db", "0x17F6AD8Ef982297579C203069C1DbfFE4348c372", "0x5c6B0f7Bf3E7ce046039Bd8FABdfD3f9F5021678", "0x03C6FcED478cBbC9a4FAB34eF9f40767739D1Ff7", "0x1aE0EA34a72D944a8C7603FfB3eC30a6669E454C", "0x0A098Eda01Ce92ff4A4CCb7A4fFFb5A43EBC70DC", "0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c", "0x14723A09ACff6D2A60DcdF7aA4AFf308FDDC160C", "0x4B0897b0513fdC7C541B6d9D7E929C4e5364D2dB", "0x583031D1113aD414F02576BD6afaBfb302140225", "0xdD870fA1b7C4700F2BD7f44238821C26f7392148" ]', 60000) }, 'Call web3.eth.getAccounts() using Web3 Provider': function (browser: NightwatchBrowser) { From d485ccb77a7d28b290b18e531978976f776cb9f9 Mon Sep 17 00:00:00 2001 From: yann300 Date: Mon, 15 Mar 2021 11:13:47 +0100 Subject: [PATCH 06/18] move execution-context --- apps/remix-ide/src/blockchain/blockchain.js | 4 +- .../src/blockchain/execution-context.js | 39 ++++++------------- libs/remix-lib/src/execution/txListener.ts | 3 +- libs/remix-lib/src/index.ts | 2 - libs/remix-lib/test/txFormat.ts | 3 -- 5 files changed, 15 insertions(+), 36 deletions(-) rename libs/remix-lib/src/execution/execution-context.ts => apps/remix-ide/src/blockchain/execution-context.js (90%) diff --git a/apps/remix-ide/src/blockchain/blockchain.js b/apps/remix-ide/src/blockchain/blockchain.js index 09ca8a05a2..973e267683 100644 --- a/apps/remix-ide/src/blockchain/blockchain.js +++ b/apps/remix-ide/src/blockchain/blockchain.js @@ -7,7 +7,7 @@ const TxRunner = remixLib.execution.TxRunner const TxRunnerWeb3 = remixLib.execution.TxRunnerWeb3 const txHelper = remixLib.execution.txHelper const EventManager = remixLib.EventManager -const executionContext = remixLib.execution.executionContext +const { ExecutionContext } = require('./execution-context') const Web3 = require('web3') const async = require('async') @@ -23,7 +23,7 @@ class Blockchain { // NOTE: the config object will need to be refactored out in remix-lib constructor (config) { this.event = new EventManager() - this.executionContext = executionContext + this.executionContext = new ExecutionContext() this.events = new EventEmitter() this.config = config diff --git a/libs/remix-lib/src/execution/execution-context.ts b/apps/remix-ide/src/blockchain/execution-context.js similarity index 90% rename from libs/remix-lib/src/execution/execution-context.ts rename to apps/remix-ide/src/blockchain/execution-context.js index 7644081040..df2d9421bb 100644 --- a/libs/remix-lib/src/execution/execution-context.ts +++ b/apps/remix-ide/src/blockchain/execution-context.js @@ -1,19 +1,17 @@ /* global ethereum */ 'use strict' import Web3 from 'web3' -import { EventManager } from '../eventManager' +import EventManager from '../lib/events' import { rlp, keccak, bufferToHex } from 'ethereumjs-util' import { Web3VmProvider } from '../web3Provider/web3VmProvider' import VM from '@ethereumjs/vm' import Common from '@ethereumjs/common' import StateManager from '@ethereumjs/vm/dist/state/stateManager' -import { StorageDump } from '@ethereumjs/vm/dist/state/interface' -declare let ethereum: any let web3 -if (typeof window !== 'undefined' && typeof window['ethereum'] !== 'undefined') { - var injectedProvider = window['ethereum'] +if (typeof window !== 'undefined' && typeof window.ethereum !== 'undefined') { + var injectedProvider = window.ethereum web3 = new Web3(injectedProvider) } else { web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545')) @@ -24,12 +22,13 @@ if (typeof window !== 'undefined' && typeof window['ethereum'] !== 'undefined') */ class StateManagerCommonStorageDump extends StateManager { - /* - * dictionary containing keccak(b) as key and b as value. used to get the initial value from its hash - */ - keyHashes: { [key: string]: string } + constructor () { super() + /* + * dictionary containing keccak(b) as key and b as value. used to get the initial value from its hash. + * type: { [key: string]: string } + */ this.keyHashes = {} } @@ -46,7 +45,7 @@ class StateManagerCommonStorageDump extends StateManager { console.log(e) throw e } - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { try { const storage = {} const stream = trie.createReadStream() @@ -66,14 +65,14 @@ class StateManagerCommonStorageDump extends StateManager { }) } - async getStateRoot (force: boolean = false): Promise { + async getStateRoot (force = false) { await this._cache.flush() const stateRoot = this._trie.root return stateRoot } - async setStateRoot (stateRoot: Buffer): Promise { + async setStateRoot (stateRoot) { await this._cache.flush() if (stateRoot === this._trie.EMPTY_TRIE_ROOT) { @@ -98,20 +97,6 @@ class StateManagerCommonStorageDump extends StateManager { trigger contextChanged, web3EndpointChanged */ export class ExecutionContext { - event - blockGasLimitDefault: number - blockGasLimit: number - customNetWorks - blocks - latestBlockNumber - txs - executionContext: string - listenOnLastBlockId - currentFork: string - vms - mainNetGenesisHash: string - customWeb3: { [key: string]: Web3 } - constructor () { this.event = new EventManager() this.executionContext = null @@ -171,7 +156,7 @@ export class ExecutionContext { return this.executionContext === 'vm' } - setWeb3 (context: string, web3: Web3) { + setWeb3 (context, web3) { this.customWeb3[context] = web3 } diff --git a/libs/remix-lib/src/execution/txListener.ts b/libs/remix-lib/src/execution/txListener.ts index 967b8ee3a8..2ec85c3fc9 100644 --- a/libs/remix-lib/src/execution/txListener.ts +++ b/libs/remix-lib/src/execution/txListener.ts @@ -4,7 +4,6 @@ import { ethers } from 'ethers' import { toBuffer } from 'ethereumjs-util' import { EventManager } from '../eventManager' import { compareByteCode } from '../util' -import { ExecutionContext } from './execution-context' import { decodeResponse } from './txFormat' import { getFunction, getReceiveInterface, getConstructorInterface, visitContracts, makeFullTypeDefinition } from './txHelper' @@ -40,7 +39,7 @@ export class TxListener { constructor (opt, executionContext) { this.event = new EventManager() // has a default for now for backwards compatability - this.executionContext = executionContext || new ExecutionContext() + this.executionContext = executionContext this._api = opt.api this._resolvedTransactions = {} this._resolvedContracts = {} diff --git a/libs/remix-lib/src/index.ts b/libs/remix-lib/src/index.ts index afcd0beae6..ae3864a8c9 100644 --- a/libs/remix-lib/src/index.ts +++ b/libs/remix-lib/src/index.ts @@ -13,7 +13,6 @@ import * as txFormat from './execution/txFormat' import { TxListener } from './execution/txListener' import { TxRunner } from './execution/txRunner' import { LogsManager } from './execution/logsManager' -import { ExecutionContext } from './execution/execution-context' import * as typeConversion from './execution/typeConversion' import { TxRunnerVM } from './execution/txRunnerVM' import { TxRunnerWeb3 } from './execution/txRunnerWeb3' @@ -40,7 +39,6 @@ function modules () { EventsDecoder: EventsDecoder, txExecution: txExecution, txHelper: txHelper, - executionContext: new ExecutionContext(), txFormat: txFormat, txListener: TxListener, TxRunner: TxRunner, diff --git a/libs/remix-lib/test/txFormat.ts b/libs/remix-lib/test/txFormat.ts index 2ab4a59b63..8bff069fc2 100644 --- a/libs/remix-lib/test/txFormat.ts +++ b/libs/remix-lib/test/txFormat.ts @@ -5,8 +5,6 @@ import * as txHelper from '../src/execution/txHelper' import { hexToIntArray } from '../src/util' let compiler = require('solc') import { compilerInput } from '../src/helpers/compilerHelper' -import { ExecutionContext } from '../src/execution/execution-context' -const executionContext = new ExecutionContext() const solidityVersion = 'v0.6.0+commit.26b70077' /* tape *********************************************************** */ @@ -151,7 +149,6 @@ function testInvalidTupleInput (st, params) { /* tape *********************************************************** */ tape('ContractParameters - (TxFormat.buildData) - link Libraries', function (t) { - executionContext.setContext('vm', null, null, null) const compileData = compiler.compile(compilerInput(deploySimpleLib)) const fakeDeployedContracts = { From 20cc1997953b2f4caade8f4e03e7e124be7ccc2f Mon Sep 17 00:00:00 2001 From: yann300 Date: Mon, 15 Mar 2021 13:44:16 +0100 Subject: [PATCH 07/18] remove uneeded execution-context reference --- .../src/methods/transactions.ts | 33 +++++++++++++++-- libs/remix-simulator/src/methods/txProcess.ts | 35 +------------------ 2 files changed, 32 insertions(+), 36 deletions(-) diff --git a/libs/remix-simulator/src/methods/transactions.ts b/libs/remix-simulator/src/methods/transactions.ts index 969eb0ff6f..ef72acfe09 100644 --- a/libs/remix-simulator/src/methods/transactions.ts +++ b/libs/remix-simulator/src/methods/transactions.ts @@ -1,11 +1,16 @@ import Web3 from 'web3' import { toChecksumAddress, BN, Address } from 'ethereumjs-util' import { processTx } from './txProcess' +import { execution } from '@remix-project/remix-lib' +const TxRunnerVM = execution.TxRunnerVM +const TxRunner = execution.TxRunner export class Transactions { vmContext accounts tags + txRunnerVMInstance + txRunnerInstance constructor (vmContext) { this.vmContext = vmContext @@ -14,6 +19,30 @@ export class Transactions { init (accounts) { this.accounts = accounts + const api = { + logMessage: (msg) => { + }, + logHtmlMessage: (msg) => { + }, + config: { + getUnpersistedProperty: (key) => { + return true + }, + get: () => { + return true + } + }, + detectNetwork: (cb) => { + cb() + }, + personalMode: () => { + return false + } + } + + this.txRunnerVMInstance = new TxRunnerVM(accounts, api, _ => this.vmContext.vm()) + this.txRunnerInstance = new TxRunner(this.txRunnerVMInstance, { runAsync: false }) + this.txRunnerInstance.vmaccounts = accounts } methods () { @@ -37,7 +66,7 @@ export class Transactions { if (payload.params && payload.params.length > 0 && payload.params[0].from) { payload.params[0].from = toChecksumAddress(payload.params[0].from) } - processTx(this.vmContext, this.accounts, payload, false, (error, result) => { + processTx(this.txRunnerInstance, payload, false, (error, result) => { if (!error && result) { this.vmContext.addBlock(result.block) const hash = '0x' + result.tx.hash().toString('hex') @@ -112,7 +141,7 @@ export class Transactions { const tag = payload.params[0].timestamp // e2e reference - processTx(this.vmContext, this.accounts, payload, true, (error, result) => { + processTx(this.txRunnerInstance, payload, true, (error, result) => { if (!error && result) { this.vmContext.addBlock(result.block) const hash = '0x' + result.tx.hash().toString('hex') diff --git a/libs/remix-simulator/src/methods/txProcess.ts b/libs/remix-simulator/src/methods/txProcess.ts index d51bbf9c73..75173af309 100644 --- a/libs/remix-simulator/src/methods/txProcess.ts +++ b/libs/remix-simulator/src/methods/txProcess.ts @@ -1,7 +1,5 @@ import { execution } from '@remix-project/remix-lib' const TxExecution = execution.txExecution -const TxRunnerVM = execution.TxRunnerVM -const TxRunner = execution.TxRunner function runCall (payload, from, to, data, value, gasLimit, txRunner, callbacks, callback) { const finalCallback = function (err, result) { @@ -36,38 +34,7 @@ function createContract (payload, from, data, value, gasLimit, txRunner, callbac TxExecution.createContract(from, data, value, gasLimit, txRunner, callbacks, finalCallback) } -let txRunnerVMInstance -let txRunnerInstance - -export function processTx (vmContext, accounts, payload, isCall, callback) { - const api = { - logMessage: (msg) => { - }, - logHtmlMessage: (msg) => { - }, - config: { - getUnpersistedProperty: (key) => { - return true - }, - get: () => { - return true - } - }, - detectNetwork: (cb) => { - cb() - }, - personalMode: () => { - return false - } - } - - if (!txRunnerVMInstance) { - txRunnerVMInstance = new TxRunnerVM(accounts, api, _ => vmContext.vm()) - } - if (!txRunnerInstance) { - txRunnerInstance = new TxRunner(txRunnerVMInstance, { runAsync: false }) - } - txRunnerInstance.vmaccounts = accounts +export function processTx (txRunnerInstance, payload, isCall, callback) { let { from, to, data, value, gas } = payload.params[0] gas = gas || 3000000 From ebf0e2cedec5f16be1a64ede04cc858c8729f98a Mon Sep 17 00:00:00 2001 From: yann300 Date: Mon, 15 Mar 2021 13:55:30 +0100 Subject: [PATCH 08/18] linting --- libs/remix-simulator/src/methods/transactions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/remix-simulator/src/methods/transactions.ts b/libs/remix-simulator/src/methods/transactions.ts index ef72acfe09..fee5059d96 100644 --- a/libs/remix-simulator/src/methods/transactions.ts +++ b/libs/remix-simulator/src/methods/transactions.ts @@ -39,7 +39,7 @@ export class Transactions { return false } } - + this.txRunnerVMInstance = new TxRunnerVM(accounts, api, _ => this.vmContext.vm()) this.txRunnerInstance = new TxRunner(this.txRunnerVMInstance, { runAsync: false }) this.txRunnerInstance.vmaccounts = accounts From dd6cc654ad9782686c7925e17d3c2a280c7bb8dd Mon Sep 17 00:00:00 2001 From: yann300 Date: Mon, 15 Mar 2021 15:35:20 +0100 Subject: [PATCH 09/18] fix e2e --- apps/remix-ide-e2e/src/tests/terminal.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/remix-ide-e2e/src/tests/terminal.test.ts b/apps/remix-ide-e2e/src/tests/terminal.test.ts index dc4fc9d462..2a17251179 100644 --- a/apps/remix-ide-e2e/src/tests/terminal.test.ts +++ b/apps/remix-ide-e2e/src/tests/terminal.test.ts @@ -63,7 +63,7 @@ module.exports = { browser .executeScript('web3.eth.getAccounts()') .pause(2000) - .waitForElementContainsText('*[data-id="terminalJournal"]', '[ "0x5B38Da6a701c568545dCfcB03FcB875f56beddC4", "0x78731D3Ca6b7E34aC0F824c42a7cC18A495cabaB", "0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2", "0x617F2E2fD72FD9D5503197092aC168c91465E7f2", "0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db", "0x17F6AD8Ef982297579C203069C1DbfFE4348c372", "0x5c6B0f7Bf3E7ce046039Bd8FABdfD3f9F5021678", "0x03C6FcED478cBbC9a4FAB34eF9f40767739D1Ff7", "0x1aE0EA34a72D944a8C7603FfB3eC30a6669E454C", "0x0A098Eda01Ce92ff4A4CCb7A4fFFb5A43EBC70DC", "0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c", "0x14723A09ACff6D2A60DcdF7aA4AFf308FDDC160C", "0x4B0897b0513fdC7C541B6d9D7E929C4e5364D2dB", "0x583031D1113aD414F02576BD6afaBfb302140225", "0xdD870fA1b7C4700F2BD7f44238821C26f7392148" ]', 60000) + .waitForElementContainsText('*[data-id="terminalJournal"]', '"0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c", "0x14723A09ACff6D2A60DcdF7aA4AFf308FDDC160C", "0x4B0897b0513fdC7C541B6d9D7E929C4e5364D2dB", "0x583031D1113aD414F02576BD6afaBfb302140225", "0xdD870fA1b7C4700F2BD7f44238821C26f7392148"', 60000) }, 'Call web3.eth.getAccounts() using Web3 Provider': function (browser: NightwatchBrowser) { From 43f164eaddae7fa0f54a49f943d7561503bbece2 Mon Sep 17 00:00:00 2001 From: yann300 Date: Mon, 3 May 2021 15:35:26 +0200 Subject: [PATCH 10/18] fix vmContext & txRunnerVM --- .../src/blockchain/execution-context.js | 112 ---------------- libs/remix-lib/src/execution/txRunnerVM.ts | 96 ++++++------- .../src/methods/transactions.ts | 2 +- libs/remix-simulator/src/vm-context.ts | 126 +++++++++++------- 4 files changed, 125 insertions(+), 211 deletions(-) diff --git a/apps/remix-ide/src/blockchain/execution-context.js b/apps/remix-ide/src/blockchain/execution-context.js index df2d9421bb..41588e3708 100644 --- a/apps/remix-ide/src/blockchain/execution-context.js +++ b/apps/remix-ide/src/blockchain/execution-context.js @@ -2,11 +2,6 @@ 'use strict' import Web3 from 'web3' import EventManager from '../lib/events' -import { rlp, keccak, bufferToHex } from 'ethereumjs-util' -import { Web3VmProvider } from '../web3Provider/web3VmProvider' -import VM from '@ethereumjs/vm' -import Common from '@ethereumjs/common' -import StateManager from '@ethereumjs/vm/dist/state/stateManager' let web3 @@ -17,82 +12,6 @@ if (typeof window !== 'undefined' && typeof window.ethereum !== 'undefined') { web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545')) } -/* - extend vm state manager and instanciate VM -*/ - -class StateManagerCommonStorageDump extends StateManager { - - constructor () { - super() - /* - * dictionary containing keccak(b) as key and b as value. used to get the initial value from its hash. - * type: { [key: string]: string } - */ - this.keyHashes = {} - } - - putContractStorage (address, key, value) { - this.keyHashes[keccak(key).toString('hex')] = bufferToHex(key) - return super.putContractStorage(address, key, value) - } - - async dumpStorage (address) { - let trie - try { - trie = await this._getStorageTrie(address) - } catch (e) { - console.log(e) - throw e - } - return new Promise((resolve, reject) => { - try { - const storage = {} - const stream = trie.createReadStream() - stream.on('data', (val) => { - const value = rlp.decode(val.value) - storage['0x' + val.key.toString('hex')] = { - key: this.keyHashes[val.key.toString('hex')], - value: '0x' + value.toString('hex') - } - }) - stream.on('end', function () { - resolve(storage) - }) - } catch (e) { - reject(e) - } - }) - } - - async getStateRoot (force = false) { - await this._cache.flush() - - const stateRoot = this._trie.root - return stateRoot - } - - async setStateRoot (stateRoot) { - await this._cache.flush() - - if (stateRoot === this._trie.EMPTY_TRIE_ROOT) { - this._trie.root = stateRoot - this._cache.clear() - this._storageTries = {} - return - } - - const hasRoot = await this._trie.checkRoot(stateRoot) - if (!hasRoot) { - throw new Error('State trie does not contain state root') - } - - this._trie.root = stateRoot - this._cache.clear() - this._storageTries = {} - } -} - /* trigger contextChanged, web3EndpointChanged */ @@ -103,15 +22,6 @@ export class ExecutionContext { this.blockGasLimitDefault = 4300000 this.blockGasLimit = this.blockGasLimitDefault this.currentFork = 'berlin' - this.vms = { - /* - byzantium: createVm('byzantium'), - constantinople: createVm('constantinople'), - petersburg: createVm('petersburg'), - istanbul: createVm('istanbul'), - */ - berlin: this.createVm('berlin') - } this.mainNetGenesisHash = '0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3' this.customNetWorks = {} this.blocks = {} @@ -129,20 +39,6 @@ export class ExecutionContext { } } - createVm (hardfork) { - const stateManager = new StateManagerCommonStorageDump() - const common = new Common({ chain: 'mainnet', hardfork }) - const vm = new VM({ - common, - activatePrecompiles: true, - stateManager: stateManager - }) - - const web3vm = new Web3VmProvider() - web3vm.setVM(vm) - return { vm, web3vm, stateManager, common } - } - askPermission () { // metamask if (ethereum && typeof ethereum.enable === 'function') ethereum.enable() @@ -217,14 +113,6 @@ export class ExecutionContext { return new Web3() } - vm () { - return this.vms[this.currentFork].vm - } - - vmObject () { - return this.vms[this.currentFork] - } - setContext (context, endPointUrl, confirmCb, infoCb) { this.executionContext = context this.executionContextChange(context, endPointUrl, confirmCb, infoCb, null) diff --git a/libs/remix-lib/src/execution/txRunnerVM.ts b/libs/remix-lib/src/execution/txRunnerVM.ts index bc7e1403a7..ba021a7d0e 100644 --- a/libs/remix-lib/src/execution/txRunnerVM.ts +++ b/libs/remix-lib/src/execution/txRunnerVM.ts @@ -1,13 +1,12 @@ 'use strict' -import { Transaction } from 'ethereumjs-tx' -import Block from 'ethereumjs-block' -import { BN, bufferToHex } from 'ethereumjs-util' +import { Transaction } from '@ethereumjs/tx' +import { Block } from '@ethereumjs/block' +import { BN, bufferToHex, Address } from 'ethereumjs-util' import { EventManager } from '../eventManager' import { LogsManager } from './logsManager' export class TxRunnerVM { event - _api blockNumber runAsync pendingTxs @@ -16,14 +15,15 @@ export class TxRunnerVM { blocks txs logsManager - getVM: () => any + commonContext + getVMObject: () => any - constructor (vmaccounts, api, getVM) { + constructor (vmaccounts, api, getVMObject) { this.event = new EventManager() this.logsManager = new LogsManager() // has a default for now for backwards compatability - this.getVM = getVM - this._api = api + this.getVMObject = getVMObject + this.commonContext = this.getVMObject().common this.blockNumber = 0 this.runAsync = true this.blockNumber = 0 // The VM is running in Homestead mode, which started at this block. @@ -53,54 +53,56 @@ export class TxRunnerVM { if (!account) { return callback('Invalid account selected') } + if (Number.isInteger(gasLimit)) { + gasLimit = '0x' + gasLimit.toString(16) + } + + this.getVMObject().stateManager.getAccount(Address.fromString(from)).then((res) => { + // See https://github.com/ethereumjs/ethereumjs-tx/blob/master/docs/classes/transaction.md#constructor + // for initialization fields and their types + value = value ? parseInt(value) : 0 + const tx = Transaction.fromTxData({ + nonce: new BN(res.nonce), + gasPrice: '0x1', + gasLimit: gasLimit, + to: to, + value: value, + data: Buffer.from(data.slice(2), 'hex') + }, { common: this.commonContext }).sign(account.privateKey) + + const coinbases = ['0x0e9281e9c6a0808672eaba6bd1220e144c9bb07a', '0x8945a1288dc78a6d8952a92c77aee6730b414778', '0x94d76e24f818426ae84aa404140e8d5f60e10e7e'] + const difficulties = [new BN('69762765929000', 10), new BN('70762765929000', 10), new BN('71762765929000', 10)] + + var block = Block.fromBlockData({ + header: { + timestamp: timestamp || (new Date().getTime() / 1000 | 0), + number: self.blockNumber, + coinbase: coinbases[self.blockNumber % coinbases.length], + difficulty: difficulties[self.blockNumber % difficulties.length], + gasLimit: new BN(gasLimit.replace('0x', ''), 16).imuln(2) + }, + transactions: [tx] + }, { common: this.commonContext }) - this.getVM().stateManager.getAccount(Buffer.from(from.replace('0x', ''), 'hex'), (err, res) => { - if (err) { - callback('Account not found') + if (!useCall) { + ++self.blockNumber + this.runBlockInVm(tx, block, callback) } else { - // See https://github.com/ethereumjs/ethereumjs-tx/blob/master/docs/classes/transaction.md#constructor - // for initialization fields and their types - value = value ? parseInt(value) : 0 - const tx = new Transaction({ - nonce: new BN(res.nonce), - gasPrice: '0x1', - gasLimit: gasLimit, - to: to, - value: value, - data: Buffer.from(data.slice(2), 'hex') - }) - tx.sign(account.privateKey) - const coinbases = ['0x0e9281e9c6a0808672eaba6bd1220e144c9bb07a', '0x8945a1288dc78a6d8952a92c77aee6730b414778', '0x94d76e24f818426ae84aa404140e8d5f60e10e7e'] - const difficulties = [new BN('69762765929000', 10), new BN('70762765929000', 10), new BN('71762765929000', 10)] - const block = new Block({ - header: { - timestamp: timestamp || (new Date().getTime() / 1000 | 0), - number: self.blockNumber, - coinbase: coinbases[self.blockNumber % coinbases.length], - difficulty: difficulties[self.blockNumber % difficulties.length], - gasLimit: new BN(gasLimit, 10).imuln(2) - }, - transactions: [tx], - uncleHeaders: [] - }) - if (!useCall) { - ++self.blockNumber - this.runBlockInVm(tx, block, callback) - } else { - this.getVM().stateManager.checkpoint(() => { - this.runBlockInVm(tx, block, (err, result) => { - this.getVM().stateManager.revert(() => { - callback(err, result) - }) + this.getVMObject().stateManager.checkpoint().then(() => { + this.runBlockInVm(tx, block, (err, result) => { + this.getVMObject().stateManager.revert().then(() => { + callback(err, result) }) }) - } + }) } + }).catch((e) => { + callback(e) }) } runBlockInVm (tx, block, callback) { - this.getVM().runBlock({ block: block, generate: true, skipBlockValidation: true, skipBalance: false }).then((results) => { + this.getVMObject().vm.runBlock({ block: block, generate: true, skipBlockValidation: true, skipBalance: false }).then((results) => { const result = results.results[0] if (result) { const status = result.execResult.exceptionError ? 0 : 1 diff --git a/libs/remix-simulator/src/methods/transactions.ts b/libs/remix-simulator/src/methods/transactions.ts index fee5059d96..c3a43f5bc4 100644 --- a/libs/remix-simulator/src/methods/transactions.ts +++ b/libs/remix-simulator/src/methods/transactions.ts @@ -40,7 +40,7 @@ export class Transactions { } } - this.txRunnerVMInstance = new TxRunnerVM(accounts, api, _ => this.vmContext.vm()) + this.txRunnerVMInstance = new TxRunnerVM(accounts, api, _ => this.vmContext.vmObject()) this.txRunnerInstance = new TxRunner(this.txRunnerVMInstance, { runAsync: false }) this.txRunnerInstance.vmaccounts = accounts } diff --git a/libs/remix-simulator/src/vm-context.ts b/libs/remix-simulator/src/vm-context.ts index e59eb68cdd..43f5f6f8c9 100644 --- a/libs/remix-simulator/src/vm-context.ts +++ b/libs/remix-simulator/src/vm-context.ts @@ -2,61 +2,82 @@ 'use strict' import Web3 from 'web3' import { rlp, keccak, bufferToHex } from 'ethereumjs-util' -import { vm, execution } from '@remix-project/remix-lib' -const EthJSVM = require('ethereumjs-vm').default -const StateManager = require('ethereumjs-vm/dist/state/stateManager').default +import { vm as remixLibVm, execution } from '@remix-project/remix-lib' +import VM from '@ethereumjs/vm' +import Common from '@ethereumjs/common' +import StateManager from '@ethereumjs/vm/dist/state/stateManager' +import { StorageDump } from '@ethereumjs/vm/dist/state/interface' /* extend vm state manager and instanciate VM */ class StateManagerCommonStorageDump extends StateManager { - constructor (arg) { - super(arg) + + keyHashes: { [key: string]: string } + constructor () { + super() this.keyHashes = {} } - putContractStorage (address, key, value, cb) { + putContractStorage (address, key, value) { this.keyHashes[keccak(key).toString('hex')] = bufferToHex(key) - super.putContractStorage(address, key, value, cb) + return super.putContractStorage(address, key, value) } - dumpStorage (address, cb) { - this._getStorageTrie(address, (err, trie) => { - if (err) { - return cb(err) + async dumpStorage (address) { + let trie + try { + trie = await this._getStorageTrie(address) + } catch (e) { + console.log(e) + throw e + } + return new Promise((resolve, reject) => { + try { + const storage = {} + const stream = trie.createReadStream() + stream.on('data', (val) => { + const value = rlp.decode(val.value) + storage['0x' + val.key.toString('hex')] = { + key: this.keyHashes[val.key.toString('hex')], + value: '0x' + value.toString('hex') + } + }) + stream.on('end', function () { + resolve(storage) + }) + } catch (e) { + reject(e) } - const storage = {} - const stream = trie.createReadStream() - stream.on('data', (val) => { - const value = rlp.decode(val.value) - storage['0x' + val.key.toString('hex')] = { - key: this.keyHashes[val.key.toString('hex')], - value: '0x' + value.toString('hex') - } - }) - stream.on('end', function () { - cb(storage) - }) }) } - getStateRoot (cb) { - const checkpoint = this._checkpointCount - this._checkpointCount = 0 - super.getStateRoot((err, stateRoot) => { - this._checkpointCount = checkpoint - cb(err, stateRoot) - }) + async getStateRoot (force = false) { + await this._cache.flush() + + const stateRoot = this._trie.root + return stateRoot } - setStateRoot (stateRoot, cb) { - const checkpoint = this._checkpointCount - this._checkpointCount = 0 - super.setStateRoot(stateRoot, (err) => { - this._checkpointCount = checkpoint - cb(err) - }) + async setStateRoot (stateRoot) { + await this._cache.flush() + + if (stateRoot === this._trie.EMPTY_TRIE_ROOT) { + this._trie.root = stateRoot + this._cache.clear() + this._storageTries = {} + return + } + + const hasRoot = await this._trie.checkRoot(stateRoot) + if (!hasRoot) { + throw new Error('State trie does not contain state root') + } + + this._trie.root = stateRoot + this._cache.clear() + this._storageTries = {} } } @@ -79,7 +100,7 @@ export class VMContext { constructor () { this.blockGasLimitDefault = 4300000 this.blockGasLimit = this.blockGasLimitDefault - this.currentFork = 'muirGlacier' + this.currentFork = 'berlin' this.vms = { /* byzantium: createVm('byzantium'), @@ -87,7 +108,7 @@ export class VMContext { petersburg: createVm('petersburg'), istanbul: createVm('istanbul'), */ - muirGlacier: this.createVm('muirGlacier') + berlin: this.createVm('berlin') } this.blocks = {} this.latestBlockNumber = 0 @@ -97,22 +118,21 @@ export class VMContext { } createVm (hardfork) { - const stateManager = new StateManagerCommonStorageDump({}) - stateManager.checkpoint(() => {}) - const ethvm = new EthJSVM({ + const stateManager = new StateManagerCommonStorageDump() + const common = new Common({ chain: 'mainnet', hardfork }) + const vm = new VM({ + common, activatePrecompiles: true, - blockchain: stateManager.blockchain, - stateManager: stateManager, - hardfork: hardfork + stateManager: stateManager }) - ethvm.blockchain.validate = false - this.web3vm = new vm.Web3VMProvider() - this.web3vm.setVM(ethvm) - return { vm: ethvm, web3vm: this.web3vm, stateManager } + + const web3vm = new remixLibVm.Web3VMProvider() + web3vm.setVM(vm) + return { vm, web3vm, stateManager, common } } web3 () { - return this.web3vm + return this.vms[this.currentFork].web3vm } blankWeb3 () { @@ -123,6 +143,10 @@ export class VMContext { return this.vms[this.currentFork].vm } + vmObject () { + return this.vms[this.currentFork] + } + addBlock (block) { let blockNumber = '0x' + block.header.number.toString('hex') if (blockNumber === '0x') { @@ -133,7 +157,7 @@ export class VMContext { this.blocks[blockNumber] = block this.latestBlockNumber = blockNumber - this.logsManager.checkBlock(blockNumber, block, this.web3vm) + this.logsManager.checkBlock(blockNumber, block, this.web3()) } trackTx (tx, block) { From 2c8d244df26a2ca4983bb33945254bbfd8d19d4f Mon Sep 17 00:00:00 2001 From: yann300 Date: Mon, 3 May 2021 15:50:36 +0200 Subject: [PATCH 11/18] linting --- libs/remix-lib/src/execution/txRunner.ts | 2 +- libs/remix-simulator/src/vm-context.ts | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/libs/remix-lib/src/execution/txRunner.ts b/libs/remix-lib/src/execution/txRunner.ts index 254b4b0474..e58ac62d5e 100644 --- a/libs/remix-lib/src/execution/txRunner.ts +++ b/libs/remix-lib/src/execution/txRunner.ts @@ -45,4 +45,4 @@ function run (self, tx, stamp, confirmationCb, gasEstimationForceSend = null, pr run(self, next.tx, next.stamp, next.callback) } }) -} \ No newline at end of file +} diff --git a/libs/remix-simulator/src/vm-context.ts b/libs/remix-simulator/src/vm-context.ts index 43f5f6f8c9..a72be89d1e 100644 --- a/libs/remix-simulator/src/vm-context.ts +++ b/libs/remix-simulator/src/vm-context.ts @@ -13,7 +13,6 @@ import { StorageDump } from '@ethereumjs/vm/dist/state/interface' */ class StateManagerCommonStorageDump extends StateManager { - keyHashes: { [key: string]: string } constructor () { super() From 87ee5f0d9bad257124a15993be98ea6f3e72e9d3 Mon Sep 17 00:00:00 2001 From: yann300 Date: Mon, 3 May 2021 15:57:04 +0200 Subject: [PATCH 12/18] linting --- .vscode/settings.json | 4 ---- apps/remix-ide/src/blockchain/providers/vm.js | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) delete mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 2fb99dc19e..0000000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "ethcode.keystore.keyStorePath": "/home/yann/.vscode/extensions/ethential.ethcode-0.2.2", - "ethcode.userConfig.defaultAccount": null -} \ No newline at end of file diff --git a/apps/remix-ide/src/blockchain/providers/vm.js b/apps/remix-ide/src/blockchain/providers/vm.js index c251743e2b..e01b7b08d2 100644 --- a/apps/remix-ide/src/blockchain/providers/vm.js +++ b/apps/remix-ide/src/blockchain/providers/vm.js @@ -1,5 +1,5 @@ const Web3 = require('web3') -const { BN, privateToAddress, stripHexPrefix, hashPersonalMessage } = require('ethereumjs-util') +const { BN, privateToAddress, hashPersonalMessage } = require('ethereumjs-util') const { Provider, extend } = require('@remix-project/remix-simulator') class VMProvider { From b46be41835cbf4e6c22fc6bac97c277f19df6b3a Mon Sep 17 00:00:00 2001 From: yann300 Date: Tue, 4 May 2021 14:37:59 +0200 Subject: [PATCH 13/18] update e2e tests --- apps/remix-ide-e2e/src/tests/terminal.test.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/remix-ide-e2e/src/tests/terminal.test.ts b/apps/remix-ide-e2e/src/tests/terminal.test.ts index 2a17251179..a7d43153fa 100644 --- a/apps/remix-ide-e2e/src/tests/terminal.test.ts +++ b/apps/remix-ide-e2e/src/tests/terminal.test.ts @@ -62,8 +62,7 @@ module.exports = { 'Call web3.eth.getAccounts() using JavaScript VM': function (browser: NightwatchBrowser) { browser .executeScript('web3.eth.getAccounts()') - .pause(2000) - .waitForElementContainsText('*[data-id="terminalJournal"]', '"0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c", "0x14723A09ACff6D2A60DcdF7aA4AFf308FDDC160C", "0x4B0897b0513fdC7C541B6d9D7E929C4e5364D2dB", "0x583031D1113aD414F02576BD6afaBfb302140225", "0xdD870fA1b7C4700F2BD7f44238821C26f7392148"', 60000) + .waitForElementContainsText('*[data-id="terminalJournal"]', '"0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c", "0x14723A09ACff6D2A60DcdF7aA4AFf308FDDC160C", "0x4B0897b0513fdC7C541B6d9D7E929C4e5364D2dB", "0x583031D1113aD414F02576BD6afaBfb302140225", "0xdD870fA1b7C4700F2BD7f44238821C26f7392148"', 80000) }, 'Call web3.eth.getAccounts() using Web3 Provider': function (browser: NightwatchBrowser) { From 87596a023eebebebf4ef21eff9c1e3c65c30670f Mon Sep 17 00:00:00 2001 From: yann300 Date: Wed, 19 May 2021 12:43:57 +0200 Subject: [PATCH 14/18] remove promise --- libs/remix-lib/src/execution/txRunnerWeb3.ts | 31 +++++++------------- 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/libs/remix-lib/src/execution/txRunnerWeb3.ts b/libs/remix-lib/src/execution/txRunnerWeb3.ts index 4554c396e8..ddef31cb50 100644 --- a/libs/remix-lib/src/execution/txRunnerWeb3.ts +++ b/libs/remix-lib/src/execution/txRunnerWeb3.ts @@ -128,29 +128,20 @@ export class TxRunnerWeb3 { } async function tryTillReceiptAvailable (txhash, web3) { - return new Promise((resolve, reject) => { - web3.eth.getTransactionReceipt(txhash, async (err, receipt) => { - if (err || !receipt) { - // Try again with a bit of delay if error or if result still null - await pause() - return resolve(await tryTillReceiptAvailable(txhash, web3)) - } - return resolve(receipt) - }) - }) + try { + const receipt = await web3.eth.getTransactionReceipt(txhash) + if (receipt) return receipt + } catch (e) {} + await pause() + return await tryTillReceiptAvailable(txhash, web3) } async function tryTillTxAvailable (txhash, web3) { - return new Promise((resolve, reject) => { - web3.eth.getTransaction(txhash, async (err, tx) => { - if (err || !tx) { - // Try again with a bit of delay if error or if result still null - await pause() - return resolve(await tryTillTxAvailable(txhash, web3)) - } - return resolve(tx) - }) - }) + try { + const tx = await web3.eth.getTransaction(txhash) + if (tx) return tx + } catch (e) {} + return await tryTillTxAvailable(txhash, web3) } async function pause () { return new Promise((resolve, reject) => { setTimeout(resolve, 500) }) } From 91b100e79bd00dd81dcd34ddfabf793f8f6dd791 Mon Sep 17 00:00:00 2001 From: yann300 Date: Wed, 19 May 2021 12:44:10 +0200 Subject: [PATCH 15/18] fix directory name --- libs/remix-tests/tsconfig.lib.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/remix-tests/tsconfig.lib.json b/libs/remix-tests/tsconfig.lib.json index aec3a4c785..7b4b23c716 100644 --- a/libs/remix-tests/tsconfig.lib.json +++ b/libs/remix-tests/tsconfig.lib.json @@ -9,7 +9,7 @@ }, "exclude": [ "**/*.spec.ts", - "test/" + "tests/" ], "include": ["**/*.ts"] } From a4d34dcfd9ef5e99c982d4cb8243fd1d110ca8ae Mon Sep 17 00:00:00 2001 From: yann300 Date: Wed, 19 May 2021 12:44:20 +0200 Subject: [PATCH 16/18] remove log --- libs/remix-tests/tests/testRunner.cli.spec.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/libs/remix-tests/tests/testRunner.cli.spec.ts b/libs/remix-tests/tests/testRunner.cli.spec.ts index 21ce34afdb..91174355af 100644 --- a/libs/remix-tests/tests/testRunner.cli.spec.ts +++ b/libs/remix-tests/tests/testRunner.cli.spec.ts @@ -17,7 +17,6 @@ describe('testRunner: remix-tests CLI', () => { describe('test various CLI options', () => { test('remix-tests version', () => { const res = spawnSync(executablePath, ['-V']) - console.log(res.stdout.toString()) expect(res.stdout.toString().trim()).toBe(require('../package.json').version) }) From 3066e509806b31d6ac79b76fa30814db923f3f08 Mon Sep 17 00:00:00 2001 From: yann300 Date: Wed, 19 May 2021 12:46:18 +0200 Subject: [PATCH 17/18] remove uneeded CI config --- .circleci/config.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 49aada7eb0..ab5e15436f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -51,7 +51,6 @@ jobs: - checkout - run: npm install - run: npm run build:libs - - run: export NODE_OPTIONS="--max_old_space_size=8192" - run: npm run test:libs remix-ide-chrome-1: From 57be7101a5e7a8742a47aff602db67b9f3e1de22 Mon Sep 17 00:00:00 2001 From: yann300 Date: Wed, 19 May 2021 13:53:43 +0200 Subject: [PATCH 18/18] linting --- libs/remix-lib/src/execution/txRunnerWeb3.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/remix-lib/src/execution/txRunnerWeb3.ts b/libs/remix-lib/src/execution/txRunnerWeb3.ts index ddef31cb50..650593dfbc 100644 --- a/libs/remix-lib/src/execution/txRunnerWeb3.ts +++ b/libs/remix-lib/src/execution/txRunnerWeb3.ts @@ -141,7 +141,7 @@ async function tryTillTxAvailable (txhash, web3) { const tx = await web3.eth.getTransaction(txhash) if (tx) return tx } catch (e) {} - return await tryTillTxAvailable(txhash, web3) + return await tryTillTxAvailable(txhash, web3) } async function pause () { return new Promise((resolve, reject) => { setTimeout(resolve, 500) }) }