fork selection for javascript VM

pull/5370/head
yann300 4 years ago
parent 54f1b726af
commit 10666349b6
  1. 3
      apps/debugger/src/app/debugger-api.ts
  2. 29
      apps/remix-ide/src/app/tabs/runTab/settings.js
  3. 4
      apps/remix-ide/src/blockchain/blockchain.js
  4. 21
      apps/remix-ide/src/blockchain/execution-context.js
  5. 12
      apps/remix-ide/src/blockchain/providers/vm.js
  6. 4
      libs/remix-debug/src/Ethdebugger.ts
  7. 6
      libs/remix-debug/src/code/codeManager.ts
  8. 6
      libs/remix-debug/src/code/codeResolver.ts
  9. 27
      libs/remix-debug/src/code/codeUtils.ts
  10. 3
      libs/remix-debug/src/debugger/debugger.ts
  11. 6
      libs/remix-debug/src/trace/traceManager.ts
  12. 6
      libs/remix-simulator/src/provider.ts
  13. 30
      libs/remix-simulator/src/vm-context.ts

@ -111,7 +111,8 @@ export const DebuggerApiMixin = (Base) => class extends Base {
} }
return null return null
}, },
debugWithGeneratedSources: false debugWithGeneratedSources: false,
fork: 'berlin'
}) })
return await debug.debugger.traceManager.getTrace(hash) return await debug.debugger.traceManager.getTrace(hash)
} }

@ -98,9 +98,17 @@ class SettingsUI {
</label> </label>
<div class="${css.environment}"> <div class="${css.environment}">
<select id="selectExEnvOptions" data-id="settingsSelectEnvOptions" class="form-control ${css.select} custom-select"> <select id="selectExEnvOptions" data-id="settingsSelectEnvOptions" class="form-control ${css.select} custom-select">
<option id="vm-mode" <option id="vm-mode-berlin"
title="Execution environment does not connect to any node, everything is local and in memory only." title="Execution environment does not connect to any node, everything is local and in memory only."
value="vm" name="executionContext"> JavaScript VM value="vm-berlin" name="executionContext" fork="berlin" > JavaScript VM (Berlin)
</option>
<option id="vm-mode-muirglacier"
title="Execution environment does not connect to any node, everything is local and in memory only."
value="vm-istanbul" name="executionContext" fork="istanbul"> JavaScript VM (Istanbul)
</option>
<option id="vm-mode-london"
title="Execution environment does not connect to any node, everything is local and in memory only."
value="vm-london" name="executionContext" fork="london"> JavaScript VM (London)
</option> </option>
<option id="injected-mode" <option id="injected-mode"
title="Execution environment has been provided by Metamask or similar provider." title="Execution environment has been provided by Metamask or similar provider."
@ -236,11 +244,14 @@ class SettingsUI {
this.blockchain.event.register('removeProvider', name => removeProvider(name)) this.blockchain.event.register('removeProvider', name => removeProvider(name))
selectExEnv.addEventListener('change', (event) => { selectExEnv.addEventListener('change', (event) => {
const context = selectExEnv.options[selectExEnv.selectedIndex].value const provider = selectExEnv.options[selectExEnv.selectedIndex]
this.setExecutionContext(context) const fork = provider.getAttribute('fork')
let context = provider.value
context = context.startsWith('vm') ? 'vm' : context
this.setExecutionContext({ context, fork })
}) })
selectExEnv.value = this.blockchain.getProvider() selectExEnv.value = this._getProviderDropdownValue()
} }
setExecutionContext (context) { setExecutionContext (context) {
@ -278,9 +289,15 @@ class SettingsUI {
` `
} }
_getProviderDropdownValue () {
const provider = this.blockchain.getProvider()
const fork = this.blockchain.getFork()
return provider === 'vm' ? provider + '-' + fork : provider
}
setFinalContext () { setFinalContext () {
// set the final context. Cause it is possible that this is not the one we've originaly selected // set the final context. Cause it is possible that this is not the one we've originaly selected
this.selectExEnv.value = this.blockchain.getProvider() this.selectExEnv.value = this._getProviderDropdownValue()
this.event.trigger('clearInstance', []) this.event.trigger('clearInstance', [])
this.updatePlusButton() this.updatePlusButton()
} }

@ -208,6 +208,10 @@ class Blockchain {
return this.executionContext.getProvider() return this.executionContext.getProvider()
} }
getFork () {
return this.executionContext.getCurrentFork()
}
isWeb3Provider () { isWeb3Provider () {
const isVM = this.getProvider() === 'vm' const isVM = this.getProvider() === 'vm'
const isInjected = this.getProvider() === 'injected' const isInjected = this.getProvider() === 'injected'

@ -21,7 +21,8 @@ export class ExecutionContext {
this.executionContext = null this.executionContext = null
this.blockGasLimitDefault = 4300000 this.blockGasLimitDefault = 4300000
this.blockGasLimit = this.blockGasLimitDefault this.blockGasLimit = this.blockGasLimitDefault
this.currentFork = 'berlin' this.defaultFork = 'berlin'
this.currentFork = this.defaultFork
this.mainNetGenesisHash = '0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3' this.mainNetGenesisHash = '0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3'
this.customNetWorks = {} this.customNetWorks = {}
this.blocks = {} this.blocks = {}
@ -48,6 +49,10 @@ export class ExecutionContext {
return this.executionContext return this.executionContext
} }
getCurrentFork () {
return this.currentFork
}
isVM () { isVM () {
return this.executionContext === 'vm' return this.executionContext === 'vm'
} }
@ -58,7 +63,7 @@ export class ExecutionContext {
web3 () { web3 () {
if (this.customWeb3[this.executionContext]) return this.customWeb3[this.executionContext] if (this.customWeb3[this.executionContext]) return this.customWeb3[this.executionContext]
return this.isVM() ? this.vms[this.currentFork].web3vm : web3 return web3
} }
detectNetwork (callback) { detectNetwork (callback) {
@ -118,16 +123,21 @@ export class ExecutionContext {
this.executionContextChange(context, endPointUrl, confirmCb, infoCb, null) this.executionContextChange(context, endPointUrl, confirmCb, infoCb, null)
} }
executionContextChange (context, endPointUrl, confirmCb, infoCb, cb) { executionContextChange (value, endPointUrl, confirmCb, infoCb, cb) {
const context = value.context
const fork = value.fork || this.defaultFork
if (!cb) cb = () => {} if (!cb) cb = () => {}
if (!confirmCb) confirmCb = () => {} if (!confirmCb) confirmCb = () => {}
if (!infoCb) infoCb = () => {} if (!infoCb) infoCb = () => {}
if (context === 'vm') { if (context === 'vm') {
this.executionContext = context this.executionContext = context
this.currentFork = fork
this.event.trigger('contextChanged', ['vm']) this.event.trigger('contextChanged', ['vm'])
return cb() return cb()
} }
this.currentFork = this.defaultFork
if (context === 'injected') { if (context === 'injected') {
if (injectedProvider === undefined) { if (injectedProvider === undefined) {
infoCb('No injected Web3 provider found. Make sure your provider (e.g. MetaMask) is active and running (when recently activated you may have to reload the page).') infoCb('No injected Web3 provider found. Make sure your provider (e.g. MetaMask) is active and running (when recently activated you may have to reload the page).')
@ -147,7 +157,7 @@ export class ExecutionContext {
} }
if (this.customNetWorks[context]) { if (this.customNetWorks[context]) {
var network = this.customNetWorks[context] var network = this.customNetWorks[context]
this.setProviderFromEndpoint(network.provider, network.name, (error) => { this.setProviderFromEndpoint(network.provider, { context: network.name }, (error) => {
if (error) infoCb(error) if (error) infoCb(error)
cb() cb()
}) })
@ -184,8 +194,9 @@ export class ExecutionContext {
// TODO: remove this when this function is moved // TODO: remove this when this function is moved
setProviderFromEndpoint (endpoint, context, cb) { setProviderFromEndpoint (endpoint, value, cb) {
const oldProvider = web3.currentProvider const oldProvider = web3.currentProvider
const context = value.context
web3.setProvider(endpoint) web3.setProvider(endpoint)
web3.eth.net.isListening((err, isConnected) => { web3.eth.net.isListening((err, isConnected) => {

@ -5,12 +5,6 @@ const { Provider, extend } = require('@remix-project/remix-simulator')
class VMProvider { class VMProvider {
constructor (executionContext) { constructor (executionContext) {
this.executionContext = executionContext this.executionContext = 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) { getAccounts (cb) {
@ -23,8 +17,14 @@ class VMProvider {
} }
resetEnvironment () { resetEnvironment () {
this.accounts = {}
this.RemixSimulatorProvider = new Provider({ fork: this.executionContext.getCurrentFork() })
this.RemixSimulatorProvider.init()
this.RemixSimulatorProvider.Accounts.resetAccounts() this.RemixSimulatorProvider.Accounts.resetAccounts()
this.web3 = new Web3(this.RemixSimulatorProvider)
extend(this.web3)
this.accounts = {} this.accounts = {}
this.executionContext.setWeb3('vm', this.web3)
} }
// TODO: is still here because of the plugin API // TODO: is still here because of the plugin API

@ -41,7 +41,7 @@ export class Ethdebugger {
this.opts = opts this.opts = opts
this.event = new EventManager() this.event = new EventManager()
this.traceManager = new TraceManager({ web3: this.web3 }) this.traceManager = new TraceManager({ web3: this.web3, fork: this.opts.fork })
this.codeManager = new CodeManager(this.traceManager) this.codeManager = new CodeManager(this.traceManager)
this.solidityProxy = new SolidityProxy({ getCurrentCalledAddressAt: this.traceManager.getCurrentCalledAddressAt.bind(this.traceManager), getCode: this.codeManager.getCode.bind(this.codeManager) }) this.solidityProxy = new SolidityProxy({ getCurrentCalledAddressAt: this.traceManager.getCurrentCalledAddressAt.bind(this.traceManager), getCode: this.codeManager.getCode.bind(this.codeManager) })
this.storageResolver = null this.storageResolver = null
@ -55,7 +55,7 @@ export class Ethdebugger {
} }
setManagers () { setManagers () {
this.traceManager = new TraceManager({ web3: this.web3 }) this.traceManager = new TraceManager({ web3: this.web3, fork: this.opts.fork })
this.codeManager = new CodeManager(this.traceManager) this.codeManager = new CodeManager(this.traceManager)
this.solidityProxy = new SolidityProxy({ getCurrentCalledAddressAt: this.traceManager.getCurrentCalledAddressAt.bind(this.traceManager), getCode: this.codeManager.getCode.bind(this.codeManager) }) this.solidityProxy = new SolidityProxy({ getCurrentCalledAddressAt: this.traceManager.getCurrentCalledAddressAt.bind(this.traceManager), getCode: this.codeManager.getCode.bind(this.codeManager) })
this.storageResolver = null this.storageResolver = null

@ -4,6 +4,7 @@ import { EventManager } from '../eventManager'
import { isContractCreation } from '../trace/traceHelper' import { isContractCreation } from '../trace/traceHelper'
import { findNodeAtInstructionIndex } from '../source/sourceMappingDecoder' import { findNodeAtInstructionIndex } from '../source/sourceMappingDecoder'
import { CodeResolver } from './codeResolver' import { CodeResolver } from './codeResolver'
import { TraceManager } from '../trace/traceManager' // eslint-disable-line
/* /*
resolve contract code referenced by vmtrace in order to be used by asm listview. resolve contract code referenced by vmtrace in order to be used by asm listview.
@ -15,7 +16,7 @@ import { CodeResolver } from './codeResolver'
export class CodeManager { export class CodeManager {
event event
isLoading: boolean isLoading: boolean
traceManager traceManager: TraceManager
codeResolver codeResolver
constructor (_traceManager) { constructor (_traceManager) {
@ -32,7 +33,8 @@ export class CodeManager {
return resolve(code) return resolve(code)
}) })
}) })
} },
fork: this.traceManager.getFork()
}) })
} }

@ -6,12 +6,14 @@ export class CodeResolver {
bytecodeByAddress bytecodeByAddress
instructionsByAddress instructionsByAddress
instructionsIndexByBytesOffset instructionsIndexByBytesOffset
fork
constructor ({ getCode }) { constructor ({ getCode, fork }) {
this.getCode = getCode this.getCode = getCode
this.bytecodeByAddress = {} // bytes code by contract addesses this.bytecodeByAddress = {} // bytes code by contract addesses
this.instructionsByAddress = {} // assembly items instructions list by contract addesses this.instructionsByAddress = {} // assembly items instructions list by contract addesses
this.instructionsIndexByBytesOffset = {} // mapping between bytes offset and instructions index. this.instructionsIndexByBytesOffset = {} // mapping between bytes offset and instructions index.
this.fork = fork
} }
clear () { clear () {
@ -39,7 +41,7 @@ export class CodeResolver {
} }
formatCode (hexCode) { formatCode (hexCode) {
const [code, instructionsIndexByBytesOffset] = nameOpCodes(Buffer.from(hexCode.substring(2), 'hex')) const [code, instructionsIndexByBytesOffset] = nameOpCodes(Buffer.from(hexCode.substring(2), 'hex'), this.fork)
return { code, instructionsIndexByBytesOffset } return { code, instructionsIndexByBytesOffset }
} }

@ -1,14 +1,23 @@
'use strict' 'use strict'
import opcodes from './opcodes' import Common from '@ethereumjs/common'
import { getOpcodesForHF } from '@ethereumjs/vm/dist/evm/opcodes'
export function nameOpCodes (raw, hardfork) {
const common = new Common({ chain: 'mainnet', hardfork })
const opcodes = getOpcodesForHF(common)
export function nameOpCodes (raw) {
let pushData = '' let pushData = ''
const codeMap = {} const codeMap = {}
const code = [] const code = []
for (let i = 0; i < raw.length; i++) { for (let i = 0; i < raw.length; i++) {
const pc = i const pc = i
const curOpCode = opcodes(raw[pc], false).name let curOpCode
try {
curOpCode = opcodes.get(raw[pc]).fullName
} catch (e) {
curOpCode = 'INVALID'
}
codeMap[i] = code.length codeMap[i] = code.length
// no destinations into the middle of PUSH // no destinations into the middle of PUSH
if (curOpCode.slice(0, 4) === 'PUSH') { if (curOpCode.slice(0, 4) === 'PUSH') {
@ -30,9 +39,17 @@ export function nameOpCodes (raw) {
* information about the opcode. * information about the opcode.
*/ */
export function parseCode (raw) { export function parseCode (raw) {
const common = new Common({ chain: 'mainnet', hardfork: 'berlin' })
const opcodes = getOpcodesForHF(common)
const code = [] const code = []
for (let i = 0; i < raw.length; i++) { for (let i = 0; i < raw.length; i++) {
const opcode = opcodes(raw[i], true) let opcode
try {
opcode = opcodes.get(raw[i]).fullName
} catch (e) {
opcode = 'INVALID'
}
if (opcode.name.slice(0, 4) === 'PUSH') { if (opcode.name.slice(0, 4) === 'PUSH') {
const length = raw[i] - 0x5f const length = raw[i] - 0x5f
opcode['pushData'] = raw.slice(i + 1, i + length + 1) opcode['pushData'] = raw.slice(i + 1, i + length + 1)
@ -60,5 +77,5 @@ export function log (num, base) {
} }
export function roundLog (num, base) { export function roundLog (num, base) {
return Math.ceil(this.log(num, base)) return Math.ceil(log(num, base))
} }

@ -26,7 +26,8 @@ export class Debugger {
this.debugger = new Ethdebugger({ this.debugger = new Ethdebugger({
web3: options.web3, web3: options.web3,
debugWithGeneratedSources: options.debugWithGeneratedSources, debugWithGeneratedSources: options.debugWithGeneratedSources,
compilationResult: this.compilationResult compilationResult: this.compilationResult,
fork: options.fork
}) })
const { traceManager, callTree, solidityProxy } = this.debugger const { traceManager, callTree, solidityProxy } = this.debugger

@ -7,6 +7,7 @@ import { util } from '@remix-project/remix-lib'
export class TraceManager { export class TraceManager {
web3 web3
fork: string
isLoading: boolean isLoading: boolean
trace trace
traceCache traceCache
@ -16,6 +17,7 @@ export class TraceManager {
constructor (options) { constructor (options) {
this.web3 = options.web3 this.web3 = options.web3
this.fork = options.fork
this.isLoading = false this.isLoading = false
this.trace = null this.trace = null
this.traceCache = new TraceCache() this.traceCache = new TraceCache()
@ -70,6 +72,10 @@ export class TraceManager {
this.traceCache.init() this.traceCache.init()
} }
getFork () {
return this.fork
}
// API section // API section
inRange (step) { inRange (step) {
return this.isLoaded() && step >= 0 && step < this.trace.length return this.isLoaded() && step >= 0 && step < this.trace.length

@ -18,14 +18,12 @@ export class Provider {
Accounts Accounts
Transactions Transactions
methods methods
host: string
connected: boolean; connected: boolean;
constructor (host: string = 'vm', options: Record<string, unknown> = {}) { constructor (options: Record<string, unknown> = {}) {
this.options = options this.options = options
this.host = host
this.connected = true this.connected = true
this.vmContext = new VMContext() this.vmContext = new VMContext(options['fork'])
this.Accounts = new Accounts(this.vmContext) this.Accounts = new Accounts(this.vmContext)
this.Transactions = new Transactions(this.vmContext) this.Transactions = new Transactions(this.vmContext)

@ -91,24 +91,18 @@ export class VMContext {
blocks blocks
latestBlockNumber latestBlockNumber
txs txs
vms defaultFork
currentVm
web3vm web3vm
logsManager logsManager
exeResults exeResults
constructor () { constructor (fork?) {
this.blockGasLimitDefault = 4300000 this.blockGasLimitDefault = 4300000
this.blockGasLimit = this.blockGasLimitDefault this.blockGasLimit = this.blockGasLimitDefault
this.currentFork = 'berlin' this.defaultFork = fork || 'berlin'
this.vms = { this.currentFork = this.defaultFork
/* this.currentVm = this.createVm(this.currentFork)
byzantium: createVm('byzantium'),
constantinople: createVm('constantinople'),
petersburg: createVm('petersburg'),
istanbul: createVm('istanbul'),
*/
berlin: this.createVm('berlin')
}
this.blocks = {} this.blocks = {}
this.latestBlockNumber = 0 this.latestBlockNumber = 0
this.txs = {} this.txs = {}
@ -122,7 +116,7 @@ export class VMContext {
const vm = new VM({ const vm = new VM({
common, common,
activatePrecompiles: true, activatePrecompiles: true,
stateManager: stateManager, stateManager,
allowUnlimitedContractSize: true allowUnlimitedContractSize: true
}) })
@ -131,8 +125,12 @@ export class VMContext {
return { vm, web3vm, stateManager, common } return { vm, web3vm, stateManager, common }
} }
getCurrentFork () {
return this.currentFork
}
web3 () { web3 () {
return this.vms[this.currentFork].web3vm return this.currentVm.web3vm
} }
blankWeb3 () { blankWeb3 () {
@ -140,11 +138,11 @@ export class VMContext {
} }
vm () { vm () {
return this.vms[this.currentFork].vm return this.currentVm.vm
} }
vmObject () { vmObject () {
return this.vms[this.currentFork] return this.currentVm
} }
addBlock (block) { addBlock (block) {

Loading…
Cancel
Save