refactor Web3VmProvider to VmProxy

pull/2054/head^2
yann300 3 years ago
parent 164715c3e2
commit 526ed2aacb
  1. 20
      apps/remix-ide-e2e/src/tests/debugger.test.ts
  2. 16
      apps/remix-ide-e2e/src/tests/solidityUnittests.test.ts
  3. 1
      libs/remix-debug/src/Ethdebugger.ts
  4. 5
      libs/remix-lib/README.md
  5. 11
      libs/remix-lib/src/index.ts
  6. 55
      libs/remix-lib/src/web3Provider/dummyProvider.ts
  7. 38
      libs/remix-lib/src/web3Provider/web3Providers.ts
  8. 49
      libs/remix-simulator/src/VmProxy.ts
  9. 40
      libs/remix-simulator/src/vm-context.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)
},

@ -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)

@ -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

@ -23,11 +23,6 @@
ui: uiHelper,
compiler: compilerHelper
},
vm: {
Web3Providers: Web3Providers,
DummyProvider: DummyProvider,
Web3VMProvider: Web3VmProvider
},
Storage: Storage,
util: util,
execution: {

@ -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 }

@ -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 {}
}
}

@ -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
}
}

@ -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

@ -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<string, Block>
latestBlockNumber: string
blockByTxHash: Record<string, Block>
txByHash: Record<string, Transaction>
currentVm: CurrentVm
web3vm: VmProxy
logsManager: any // LogsManager
exeResults: Record<string, Transaction>
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'

Loading…
Cancel
Save