|
|
|
@ -2,7 +2,7 @@ |
|
|
|
|
'use strict' |
|
|
|
|
import { Cache } from '@ethereumjs/statemanager/dist/cache' |
|
|
|
|
import { hash } from '@remix-project/remix-lib' |
|
|
|
|
import { bufferToHex, Account, toBuffer, bufferToBigInt} from '@ethereumjs/util' |
|
|
|
|
import { bufferToHex, Account, toBuffer, bufferToBigInt, bigIntToHex } from '@ethereumjs/util' |
|
|
|
|
import { keccak256 } from 'ethereum-cryptography/keccak' |
|
|
|
|
import type { Address } from '@ethereumjs/util' |
|
|
|
|
import { decode } from 'rlp' |
|
|
|
@ -13,7 +13,7 @@ import { VmProxy } from './VmProxy' |
|
|
|
|
import { VM } from '@ethereumjs/vm' |
|
|
|
|
import type { BigIntLike } from '@ethereumjs/util' |
|
|
|
|
import { Common, ConsensusType } from '@ethereumjs/common' |
|
|
|
|
import { Trie } from '@ethereumjs/trie' |
|
|
|
|
import { Trie, MapDB } from '@ethereumjs/trie' |
|
|
|
|
import { DefaultStateManager, StateManager, EthersStateManager, EthersStateManagerOpts } from '@ethereumjs/statemanager' |
|
|
|
|
import { StorageDump } from '@ethereumjs/statemanager/dist/interface' |
|
|
|
|
import { EVM } from '@ethereumjs/evm' |
|
|
|
@ -21,7 +21,7 @@ import { EEI } from '@ethereumjs/vm' |
|
|
|
|
import { Blockchain } from '@ethereumjs/blockchain' |
|
|
|
|
import { Block } from '@ethereumjs/block' |
|
|
|
|
import { Transaction } from '@ethereumjs/tx' |
|
|
|
|
import { bigIntToHex } from '@ethereumjs/util' |
|
|
|
|
import { State } from './provider' |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Options for constructing a {@link StateManager}. |
|
|
|
@ -50,6 +50,11 @@ class StateManagerCommonStorageDump extends DefaultStateManager { |
|
|
|
|
this.keyHashes = {} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
getDb () { |
|
|
|
|
// @ts-ignore
|
|
|
|
|
return this._trie.database().db |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
putContractStorage (address, key, value) { |
|
|
|
|
this.keyHashes[hash.keccak(key).toString('hex')] = bufferToHex(key) |
|
|
|
|
return super.putContractStorage(address, key, value) |
|
|
|
@ -100,7 +105,6 @@ export interface CustomEthersStateManagerOpts { |
|
|
|
|
class CustomEthersStateManager extends StateManagerCommonStorageDump { |
|
|
|
|
private provider: ethers.providers.StaticJsonRpcProvider | ethers.providers.JsonRpcProvider |
|
|
|
|
private blockTag: string |
|
|
|
|
|
|
|
|
|
constructor(opts: CustomEthersStateManagerOpts) { |
|
|
|
|
super(opts) |
|
|
|
|
if (typeof opts.provider === 'string') { |
|
|
|
@ -258,7 +262,7 @@ class CustomEthersStateManager extends StateManagerCommonStorageDump { |
|
|
|
|
export type CurrentVm = { |
|
|
|
|
vm: VM, |
|
|
|
|
web3vm: VmProxy, |
|
|
|
|
stateManager: StateManager, |
|
|
|
|
stateManager: StateManagerCommonStorageDump, |
|
|
|
|
common: Common |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -298,12 +302,16 @@ export class VMContext { |
|
|
|
|
exeResults: Record<string, Transaction> |
|
|
|
|
nodeUrl: string |
|
|
|
|
blockNumber: number | 'latest' |
|
|
|
|
stateDb: State |
|
|
|
|
rawBlocks: string[] |
|
|
|
|
serializedBlocks: Buffer[] |
|
|
|
|
|
|
|
|
|
constructor (fork?: string, nodeUrl?: string, blockNumber?: number | 'latest') { |
|
|
|
|
constructor (fork?: string, nodeUrl?: string, blockNumber?: number | 'latest', stateDb?: State, blocksData?: string[]) { |
|
|
|
|
this.blockGasLimitDefault = 4300000 |
|
|
|
|
this.blockGasLimit = this.blockGasLimitDefault |
|
|
|
|
this.currentFork = fork || 'merge' |
|
|
|
|
this.nodeUrl = nodeUrl |
|
|
|
|
this.stateDb = stateDb |
|
|
|
|
this.blockNumber = blockNumber |
|
|
|
|
this.blocks = {} |
|
|
|
|
this.latestBlockNumber = "0x0" |
|
|
|
@ -311,6 +319,8 @@ export class VMContext { |
|
|
|
|
this.txByHash = {} |
|
|
|
|
this.exeResults = {} |
|
|
|
|
this.logsManager = new LogsManager() |
|
|
|
|
this.rawBlocks = blocksData |
|
|
|
|
this.serializedBlocks = [] |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
async init () { |
|
|
|
@ -318,7 +328,7 @@ export class VMContext { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
async createVm (hardfork) { |
|
|
|
|
let stateManager: StateManager |
|
|
|
|
let stateManager: StateManagerCommonStorageDump |
|
|
|
|
if (this.nodeUrl) { |
|
|
|
|
let block = this.blockNumber |
|
|
|
|
if (this.blockNumber === 'latest') { |
|
|
|
@ -335,15 +345,25 @@ export class VMContext { |
|
|
|
|
blockTag: '0x' + this.blockNumber.toString(16) |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
} else{ |
|
|
|
|
const db = this.stateDb ? new Map(Object.entries(this.stateDb).map(([k, v]) => [k, toBuffer(v)])) : new Map() |
|
|
|
|
const mapDb = new MapDB(db) |
|
|
|
|
const trie = await Trie.create({ useKeyHashing: true, db: mapDb, useRootPersistence: true }) |
|
|
|
|
|
|
|
|
|
} else |
|
|
|
|
stateManager = new StateManagerCommonStorageDump() |
|
|
|
|
stateManager = new StateManagerCommonStorageDump({ trie }) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const consensusType = hardfork === 'berlin' || hardfork === 'london' ? ConsensusType.ProofOfWork : ConsensusType.ProofOfStake |
|
|
|
|
const difficulty = consensusType === ConsensusType.ProofOfStake ? 0 : 69762765929000 |
|
|
|
|
|
|
|
|
|
const common = new VMCommon({ chain: 'mainnet', hardfork }) |
|
|
|
|
const genesisBlock: Block = Block.fromBlockData({ |
|
|
|
|
const blocks = (this.rawBlocks || []).map(block => { |
|
|
|
|
const serializedBlock = toBuffer(block) |
|
|
|
|
|
|
|
|
|
this.serializedBlocks.push(serializedBlock) |
|
|
|
|
return Block.fromRLPSerializedBlock(serializedBlock, { common }) |
|
|
|
|
}) |
|
|
|
|
const genesisBlock: Block = blocks.length > 0 && (blocks[0] || {}).isGenesis ? blocks[0] : Block.fromBlockData({ |
|
|
|
|
header: { |
|
|
|
|
timestamp: (new Date().getTime() / 1000 | 0), |
|
|
|
|
number: 0, |
|
|
|
@ -352,7 +372,6 @@ export class VMContext { |
|
|
|
|
gasLimit: 8000000 |
|
|
|
|
} |
|
|
|
|
}, { common, hardforkByBlockNumber: false, hardforkByTTD: undefined }) |
|
|
|
|
|
|
|
|
|
const blockchain = await Blockchain.create({ common, validateBlocks: false, validateConsensus: false, genesisBlock }) |
|
|
|
|
const eei = new EEI(stateManager, common, blockchain) |
|
|
|
|
const evm = new EVM({ common, eei, allowUnlimitedContractSize: true }) |
|
|
|
@ -365,13 +384,17 @@ export class VMContext { |
|
|
|
|
blockchain, |
|
|
|
|
evm |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
// 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) |
|
|
|
|
this.addBlock(genesisBlock, true) |
|
|
|
|
return { vm, web3vm, stateManager, common } |
|
|
|
|
if (blocks.length > 0) blocks.splice(0, 1) |
|
|
|
|
blocks.forEach(block => { |
|
|
|
|
blockchain.putBlock(block) |
|
|
|
|
this.addBlock(block, false, false, web3vm) |
|
|
|
|
}) |
|
|
|
|
return { vm, web3vm, stateManager, common, blocks } |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
getCurrentFork () { |
|
|
|
@ -390,7 +413,7 @@ export class VMContext { |
|
|
|
|
return this.currentVm |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
addBlock (block: Block, genesis?: boolean, isCall?: boolean) { |
|
|
|
|
addBlock (block: Block, genesis?: boolean, isCall?: boolean, web3vm?: VmProxy) { |
|
|
|
|
let blockNumber = bigIntToHex(block.header.number) |
|
|
|
|
if (blockNumber === '0x') { |
|
|
|
|
blockNumber = '0x0' |
|
|
|
@ -400,7 +423,8 @@ export class VMContext { |
|
|
|
|
this.blocks[blockNumber] = block |
|
|
|
|
this.latestBlockNumber = blockNumber |
|
|
|
|
|
|
|
|
|
if (!isCall && !genesis) this.logsManager.checkBlock(blockNumber, block, this.web3()) |
|
|
|
|
if (!isCall && !genesis && web3vm) this.logsManager.checkBlock(blockNumber, block, web3vm) |
|
|
|
|
if (!isCall && !genesis && !web3vm) this.logsManager.checkBlock(blockNumber, block, this.web3()) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
trackTx (txHash, block, tx) { |
|
|
|
|