diff --git a/apps/remix-ide/src/blockchain/execution-context.js b/apps/remix-ide/src/blockchain/execution-context.js index df2d9421bb..41588e3708 100644 --- a/apps/remix-ide/src/blockchain/execution-context.js +++ b/apps/remix-ide/src/blockchain/execution-context.js @@ -2,11 +2,6 @@ 'use strict' import Web3 from 'web3' import EventManager from '../lib/events' -import { rlp, keccak, bufferToHex } from 'ethereumjs-util' -import { Web3VmProvider } from '../web3Provider/web3VmProvider' -import VM from '@ethereumjs/vm' -import Common from '@ethereumjs/common' -import StateManager from '@ethereumjs/vm/dist/state/stateManager' let web3 @@ -17,82 +12,6 @@ if (typeof window !== 'undefined' && typeof window.ethereum !== 'undefined') { web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545')) } -/* - extend vm state manager and instanciate VM -*/ - -class StateManagerCommonStorageDump extends StateManager { - - constructor () { - super() - /* - * dictionary containing keccak(b) as key and b as value. used to get the initial value from its hash. - * type: { [key: string]: string } - */ - this.keyHashes = {} - } - - putContractStorage (address, key, value) { - this.keyHashes[keccak(key).toString('hex')] = bufferToHex(key) - return super.putContractStorage(address, key, value) - } - - async dumpStorage (address) { - let trie - try { - trie = await this._getStorageTrie(address) - } catch (e) { - console.log(e) - throw e - } - return new Promise((resolve, reject) => { - try { - const storage = {} - const stream = trie.createReadStream() - stream.on('data', (val) => { - const value = rlp.decode(val.value) - storage['0x' + val.key.toString('hex')] = { - key: this.keyHashes[val.key.toString('hex')], - value: '0x' + value.toString('hex') - } - }) - stream.on('end', function () { - resolve(storage) - }) - } catch (e) { - reject(e) - } - }) - } - - async getStateRoot (force = false) { - await this._cache.flush() - - const stateRoot = this._trie.root - return stateRoot - } - - async setStateRoot (stateRoot) { - await this._cache.flush() - - if (stateRoot === this._trie.EMPTY_TRIE_ROOT) { - this._trie.root = stateRoot - this._cache.clear() - this._storageTries = {} - return - } - - const hasRoot = await this._trie.checkRoot(stateRoot) - if (!hasRoot) { - throw new Error('State trie does not contain state root') - } - - this._trie.root = stateRoot - this._cache.clear() - this._storageTries = {} - } -} - /* trigger contextChanged, web3EndpointChanged */ @@ -103,15 +22,6 @@ export class ExecutionContext { 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.mainNetGenesisHash = '0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3' this.customNetWorks = {} this.blocks = {} @@ -129,20 +39,6 @@ export class ExecutionContext { } } - createVm (hardfork) { - const stateManager = new StateManagerCommonStorageDump() - const common = new Common({ chain: 'mainnet', hardfork }) - const vm = new VM({ - common, - activatePrecompiles: true, - stateManager: stateManager - }) - - const web3vm = new Web3VmProvider() - web3vm.setVM(vm) - return { vm, web3vm, stateManager, common } - } - askPermission () { // metamask if (ethereum && typeof ethereum.enable === 'function') ethereum.enable() @@ -217,14 +113,6 @@ export class ExecutionContext { return new Web3() } - vm () { - return this.vms[this.currentFork].vm - } - - vmObject () { - return this.vms[this.currentFork] - } - setContext (context, endPointUrl, confirmCb, infoCb) { this.executionContext = context this.executionContextChange(context, endPointUrl, confirmCb, infoCb, null) diff --git a/libs/remix-lib/src/execution/txRunnerVM.ts b/libs/remix-lib/src/execution/txRunnerVM.ts index bc7e1403a7..ba021a7d0e 100644 --- a/libs/remix-lib/src/execution/txRunnerVM.ts +++ b/libs/remix-lib/src/execution/txRunnerVM.ts @@ -1,13 +1,12 @@ 'use strict' -import { Transaction } from 'ethereumjs-tx' -import Block from 'ethereumjs-block' -import { BN, bufferToHex } from 'ethereumjs-util' +import { Transaction } from '@ethereumjs/tx' +import { Block } from '@ethereumjs/block' +import { BN, bufferToHex, Address } from 'ethereumjs-util' import { EventManager } from '../eventManager' import { LogsManager } from './logsManager' export class TxRunnerVM { event - _api blockNumber runAsync pendingTxs @@ -16,14 +15,15 @@ export class TxRunnerVM { blocks txs logsManager - getVM: () => any + commonContext + getVMObject: () => any - constructor (vmaccounts, api, getVM) { + constructor (vmaccounts, api, getVMObject) { this.event = new EventManager() this.logsManager = new LogsManager() // has a default for now for backwards compatability - this.getVM = getVM - this._api = api + this.getVMObject = getVMObject + this.commonContext = this.getVMObject().common this.blockNumber = 0 this.runAsync = true this.blockNumber = 0 // The VM is running in Homestead mode, which started at this block. @@ -53,54 +53,56 @@ export class TxRunnerVM { if (!account) { return callback('Invalid account selected') } + if (Number.isInteger(gasLimit)) { + gasLimit = '0x' + gasLimit.toString(16) + } + + this.getVMObject().stateManager.getAccount(Address.fromString(from)).then((res) => { + // See https://github.com/ethereumjs/ethereumjs-tx/blob/master/docs/classes/transaction.md#constructor + // for initialization fields and their types + value = value ? parseInt(value) : 0 + const tx = Transaction.fromTxData({ + nonce: new BN(res.nonce), + gasPrice: '0x1', + gasLimit: gasLimit, + to: to, + value: value, + data: Buffer.from(data.slice(2), 'hex') + }, { common: this.commonContext }).sign(account.privateKey) + + const coinbases = ['0x0e9281e9c6a0808672eaba6bd1220e144c9bb07a', '0x8945a1288dc78a6d8952a92c77aee6730b414778', '0x94d76e24f818426ae84aa404140e8d5f60e10e7e'] + const difficulties = [new BN('69762765929000', 10), new BN('70762765929000', 10), new BN('71762765929000', 10)] + + var block = Block.fromBlockData({ + header: { + timestamp: timestamp || (new Date().getTime() / 1000 | 0), + number: self.blockNumber, + coinbase: coinbases[self.blockNumber % coinbases.length], + difficulty: difficulties[self.blockNumber % difficulties.length], + gasLimit: new BN(gasLimit.replace('0x', ''), 16).imuln(2) + }, + transactions: [tx] + }, { common: this.commonContext }) - this.getVM().stateManager.getAccount(Buffer.from(from.replace('0x', ''), 'hex'), (err, res) => { - if (err) { - callback('Account not found') + if (!useCall) { + ++self.blockNumber + this.runBlockInVm(tx, block, callback) } else { - // See https://github.com/ethereumjs/ethereumjs-tx/blob/master/docs/classes/transaction.md#constructor - // for initialization fields and their types - value = value ? parseInt(value) : 0 - const tx = new Transaction({ - nonce: new BN(res.nonce), - gasPrice: '0x1', - gasLimit: gasLimit, - to: to, - value: value, - data: Buffer.from(data.slice(2), 'hex') - }) - tx.sign(account.privateKey) - const coinbases = ['0x0e9281e9c6a0808672eaba6bd1220e144c9bb07a', '0x8945a1288dc78a6d8952a92c77aee6730b414778', '0x94d76e24f818426ae84aa404140e8d5f60e10e7e'] - const difficulties = [new BN('69762765929000', 10), new BN('70762765929000', 10), new BN('71762765929000', 10)] - const block = new Block({ - header: { - timestamp: timestamp || (new Date().getTime() / 1000 | 0), - number: self.blockNumber, - coinbase: coinbases[self.blockNumber % coinbases.length], - difficulty: difficulties[self.blockNumber % difficulties.length], - gasLimit: new BN(gasLimit, 10).imuln(2) - }, - transactions: [tx], - uncleHeaders: [] - }) - if (!useCall) { - ++self.blockNumber - this.runBlockInVm(tx, block, callback) - } else { - this.getVM().stateManager.checkpoint(() => { - this.runBlockInVm(tx, block, (err, result) => { - this.getVM().stateManager.revert(() => { - callback(err, result) - }) + this.getVMObject().stateManager.checkpoint().then(() => { + this.runBlockInVm(tx, block, (err, result) => { + this.getVMObject().stateManager.revert().then(() => { + callback(err, result) }) }) - } + }) } + }).catch((e) => { + callback(e) }) } runBlockInVm (tx, block, callback) { - this.getVM().runBlock({ block: block, generate: true, skipBlockValidation: true, skipBalance: false }).then((results) => { + this.getVMObject().vm.runBlock({ block: block, generate: true, skipBlockValidation: true, skipBalance: false }).then((results) => { const result = results.results[0] if (result) { const status = result.execResult.exceptionError ? 0 : 1 diff --git a/libs/remix-simulator/src/methods/transactions.ts b/libs/remix-simulator/src/methods/transactions.ts index fee5059d96..c3a43f5bc4 100644 --- a/libs/remix-simulator/src/methods/transactions.ts +++ b/libs/remix-simulator/src/methods/transactions.ts @@ -40,7 +40,7 @@ export class Transactions { } } - this.txRunnerVMInstance = new TxRunnerVM(accounts, api, _ => this.vmContext.vm()) + this.txRunnerVMInstance = new TxRunnerVM(accounts, api, _ => this.vmContext.vmObject()) this.txRunnerInstance = new TxRunner(this.txRunnerVMInstance, { runAsync: false }) this.txRunnerInstance.vmaccounts = accounts } diff --git a/libs/remix-simulator/src/vm-context.ts b/libs/remix-simulator/src/vm-context.ts index e59eb68cdd..43f5f6f8c9 100644 --- a/libs/remix-simulator/src/vm-context.ts +++ b/libs/remix-simulator/src/vm-context.ts @@ -2,61 +2,82 @@ 'use strict' import Web3 from 'web3' import { rlp, keccak, bufferToHex } from 'ethereumjs-util' -import { vm, execution } from '@remix-project/remix-lib' -const EthJSVM = require('ethereumjs-vm').default -const StateManager = require('ethereumjs-vm/dist/state/stateManager').default +import { vm as remixLibVm, execution } from '@remix-project/remix-lib' +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' /* extend vm state manager and instanciate VM */ class StateManagerCommonStorageDump extends StateManager { - constructor (arg) { - super(arg) + + keyHashes: { [key: string]: string } + constructor () { + super() this.keyHashes = {} } - putContractStorage (address, key, value, cb) { + putContractStorage (address, key, value) { this.keyHashes[keccak(key).toString('hex')] = bufferToHex(key) - super.putContractStorage(address, key, value, cb) + return super.putContractStorage(address, key, value) } - dumpStorage (address, cb) { - this._getStorageTrie(address, (err, trie) => { - if (err) { - return cb(err) + async dumpStorage (address) { + let trie + try { + trie = await this._getStorageTrie(address) + } catch (e) { + console.log(e) + throw e + } + return new Promise((resolve, reject) => { + try { + const storage = {} + const stream = trie.createReadStream() + stream.on('data', (val) => { + const value = rlp.decode(val.value) + storage['0x' + val.key.toString('hex')] = { + key: this.keyHashes[val.key.toString('hex')], + value: '0x' + value.toString('hex') + } + }) + stream.on('end', function () { + resolve(storage) + }) + } catch (e) { + reject(e) } - const storage = {} - const stream = trie.createReadStream() - stream.on('data', (val) => { - const value = rlp.decode(val.value) - storage['0x' + val.key.toString('hex')] = { - key: this.keyHashes[val.key.toString('hex')], - value: '0x' + value.toString('hex') - } - }) - stream.on('end', function () { - cb(storage) - }) }) } - getStateRoot (cb) { - const checkpoint = this._checkpointCount - this._checkpointCount = 0 - super.getStateRoot((err, stateRoot) => { - this._checkpointCount = checkpoint - cb(err, stateRoot) - }) + async getStateRoot (force = false) { + await this._cache.flush() + + const stateRoot = this._trie.root + return stateRoot } - setStateRoot (stateRoot, cb) { - const checkpoint = this._checkpointCount - this._checkpointCount = 0 - super.setStateRoot(stateRoot, (err) => { - this._checkpointCount = checkpoint - cb(err) - }) + async setStateRoot (stateRoot) { + await this._cache.flush() + + if (stateRoot === this._trie.EMPTY_TRIE_ROOT) { + this._trie.root = stateRoot + this._cache.clear() + this._storageTries = {} + return + } + + const hasRoot = await this._trie.checkRoot(stateRoot) + if (!hasRoot) { + throw new Error('State trie does not contain state root') + } + + this._trie.root = stateRoot + this._cache.clear() + this._storageTries = {} } } @@ -79,7 +100,7 @@ export class VMContext { constructor () { this.blockGasLimitDefault = 4300000 this.blockGasLimit = this.blockGasLimitDefault - this.currentFork = 'muirGlacier' + this.currentFork = 'berlin' this.vms = { /* byzantium: createVm('byzantium'), @@ -87,7 +108,7 @@ export class VMContext { petersburg: createVm('petersburg'), istanbul: createVm('istanbul'), */ - muirGlacier: this.createVm('muirGlacier') + berlin: this.createVm('berlin') } this.blocks = {} this.latestBlockNumber = 0 @@ -97,22 +118,21 @@ export class VMContext { } createVm (hardfork) { - const stateManager = new StateManagerCommonStorageDump({}) - stateManager.checkpoint(() => {}) - const ethvm = new EthJSVM({ + const stateManager = new StateManagerCommonStorageDump() + const common = new Common({ chain: 'mainnet', hardfork }) + const vm = new VM({ + common, activatePrecompiles: true, - blockchain: stateManager.blockchain, - stateManager: stateManager, - hardfork: hardfork + stateManager: stateManager }) - ethvm.blockchain.validate = false - this.web3vm = new vm.Web3VMProvider() - this.web3vm.setVM(ethvm) - return { vm: ethvm, web3vm: this.web3vm, stateManager } + + const web3vm = new remixLibVm.Web3VMProvider() + web3vm.setVM(vm) + return { vm, web3vm, stateManager, common } } web3 () { - return this.web3vm + return this.vms[this.currentFork].web3vm } blankWeb3 () { @@ -123,6 +143,10 @@ export class VMContext { return this.vms[this.currentFork].vm } + vmObject () { + return this.vms[this.currentFork] + } + addBlock (block) { let blockNumber = '0x' + block.header.number.toString('hex') if (blockNumber === '0x') { @@ -133,7 +157,7 @@ export class VMContext { this.blocks[blockNumber] = block this.latestBlockNumber = blockNumber - this.logsManager.checkBlock(blockNumber, block, this.web3vm) + this.logsManager.checkBlock(blockNumber, block, this.web3()) } trackTx (tx, block) {