From 526ed2aacb97d59185309f29e84f7e4f11058b38 Mon Sep 17 00:00:00 2001 From: yann300 Date: Thu, 17 Feb 2022 16:34:37 +0100 Subject: [PATCH] refactor Web3VmProvider to VmProxy --- apps/remix-ide-e2e/src/tests/debugger.test.ts | 20 +------ .../src/tests/solidityUnittests.test.ts | 16 ++---- libs/remix-debug/src/Ethdebugger.ts | 1 - libs/remix-lib/README.md | 5 -- libs/remix-lib/src/index.ts | 11 +--- .../src/web3Provider/dummyProvider.ts | 55 ------------------- .../src/web3Provider/web3Providers.ts | 38 ------------- .../src/VmProxy.ts} | 49 ++++++++++------- libs/remix-simulator/src/vm-context.ts | 40 +++++++++----- 9 files changed, 65 insertions(+), 170 deletions(-) delete mode 100644 libs/remix-lib/src/web3Provider/dummyProvider.ts delete mode 100644 libs/remix-lib/src/web3Provider/web3Providers.ts rename libs/{remix-lib/src/web3Provider/web3VmProvider.ts => remix-simulator/src/VmProxy.ts} (89%) diff --git a/apps/remix-ide-e2e/src/tests/debugger.test.ts b/apps/remix-ide-e2e/src/tests/debugger.test.ts index cd3fafc3b3..e9292689e8 100644 --- a/apps/remix-ide-e2e/src/tests/debugger.test.ts +++ b/apps/remix-ide-e2e/src/tests/debugger.test.ts @@ -39,10 +39,7 @@ module.exports = { 'Should debug transaction using slider #group1': function (browser: NightwatchBrowser) { browser.waitForElementVisible('*[data-id="verticalIconsKindudapp"]') .waitForElementVisible('*[data-id="slider"]') - // eslint-disable-next-line dot-notation - .execute(function () { document.getElementById('slider')['value'] = '50' }) // It only moves slider to 50 but vm traces are not updated - .setValue('*[data-id="slider"]', new Array(1).fill(browser.Keys.RIGHT_ARROW)) - .pause(2000) + .goToVMTraceStep(51) .click('*[data-id="dropdownPanelSolidityLocals"]') .waitForElementContainsText('*[data-id="solidityLocals"]', 'no locals', 60000) .waitForElementContainsText('*[data-id="stepdetail"]', 'vm trace step:\n51', 60000) @@ -159,10 +156,7 @@ module.exports = { .pause(2000) .debugTransaction(0) .waitForElementVisible('*[data-id="slider"]').pause(2000) - // .setValue('*[data-id="slider"]', '5000') // Like this, setValue doesn't work properly for input type = range - // eslint-disable-next-line dot-notation - .execute(function () { document.getElementById('slider')['value'] = '7450' }).pause(10000) // It only moves slider to 7450 but vm traces are not updated - .setValue('*[data-id="slider"]', new Array(3).fill(browser.Keys.RIGHT_ARROW)) // This will press NEXT 3 times and will update the trace details + .goToVMTraceStep(7453) .waitForElementPresent('*[data-id="treeViewDivtreeViewItemarray"]') .click('*[data-id="treeViewDivtreeViewItemarray"]') .waitForElementPresent('*[data-id="treeViewDivtreeViewLoadMore"]') @@ -210,15 +204,7 @@ module.exports = { .pause(3000) .clickLaunchIcon('debugger') .waitForElementVisible('*[data-id="slider"]') - // eslint-disable-next-line dot-notation - .execute(function () { document.getElementById('slider')['value'] = '153' }) // It only moves slider to 153 but vm traces are not updated - .setValue('*[data-id="slider"]', new Array(1).fill(browser.Keys.RIGHT_ARROW)) - .pause(1000) - /* - setting the slider to 5 leads to "vm trace step: 91" for chrome and "vm trace step: 92" for firefox - => There is something going wrong with the nightwatch API here. - As we are only testing if debugger is active, this is ok to keep that for now. - */ + .goToVMTraceStep(154) .waitForElementContainsText('*[data-id="stepdetail"]', 'vm trace step:\n154', 60000) }, diff --git a/apps/remix-ide-e2e/src/tests/solidityUnittests.test.ts b/apps/remix-ide-e2e/src/tests/solidityUnittests.test.ts index 30d1a2c83c..bc1bb12cd7 100644 --- a/apps/remix-ide-e2e/src/tests/solidityUnittests.test.ts +++ b/apps/remix-ide-e2e/src/tests/solidityUnittests.test.ts @@ -283,9 +283,7 @@ module.exports = { .waitForElementVisible('*[data-id="dropdownPanelSolidityLocals"]').pause(1000) .click('*[data-id="dropdownPanelSolidityLocals"]') .waitForElementContainsText('*[data-id="solidityLocals"]', 'no locals', 60000) - // eslint-disable-next-line dot-notation - .execute(function () { document.getElementById('slider')['value'] = '315' }) // It only moves slider to 315 but vm traces are not updated - .setValue('*[data-id="slider"]', new Array(1).fill(browser.Keys.RIGHT_ARROW)) + .goToVMTraceStep(316) .waitForElementContainsText('*[data-id="functionPanel"]', 'checkWinningProposalFailed()', 60000) .waitForElementContainsText('*[data-id="functionPanel"]', 'vote(proposal)', 60000) .pause(5000) @@ -295,9 +293,7 @@ module.exports = { .scrollAndClick('#Check_winning_proposal_passed') .waitForElementContainsText('*[data-id="sidePanelSwapitTitle"]', 'DEBUGGER', 60000) .waitForElementContainsText('*[data-id="functionPanel"]', 'checkWinningProposalPassed()', 60000) - // eslint-disable-next-line dot-notation - .execute(function () { document.getElementById('slider')['value'] = '1450' }) - .setValue('*[data-id="slider"]', new Array(1).fill(browser.Keys.RIGHT_ARROW)) + .goToVMTraceStep(1451) .waitForElementContainsText('*[data-id="functionPanel"]', 'equal(a, b, message)', 60000) .waitForElementContainsText('*[data-id="functionPanel"]', 'checkWinningProposalPassed()', 60000) // remix_test.sol should be opened in editor @@ -307,9 +303,7 @@ module.exports = { .scrollAndClick('#Check_winning_proposal_again') .waitForElementContainsText('*[data-id="sidePanelSwapitTitle"]', 'DEBUGGER', 60000) .waitForElementContainsText('*[data-id="functionPanel"]', 'checkWinningProposalAgain()', 60000) - // eslint-disable-next-line dot-notation - .execute(function () { document.getElementById('slider')['value'] = '1150' }) - .setValue('*[data-id="slider"]', new Array(1).fill(browser.Keys.RIGHT_ARROW)) + .goToVMTraceStep(1151) .waitForElementContainsText('*[data-id="functionPanel"]', 'equal(a, b, message)', 60000) .waitForElementContainsText('*[data-id="functionPanel"]', 'checkWinningProposalAgain()', 60000) .pause(5000) @@ -317,9 +311,7 @@ module.exports = { .scrollAndClick('#Check_winnin_proposal_with_return_value').pause(5000) .waitForElementContainsText('*[data-id="sidePanelSwapitTitle"]', 'DEBUGGER', 60000) .waitForElementContainsText('*[data-id="functionPanel"]', 'checkWinninProposalWithReturnValue()', 60000) - // eslint-disable-next-line dot-notation - .execute(function () { document.getElementById('slider')['value'] = '320' }) - .setValue('*[data-id="slider"]', new Array(1).fill(browser.Keys.RIGHT_ARROW)) + .goToVMTraceStep(321) .waitForElementContainsText('*[data-id="functionPanel"]', 'checkWinninProposalWithReturnValue()', 60000) .clickLaunchIcon('filePanel') .pause(2000) diff --git a/libs/remix-debug/src/Ethdebugger.ts b/libs/remix-debug/src/Ethdebugger.ts index 0026c97ee9..e0f1a92ab5 100644 --- a/libs/remix-debug/src/Ethdebugger.ts +++ b/libs/remix-debug/src/Ethdebugger.ts @@ -11,7 +11,6 @@ import { SolidityProxy, stateDecoder, localDecoder, InternalCallTree } from './s /** * Ethdebugger is a wrapper around a few classes that helps debugging a transaction * - * - Web3Providers - define which environment (web3) the transaction will be retrieved from * - TraceManager - Load / Analyze the trace and retrieve details of specific test * - CodeManager - Retrieve loaded byte code and help to resolve AST item from vmtrace index * - SolidityProxy - Basically used to extract state variable from AST diff --git a/libs/remix-lib/README.md b/libs/remix-lib/README.md index 35be57053c..31075ad154 100644 --- a/libs/remix-lib/README.md +++ b/libs/remix-lib/README.md @@ -23,11 +23,6 @@ ui: uiHelper, compiler: compilerHelper }, - vm: { - Web3Providers: Web3Providers, - DummyProvider: DummyProvider, - Web3VMProvider: Web3VmProvider - }, Storage: Storage, util: util, execution: { diff --git a/libs/remix-lib/src/index.ts b/libs/remix-lib/src/index.ts index 54646e326e..1f2cdb6676 100644 --- a/libs/remix-lib/src/index.ts +++ b/libs/remix-lib/src/index.ts @@ -2,9 +2,6 @@ import { EventManager } from './eventManager' import * as uiHelper from './helpers/uiHelper' import * as compilerHelper from './helpers/compilerHelper' import * as util from './util' -import { Web3Providers } from './web3Provider/web3Providers' -import { DummyProvider } from './web3Provider/dummyProvider' -import { Web3VmProvider } from './web3Provider/web3VmProvider' import { Storage } from './storage' import { EventsDecoder } from './execution/eventsDecoder' import * as txExecution from './execution/txExecution' @@ -18,6 +15,7 @@ import * as typeConversion from './execution/typeConversion' import { TxRunnerVM } from './execution/txRunnerVM' import { TxRunnerWeb3 } from './execution/txRunnerWeb3' import * as txResultHelper from './helpers/txResultHelper' +export { ConsoleLogs } from './helpers/hhconsoleSigs' export { ICompilerApi, ConfigurationSettings } from './types/ICompilerApi' export { QueryParams } from './query-params' @@ -26,11 +24,6 @@ const helpers = { compiler: compilerHelper, txResultHelper } -const vm = { - Web3Providers: Web3Providers, - DummyProvider: DummyProvider, - Web3VMProvider: Web3VmProvider -} const execution = { EventsDecoder: EventsDecoder, txExecution: txExecution, @@ -44,4 +37,4 @@ const execution = { LogsManager, forkAt } -export { EventManager, helpers, vm, Storage, util, execution } +export { EventManager, helpers, Storage, util, execution } diff --git a/libs/remix-lib/src/web3Provider/dummyProvider.ts b/libs/remix-lib/src/web3Provider/dummyProvider.ts deleted file mode 100644 index c6e4ec8d82..0000000000 --- a/libs/remix-lib/src/web3Provider/dummyProvider.ts +++ /dev/null @@ -1,55 +0,0 @@ -export class DummyProvider { - eth - debug - providers - currentProvider - - constructor () { - this.eth = {} - this.debug = {} - this.eth.getCode = (address, cb) => { return this.getCode(address, cb) } - this.eth.getTransaction = (hash, cb) => { return this.getTransaction(hash, cb) } - this.eth.getTransactionFromBlock = (blockNumber, txIndex, cb) => { return this.getTransactionFromBlock(blockNumber, txIndex, cb) } - this.eth.getBlockNumber = (cb) => { return this.getBlockNumber(cb) } - this.debug.traceTransaction = (hash, options, cb) => { return this.traceTransaction(hash, options, cb) } - this.debug.storageRangeAt = (blockNumber, txIndex, address, start, end, maxLength, cb) => { return this.storageRangeAt(blockNumber, txIndex, address, start, end, maxLength, cb) } - this.providers = { HttpProvider: function (url) {} } - this.currentProvider = { host: '' } - } - - getCode (address, cb) { - cb(null, '') - } - - setProvider (provider) {} - - traceTransaction (txHash, options, cb) { - if (cb) { - cb(null, {}) - } - return {} - } - - storageRangeAt (blockNumber, txIndex, address, start, end, maxLength, cb) { - if (cb) { - cb(null, {}) - } - return {} - } - - getBlockNumber (cb) { cb(null, '') } - - getTransaction (txHash, cb) { - if (cb) { - cb(null, {}) - } - return {} - } - - getTransactionFromBlock (blockNumber, txIndex, cb) { - if (cb) { - cb(null, {}) - } - return {} - } -} diff --git a/libs/remix-lib/src/web3Provider/web3Providers.ts b/libs/remix-lib/src/web3Provider/web3Providers.ts deleted file mode 100644 index dc68edb434..0000000000 --- a/libs/remix-lib/src/web3Provider/web3Providers.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { Web3VmProvider } from './web3VmProvider' -import { loadWeb3, extendWeb3 } from '../init' - -export class Web3Providers { - modes - constructor () { - this.modes = {} - } - - addProvider (type, obj) { - if (type === 'INTERNAL') { - const web3 = loadWeb3() - this.addWeb3(type, web3) - } else if (type === 'vm') { - this.addVM(type, obj) - } else { - extendWeb3(obj) - this.addWeb3(type, obj) - } - } - - get (type, cb) { - if (this.modes[type]) { - return cb(null, this.modes[type]) - } - cb('error: this provider has not been setup (' + type + ')', null) - } - - addWeb3 (type, web3) { - this.modes[type] = web3 - } - - addVM (type, vm) { - const vmProvider = new Web3VmProvider() - vmProvider.setVM(vm) - this.modes[type] = vmProvider - } -} diff --git a/libs/remix-lib/src/web3Provider/web3VmProvider.ts b/libs/remix-simulator/src/VmProxy.ts similarity index 89% rename from libs/remix-lib/src/web3Provider/web3VmProvider.ts rename to libs/remix-simulator/src/VmProxy.ts index ecc006da9e..f7090d8b1d 100644 --- a/libs/remix-lib/src/web3Provider/web3VmProvider.ts +++ b/libs/remix-simulator/src/VmProxy.ts @@ -1,12 +1,16 @@ -import { hexListFromBNs, formatMemory } from '../util' -import { normalizeHexAddress } from '../helpers/uiHelper' -import { ConsoleLogs } from '../helpers/hhconsoleSigs' +import { util } from '@remix-project/remix-lib' +const { hexListFromBNs, formatMemory } = util +import { helpers } from '@remix-project/remix-lib' +const { normalizeHexAddress } = helpers.ui +import { ConsoleLogs } from '@remix-project/remix-lib' import { toChecksumAddress, BN, bufferToHex, Address } from 'ethereumjs-util' import Web3 from 'web3' import { ethers } from 'ethers' +import { VMContext } from './vm-context' -export class Web3VmProvider { - web3 +export class VmProxy { + vmContext: VMContext + web3: Web3 vm vmTraces txs @@ -38,7 +42,8 @@ export class Web3VmProvider { blocks latestBlockNumber - constructor () { + constructor (vmContext: VMContext) { + this.vmContext = vmContext this.web3 = new Web3() this.vm = null this.vmTraces = {} @@ -66,15 +71,15 @@ export class Web3VmProvider { this.lastProcessedStorageTxHash = {} this.sha3Preimages = {} // util - this.sha3 = (...args) => this.web3.utils.sha3(...args) - this.toHex = (...args) => this.web3.utils.toHex(...args) - this.toAscii = (...args) => this.web3.utils.hexToAscii(...args) - this.fromAscii = (...args) => this.web3.utils.asciiToHex(...args) - this.fromDecimal = (...args) => this.web3.utils.numberToHex(...args) - this.fromWei = (...args) => this.web3.utils.fromWei(...args) - this.toWei = (...args) => this.web3.utils.toWei(...args) - this.toBigNumber = (...args) => this.web3.utils.toBN(...args) - this.isAddress = (...args) => this.web3.utils.isAddress(...args) + this.sha3 = (...args) => this.web3.utils.sha3.apply(this, args) + this.toHex = (...args) => this.web3.utils.toHex.apply(this, args) + this.toAscii = (...args) => this.web3.utils.toAscii.apply(this, args) + this.fromAscii = (...args) => this.web3.utils.fromAscii.apply(this, args) + this.fromDecimal = (...args) => this.web3.utils.fromDecimal.apply(this, args) + this.fromWei = (...args) => this.web3.utils.fromWei.apply(this, args) + this.toWei = (...args) => this.web3.utils.toWei.apply(this, args) + this.toBigNumber = (...args) => this.web3.utils.toBN.apply(this, args) + this.isAddress = (...args) => this.web3.utils.isAddress.apply(this, args) this.utils = Web3.utils || [] this.txsMapBlock = {} this.blocks = {} @@ -289,16 +294,22 @@ export class Web3VmProvider { } } - storageRangeAt (blockNumber, txIndex, address, start, maxLength, cb) { // txIndex is the hash in the case of the VM + storageRangeAt (blockNumber, txIndex, address, start, maxLength, cb) { // we don't use the range params here address = toChecksumAddress(address) + let txHash if (txIndex === 'latest') { - txIndex = this.lastProcessedStorageTxHash[address] + txHash = this.lastProcessedStorageTxHash[address] + } else { + const block = this.vmContext.blocks[blockNumber] + txHash = '0x' + block.transactions[txIndex].hash().toString('hex') } + + - if (this.storageCache[txIndex] && this.storageCache[txIndex][address]) { - const storage = this.storageCache[txIndex][address] + if (this.storageCache[txHash] && this.storageCache[txHash][address]) { + const storage = this.storageCache[txHash][address] return cb(null, { storage: JSON.parse(JSON.stringify(storage)), nextKey: null diff --git a/libs/remix-simulator/src/vm-context.ts b/libs/remix-simulator/src/vm-context.ts index e862fd0e3f..9e9afdef0a 100644 --- a/libs/remix-simulator/src/vm-context.ts +++ b/libs/remix-simulator/src/vm-context.ts @@ -2,11 +2,15 @@ 'use strict' import Web3 from 'web3' import { rlp, keccak, bufferToHex } from 'ethereumjs-util' -import { vm as remixLibVm, execution } from '@remix-project/remix-lib' +import { execution } from '@remix-project/remix-lib' +const { LogsManager } = execution +import { VmProxy } from './VmProxy' 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' +import { Block } from '@ethereumjs/block' +import { Transaction } from '@ethereumjs/tx' /* extend vm state manager and instanciate VM @@ -75,6 +79,13 @@ class StateManagerCommonStorageDump extends StateManager { } } +export type CurrentVm = { + vm: VM, + web3vm: VmProxy, + stateManager: StateManagerCommonStorageDump, + common: Common +} + /* trigger contextChanged, web3EndpointChanged */ @@ -82,15 +93,14 @@ export class VMContext { currentFork: string blockGasLimitDefault: number blockGasLimit: number - customNetWorks - blocks - latestBlockNumber - blockByTxHash - txByHash - currentVm - web3vm - logsManager - exeResults + blocks: Record + latestBlockNumber: string + blockByTxHash: Record + txByHash: Record + currentVm: CurrentVm + web3vm: VmProxy + logsManager: any // LogsManager + exeResults: Record constructor (fork?) { this.blockGasLimitDefault = 4300000 @@ -98,11 +108,11 @@ export class VMContext { this.currentFork = fork || 'london' this.currentVm = this.createVm(this.currentFork) this.blocks = {} - this.latestBlockNumber = 0 + this.latestBlockNumber = "0x0" this.blockByTxHash = {} this.txByHash = {} this.exeResults = {} - this.logsManager = new execution.LogsManager() + this.logsManager = new LogsManager() } createVm (hardfork) { @@ -115,7 +125,9 @@ export class VMContext { allowUnlimitedContractSize: true }) - const web3vm = new remixLibVm.Web3VMProvider() + // VmProxy and VMContext are very intricated. + // VmProxy is used to track the EVM execution (to listen on opcode execution, in order for instance to generate the VM trace) + const web3vm = new VmProxy(this) web3vm.setVM(vm) return { vm, web3vm, stateManager, common } } @@ -140,7 +152,7 @@ export class VMContext { return this.currentVm } - addBlock (block) { + addBlock (block: Block) { let blockNumber = '0x' + block.header.number.toString('hex') if (blockNumber === '0x') { blockNumber = '0x0'