Merge branch 'master' into search

pull/2092/head
bunsenstraat 3 years ago committed by GitHub
commit a45c1254b6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      apps/remix-ide-e2e/src/commands/goToVMTraceStep.ts
  2. 81
      apps/remix-ide-e2e/src/tests/debugger.test.ts
  3. 16
      apps/remix-ide-e2e/src/tests/solidityUnittests.test.ts
  4. 2
      apps/remix-ide/src/remixEngine.js
  5. 2
      libs/remix-core-plugin/src/lib/compiler-fetch-and-compile.ts
  6. 1
      libs/remix-debug/src/Ethdebugger.ts
  7. 6
      libs/remix-debug/src/debugger/debugger.ts
  8. 13
      libs/remix-debug/src/debugger/stepManager.ts
  9. 2
      libs/remix-debug/src/storage/storageResolver.ts
  10. 5
      libs/remix-lib/README.md
  11. 11
      libs/remix-lib/src/index.ts
  12. 55
      libs/remix-lib/src/web3Provider/dummyProvider.ts
  13. 38
      libs/remix-lib/src/web3Provider/web3Providers.ts
  14. 49
      libs/remix-simulator/src/VmProxy.ts
  15. 40
      libs/remix-simulator/src/vm-context.ts
  16. 4
      libs/remix-ui/debugger-ui/src/lib/button-navigator/button-navigator.css
  17. 9
      libs/remix-ui/debugger-ui/src/lib/button-navigator/button-navigator.tsx
  18. 27
      libs/remix-ui/debugger-ui/src/lib/debugger-ui.tsx
  19. 17
      libs/remix-ui/debugger-ui/src/lib/slider/slider.tsx
  20. 2
      libs/remix-ui/debugger-ui/src/lib/vm-debugger/assembly-items.tsx

@ -12,6 +12,7 @@ class GoToVmTraceStep extends EventEmitter {
function goToVMtraceStep (browser: NightwatchBrowser, step: number, incr: number, done: VoidFunction) { function goToVMtraceStep (browser: NightwatchBrowser, step: number, incr: number, done: VoidFunction) {
browser.execute(function (step) { (document.getElementById('slider') as HTMLInputElement).value = (step - 1).toString() }, [step]) browser.execute(function (step) { (document.getElementById('slider') as HTMLInputElement).value = (step - 1).toString() }, [step])
.setValue('*[data-id="slider"]', new Array(1).fill(browser.Keys.RIGHT_ARROW)) .setValue('*[data-id="slider"]', new Array(1).fill(browser.Keys.RIGHT_ARROW))
.pause(1000)
.perform(() => { .perform(() => {
done() done()
}) })

@ -39,12 +39,9 @@ module.exports = {
'Should debug transaction using slider #group1': function (browser: NightwatchBrowser) { 'Should debug transaction using slider #group1': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('*[data-id="verticalIconsKindudapp"]') browser.waitForElementVisible('*[data-id="verticalIconsKindudapp"]')
.waitForElementVisible('*[data-id="slider"]') .waitForElementVisible('*[data-id="slider"]')
// eslint-disable-next-line dot-notation .goToVMTraceStep(51)
.execute(function () { document.getElementById('slider')['value'] = '50' }) // It only moves slider to 50 but vm traces are not updated .waitForElementContainsText('*[data-id="solidityLocals"]', 'toast', 60000)
.setValue('*[data-id="slider"]', new Array(1).fill(browser.Keys.RIGHT_ARROW)) .waitForElementContainsText('*[data-id="solidityLocals"]', '999', 60000)
.pause(2000)
.click('*[data-id="dropdownPanelSolidityLocals"]')
.waitForElementContainsText('*[data-id="solidityLocals"]', 'no locals', 60000)
.waitForElementContainsText('*[data-id="stepdetail"]', 'vm trace step:\n51', 60000) .waitForElementContainsText('*[data-id="stepdetail"]', 'vm trace step:\n51', 60000)
}, },
@ -159,10 +156,7 @@ module.exports = {
.pause(2000) .pause(2000)
.debugTransaction(0) .debugTransaction(0)
.waitForElementVisible('*[data-id="slider"]').pause(2000) .waitForElementVisible('*[data-id="slider"]').pause(2000)
// .setValue('*[data-id="slider"]', '5000') // Like this, setValue doesn't work properly for input type = range .goToVMTraceStep(7453)
// 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
.waitForElementPresent('*[data-id="treeViewDivtreeViewItemarray"]') .waitForElementPresent('*[data-id="treeViewDivtreeViewItemarray"]')
.click('*[data-id="treeViewDivtreeViewItemarray"]') .click('*[data-id="treeViewDivtreeViewItemarray"]')
.waitForElementPresent('*[data-id="treeViewDivtreeViewLoadMore"]') .waitForElementPresent('*[data-id="treeViewDivtreeViewLoadMore"]')
@ -210,15 +204,7 @@ module.exports = {
.pause(3000) .pause(3000)
.clickLaunchIcon('debugger') .clickLaunchIcon('debugger')
.waitForElementVisible('*[data-id="slider"]') .waitForElementVisible('*[data-id="slider"]')
// eslint-disable-next-line dot-notation .goToVMTraceStep(154)
.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.
*/
.waitForElementContainsText('*[data-id="stepdetail"]', 'vm trace step:\n154', 60000) .waitForElementContainsText('*[data-id="stepdetail"]', 'vm trace step:\n154', 60000)
}, },
@ -242,7 +228,22 @@ module.exports = {
.pause(10000) .pause(10000)
.checkVariableDebug('soliditylocals', { num: { value: '2', type: 'uint256' } }) .checkVariableDebug('soliditylocals', { num: { value: '2', type: 'uint256' } })
.checkVariableDebug('soliditystate', { number: { value: '0', type: 'uint256', constant: false, immutable: false } }) .checkVariableDebug('soliditystate', { number: { value: '0', type: 'uint256', constant: false, immutable: false } })
.end() },
'Should debug reverted transactions #group5': function (browser: NightwatchBrowser) {
browser
.testContracts('reverted.sol', sources[6]['reverted.sol'], ['A', 'B', 'C'])
.clickLaunchIcon('udapp')
.selectContract('A')
.createContract('')
.pause(500)
.clickInstance(0)
.clickFunction('callA - transact (not payable)')
.debugTransaction(1)
.goToVMTraceStep(79)
.waitForElementVisible('*[data-id="debugGoToRevert"]', 60000)
.click('*[data-id="debugGoToRevert"]')
.waitForElementContainsText('*[data-id="asmitems"] div[selected="selected"]', '117 REVERT')
} }
} }
@ -366,6 +367,46 @@ const sources = [
} }
` `
} }
},
{
'reverted.sol': {
content: `contract A {
B b;
uint p;
constructor () {
b = new B();
}
function callA() public {
p = 123;
try b.callB() {
}
catch (bytes memory reason) {
}
}
}
contract B {
C c;
uint p;
constructor () {
c = new C();
}
function callB() public {
p = 124;
revert("revert!");
c.callC();
}
}
contract C {
uint p;
function callC() public {
p = 125;
}
}`
}
} }
] ]

@ -283,9 +283,7 @@ module.exports = {
.waitForElementVisible('*[data-id="dropdownPanelSolidityLocals"]').pause(1000) .waitForElementVisible('*[data-id="dropdownPanelSolidityLocals"]').pause(1000)
.click('*[data-id="dropdownPanelSolidityLocals"]') .click('*[data-id="dropdownPanelSolidityLocals"]')
.waitForElementContainsText('*[data-id="solidityLocals"]', 'no locals', 60000) .waitForElementContainsText('*[data-id="solidityLocals"]', 'no locals', 60000)
// eslint-disable-next-line dot-notation .goToVMTraceStep(316)
.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))
.waitForElementContainsText('*[data-id="functionPanel"]', 'checkWinningProposalFailed()', 60000) .waitForElementContainsText('*[data-id="functionPanel"]', 'checkWinningProposalFailed()', 60000)
.waitForElementContainsText('*[data-id="functionPanel"]', 'vote(proposal)', 60000) .waitForElementContainsText('*[data-id="functionPanel"]', 'vote(proposal)', 60000)
.pause(5000) .pause(5000)
@ -295,9 +293,7 @@ module.exports = {
.scrollAndClick('#Check_winning_proposal_passed') .scrollAndClick('#Check_winning_proposal_passed')
.waitForElementContainsText('*[data-id="sidePanelSwapitTitle"]', 'DEBUGGER', 60000) .waitForElementContainsText('*[data-id="sidePanelSwapitTitle"]', 'DEBUGGER', 60000)
.waitForElementContainsText('*[data-id="functionPanel"]', 'checkWinningProposalPassed()', 60000) .waitForElementContainsText('*[data-id="functionPanel"]', 'checkWinningProposalPassed()', 60000)
// eslint-disable-next-line dot-notation .goToVMTraceStep(1451)
.execute(function () { document.getElementById('slider')['value'] = '1450' })
.setValue('*[data-id="slider"]', new Array(1).fill(browser.Keys.RIGHT_ARROW))
.waitForElementContainsText('*[data-id="functionPanel"]', 'equal(a, b, message)', 60000) .waitForElementContainsText('*[data-id="functionPanel"]', 'equal(a, b, message)', 60000)
.waitForElementContainsText('*[data-id="functionPanel"]', 'checkWinningProposalPassed()', 60000) .waitForElementContainsText('*[data-id="functionPanel"]', 'checkWinningProposalPassed()', 60000)
// remix_test.sol should be opened in editor // remix_test.sol should be opened in editor
@ -307,9 +303,7 @@ module.exports = {
.scrollAndClick('#Check_winning_proposal_again') .scrollAndClick('#Check_winning_proposal_again')
.waitForElementContainsText('*[data-id="sidePanelSwapitTitle"]', 'DEBUGGER', 60000) .waitForElementContainsText('*[data-id="sidePanelSwapitTitle"]', 'DEBUGGER', 60000)
.waitForElementContainsText('*[data-id="functionPanel"]', 'checkWinningProposalAgain()', 60000) .waitForElementContainsText('*[data-id="functionPanel"]', 'checkWinningProposalAgain()', 60000)
// eslint-disable-next-line dot-notation .goToVMTraceStep(1151)
.execute(function () { document.getElementById('slider')['value'] = '1150' })
.setValue('*[data-id="slider"]', new Array(1).fill(browser.Keys.RIGHT_ARROW))
.waitForElementContainsText('*[data-id="functionPanel"]', 'equal(a, b, message)', 60000) .waitForElementContainsText('*[data-id="functionPanel"]', 'equal(a, b, message)', 60000)
.waitForElementContainsText('*[data-id="functionPanel"]', 'checkWinningProposalAgain()', 60000) .waitForElementContainsText('*[data-id="functionPanel"]', 'checkWinningProposalAgain()', 60000)
.pause(5000) .pause(5000)
@ -317,9 +311,7 @@ module.exports = {
.scrollAndClick('#Check_winnin_proposal_with_return_value').pause(5000) .scrollAndClick('#Check_winnin_proposal_with_return_value').pause(5000)
.waitForElementContainsText('*[data-id="sidePanelSwapitTitle"]', 'DEBUGGER', 60000) .waitForElementContainsText('*[data-id="sidePanelSwapitTitle"]', 'DEBUGGER', 60000)
.waitForElementContainsText('*[data-id="functionPanel"]', 'checkWinninProposalWithReturnValue()', 60000) .waitForElementContainsText('*[data-id="functionPanel"]', 'checkWinninProposalWithReturnValue()', 60000)
// eslint-disable-next-line dot-notation .goToVMTraceStep(321)
.execute(function () { document.getElementById('slider')['value'] = '320' })
.setValue('*[data-id="slider"]', new Array(1).fill(browser.Keys.RIGHT_ARROW))
.waitForElementContainsText('*[data-id="functionPanel"]', 'checkWinninProposalWithReturnValue()', 60000) .waitForElementContainsText('*[data-id="functionPanel"]', 'checkWinninProposalWithReturnValue()', 60000)
.clickLaunchIcon('filePanel') .clickLaunchIcon('filePanel')
.pause(2000) .pause(2000)

@ -15,6 +15,8 @@ export class RemixEngine extends Engine {
if (name === 'hardhat') return { queueTimeout: 60000 * 4 } if (name === 'hardhat') return { queueTimeout: 60000 * 4 }
if (name === 'localPlugin') return { queueTimeout: 60000 * 4 } if (name === 'localPlugin') return { queueTimeout: 60000 * 4 }
if (name === 'notification') return { queueTimeout: 60000 * 4 } if (name === 'notification') return { queueTimeout: 60000 * 4 }
if (name === 'sourcify') return { queueTimeout: 60000 * 4 }
if (name === 'fetchAndCompile') return { queueTimeout: 60000 * 4 }
return { queueTimeout: 10000 } return { queueTimeout: 10000 }
} }

@ -108,7 +108,7 @@ export class FetchAndCompile extends Plugin {
language: data.metadata.language, language: data.metadata.language,
evmVersion: data.metadata.settings.evmVersion, evmVersion: data.metadata.settings.evmVersion,
optimize: data.metadata.settings.optimizer.enabled, optimize: data.metadata.settings.optimizer.enabled,
runs: data.metadata.settings.runs runs: data.metadata.settings.optimizer.runs
} }
try { try {
setTimeout(_ => this.emit('compiling', settings), 0) setTimeout(_ => this.emit('compiling', settings), 0)

@ -11,7 +11,6 @@ import { SolidityProxy, stateDecoder, localDecoder, InternalCallTree } from './s
/** /**
* Ethdebugger is a wrapper around a few classes that helps debugging a transaction * 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 * - 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 * - CodeManager - Retrieve loaded byte code and help to resolve AST item from vmtrace index
* - SolidityProxy - Basically used to extract state variable from AST * - SolidityProxy - Basically used to extract state variable from AST

@ -68,7 +68,10 @@ export class Debugger {
try { try {
const address = this.debugger.traceManager.getCurrentCalledAddressAt(index) const address = this.debugger.traceManager.getCurrentCalledAddressAt(index)
const compilationResultForAddress = await this.compilationResult(address) const compilationResultForAddress = await this.compilationResult(address)
if (!compilationResultForAddress) return if (!compilationResultForAddress) {
this.event.trigger('newSourceLocation', [null])
return
}
this.debugger.callTree.sourceLocationTracker.getValidSourceLocationFromVMTraceIndex(address, index, compilationResultForAddress.data.contracts).then(async (rawLocation) => { this.debugger.callTree.sourceLocationTracker.getValidSourceLocationFromVMTraceIndex(address, index, compilationResultForAddress.data.contracts).then(async (rawLocation) => {
if (compilationResultForAddress && compilationResultForAddress.data) { if (compilationResultForAddress && compilationResultForAddress.data) {
@ -91,6 +94,7 @@ export class Debugger {
}) })
// }) // })
} catch (error) { } catch (error) {
this.event.trigger('newSourceLocation', [null])
return console.log(error) return console.log(error)
} }
} }

@ -51,15 +51,18 @@ export class DebuggerStepManager {
this.traceManager.buildCallPath(index).then((callsPath) => { this.traceManager.buildCallPath(index).then((callsPath) => {
this.currentCall = callsPath[callsPath.length - 1] this.currentCall = callsPath[callsPath.length - 1]
if (this.currentCall.reverted) { if (this.currentCall.reverted) {
const revertedReason = this.currentCall.outofgas ? 'outofgas' : '' const revertedReason = this.currentCall.outofgas ? 'outofgas' : 'reverted'
this.revertionPoint = this.currentCall.return this.revertionPoint = this.currentCall.return
return this.event.trigger('revertWarning', [revertedReason]) this.event.trigger('revertWarning', [revertedReason])
return
} }
for (let k = callsPath.length - 2; k >= 0; k--) { for (let k = callsPath.length - 2; k >= 0; k--) {
const parent = callsPath[k] const parent = callsPath[k]
if (!parent.reverted) continue if (parent.reverted) {
this.revertionPoint = parent.return this.revertionPoint = parent.return
this.event.trigger('revertWarning', ['parenthasthrown']) this.event.trigger('revertWarning', ['parenthasthrown'])
return
}
} }
this.event.trigger('revertWarning', ['']) this.event.trigger('revertWarning', [''])
}).catch((error) => { }).catch((error) => {

@ -133,7 +133,7 @@ export class StorageResolver {
resolve([{}, null]) resolve([{}, null])
} else { } else {
this.web3.debug.storageRangeAt( this.web3.debug.storageRangeAt(
tx.blockHash, tx.hash, tx.blockHash, tx.transactionIndex,
address, address,
start, start,
maxSize, maxSize,

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

@ -2,9 +2,6 @@ import { EventManager } from './eventManager'
import * as uiHelper from './helpers/uiHelper' import * as uiHelper from './helpers/uiHelper'
import * as compilerHelper from './helpers/compilerHelper' import * as compilerHelper from './helpers/compilerHelper'
import * as util from './util' 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 { Storage } from './storage'
import { EventsDecoder } from './execution/eventsDecoder' import { EventsDecoder } from './execution/eventsDecoder'
import * as txExecution from './execution/txExecution' import * as txExecution from './execution/txExecution'
@ -18,6 +15,7 @@ import * as typeConversion from './execution/typeConversion'
import { TxRunnerVM } from './execution/txRunnerVM' import { TxRunnerVM } from './execution/txRunnerVM'
import { TxRunnerWeb3 } from './execution/txRunnerWeb3' import { TxRunnerWeb3 } from './execution/txRunnerWeb3'
import * as txResultHelper from './helpers/txResultHelper' import * as txResultHelper from './helpers/txResultHelper'
export { ConsoleLogs } from './helpers/hhconsoleSigs'
export { ICompilerApi, ConfigurationSettings } from './types/ICompilerApi' export { ICompilerApi, ConfigurationSettings } from './types/ICompilerApi'
export { QueryParams } from './query-params' export { QueryParams } from './query-params'
@ -26,11 +24,6 @@ const helpers = {
compiler: compilerHelper, compiler: compilerHelper,
txResultHelper txResultHelper
} }
const vm = {
Web3Providers: Web3Providers,
DummyProvider: DummyProvider,
Web3VMProvider: Web3VmProvider
}
const execution = { const execution = {
EventsDecoder: EventsDecoder, EventsDecoder: EventsDecoder,
txExecution: txExecution, txExecution: txExecution,
@ -44,4 +37,4 @@ const execution = {
LogsManager, LogsManager,
forkAt 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 { util } from '@remix-project/remix-lib'
import { normalizeHexAddress } from '../helpers/uiHelper' const { hexListFromBNs, formatMemory } = util
import { ConsoleLogs } from '../helpers/hhconsoleSigs' 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 { toChecksumAddress, BN, bufferToHex, Address } from 'ethereumjs-util'
import Web3 from 'web3' import Web3 from 'web3'
import { ethers } from 'ethers' import { ethers } from 'ethers'
import { VMContext } from './vm-context'
export class Web3VmProvider { export class VmProxy {
web3 vmContext: VMContext
web3: Web3
vm vm
vmTraces vmTraces
txs txs
@ -38,7 +42,8 @@ export class Web3VmProvider {
blocks blocks
latestBlockNumber latestBlockNumber
constructor () { constructor (vmContext: VMContext) {
this.vmContext = vmContext
this.web3 = new Web3() this.web3 = new Web3()
this.vm = null this.vm = null
this.vmTraces = {} this.vmTraces = {}
@ -66,15 +71,15 @@ export class Web3VmProvider {
this.lastProcessedStorageTxHash = {} this.lastProcessedStorageTxHash = {}
this.sha3Preimages = {} this.sha3Preimages = {}
// util // util
this.sha3 = (...args) => this.web3.utils.sha3(...args) this.sha3 = (...args) => this.web3.utils.sha3.apply(this, args)
this.toHex = (...args) => this.web3.utils.toHex(...args) this.toHex = (...args) => this.web3.utils.toHex.apply(this, args)
this.toAscii = (...args) => this.web3.utils.hexToAscii(...args) this.toAscii = (...args) => this.web3.utils.toAscii.apply(this, args)
this.fromAscii = (...args) => this.web3.utils.asciiToHex(...args) this.fromAscii = (...args) => this.web3.utils.fromAscii.apply(this, args)
this.fromDecimal = (...args) => this.web3.utils.numberToHex(...args) this.fromDecimal = (...args) => this.web3.utils.fromDecimal.apply(this, args)
this.fromWei = (...args) => this.web3.utils.fromWei(...args) this.fromWei = (...args) => this.web3.utils.fromWei.apply(this, args)
this.toWei = (...args) => this.web3.utils.toWei(...args) this.toWei = (...args) => this.web3.utils.toWei.apply(this, args)
this.toBigNumber = (...args) => this.web3.utils.toBN(...args) this.toBigNumber = (...args) => this.web3.utils.toBN.apply(this, args)
this.isAddress = (...args) => this.web3.utils.isAddress(...args) this.isAddress = (...args) => this.web3.utils.isAddress.apply(this, args)
this.utils = Web3.utils || [] this.utils = Web3.utils || []
this.txsMapBlock = {} this.txsMapBlock = {}
this.blocks = {} 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 // we don't use the range params here
address = toChecksumAddress(address) address = toChecksumAddress(address)
let txHash
if (txIndex === 'latest') { 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, { return cb(null, {
storage: JSON.parse(JSON.stringify(storage)), storage: JSON.parse(JSON.stringify(storage)),
nextKey: null nextKey: null

@ -2,11 +2,15 @@
'use strict' 'use strict'
import Web3 from 'web3' import Web3 from 'web3'
import { rlp, keccak, bufferToHex } from 'ethereumjs-util' 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 VM from '@ethereumjs/vm'
import Common from '@ethereumjs/common' import Common from '@ethereumjs/common'
import StateManager from '@ethereumjs/vm/dist/state/stateManager' import StateManager from '@ethereumjs/vm/dist/state/stateManager'
import { StorageDump } from '@ethereumjs/vm/dist/state/interface' 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 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 trigger contextChanged, web3EndpointChanged
*/ */
@ -82,15 +93,14 @@ export class VMContext {
currentFork: string currentFork: string
blockGasLimitDefault: number blockGasLimitDefault: number
blockGasLimit: number blockGasLimit: number
customNetWorks blocks: Record<string, Block>
blocks latestBlockNumber: string
latestBlockNumber blockByTxHash: Record<string, Block>
blockByTxHash txByHash: Record<string, Transaction>
txByHash currentVm: CurrentVm
currentVm web3vm: VmProxy
web3vm logsManager: any // LogsManager
logsManager exeResults: Record<string, Transaction>
exeResults
constructor (fork?) { constructor (fork?) {
this.blockGasLimitDefault = 4300000 this.blockGasLimitDefault = 4300000
@ -98,11 +108,11 @@ export class VMContext {
this.currentFork = fork || 'london' this.currentFork = fork || 'london'
this.currentVm = this.createVm(this.currentFork) this.currentVm = this.createVm(this.currentFork)
this.blocks = {} this.blocks = {}
this.latestBlockNumber = 0 this.latestBlockNumber = "0x0"
this.blockByTxHash = {} this.blockByTxHash = {}
this.txByHash = {} this.txByHash = {}
this.exeResults = {} this.exeResults = {}
this.logsManager = new execution.LogsManager() this.logsManager = new LogsManager()
} }
createVm (hardfork) { createVm (hardfork) {
@ -115,7 +125,9 @@ export class VMContext {
allowUnlimitedContractSize: true 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) web3vm.setVM(vm)
return { vm, web3vm, stateManager, common } return { vm, web3vm, stateManager, common }
} }
@ -140,7 +152,7 @@ export class VMContext {
return this.currentVm return this.currentVm
} }
addBlock (block) { addBlock (block: Block) {
let blockNumber = '0x' + block.header.number.toString('hex') let blockNumber = '0x' + block.header.number.toString('hex')
if (blockNumber === '0x') { if (blockNumber === '0x') {
blockNumber = '0x0' blockNumber = '0x0'

@ -20,3 +20,7 @@
} }
.navigator:hover { .navigator:hover {
} }
.cursorPointerRemixDebugger {
cursor: pointer;
}

@ -65,11 +65,10 @@ export const ButtonNavigation = ({ stepOverBack, stepIntoBack, stepIntoForward,
<button className='btn btn-primary btn-sm navigator jumpButton fas fa-step-forward' id='jumpnextbreakpoint' data-id="buttonNavigatorJumpNextBreakpoint" title='Jump to the next breakpoint' onClick={() => { jumpNextBreakpoint && jumpNextBreakpoint() }} disabled={state.jumpNextBreakpointDisabled}></button> <button className='btn btn-primary btn-sm navigator jumpButton fas fa-step-forward' id='jumpnextbreakpoint' data-id="buttonNavigatorJumpNextBreakpoint" title='Jump to the next breakpoint' onClick={() => { jumpNextBreakpoint && jumpNextBreakpoint() }} disabled={state.jumpNextBreakpointDisabled}></button>
</div> </div>
<div id='reverted' style={{ display: revertedReason === '' ? 'none' : 'block' }}> <div id='reverted' style={{ display: revertedReason === '' ? 'none' : 'block' }}>
<button id='jumptoexception' title='Jump to exception' className='btn btn-danger btn-sm navigator button fas fa-exclamation-triangle' onClick={() => { jumpToException && jumpToException() }} disabled={state.jumpOutDisabled}> <span className='text-warning'>This call has reverted, state changes made during the call will be reverted.</span>
</button> <span className='text-warning' id='outofgas' style={{ display: revertedReason === 'outofgas' ? 'inline' : 'none' }}>This call will run out of gas.</span>
<span>State changes made during this call will be reverted.</span> <span className='text-warning' id='parenthasthrown' style={{ display: revertedReason === 'parenthasthrown' ? 'inline' : 'none' }}>The parent call will throw an exception</span>
<span id='outofgas' style={{ display: revertedReason === 'outofgas' ? 'inline' : 'none' }}>This call will run out of gas.</span> <div className='text-warning'>Click <u data-id="debugGoToRevert" className="cursorPointerRemixDebugger" role="button" onClick={() => { jumpToException && jumpToException() }}>here</u> to jump where the call reverted.</div>
<span id='parenthasthrown' style={{ display: revertedReason === 'parenthasthrown' ? 'inline' : 'none' }}>The parent call will throw an exception</span>
</div> </div>
</div> </div>
) )

@ -32,7 +32,8 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
toastMessage: '', toastMessage: '',
validationError: '', validationError: '',
txNumberIsEmpty: true, txNumberIsEmpty: true,
isLocalNodeUsed: false isLocalNodeUsed: false,
sourceLocationStatus: ''
}) })
useEffect(() => { useEffect(() => {
@ -87,7 +88,13 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
}) })
debuggerInstance.event.register('newSourceLocation', async (lineColumnPos, rawLocation, generatedSources, address) => { debuggerInstance.event.register('newSourceLocation', async (lineColumnPos, rawLocation, generatedSources, address) => {
if (!lineColumnPos) return if (!lineColumnPos) {
await debuggerModule.discardHighlight()
setState(prevState => {
return { ...prevState, sourceLocationStatus: 'Source location not available.' }
})
return
}
const contracts = await debuggerModule.fetchContractAndCompile( const contracts = await debuggerModule.fetchContractAndCompile(
address || currentReceipt.contractAddress || currentReceipt.to, address || currentReceipt.contractAddress || currentReceipt.to,
currentReceipt) currentReceipt)
@ -113,6 +120,9 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
} }
} }
if (path) { if (path) {
setState(prevState => {
return { ...prevState, sourceLocationStatus: '' }
})
await debuggerModule.discardHighlight() await debuggerModule.discardHighlight()
await debuggerModule.highlight(lineColumnPos, path) await debuggerModule.highlight(lineColumnPos, path)
} }
@ -138,6 +148,12 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
const unloadRequested = (blockNumber, txIndex, tx) => { const unloadRequested = (blockNumber, txIndex, tx) => {
unLoad() unLoad()
setState(prevState => {
return {
...prevState,
sourceLocationStatus: ''
}
})
} }
const unLoad = () => { const unLoad = () => {
@ -168,7 +184,8 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
setState(prevState => { setState(prevState => {
return { return {
...prevState, ...prevState,
txNumber: txNumber txNumber: txNumber,
sourceLocationStatus: ''
} }
}) })
if (!isValidHash(txNumber)) { if (!isValidHash(txNumber)) {
@ -266,7 +283,8 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
return { return {
...prevState, ...prevState,
validationError: '', validationError: '',
txNumber: txHash txNumber: txHash,
sourceLocationStatus: ''
} }
}) })
startDebugging(null, txHash, null, web3) startDebugging(null, txHash, null, web3)
@ -315,6 +333,7 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
{ state.validationError && <span className="w-100 py-1 text-danger validationError">{state.validationError}</span> } { state.validationError && <span className="w-100 py-1 text-danger validationError">{state.validationError}</span> }
</div> </div>
<TxBrowser requestDebug={ requestDebug } unloadRequested={ unloadRequested } updateTxNumberFlag={ updateTxNumberFlag } transactionNumber={ state.txNumber } debugging={ state.debugging } /> <TxBrowser requestDebug={ requestDebug } unloadRequested={ unloadRequested } updateTxNumberFlag={ updateTxNumberFlag } transactionNumber={ state.txNumber } debugging={ state.debugging } />
{ state.debugging && state.sourceLocationStatus && <div className="text-warning"><i className="fas fa-exclamation-triangle" aria-hidden="true"></i> {state.sourceLocationStatus}</div> }
{ state.debugging && <StepManager stepManager={ stepManager } /> } { state.debugging && <StepManager stepManager={ stepManager } /> }
{ state.debugging && <VmDebuggerHead vmDebugger={ vmDebugger } /> } { state.debugging && <VmDebuggerHead vmDebugger={ vmDebugger } /> }
</div> </div>

@ -1,10 +1,12 @@
import React, { useState, useEffect } from 'react' // eslint-disable-line import React, { useState, useEffect, useRef } from 'react' // eslint-disable-line
export const Slider = ({ jumpTo, sliderValue, traceLength }) => { export const Slider = ({ jumpTo, sliderValue, traceLength }) => {
const [state, setState] = useState({ const [state, setState] = useState({
currentValue: 0 currentValue: 0
}) })
const onChangeId = useRef(null)
useEffect(() => { useEffect(() => {
setValue(sliderValue) setValue(sliderValue)
}, [sliderValue]) }, [sliderValue])
@ -18,9 +20,16 @@ export const Slider = ({ jumpTo, sliderValue, traceLength }) => {
} }
const handleChange = (e) => { const handleChange = (e) => {
const value = parseInt(e.target.value) if (onChangeId.current) {
window.clearTimeout(onChangeId.current)
setValue(value) }
((value) => {
onChangeId.current = setTimeout(() => {
console.log(value)
value = parseInt(value)
setValue(value)
}, 100)
})(e.target.value)
} }
return ( return (

@ -125,7 +125,7 @@ export const AssemblyItems = ({ registerEvent }) => {
<div className="border rounded px-1 mt-1 bg-light"> <div className="border rounded px-1 mt-1 bg-light">
<div className='dropdownpanel'> <div className='dropdownpanel'>
<div className='dropdowncontent'> <div className='dropdowncontent'>
<div className="pl-2 my-1 small instructions" id='asmitems' ref={asmItemsRef}> <div className="pl-2 my-1 small instructions" data-id="asmitems" id='asmitems' ref={asmItemsRef}>
{ {
assemblyItems.display.map((item, i) => { assemblyItems.display.map((item, i) => {
return <div className="px-1" key={i} ref={ref => { refs.current[i] = ref }}><span>{item}</span></div> return <div className="px-1" key={i} ref={ref => { refs.current[i] = ref }}><span>{item}</span></div>

Loading…
Cancel
Save