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
},
debugWithGeneratedSources: false
debugWithGeneratedSources: false,
fork: 'berlin'
})
return await debug.debugger.traceManager.getTrace(hash)
}

@ -98,9 +98,17 @@ class SettingsUI {
</label>
<div class="${css.environment}">
<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."
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 id="injected-mode"
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))
selectExEnv.addEventListener('change', (event) => {
const context = selectExEnv.options[selectExEnv.selectedIndex].value
this.setExecutionContext(context)
const provider = selectExEnv.options[selectExEnv.selectedIndex]
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) {
@ -278,9 +289,15 @@ class SettingsUI {
`
}
_getProviderDropdownValue () {
const provider = this.blockchain.getProvider()
const fork = this.blockchain.getFork()
return provider === 'vm' ? provider + '-' + fork : provider
}
setFinalContext () {
// 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.updatePlusButton()
}

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

@ -21,7 +21,8 @@ export class ExecutionContext {
this.executionContext = null
this.blockGasLimitDefault = 4300000
this.blockGasLimit = this.blockGasLimitDefault
this.currentFork = 'berlin'
this.defaultFork = 'berlin'
this.currentFork = this.defaultFork
this.mainNetGenesisHash = '0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3'
this.customNetWorks = {}
this.blocks = {}
@ -48,6 +49,10 @@ export class ExecutionContext {
return this.executionContext
}
getCurrentFork () {
return this.currentFork
}
isVM () {
return this.executionContext === 'vm'
}
@ -58,7 +63,7 @@ export class ExecutionContext {
web3 () {
if (this.customWeb3[this.executionContext]) return this.customWeb3[this.executionContext]
return this.isVM() ? this.vms[this.currentFork].web3vm : web3
return web3
}
detectNetwork (callback) {
@ -118,16 +123,21 @@ export class ExecutionContext {
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 (!confirmCb) confirmCb = () => {}
if (!infoCb) infoCb = () => {}
if (context === 'vm') {
this.executionContext = context
this.currentFork = fork
this.event.trigger('contextChanged', ['vm'])
return cb()
}
this.currentFork = this.defaultFork
if (context === 'injected') {
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).')
@ -147,7 +157,7 @@ export class ExecutionContext {
}
if (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)
cb()
})
@ -184,8 +194,9 @@ export class ExecutionContext {
// TODO: remove this when this function is moved
setProviderFromEndpoint (endpoint, context, cb) {
setProviderFromEndpoint (endpoint, value, cb) {
const oldProvider = web3.currentProvider
const context = value.context
web3.setProvider(endpoint)
web3.eth.net.isListening((err, isConnected) => {

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

@ -41,7 +41,7 @@ export class Ethdebugger {
this.opts = opts
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.solidityProxy = new SolidityProxy({ getCurrentCalledAddressAt: this.traceManager.getCurrentCalledAddressAt.bind(this.traceManager), getCode: this.codeManager.getCode.bind(this.codeManager) })
this.storageResolver = null
@ -55,7 +55,7 @@ export class Ethdebugger {
}
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.solidityProxy = new SolidityProxy({ getCurrentCalledAddressAt: this.traceManager.getCurrentCalledAddressAt.bind(this.traceManager), getCode: this.codeManager.getCode.bind(this.codeManager) })
this.storageResolver = null

@ -4,6 +4,7 @@ import { EventManager } from '../eventManager'
import { isContractCreation } from '../trace/traceHelper'
import { findNodeAtInstructionIndex } from '../source/sourceMappingDecoder'
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.
@ -15,7 +16,7 @@ import { CodeResolver } from './codeResolver'
export class CodeManager {
event
isLoading: boolean
traceManager
traceManager: TraceManager
codeResolver
constructor (_traceManager) {
@ -32,7 +33,8 @@ export class CodeManager {
return resolve(code)
})
})
}
},
fork: this.traceManager.getFork()
})
}

@ -6,12 +6,14 @@ export class CodeResolver {
bytecodeByAddress
instructionsByAddress
instructionsIndexByBytesOffset
fork
constructor ({ getCode }) {
constructor ({ getCode, fork }) {
this.getCode = getCode
this.bytecodeByAddress = {} // bytes code by contract addesses
this.instructionsByAddress = {} // assembly items instructions list by contract addesses
this.instructionsIndexByBytesOffset = {} // mapping between bytes offset and instructions index.
this.fork = fork
}
clear () {
@ -39,7 +41,7 @@ export class CodeResolver {
}
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 }
}

@ -1,14 +1,23 @@
'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 = ''
const codeMap = {}
const code = []
for (let i = 0; i < raw.length; 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
// no destinations into the middle of PUSH
if (curOpCode.slice(0, 4) === 'PUSH') {
@ -30,9 +39,17 @@ export function nameOpCodes (raw) {
* information about the opcode.
*/
export function parseCode (raw) {
const common = new Common({ chain: 'mainnet', hardfork: 'berlin' })
const opcodes = getOpcodesForHF(common)
const code = []
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') {
const length = raw[i] - 0x5f
opcode['pushData'] = raw.slice(i + 1, i + length + 1)
@ -60,5 +77,5 @@ export function log (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({
web3: options.web3,
debugWithGeneratedSources: options.debugWithGeneratedSources,
compilationResult: this.compilationResult
compilationResult: this.compilationResult,
fork: options.fork
})
const { traceManager, callTree, solidityProxy } = this.debugger

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

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

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

Loading…
Cancel
Save