diff --git a/package-lock.json b/package-lock.json index 2d3b307f24..1c039ffd7b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17940,6 +17940,7 @@ "devDependencies": { "@ethereumjs/blockchain": "^8.0.0-alpha.1", "@ethereumjs/ethash": "^4.0.0-alpha.1", + "@ethereumjs/verkle": "^0.2.0-alpha.1", "@paulmillr/trusted-setups": "^0.1.2", "@types/benchmark": "^1.0.33", "@types/core-js": "^2.5.0", @@ -17959,6 +17960,84 @@ "node": ">=18" } }, + "packages/vm/node_modules/@ethereumjs/blockchain/node_modules/@ethereumjs/block": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/block/-/block-5.3.0.tgz", + "integrity": "sha512-cyphdEB/ywIERqWLRHdAS6muTkAcd6BibMOp6XmGbeWgvtIhe4ArxcMDI78MVstJzT/faihvRI4rKQKy+MpdKQ==", + "extraneous": true, + "dependencies": { + "@ethereumjs/common": "^4.4.0", + "@ethereumjs/rlp": "^5.0.2", + "@ethereumjs/trie": "^6.2.1", + "@ethereumjs/tx": "^5.4.0", + "@ethereumjs/util": "^9.1.0", + "ethereum-cryptography": "^2.2.1" + }, + "engines": { + "node": ">=18" + } + }, + "packages/vm/node_modules/@ethereumjs/blockchain/node_modules/@ethereumjs/common": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/common/-/common-4.4.0.tgz", + "integrity": "sha512-Fy5hMqF6GsE6DpYTyqdDIJPJgUtDn4dL120zKw+Pswuo+iLyBsEYuSyzMw6NVzD2vDzcBG9fE4+qX4X2bPc97w==", + "extraneous": true, + "dependencies": { + "@ethereumjs/util": "^9.1.0" + } + }, + "packages/vm/node_modules/@ethereumjs/blockchain/node_modules/@ethereumjs/rlp": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@ethereumjs/rlp/-/rlp-5.0.2.tgz", + "integrity": "sha512-DziebCdg4JpGlEqEdGgXmjqcFoJi+JGulUXwEjsZGAscAQ7MyD/7LE/GVCP29vEQxKc7AAwjT3A2ywHp2xfoCA==", + "extraneous": true, + "bin": { + "rlp": "bin/rlp.cjs" + }, + "engines": { + "node": ">=18" + } + }, + "packages/vm/node_modules/@ethereumjs/blockchain/node_modules/@ethereumjs/tx": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/tx/-/tx-5.4.0.tgz", + "integrity": "sha512-SCHnK7m/AouZ7nyoR0MEXw1OO/tQojSbp88t8oxhwes5iZkZCtfFdUrJaiIb72qIpH2FVw6s1k1uP7LXuH7PsA==", + "extraneous": true, + "dependencies": { + "@ethereumjs/common": "^4.4.0", + "@ethereumjs/rlp": "^5.0.2", + "@ethereumjs/util": "^9.1.0", + "ethereum-cryptography": "^2.2.1" + }, + "engines": { + "node": ">=18" + } + }, + "packages/vm/node_modules/@ethereumjs/blockchain/node_modules/@ethereumjs/util": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/util/-/util-9.1.0.tgz", + "integrity": "sha512-XBEKsYqLGXLah9PNJbgdkigthkG7TAGvlD/sH12beMXEyHDyigfcbdvHhmLyDWgDyOJn4QwiQUaF7yeuhnjdog==", + "extraneous": true, + "dependencies": { + "@ethereumjs/rlp": "^5.0.2", + "ethereum-cryptography": "^2.2.1" + }, + "engines": { + "node": ">=18" + } + }, + "packages/vm/node_modules/@ethereumjs/blockchain/node_modules/ethereum-cryptography": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.2.1.tgz", + "integrity": "sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg==", + "extraneous": true, + "dependencies": { + "@noble/curves": "1.4.2", + "@noble/hashes": "1.4.0", + "@scure/bip32": "1.4.0", + "@scure/bip39": "1.3.0" + } + }, "packages/vm/node_modules/@ethereumjs/ethash/node_modules/@ethereumjs/block": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/@ethereumjs/block/-/block-5.3.0.tgz", diff --git a/packages/blockchain/src/helpers.ts b/packages/blockchain/src/helpers.ts index d3c0272b03..72696c6b76 100644 --- a/packages/blockchain/src/helpers.ts +++ b/packages/blockchain/src/helpers.ts @@ -41,7 +41,7 @@ export async function getGenesisStateRoot(chainId: Chain, common: Common): Promi return chainGenesis !== undefined ? chainGenesis.stateRoot : genGenesisStateRoot({}, common) } -/* +/* The code below calculates the empty requests hash as of devnet-4 for EIP 7685 Note: it is not possible to calculate this directly in the blockchain package, this introduces the `ethereum-cryptography` dependency. diff --git a/packages/client/bin/cli.ts b/packages/client/bin/cli.ts index ef3f2d09a8..9592b28830 100755 --- a/packages/client/bin/cli.ts +++ b/packages/client/bin/cli.ts @@ -1176,7 +1176,6 @@ async function run() { dnsNetworks: args.dnsNetworks, extIP: args.extIP, key, - logger, maxPeers: args.maxPeers, maxPerRequest: args.maxPerRequest, diff --git a/packages/client/src/execution/vmexecution.ts b/packages/client/src/execution/vmexecution.ts index b921e54734..0567b9de94 100644 --- a/packages/client/src/execution/vmexecution.ts +++ b/packages/client/src/execution/vmexecution.ts @@ -212,6 +212,10 @@ export class VMExecution extends Execution { } else throw new Error('EIP-6800 active and no verkle execution mode specified') await mcl.init(mcl.BLS12_381) const rustBN = await initRustBN() + console.log( + 'this.config.execCommon is activated 2935', + this.config.execCommon.isActivatedEIP(2935), + ) this.verkleVM = await createVM({ common: this.config.execCommon, blockchain: this.chain.blockchain, diff --git a/packages/common/src/chains.ts b/packages/common/src/chains.ts index eb1cb3b21f..e917b8e9f1 100644 --- a/packages/common/src/chains.ts +++ b/packages/common/src/chains.ts @@ -121,6 +121,10 @@ export const Mainnet: ChainConfig = { name: 'osaka', block: null, }, + { + name: 'verkle', + block: null, + }, ], bootstrapNodes: [ { diff --git a/packages/common/src/common.ts b/packages/common/src/common.ts index 6b5d6113ec..80e632ec4b 100644 --- a/packages/common/src/common.ts +++ b/packages/common/src/common.ts @@ -317,7 +317,7 @@ export class Common { for (const hfChanges of this.HARDFORK_CHANGES) { // EIP-referencing HF config (e.g. for berlin) if ('eips' in hfChanges[1]) { - const hfEIPs = hfChanges[1]['eips'] ?? [] + const hfEIPs = hfChanges[1].eips ?? [] for (const eip of hfEIPs) { this._mergeWithParamsCache(this._params[eip] ?? {}) } diff --git a/packages/evm/src/evm.ts b/packages/evm/src/evm.ts index ea05c7930f..5f20d8e3c6 100644 --- a/packages/evm/src/evm.ts +++ b/packages/evm/src/evm.ts @@ -102,6 +102,7 @@ export class EVM implements EVMInterface { public blockchain: EVMMockBlockchainInterface public journal: Journal public verkleAccessWitness?: VerkleAccessWitness + public systemVerkleAccessWitness?: VerkleAccessWitness public readonly transientStorage: TransientStorage diff --git a/packages/evm/src/opcodes/EIP2929.ts b/packages/evm/src/opcodes/EIP2929.ts index a273d96baa..bbfe382032 100644 --- a/packages/evm/src/opcodes/EIP2929.ts +++ b/packages/evm/src/opcodes/EIP2929.ts @@ -31,6 +31,10 @@ export function accessAddressEIP2929( // if verkle not activated if (chargeGas && !common.isActivatedEIP(6800)) { return common.param('coldaccountaccessGas') + } else if (chargeGas && common.isActivatedEIP(6800)) { + // If Verkle is active, then the warmstoragereadGas should still be charged + // This is because otherwise opcodes will have cost 0 (this is thus the base fee) + return common.param('warmstoragereadGas') } // Warm: (selfdestruct beneficiary address reads are not charged when warm) } else if (chargeGas && !isSelfdestruct) { diff --git a/packages/evm/src/opcodes/gas.ts b/packages/evm/src/opcodes/gas.ts index d6af6993b0..2b968f46af 100644 --- a/packages/evm/src/opcodes/gas.ts +++ b/packages/evm/src/opcodes/gas.ts @@ -173,7 +173,8 @@ export const dynamicGasHandlers: Map { let account = await evm.stateManager.getAccount(address) if (account === undefined) { - if (common.isActivatedEIP(6800) === true) { - if (evm.verkleAccessWitness === undefined) { + if (common.isActivatedEIP(6800) === true && reward !== BIGINT_0) { + if (evm.systemVerkleAccessWitness === undefined) { throw Error(`verkleAccessWitness required if verkle (EIP-6800) is activated`) } - evm.verkleAccessWitness.touchAndChargeProofOfAbsence(address) + evm.systemVerkleAccessWitness.touchAddressOnWriteAndComputeGas( + address, + 0, + VERKLE_BASIC_DATA_LEAF_KEY, + ) + evm.systemVerkleAccessWitness.touchAddressOnWriteAndComputeGas( + address, + 0, + VERKLE_CODE_HASH_LEAF_KEY, + ) } account = new Account() } account.balance += reward await evm.journal.putAccount(address, account) - if (common.isActivatedEIP(6800) === true) { - if (evm.verkleAccessWitness === undefined) { + if (common.isActivatedEIP(6800) === true && reward !== BIGINT_0) { + if (evm.systemVerkleAccessWitness === undefined) { throw Error(`verkleAccessWitness required if verkle (EIP-6800) is activated`) } // use vm utility to build access but the computed gas is not charged and hence free - evm.verkleAccessWitness.touchTxTargetAndComputeGas(address, { + evm.systemVerkleAccessWitness.touchTxTargetAndComputeGas(address, { sendsValue: true, }) } diff --git a/packages/vm/src/runTx.ts b/packages/vm/src/runTx.ts index 691233141a..eb351e84d8 100644 --- a/packages/vm/src/runTx.ts +++ b/packages/vm/src/runTx.ts @@ -12,6 +12,8 @@ import { KECCAK256_NULL, MAX_UINT64, SECP256K1_ORDER_DIV_2, + VERKLE_BASIC_DATA_LEAF_KEY, + VERKLE_CODE_HASH_LEAF_KEY, bytesToBigInt, bytesToHex, bytesToUnprefixedHex, @@ -669,6 +671,17 @@ async function _runTx(vm: VM, opts: RunTxOpts): Promise { vm.evm.verkleAccessWitness.touchAndChargeProofOfAbsence(miner) } minerAccount = new Account() + // Add the miner account to the system verkle access witness + vm.evm.systemVerkleAccessWitness?.touchAddressOnWriteAndComputeGas( + miner, + 0, + VERKLE_BASIC_DATA_LEAF_KEY, + ) + vm.evm.systemVerkleAccessWitness?.touchAddressOnWriteAndComputeGas( + miner, + 0, + VERKLE_CODE_HASH_LEAF_KEY, + ) } // add the amount spent on gas to the miner's account results.minerValue = vm.common.isActivatedEIP(1559) @@ -676,7 +689,14 @@ async function _runTx(vm: VM, opts: RunTxOpts): Promise { : results.amountSpent minerAccount.balance += results.minerValue - if (vm.common.isActivatedEIP(6800)) { + // If the miner value is zero, revert the access witness as the proof of absence was not necessary. + if (results.minerValue === BIGINT_0) { + vm.evm.verkleAccessWitness?.revert() + } else { + vm.evm.verkleAccessWitness?.commit() + } + + if (vm.common.isActivatedEIP(6800) && results.minerValue !== BIGINT_0) { if (vm.evm.verkleAccessWitness === undefined) { throw Error(`verkleAccessWitness required if verkle (EIP-6800) is activated`) } diff --git a/packages/vm/test/tester/config.ts b/packages/vm/test/tester/config.ts index 71aa1d4f9b..3a0906c9ad 100644 --- a/packages/vm/test/tester/config.ts +++ b/packages/vm/test/tester/config.ts @@ -1,8 +1,9 @@ import { Common, Hardfork, Mainnet, createCustomCommon } from '@ethereumjs/common' -import { type KZG } from '@ethereumjs/util' +import * as verkle from 'micro-eth-signer/verkle' import * as path from 'path' import type { HardforkTransitionConfig } from '@ethereumjs/common' +import type { KZG } from '@ethereumjs/util' /** * Default tests path (git submodule: ethereum-tests) @@ -109,6 +110,7 @@ const normalHardforks = [ 'cancun', 'prague', 'osaka', + 'verkle', ] const transitionNetworks = { @@ -278,6 +280,70 @@ function setupCommonWithNetworks(network: string, ttd?: number, timestamp?: numb return common } +/** + * Returns a common instance configured for verkle + * @param network Network target (this can include EIPs, such as Byzantium+2537+2929) + * @param ttd If set: total terminal difficulty to switch to merge + * @returns + */ +function setupCommonForVerkle(network: string, timestamp?: number, kzg?: KZG) { + let ttd + // hard fork that verkle tests are filled on + const hfName = 'shanghai' + const mainnetCommon = new Common({ chain: Mainnet, hardfork: hfName }) + const hardforks = mainnetCommon.hardforks().slice(0, 17) // skip hardforks after Shanghai + const testHardforks: HardforkTransitionConfig[] = [] + for (const hf of hardforks) { + // check if we enable this hf + // disable dao hf by default (if enabled at block 0 forces the first 10 blocks to have dao-hard-fork in extraData of block header) + if (mainnetCommon.gteHardfork(hf.name) && hf.name !== Hardfork.Dao) { + // this hardfork should be activated at block 0 + testHardforks.push({ + name: hf.name, + // Current type definition Partial in Common is currently not allowing to pass in forkHash + // forkHash: hf.forkHash, + block: 0, + }) + } else { + // disable hardforks newer than the test hardfork (but do add "support" for it, it just never gets activated) + if ( + (ttd === undefined && timestamp === undefined) || + (hf.name === 'paris' && ttd !== undefined) + ) { + testHardforks.push({ + name: hf.name, + block: null, + }) + } + if (timestamp !== undefined && hf.name !== Hardfork.Dao) { + testHardforks.push({ + name: hf.name, + block: null, + timestamp, + }) + } + } + } + + testHardforks.push({ name: 'verkle', block: 1 }) + const common = createCustomCommon( + { + hardforks: testHardforks, + defaultHardfork: 'verkle', + }, + Mainnet, + { eips: [2935, 3607], customCrypto: { kzg, verkle } }, + ) + + console.log(common.eips()) + // Activate EIPs + const eips = network.match(/(?<=\+)(.\d+)/g) + if (eips) { + common.setEIPs(eips.map((e: string) => parseInt(e))) + } + return common +} + /** * Returns a Common for the given network (a test parameter) * @param network - the network field of a test. @@ -288,6 +354,8 @@ function setupCommonWithNetworks(network: string, ttd?: number, timestamp?: numb * @returns the Common which should be used */ export function getCommon(network: string, kzg?: KZG): Common { + // Special handler for verkle tests + if (network.toLowerCase().includes('verkle')) return setupCommonForVerkle(network, undefined, kzg) if (retestethAlias[network as keyof typeof retestethAlias] !== undefined) { network = retestethAlias[network as keyof typeof retestethAlias] } diff --git a/packages/vm/test/tester/index.ts b/packages/vm/test/tester/index.ts index 8db242cf28..027337cdbb 100755 --- a/packages/vm/test/tester/index.ts +++ b/packages/vm/test/tester/index.ts @@ -142,6 +142,7 @@ async function runTests() { profile: boolean bls: EVMBLSInterface bn254: EVMBN254Interface + stateManager: string } = { forkConfigVM: FORK_CONFIG_VM, forkConfigTestSuite: FORK_CONFIG_TEST_SUITE, @@ -156,6 +157,7 @@ async function runTests() { bls, profile: RUN_PROFILER, bn254, + stateManager: argv.stateManager, } /** diff --git a/packages/vm/test/tester/runners/BlockchainTestsRunner.ts b/packages/vm/test/tester/runners/BlockchainTestsRunner.ts index cf7b6e4a32..3fc515d849 100644 --- a/packages/vm/test/tester/runners/BlockchainTestsRunner.ts +++ b/packages/vm/test/tester/runners/BlockchainTestsRunner.ts @@ -1,28 +1,28 @@ -import { createBlock, createBlockFromRLP } from '@ethereumjs/block' +import { createBlock } from '@ethereumjs/block' import { EthashConsensus, createBlockchain } from '@ethereumjs/blockchain' import { ConsensusAlgorithm } from '@ethereumjs/common' import { Ethash } from '@ethereumjs/ethash' import { MerklePatriciaTrie } from '@ethereumjs/mpt' -import { RLP } from '@ethereumjs/rlp' -import { Caches, MerkleStateManager } from '@ethereumjs/statemanager' +import { Caches, MerkleStateManager, StatefulVerkleStateManager } from '@ethereumjs/statemanager' import { createTxFromRLP } from '@ethereumjs/tx' import { MapDB, - bytesToBigInt, bytesToHex, hexToBytes, isHexString, stripHexPrefix, toBytes, } from '@ethereumjs/util' +import { createVerkleTree } from '@ethereumjs/verkle' import { buildBlock, createVM, runBlock } from '../../../src/index.js' import { setupPreConditions, verifyPostConditions } from '../../util.js' import type { Block } from '@ethereumjs/block' import type { Blockchain, ConsensusDict } from '@ethereumjs/blockchain' -import type { Common } from '@ethereumjs/common' +import type { Common, StateManagerInterface } from '@ethereumjs/common' import type { PrefixedHexString } from '@ethereumjs/util' +import type { VerkleTree } from '@ethereumjs/verkle' import type * as tape from 'tape' function formatBlockHeader(data: any) { @@ -35,7 +35,7 @@ function formatBlockHeader(data: any) { export async function runBlockchainTest(options: any, testData: any, t: tape.Test) { // ensure that the test data is the right fork data - if (testData.network !== options.forkConfigTestSuite) { + if (testData.network.toLowerCase() !== options.forkConfigTestSuite) { t.comment(`skipping test: no data available for ${options.forkConfigTestSuite}`) return } @@ -46,13 +46,25 @@ export async function runBlockchainTest(options: any, testData: any, t: tape.Tes let common = options.common.copy() as Common common.setHardforkBy({ blockNumber: 0 }) + let stateTree: MerklePatriciaTrie | VerkleTree + let stateManager: StateManagerInterface + + if (options.stateManager === 'verkle') { + stateTree = await createVerkleTree() + stateManager = new StatefulVerkleStateManager({ + trie: stateTree, + common: options.common, + }) + } else { + stateTree = new MerklePatriciaTrie({ useKeyHashing: true, common }) + stateManager = new MerkleStateManager({ + caches: new Caches(), + trie: stateTree, + common, + }) + } + let cacheDB = new MapDB() - let state = new MerklePatriciaTrie({ useKeyHashing: true, common }) - let stateManager = new MerkleStateManager({ - caches: new Caches(), - trie: state, - common, - }) let validatePow = false // Only run with block validation when sealEngine present in test file @@ -86,7 +98,7 @@ export async function runBlockchainTest(options: any, testData: any, t: tape.Tes }) if (validatePow) { - ;(blockchain.consensus as EthashConsensus)._ethash!.cacheDB = cacheDB as any + ;(blockchain.consensus as EthashConsensus)._ethash!.cacheDB = cacheDB } const begin = Date.now() @@ -137,26 +149,29 @@ export async function runBlockchainTest(options: any, testData: any, t: tape.Tes // Here we decode the rlp to extract the block number // The block library cannot be used, as this throws on certain EIP1559 blocks when trying to convert - try { - const blockRlp = hexToBytes(raw.rlp as PrefixedHexString) - const decodedRLP: any = RLP.decode(Uint8Array.from(blockRlp)) - currentBlock = bytesToBigInt(decodedRLP[0][8]) - } catch (e: any) { - await handleError(e, expectException) - continue - } + // try { + // const blockRlp = hexToBytes(raw.rlp as PrefixedHexString) + // const decodedRLP: any = RLP.decode(Uint8Array.from(blockRlp)) + // currentBlock = bytesToBigInt(decodedRLP[0][8]) + // } catch (e: any) { + // await handleError(e, expectException) + // continue + // } try { - const blockRlp = hexToBytes(raw.rlp as PrefixedHexString) - // Update common HF - let timestamp: bigint | undefined = undefined - try { - const decoded: any = RLP.decode(blockRlp) - timestamp = bytesToBigInt(decoded[0][11]) - // eslint-disable-next-line no-empty - } catch (e) {} + // const blockRlp = hexToBytes(raw.rlp as PrefixedHexString) + // // Update common HF + // const timestamp: bigint | undefined = undefined + // try { + // const decoded: any = RLP.decode(blockRlp) + // const timestamp = bytesToBigInt(decoded[0][11]) + // // eslint-disable-next-line no-empty + // } catch (e) {} - common.setHardforkBy({ blockNumber: currentBlock, timestamp }) + common.setHardforkBy({ + blockNumber: BigInt(raw.blockHeader.number), + timestamp: BigInt(raw.blockHeader.timestamp), + }) // transactionSequence is provided when txs are expected to be rejected. // To run this field we try to import them on the current state. @@ -190,7 +205,22 @@ export async function runBlockchainTest(options: any, testData: any, t: tape.Tes await blockBuilder.revert() // will only revert if checkpointed } - const block = createBlockFromRLP(blockRlp, { common, setHardfork: true }) + // TODO: Decide if we want to keep this variant of the block construction + // Create the block from the JSON block data since the RLP doesn't include the execution witness + const block = createBlock( + { + header: raw.blockHeader, + transactions: raw.transactions, + uncleHeaders: raw.uncleHeaders, + withdrawals: raw.withdrawals, + executionWitness: raw.witness, + }, + { + common, + setHardfork: true, + }, + ) + await blockchain.putBlock(block) // This is a trick to avoid generating the canonical genesis @@ -224,7 +254,7 @@ export async function runBlockchainTest(options: any, testData: any, t: tape.Tes const headBlock = await (vm.blockchain as Blockchain).getIteratorHead() await vm.stateManager.setStateRoot(headBlock.header.stateRoot) } else { - await verifyPostConditions(state, testData.postState, t) + await verifyPostConditions(stateTree, testData.postState, t) } throw e @@ -235,6 +265,7 @@ export async function runBlockchainTest(options: any, testData: any, t: tape.Tes return } } catch (error: any) { + console.log(error) // caught an error, reduce block number currentBlock-- await handleError(error, expectException) @@ -242,7 +273,7 @@ export async function runBlockchainTest(options: any, testData: any, t: tape.Tes } t.equal( - bytesToHex((blockchain as any)._headHeaderHash), + bytesToHex(blockchain['_headHeaderHash']), '0x' + testData.lastblockhash, 'correct last header block', ) @@ -251,6 +282,5 @@ export async function runBlockchainTest(options: any, testData: any, t: tape.Tes const timeSpent = `${(end - begin) / 1000} secs` t.comment(`Time: ${timeSpent}`) - // @ts-ignore Explicitly delete objects for memory optimization (early GC) - common = blockchain = state = stateManager = vm = cacheDB = null // eslint-disable-line + common = blockchain = stateTree = stateManager = vm = cacheDB = null as any } diff --git a/packages/vm/test/tester/runners/GeneralStateTestsRunner.ts b/packages/vm/test/tester/runners/GeneralStateTestsRunner.ts index 862ec2ceb6..be3ad5a656 100644 --- a/packages/vm/test/tester/runners/GeneralStateTestsRunner.ts +++ b/packages/vm/test/tester/runners/GeneralStateTestsRunner.ts @@ -3,19 +3,25 @@ import { Block } from '@ethereumjs/block' import { createBlockchain } from '@ethereumjs/blockchain' import { type InterpreterStep } from '@ethereumjs/evm' import { MerklePatriciaTrie } from '@ethereumjs/mpt' -import { Caches, MerkleStateManager } from '@ethereumjs/statemanager' +import { Caches, MerkleStateManager, StatefulVerkleStateManager } from '@ethereumjs/statemanager' import { Account, + MapDB, bytesToHex, createAddressFromString, equalsBytes, toBytes, } from '@ethereumjs/util' +import { createVerkleTree } from '@ethereumjs/verkle' +import * as verkle from 'micro-eth-signer/verkle' import { createVM, runTx } from '../../../src/index.js' import { makeBlockFromEnv, makeTx, setupPreConditions } from '../../util.js' +import type { StateManagerInterface } from '@ethereumjs/common' +import type { VerkleTree } from '@ethereumjs/verkle' import type * as tape from 'tape' +const loadVerkleCrypto = () => Promise.resolve(verkle) function parseTestCases( forkConfigTestSuite: string, @@ -75,23 +81,34 @@ async function runTestCase(options: any, testData: any, t: tape.Test) { const begin = Date.now() // Copy the common object to not create long-lasting // references in memory which might prevent GC - const common = options.common.copy() + let common = options.common.copy() // Have to create a blockchain with empty block as genesisBlock for Merge // Otherwise mainnet genesis will throw since this has difficulty nonzero const genesisBlock = new Block(undefined, undefined, undefined, undefined, { common }) - const blockchain = await createBlockchain({ genesisBlock, common }) - const state = new MerklePatriciaTrie({ useKeyHashing: true, common }) - const stateManager = new MerkleStateManager({ - caches: new Caches(), - trie: state, - common, - }) + let blockchain = await createBlockchain({ genesisBlock, common }) + let stateTree: VerkleTree | MerklePatriciaTrie + let stateManager: StateManagerInterface + if (options.stateManager === 'verkle') { + const verkleCrypto = await loadVerkleCrypto() + stateTree = await createVerkleTree({ verkleCrypto, db: new MapDB() }) + stateManager = new StatefulVerkleStateManager({ + common, + trie: stateTree, + }) + } else { + stateTree = new MerklePatriciaTrie({ useKeyHashing: true, common }) + stateManager = new MerkleStateManager({ + caches: new Caches(), + trie: stateTree, + common, + }) + } const evmOpts = { bls: options.bls, bn254: options.bn254, } - const vm = await createVM({ + let vm = await createVM({ stateManager, common, blockchain, @@ -107,8 +124,6 @@ async function runTestCase(options: any, testData: any, t: tape.Test) { try { tx = makeTx(testData.transaction, { common }) } catch (e: any) { - console.log('error: ', e) - console.log('testData.transaction: ', testData.transaction) execInfo = 'tx instantiation exception' } @@ -180,9 +195,7 @@ async function runTestCase(options: any, testData: any, t: tape.Test) { vm.evm.events!.removeListener('step', stepHandler) vm.events.removeListener('afterTx', afterTxHandler) - // @ts-ignore Explicitly delete objects for memory optimization (early GC) - // TODO FIXME - //common = blockchain = state = stateManager = evm = vm = null // eslint-disable-line + common = blockchain = stateTree = stateManager = vm = null as any return parseFloat(timeSpent) } diff --git a/packages/vm/test/tester/testLoader.ts b/packages/vm/test/tester/testLoader.ts index a7ad7fb22d..e1bfcf2ada 100644 --- a/packages/vm/test/tester/testLoader.ts +++ b/packages/vm/test/tester/testLoader.ts @@ -117,7 +117,7 @@ export async function getTestsFromArgs(testType: string, onFile: Function, args: if (new RegExp(`BlockchainTests`).test(testType)) { const forkFilter = new RegExp(`${args.forkConfig}$`) skipFn = (name: string, test: any) => { - return forkFilter.test(test.network) === false || skipTest(name, args.skipTests) + return forkFilter.test(test.network.toLowerCase()) === false || skipTest(name, args.skipTests) } } if (new RegExp(`GeneralStateTests`).test(testType)) { diff --git a/packages/vm/test/util.ts b/packages/vm/test/util.ts index c5b5ad5205..d6367765fb 100644 --- a/packages/vm/test/util.ts +++ b/packages/vm/test/util.ts @@ -423,6 +423,7 @@ export async function setupPreConditions(state: StateManagerInterface, testData: const storageRoot = (await state.getAccount(address))!.storageRoot if (testData.exec?.address === addressStr) { + // TODO: Figure out what in the world this is doing testData.root(storageRoot) }