From 34a5f08f94d76a30e719816ca2dd43a17eea34c5 Mon Sep 17 00:00:00 2001 From: Gabriel Rocheleau Date: Sun, 29 Sep 2024 12:30:38 -0400 Subject: [PATCH 01/60] vm: verkle testing wip --- packages/common/src/chains.ts | 4 ++++ packages/common/src/enums.ts | 1 + packages/common/src/hardforks.ts | 4 ++++ packages/evm/src/evm.ts | 1 + packages/vm/test/tester/config.ts | 1 + .../tester/runners/BlockchainTestsRunner.ts | 19 +++++++++---------- 6 files changed, 20 insertions(+), 10 deletions(-) diff --git a/packages/common/src/chains.ts b/packages/common/src/chains.ts index b463c81d1b..6c40a0b3d0 100644 --- a/packages/common/src/chains.ts +++ b/packages/common/src/chains.ts @@ -117,6 +117,10 @@ export const Mainnet: ChainConfig = { name: 'prague', block: null, }, + { + name: 'verkle', + block: null, + }, ], bootstrapNodes: [ { diff --git a/packages/common/src/enums.ts b/packages/common/src/enums.ts index d4fbf08b0a..8f4f673d57 100644 --- a/packages/common/src/enums.ts +++ b/packages/common/src/enums.ts @@ -72,6 +72,7 @@ export enum Hardfork { Cancun = 'cancun', Prague = 'prague', Osaka = 'osaka', + Verkle = 'verkle', } export enum ConsensusType { diff --git a/packages/common/src/hardforks.ts b/packages/common/src/hardforks.ts index 527ae642a1..e6da490c09 100644 --- a/packages/common/src/hardforks.ts +++ b/packages/common/src/hardforks.ts @@ -170,4 +170,8 @@ export const hardforksDict: HardforksDict = { osaka: { eips: [2935, 6800], }, + // TODO: WIP + verkle: { + eips: [2935, 6800], + }, } diff --git a/packages/evm/src/evm.ts b/packages/evm/src/evm.ts index 869b479db7..068844cba9 100644 --- a/packages/evm/src/evm.ts +++ b/packages/evm/src/evm.ts @@ -85,6 +85,7 @@ export class EVM implements EVMInterface { Hardfork.Cancun, Hardfork.Prague, Hardfork.Osaka, + Hardfork.Verkle, ] protected _tx?: { gasPrice: bigint diff --git a/packages/vm/test/tester/config.ts b/packages/vm/test/tester/config.ts index 608dcb2752..7c152ad0f7 100644 --- a/packages/vm/test/tester/config.ts +++ b/packages/vm/test/tester/config.ts @@ -108,6 +108,7 @@ const normalHardforks = [ 'arrowGlacier', // This network has no tests, but need to add it due to common generation logic 'cancun', 'prague', + 'verkle', ] const transitionNetworks = { diff --git a/packages/vm/test/tester/runners/BlockchainTestsRunner.ts b/packages/vm/test/tester/runners/BlockchainTestsRunner.ts index 11edabeea2..6ed8a5a075 100644 --- a/packages/vm/test/tester/runners/BlockchainTestsRunner.ts +++ b/packages/vm/test/tester/runners/BlockchainTestsRunner.ts @@ -3,8 +3,7 @@ import { EthashConsensus, createBlockchain } from '@ethereumjs/blockchain' import { ConsensusAlgorithm } from '@ethereumjs/common' import { Ethash } from '@ethereumjs/ethash' import { RLP } from '@ethereumjs/rlp' -import { Caches, MerkleStateManager } from '@ethereumjs/statemanager' -import { Trie } from '@ethereumjs/trie' +import { StatefulVerkleStateManager } from '@ethereumjs/statemanager' import { createTxFromRLP } from '@ethereumjs/tx' import { MapDB, @@ -15,6 +14,7 @@ import { stripHexPrefix, toBytes, } from '@ethereumjs/util' +import { loadVerkleCrypto } from 'verkle-cryptography-wasm' import { buildBlock, createVM, runBlock } from '../../../src/index.js' import { setupPreConditions, verifyPostConditions } from '../../util.js' @@ -46,14 +46,13 @@ export async function runBlockchainTest(options: any, testData: any, t: tape.Tes let common = options.common.copy() as Common common.setHardforkBy({ blockNumber: 0 }) - let cacheDB = new MapDB() - let state = new Trie({ useKeyHashing: true, common }) - let stateManager = new MerkleStateManager({ - caches: new Caches(), - trie: state, - common, + const verkleCrypto = await loadVerkleCrypto() + const state = new StatefulVerkleStateManager({ + verkleCrypto, }) + let cacheDB = new MapDB() + let validatePow = false // Only run with block validation when sealEngine present in test file // and being set to Ethash PoW validation @@ -86,7 +85,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() @@ -96,7 +95,7 @@ export async function runBlockchainTest(options: any, testData: any, t: tape.Tes bn254: options.bn254, } let vm = await createVM({ - stateManager, + stateManager: state, blockchain, common, setHardfork: true, From ac621b7728dfd25f9151ea2c2af9159db64c41b4 Mon Sep 17 00:00:00 2001 From: acolytec3 <17355484+acolytec3@users.noreply.github.com> Date: Mon, 30 Sep 2024 13:28:17 -0400 Subject: [PATCH 02/60] Add missing getStateRoot method --- packages/statemanager/src/statefulVerkleStateManager.ts | 2 +- packages/vm/test/tester/runners/BlockchainTestsRunner.ts | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/statemanager/src/statefulVerkleStateManager.ts b/packages/statemanager/src/statefulVerkleStateManager.ts index 6bc15969d2..5f4ad17c62 100644 --- a/packages/statemanager/src/statefulVerkleStateManager.ts +++ b/packages/statemanager/src/statefulVerkleStateManager.ts @@ -446,7 +446,7 @@ export class StatefulVerkleStateManager implements StateManagerInterface { } getStateRoot(): Promise { - throw new Error('Method not implemented.') + return Promise.resolve(this._trie.root()) } setStateRoot(_stateRoot: Uint8Array, _clearCache?: boolean): Promise { throw new Error('Method not implemented.') diff --git a/packages/vm/test/tester/runners/BlockchainTestsRunner.ts b/packages/vm/test/tester/runners/BlockchainTestsRunner.ts index 6ed8a5a075..7aad993087 100644 --- a/packages/vm/test/tester/runners/BlockchainTestsRunner.ts +++ b/packages/vm/test/tester/runners/BlockchainTestsRunner.ts @@ -16,6 +16,7 @@ import { } from '@ethereumjs/util' import { loadVerkleCrypto } from 'verkle-cryptography-wasm' +import { createVerkleTree } from '../../../../verkle/dist/esm/constructors.js' import { buildBlock, createVM, runBlock } from '../../../src/index.js' import { setupPreConditions, verifyPostConditions } from '../../util.js' @@ -47,8 +48,10 @@ export async function runBlockchainTest(options: any, testData: any, t: tape.Tes common.setHardforkBy({ blockNumber: 0 }) const verkleCrypto = await loadVerkleCrypto() + const trie = await createVerkleTree({ verkleCrypto, db: new MapDB() }) const state = new StatefulVerkleStateManager({ verkleCrypto, + trie, }) let cacheDB = new MapDB() From 618074cac4c8e00f613b52c5417e7ccd317ef469 Mon Sep 17 00:00:00 2001 From: Gabriel Rocheleau Date: Mon, 30 Sep 2024 16:43:41 -0400 Subject: [PATCH 03/60] vm: add option for state manager --- .../tester/runners/BlockchainTestsRunner.ts | 36 +++++++++++++------ 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/packages/vm/test/tester/runners/BlockchainTestsRunner.ts b/packages/vm/test/tester/runners/BlockchainTestsRunner.ts index 7aad993087..dc595d2add 100644 --- a/packages/vm/test/tester/runners/BlockchainTestsRunner.ts +++ b/packages/vm/test/tester/runners/BlockchainTestsRunner.ts @@ -3,7 +3,8 @@ import { EthashConsensus, createBlockchain } from '@ethereumjs/blockchain' import { ConsensusAlgorithm } from '@ethereumjs/common' import { Ethash } from '@ethereumjs/ethash' import { RLP } from '@ethereumjs/rlp' -import { StatefulVerkleStateManager } from '@ethereumjs/statemanager' +import { Caches, MerkleStateManager, StatefulVerkleStateManager } from '@ethereumjs/statemanager' +import { Trie } from '@ethereumjs/trie' import { createTxFromRLP } from '@ethereumjs/tx' import { MapDB, @@ -14,16 +15,17 @@ import { stripHexPrefix, toBytes, } from '@ethereumjs/util' +import { createVerkleTree } from '@ethereumjs/verkle' import { loadVerkleCrypto } from 'verkle-cryptography-wasm' -import { createVerkleTree } from '../../../../verkle/dist/esm/constructors.js' 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) { @@ -47,12 +49,24 @@ export async function runBlockchainTest(options: any, testData: any, t: tape.Tes let common = options.common.copy() as Common common.setHardforkBy({ blockNumber: 0 }) - const verkleCrypto = await loadVerkleCrypto() - const trie = await createVerkleTree({ verkleCrypto, db: new MapDB() }) - const state = new StatefulVerkleStateManager({ - verkleCrypto, - trie, - }) + let stateTree: Trie | VerkleTree + let stateManager: StateManagerInterface + + if (options.stateManager === 'verkle') { + const verkleCrypto = await loadVerkleCrypto() + stateTree = await createVerkleTree({ verkleCrypto, db: new MapDB() }) + stateManager = new StatefulVerkleStateManager({ + verkleCrypto, + trie: stateTree, + }) + } else { + stateTree = new Trie({ useKeyHashing: true, common }) + stateManager = new MerkleStateManager({ + caches: new Caches(), + trie: stateTree, + common, + }) + } let cacheDB = new MapDB() @@ -98,7 +112,7 @@ export async function runBlockchainTest(options: any, testData: any, t: tape.Tes bn254: options.bn254, } let vm = await createVM({ - stateManager: state, + stateManager, blockchain, common, setHardfork: true, @@ -226,7 +240,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 From a93279014ae3238dd8c95f339d62eff5c3b39257 Mon Sep 17 00:00:00 2001 From: acolytec3 <17355484+acolytec3@users.noreply.github.com> Date: Mon, 30 Sep 2024 21:21:13 -0400 Subject: [PATCH 04/60] Add expanded logging --- .../statemanager/src/statefulVerkleStateManager.ts | 2 +- packages/statemanager/src/util.ts | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/packages/statemanager/src/statefulVerkleStateManager.ts b/packages/statemanager/src/statefulVerkleStateManager.ts index 5f4ad17c62..ad7f9c061f 100644 --- a/packages/statemanager/src/statefulVerkleStateManager.ts +++ b/packages/statemanager/src/statefulVerkleStateManager.ts @@ -147,7 +147,7 @@ export class StatefulVerkleStateManager implements StateManagerInterface { putAccount = async (address: Address, account?: Account): Promise => { if (this.DEBUG) { this._debug( - `Save account address=${address} nonce=${account?.nonce} balance=${ + `putAccount address=${address} nonce=${account?.nonce} balance=${ account?.balance } contract=${account && account.isContract() ? 'yes' : 'no'} empty=${ account && account.isEmpty() ? 'yes' : 'no' diff --git a/packages/statemanager/src/util.ts b/packages/statemanager/src/util.ts index ded1675288..4e9d9d5e48 100644 --- a/packages/statemanager/src/util.ts +++ b/packages/statemanager/src/util.ts @@ -1,4 +1,4 @@ -import { Account } from '@ethereumjs/util' +import { Account, bytesToHex } from '@ethereumjs/util' import type { AccountFields, StateManagerInterface } from '@ethereumjs/common' import type { Address } from '@ethereumjs/util' @@ -15,5 +15,14 @@ export async function modifyAccountFields( account.storageRoot = accountFields.storageRoot ?? account.storageRoot account.codeHash = accountFields.codeHash ?? account.codeHash account.codeSize = accountFields.codeSize ?? account.codeSize + // @ts-ignore + if (stateManager['_debug'] !== undefined) { + for (const [field, value] of Object.entries(accountFields)) { + //@ts-ignore + stateManager['_debug']( + `modifyAccountFields address=${address.toString()} ${field}=${value instanceof Uint8Array ? bytesToHex(value) : value} `, + ) + } + } await stateManager.putAccount(address, account) } From a6bd723e2dc5223c3bdd67c4bc9fb40b65b35b35 Mon Sep 17 00:00:00 2001 From: acolytec3 <17355484+acolytec3@users.noreply.github.com> Date: Mon, 30 Sep 2024 21:21:25 -0400 Subject: [PATCH 05/60] fix account creation in preconditions step --- packages/vm/test/util.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/vm/test/util.ts b/packages/vm/test/util.ts index c2b8497a2b..5447b41e6a 100644 --- a/packages/vm/test/util.ts +++ b/packages/vm/test/util.ts @@ -15,8 +15,8 @@ import { bigIntToBytes, bytesToBigInt, bytesToHex, - createAccount, createAccountFromRLP, + createPartialAccount, equalsBytes, hexToBytes, isHexString, @@ -392,11 +392,18 @@ 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) } // Put account data - const account = createAccount({ nonce, balance, codeHash, storageRoot }) + const account = createPartialAccount({ + nonce, + balance, + codeHash, + storageRoot, + codeSize: codeBuf.length, + }) await state.putAccount(address, account) } await state.commit() From eff3d6f34bd3f8b4ea0955917912afecf597a939 Mon Sep 17 00:00:00 2001 From: acolytec3 <17355484+acolytec3@users.noreply.github.com> Date: Mon, 30 Sep 2024 21:30:21 -0400 Subject: [PATCH 06/60] Add statemanager arg to runner args --- packages/vm/test/tester/index.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/vm/test/tester/index.ts b/packages/vm/test/tester/index.ts index 6c93817d7a..65d5a3e79d 100755 --- a/packages/vm/test/tester/index.ts +++ b/packages/vm/test/tester/index.ts @@ -141,6 +141,7 @@ async function runTests() { profile: boolean bls: EVMBLSInterface bn254: EVMBN254Interface + stateManager: string } = { forkConfigVM: FORK_CONFIG_VM, forkConfigTestSuite: FORK_CONFIG_TEST_SUITE, @@ -155,6 +156,7 @@ async function runTests() { bls, profile: RUN_PROFILER, bn254, + stateManager: argv.stateManager, } /** From 8459c6c5359b01d611885d7ac2208e87da145a37 Mon Sep 17 00:00:00 2001 From: acolytec3 <17355484+acolytec3@users.noreply.github.com> Date: Tue, 1 Oct 2024 11:28:44 -0400 Subject: [PATCH 07/60] add setStateRoot to verkleStateful --- packages/statemanager/src/statefulVerkleStateManager.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/statemanager/src/statefulVerkleStateManager.ts b/packages/statemanager/src/statefulVerkleStateManager.ts index ad7f9c061f..0b1d198056 100644 --- a/packages/statemanager/src/statefulVerkleStateManager.ts +++ b/packages/statemanager/src/statefulVerkleStateManager.ts @@ -448,8 +448,10 @@ export class StatefulVerkleStateManager implements StateManagerInterface { getStateRoot(): Promise { return Promise.resolve(this._trie.root()) } - setStateRoot(_stateRoot: Uint8Array, _clearCache?: boolean): Promise { - throw new Error('Method not implemented.') + setStateRoot(stateRoot: Uint8Array, clearCache?: boolean): Promise { + this._trie.root(stateRoot) + clearCache === true && this.clearCaches() + return Promise.resolve() } hasStateRoot(_root: Uint8Array): Promise { throw new Error('Method not implemented.') @@ -461,7 +463,7 @@ export class StatefulVerkleStateManager implements StateManagerInterface { throw new Error('Method not implemented.') } clearCaches(): void { - throw new Error('Method not implemented.') + this._caches?.clear() } shallowCopy(_downlevelCaches?: boolean): StateManagerInterface { throw new Error('Method not implemented.') From f971e70cda05cb531d3ce45cccb55d3a2231716b Mon Sep 17 00:00:00 2001 From: Gabriel Rocheleau Date: Tue, 1 Oct 2024 15:45:17 -0400 Subject: [PATCH 08/60] vm: fix test runner --- package-lock.json | 1 + packages/vm/package.json | 1 + packages/vm/test/tester/runners/BlockchainTestsRunner.ts | 5 ++--- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5e690fc04a..a401cef174 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15525,6 +15525,7 @@ "devDependencies": { "@ethereumjs/blockchain": "^7.3.0", "@ethereumjs/ethash": "^3.0.3", + "@ethereumjs/verkle": "0.1.0", "@ethersproject/abi": "^5.0.12", "@paulmillr/trusted-setups": "^0.1.2", "@types/benchmark": "^1.0.33", diff --git a/packages/vm/package.json b/packages/vm/package.json index c3edd7fc66..e1df73d6ec 100644 --- a/packages/vm/package.json +++ b/packages/vm/package.json @@ -78,6 +78,7 @@ "devDependencies": { "@ethereumjs/blockchain": "^7.3.0", "@ethereumjs/ethash": "^3.0.3", + "@ethereumjs/verkle": "0.1.0", "@ethersproject/abi": "^5.0.12", "@paulmillr/trusted-setups": "^0.1.2", "@types/benchmark": "^1.0.33", diff --git a/packages/vm/test/tester/runners/BlockchainTestsRunner.ts b/packages/vm/test/tester/runners/BlockchainTestsRunner.ts index dc595d2add..46ff10042f 100644 --- a/packages/vm/test/tester/runners/BlockchainTestsRunner.ts +++ b/packages/vm/test/tester/runners/BlockchainTestsRunner.ts @@ -258,7 +258,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', ) @@ -267,6 +267,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 } From b29c56af1540e4cba94839134b13cc56348fcaa9 Mon Sep 17 00:00:00 2001 From: Gabriel Rocheleau Date: Tue, 1 Oct 2024 20:36:42 -0400 Subject: [PATCH 09/60] vm: update generalStateTestRunner with verkle stateManager option --- .../tester/runners/GeneralStateTestsRunner.ts | 40 +++++++++++++------ 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/packages/vm/test/tester/runners/GeneralStateTestsRunner.ts b/packages/vm/test/tester/runners/GeneralStateTestsRunner.ts index 310bb35a61..54f74c0151 100644 --- a/packages/vm/test/tester/runners/GeneralStateTestsRunner.ts +++ b/packages/vm/test/tester/runners/GeneralStateTestsRunner.ts @@ -1,19 +1,24 @@ import { Block } from '@ethereumjs/block' import { createBlockchain } from '@ethereumjs/blockchain' import { type InterpreterStep } from '@ethereumjs/evm' -import { Caches, MerkleStateManager } from '@ethereumjs/statemanager' +import { Caches, MerkleStateManager, StatefulVerkleStateManager } from '@ethereumjs/statemanager' import { Trie } from '@ethereumjs/trie' import { Account, + MapDB, bytesToHex, createAddressFromString, equalsBytes, toBytes, } from '@ethereumjs/util' +import { createVerkleTree } from '@ethereumjs/verkle' +import { loadVerkleCrypto } from 'verkle-cryptography-wasm' 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' function parseTestCases( @@ -74,23 +79,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 Trie({ useKeyHashing: true, common }) - const stateManager = new MerkleStateManager({ - caches: new Caches(), - trie: state, - common, - }) + let blockchain = await createBlockchain({ genesisBlock, common }) + let stateTree: VerkleTree | Trie + let stateManager: StateManagerInterface + if (options.stateManager === 'verkle') { + const verkleCrypto = await loadVerkleCrypto() + stateTree = await createVerkleTree({ verkleCrypto, db: new MapDB() }) + stateManager = new StatefulVerkleStateManager({ + verkleCrypto, + trie: stateTree, + }) + } else { + stateTree = new Trie({ 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, @@ -177,9 +193,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) } From bf7b1527ff3534adb2c801f68d1070b70d2a2831 Mon Sep 17 00:00:00 2001 From: acolytec3 <17355484+acolytec3@users.noreply.github.com> Date: Thu, 3 Oct 2024 10:39:56 -0400 Subject: [PATCH 10/60] Add verkle specific common setup --- packages/vm/test/tester/config.ts | 65 +++++++++++++++++++ .../tester/runners/BlockchainTestsRunner.ts | 2 +- packages/vm/test/tester/testLoader.ts | 2 +- 3 files changed, 67 insertions(+), 2 deletions(-) diff --git a/packages/vm/test/tester/config.ts b/packages/vm/test/tester/config.ts index 7c152ad0f7..2d9db3dfea 100644 --- a/packages/vm/test/tester/config.ts +++ b/packages/vm/test/tester/config.ts @@ -278,6 +278,69 @@ 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: null }) + + const common = createCustomCommon( + { + hardforks: testHardforks, + defaultHardfork: 'verkle', + }, + Mainnet, + { eips: [3607], customCrypto: { kzg } }, + ) + // 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 +351,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/runners/BlockchainTestsRunner.ts b/packages/vm/test/tester/runners/BlockchainTestsRunner.ts index 46ff10042f..51f1acd6d7 100644 --- a/packages/vm/test/tester/runners/BlockchainTestsRunner.ts +++ b/packages/vm/test/tester/runners/BlockchainTestsRunner.ts @@ -38,7 +38,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 } 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)) { From f83a1c5ba9d3cd3af9a5372025bf8aecc9869fd2 Mon Sep 17 00:00:00 2001 From: acolytec3 <17355484+acolytec3@users.noreply.github.com> Date: Tue, 8 Oct 2024 10:44:35 -0400 Subject: [PATCH 11/60] lint --- packages/vm/test/tester/runners/BlockchainTestsRunner.ts | 2 +- packages/vm/test/tester/runners/GeneralStateTestsRunner.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/vm/test/tester/runners/BlockchainTestsRunner.ts b/packages/vm/test/tester/runners/BlockchainTestsRunner.ts index e9f760283c..94c3f150cb 100644 --- a/packages/vm/test/tester/runners/BlockchainTestsRunner.ts +++ b/packages/vm/test/tester/runners/BlockchainTestsRunner.ts @@ -2,9 +2,9 @@ import { createBlock, createBlockFromRLP } 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, StatefulVerkleStateManager } from '@ethereumjs/statemanager' -import { MerklePatriciaTrie } from '@ethereumjs/mpt' import { createTxFromRLP } from '@ethereumjs/tx' import { MapDB, diff --git a/packages/vm/test/tester/runners/GeneralStateTestsRunner.ts b/packages/vm/test/tester/runners/GeneralStateTestsRunner.ts index 6586ac1d28..ee2c062dbe 100644 --- a/packages/vm/test/tester/runners/GeneralStateTestsRunner.ts +++ b/packages/vm/test/tester/runners/GeneralStateTestsRunner.ts @@ -1,8 +1,8 @@ import { Block } from '@ethereumjs/block' import { createBlockchain } from '@ethereumjs/blockchain' import { type InterpreterStep } from '@ethereumjs/evm' -import { Caches, MerkleStateManager, StatefulVerkleStateManager } from '@ethereumjs/statemanager' import { MerklePatriciaTrie } from '@ethereumjs/mpt' +import { Caches, MerkleStateManager, StatefulVerkleStateManager } from '@ethereumjs/statemanager' import { Account, MapDB, From b6e0ac71a8792e924f09805f451196b481fd733b Mon Sep 17 00:00:00 2001 From: acolytec3 <17355484+acolytec3@users.noreply.github.com> Date: Mon, 28 Oct 2024 11:49:32 -0400 Subject: [PATCH 12/60] Add fixes to allow verkle blocks to run --- packages/vm/test/tester/config.ts | 2 +- .../tester/runners/BlockchainTestsRunner.ts | 58 ++++++++++++------- 2 files changed, 38 insertions(+), 22 deletions(-) diff --git a/packages/vm/test/tester/config.ts b/packages/vm/test/tester/config.ts index 2d9db3dfea..f66fd2df66 100644 --- a/packages/vm/test/tester/config.ts +++ b/packages/vm/test/tester/config.ts @@ -323,7 +323,7 @@ function setupCommonForVerkle(network: string, timestamp?: number, kzg?: KZG) { } } - testHardforks.push({ name: 'verkle', block: null }) + testHardforks.push({ name: 'verkle', block: 1 }) const common = createCustomCommon( { diff --git a/packages/vm/test/tester/runners/BlockchainTestsRunner.ts b/packages/vm/test/tester/runners/BlockchainTestsRunner.ts index 94c3f150cb..4bb1ad2a86 100644 --- a/packages/vm/test/tester/runners/BlockchainTestsRunner.ts +++ b/packages/vm/test/tester/runners/BlockchainTestsRunner.ts @@ -1,4 +1,4 @@ -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' @@ -8,7 +8,6 @@ import { Caches, MerkleStateManager, StatefulVerkleStateManager } from '@ethereu import { createTxFromRLP } from '@ethereumjs/tx' import { MapDB, - bytesToBigInt, bytesToHex, hexToBytes, isHexString, @@ -153,26 +152,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) {} - - common.setHardforkBy({ blockNumber: currentBlock, timestamp }) + // 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: 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. @@ -206,7 +208,20 @@ 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 }) + 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 @@ -251,6 +266,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) From f7f8f73a830b7cc2375420822dc90039c341b017 Mon Sep 17 00:00:00 2001 From: acolytec3 <17355484+acolytec3@users.noreply.github.com> Date: Mon, 28 Oct 2024 11:51:27 -0400 Subject: [PATCH 13/60] Add comments on block constructor --- packages/vm/test/tester/runners/BlockchainTestsRunner.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/vm/test/tester/runners/BlockchainTestsRunner.ts b/packages/vm/test/tester/runners/BlockchainTestsRunner.ts index 4bb1ad2a86..9b411e992c 100644 --- a/packages/vm/test/tester/runners/BlockchainTestsRunner.ts +++ b/packages/vm/test/tester/runners/BlockchainTestsRunner.ts @@ -208,6 +208,8 @@ export async function runBlockchainTest(options: any, testData: any, t: tape.Tes await blockBuilder.revert() // will only revert if checkpointed } + // 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 witnesss const block = createBlock( { header: raw.blockHeader, From 91d3dd33d8dc61216afd29dafec25e36861390cc Mon Sep 17 00:00:00 2001 From: acolytec3 <17355484+acolytec3@users.noreply.github.com> Date: Mon, 28 Oct 2024 12:45:31 -0400 Subject: [PATCH 14/60] initialize witness --- packages/vm/src/runBlock.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/vm/src/runBlock.ts b/packages/vm/src/runBlock.ts index 0ec31d43d4..1db4c0d5e2 100644 --- a/packages/vm/src/runBlock.ts +++ b/packages/vm/src/runBlock.ts @@ -47,6 +47,7 @@ import type { import type { VM } from './vm.js' import type { Block } from '@ethereumjs/block' import type { Common } from '@ethereumjs/common' +import type { StatefulVerkleStateManager } from '@ethereumjs/statemanager' import type { CLRequest, CLRequestType, PrefixedHexString } from '@ethereumjs/util' const debug = debugDefault('vm:block') @@ -134,6 +135,12 @@ export async function runBlock(vm: VM, opts: RunBlockOpts): Promise Date: Mon, 28 Oct 2024 12:53:17 -0400 Subject: [PATCH 15/60] fix typo in postState verification check --- packages/vm/src/runBlock.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vm/src/runBlock.ts b/packages/vm/src/runBlock.ts index 1db4c0d5e2..45ebc08bac 100644 --- a/packages/vm/src/runBlock.ts +++ b/packages/vm/src/runBlock.ts @@ -330,7 +330,7 @@ export async function runBlock(vm: VM, opts: RunBlockOpts): Promise Date: Mon, 28 Oct 2024 14:14:56 -0400 Subject: [PATCH 16/60] Reorganize verkle checks --- .../src/statefulVerkleStateManager.ts | 1 - packages/vm/src/runBlock.ts | 43 ++++++++----------- 2 files changed, 19 insertions(+), 25 deletions(-) diff --git a/packages/statemanager/src/statefulVerkleStateManager.ts b/packages/statemanager/src/statefulVerkleStateManager.ts index 42b610326d..288197b8e7 100644 --- a/packages/statemanager/src/statefulVerkleStateManager.ts +++ b/packages/statemanager/src/statefulVerkleStateManager.ts @@ -538,7 +538,6 @@ export class StatefulVerkleStateManager implements StateManagerInterface { return bytesToHex(basicDataBytes) } else { const encodedAccount = this._caches?.account?.get(address)?.accountRLP - this._debug(`we have encoded account ${encodedAccount}`) if (encodedAccount === undefined) { return null } diff --git a/packages/vm/src/runBlock.ts b/packages/vm/src/runBlock.ts index 45ebc08bac..bc52fcd8c7 100644 --- a/packages/vm/src/runBlock.ts +++ b/packages/vm/src/runBlock.ts @@ -141,36 +141,31 @@ export async function runBlock(vm: VM, opts: RunBlockOpts): Promise Date: Mon, 28 Oct 2024 15:37:22 -0400 Subject: [PATCH 17/60] fix code chunking size calculation --- packages/statemanager/src/statefulVerkleStateManager.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/statemanager/src/statefulVerkleStateManager.ts b/packages/statemanager/src/statefulVerkleStateManager.ts index 288197b8e7..0d8ad6e3fa 100644 --- a/packages/statemanager/src/statefulVerkleStateManager.ts +++ b/packages/statemanager/src/statefulVerkleStateManager.ts @@ -385,9 +385,9 @@ export class StatefulVerkleStateManager implements StateManagerInterface { // Determine code ending byte (if we're on the last chunk) let sliceEnd = 32 if (x === chunks.length - 1) { - sliceEnd = (codeSize % VERKLE_CODE_CHUNK_SIZE) + 1 + // On the last chunk, the end of the slice is either codeSize (if only one chunk) or codeSize % chunkSize + sliceEnd = (x === 0 ? codeSize : codeSize % VERKLE_CODE_CHUNK_SIZE) + 1 } - code.set(chunks[x]!.slice(1, sliceEnd), code.byteOffset + x * VERKLE_CODE_CHUNK_SIZE) } this._caches?.code?.put(address, code) @@ -665,6 +665,7 @@ export class StatefulVerkleStateManager implements StateManagerInterface { canonicalValue = ZEROVALUE } + this._debug(`computed ${computedValue} canonical ${canonicalValue}`) if (computedValue !== canonicalValue) { if (type === VerkleAccessedStateType.BasicData) { this.DEBUG && From 4fe493f2bf00b956be0f31dfa6da55eddf95ac8a Mon Sep 17 00:00:00 2001 From: acolytec3 <17355484+acolytec3@users.noreply.github.com> Date: Mon, 28 Oct 2024 15:37:35 -0400 Subject: [PATCH 18/60] clean up --- packages/vm/test/tester/runners/BlockchainTestsRunner.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vm/test/tester/runners/BlockchainTestsRunner.ts b/packages/vm/test/tester/runners/BlockchainTestsRunner.ts index 9b411e992c..3d6ded0678 100644 --- a/packages/vm/test/tester/runners/BlockchainTestsRunner.ts +++ b/packages/vm/test/tester/runners/BlockchainTestsRunner.ts @@ -53,7 +53,7 @@ export async function runBlockchainTest(options: any, testData: any, t: tape.Tes if (options.stateManager === 'verkle') { const verkleCrypto = await loadVerkleCrypto() - stateTree = await createVerkleTree({ verkleCrypto, db: new MapDB() }) + stateTree = await createVerkleTree() stateManager = new StatefulVerkleStateManager({ verkleCrypto, trie: stateTree, From f29f4587005961283cbdc6f3aa1ee6ba78b37b23 Mon Sep 17 00:00:00 2001 From: acolytec3 <17355484+acolytec3@users.noreply.github.com> Date: Tue, 29 Oct 2024 11:13:51 -0400 Subject: [PATCH 19/60] Add trie walking helper --- packages/verkle/src/util.ts | 36 ++++++++++++++++ packages/verkle/test/verkle.spec.ts | 67 ++++++++++++++++++++++++++++- 2 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 packages/verkle/src/util.ts diff --git a/packages/verkle/src/util.ts b/packages/verkle/src/util.ts new file mode 100644 index 0000000000..f6173c417b --- /dev/null +++ b/packages/verkle/src/util.ts @@ -0,0 +1,36 @@ +import { bytesToHex, concatBytes, equalsBytes } from '@ethereumjs/util' + +import { LeafVerkleNode, LeafVerkleNodeValue, decodeVerkleNode } from './node/index.js' + +import type { VerkleTree } from './verkleTree.js' +import type { PrefixedHexString } from '@ethereumjs/util' + +export const dumpLeafValues = async ( + tree: VerkleTree, + startingNode: Uint8Array, +): Promise<[PrefixedHexString, PrefixedHexString][] | undefined> => { + if (equalsBytes(startingNode, tree.EMPTY_TREE_ROOT) === true) return + // Retrieve starting node from DB + const rawNode = await tree['_db'].get(startingNode) + if (rawNode === undefined) return + const node = decodeVerkleNode(rawNode, tree['verkleCrypto']) + const entries: [PrefixedHexString, PrefixedHexString][] = [] + if (node instanceof LeafVerkleNode) { + for (const [idx, val] of node.values.entries()) { + if (val !== LeafVerkleNodeValue.Untouched) { + entries.push([ + bytesToHex(concatBytes(node.stem, Uint8Array.from([idx]))), + bytesToHex(val === LeafVerkleNodeValue.Deleted ? new Uint8Array(32) : val), + ]) + } + } + return entries + } else { + const childPaths = node.children + .filter((value) => value !== null) + .map((value) => dumpLeafValues(tree, tree['verkleCrypto'].hashCommitment(value.commitment))) + + const res = (await Promise.all(childPaths)).filter((val) => val !== undefined) + return res.flat(1) + } +} diff --git a/packages/verkle/test/verkle.spec.ts b/packages/verkle/test/verkle.spec.ts index f282c53fb5..c60a77523c 100644 --- a/packages/verkle/test/verkle.spec.ts +++ b/packages/verkle/test/verkle.spec.ts @@ -1,4 +1,4 @@ -import { MapDB, equalsBytes, hexToBytes, matchingBytesLength } from '@ethereumjs/util' +import { MapDB, bytesToHex, equalsBytes, hexToBytes, matchingBytesLength } from '@ethereumjs/util' import { loadVerkleCrypto } from 'verkle-cryptography-wasm' import { assert, beforeAll, describe, it } from 'vitest' @@ -10,6 +10,7 @@ import { createVerkleTree, decodeVerkleNode, } from '../src/index.js' +import { dumpLeafValues } from '../src/util.js' import type { VerkleNode } from '../src/index.js' import type { PrefixedHexString, VerkleCrypto } from '@ethereumjs/util' @@ -279,3 +280,67 @@ describe('Verkle tree', () => { assert.deepEqual((res.node as LeafVerkleNode).values[keys[0][31]], LeafVerkleNodeValue.Deleted) }) }) + +describe('dump leaf values', () => { + // Values taken from verkle fixtures + const values = [ + [ + '0x0365b079a274a1808d56484ce5bd97914629907d75767f51439102e22cd50d00', + '0x00000000000000000000000000000001000000000000003635c9adc5de9ccbaa', + ], + [ + '0x0365b079a274a1808d56484ce5bd97914629907d75767f51439102e22cd50d01', + '0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470', + ], + [ + '0x5b5fdfedd6a0e932da408ac7d772a36513d1eee9b9926e52620c43a433aad700', + '0x0000000000000036000000000000000100000000000000000000000000000000', + ], + [ + '0x5b5fdfedd6a0e932da408ac7d772a36513d1eee9b9926e52620c43a433aad701', + '0xdf61faef43babbb1ebde8fd82ab9cb4cb74c240d0025138521477e073f72080a', + ], + [ + '0x5b5fdfedd6a0e932da408ac7d772a36513d1eee9b9926e52620c43a433aad740', + '0x00b2e892fbf04dcdbb33d71633d7cea0722aed27f8a9d0cf9912f97b34f9dadd', + ], + [ + '0x5b5fdfedd6a0e932da408ac7d772a36513d1eee9b9926e52620c43a433aad780', + '0x0060203611603157600143035f35116029575f35612000014311602957612000', + ], + [ + '0x5b5fdfedd6a0e932da408ac7d772a36513d1eee9b9926e52620c43a433aad781', + '0x005f3506545f5260205ff35b5f5f5260205ff35b5f5ffd000000000000000000', + ], + [ + '0x914ec5f0e0c27fe094862fbd89a6abe684939af6940434d8bf218cedb2d62400', + '0x000000000000000000000000000000000000000000000000000000000000f618', + ], + [ + '0x914ec5f0e0c27fe094862fbd89a6abe684939af6940434d8bf218cedb2d62401', + '0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470', + ], + [ + '0xecb505156708480caf702cd85124f67f3ed78ae4bc890a6dcb62574ba9a90c00', + '0x0000000000000000000000000000000000000000000000000000000000000006', + ], + [ + '0xecb505156708480caf702cd85124f67f3ed78ae4bc890a6dcb62574ba9a90c01', + '0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470', + ], + ] + it('should produce a correct state root', async () => { + const trie = await createVerkleTree() + for (const [key, val] of values) { + const treeKey = hexToBytes(key) + const value = hexToBytes(val) + await trie.put(treeKey.slice(0, 31), [treeKey[31]], [value]) + } + assert.equal( + bytesToHex(trie.root()), + '0x3a36cd08db733a2c1ef113142c754bd88bfea541cb56361ed1825d5883378e71', + ) + const entries = await dumpLeafValues(trie, trie.root()) + assert.deepEqual(entries, values) + }, 10000) +}) From e0adfb6bae795ec4e15fe92baef46dd3025da713 Mon Sep 17 00:00:00 2001 From: acolytec3 <17355484+acolytec3@users.noreply.github.com> Date: Tue, 29 Oct 2024 12:37:57 -0400 Subject: [PATCH 20/60] remove rlp encoding of storage values --- packages/statemanager/src/statefulVerkleStateManager.ts | 7 +++---- packages/verkle/src/index.ts | 1 + packages/verkle/src/util.ts | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/statemanager/src/statefulVerkleStateManager.ts b/packages/statemanager/src/statefulVerkleStateManager.ts index 0d8ad6e3fa..d05944f8cb 100644 --- a/packages/statemanager/src/statefulVerkleStateManager.ts +++ b/packages/statemanager/src/statefulVerkleStateManager.ts @@ -424,12 +424,11 @@ export class StatefulVerkleStateManager implements StateManagerInterface { const value = await this._trie.get(storageKey.slice(0, 31), [storageKey[31]]) this._caches?.storage?.put(address, key, value[0] ?? hexToBytes('0x80')) - const decoded = RLP.decode(value[0] ?? new Uint8Array(0)) as Uint8Array - return decoded + const decoded = (value[0] ?? new Uint8Array(0)) as Uint8Array + return setLengthLeft(decoded, 32) } putStorage = async (address: Address, key: Uint8Array, value: Uint8Array): Promise => { - value = unpadBytes(value) this._caches?.storage?.put(address, key, RLP.encode(value)) if (this._caches?.storage === undefined) { const storageKey = await getVerkleTreeKeyForStorageSlot( @@ -437,7 +436,7 @@ export class StatefulVerkleStateManager implements StateManagerInterface { bytesToBigInt(key, true), this.verkleCrypto, ) - await this._trie.put(storageKey.slice(0, 31), [storageKey[31]], [RLP.encode(value)]) + await this._trie.put(storageKey.slice(0, 31), [storageKey[31]], [value]) } } diff --git a/packages/verkle/src/index.ts b/packages/verkle/src/index.ts index c1adab8a4d..3f5eaa9c31 100644 --- a/packages/verkle/src/index.ts +++ b/packages/verkle/src/index.ts @@ -2,4 +2,5 @@ export * from './constructors.js' export * from './db/index.js' export * from './node/index.js' export * from './types.js' +export * from './util.js' export * from './verkleTree.js' diff --git a/packages/verkle/src/util.ts b/packages/verkle/src/util.ts index f6173c417b..3327f4388b 100644 --- a/packages/verkle/src/util.ts +++ b/packages/verkle/src/util.ts @@ -31,6 +31,6 @@ export const dumpLeafValues = async ( .map((value) => dumpLeafValues(tree, tree['verkleCrypto'].hashCommitment(value.commitment))) const res = (await Promise.all(childPaths)).filter((val) => val !== undefined) - return res.flat(1) + return res.flat(1) as [PrefixedHexString, PrefixedHexString][] } } From 0ab7ba20b720b1d75f7fbeed31cb162e6e1d9129 Mon Sep 17 00:00:00 2001 From: Gabriel Rocheleau Date: Wed, 30 Oct 2024 09:12:08 -0700 Subject: [PATCH 21/60] cspell fix --- packages/vm/test/tester/runners/BlockchainTestsRunner.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vm/test/tester/runners/BlockchainTestsRunner.ts b/packages/vm/test/tester/runners/BlockchainTestsRunner.ts index 3d6ded0678..f7ba64f433 100644 --- a/packages/vm/test/tester/runners/BlockchainTestsRunner.ts +++ b/packages/vm/test/tester/runners/BlockchainTestsRunner.ts @@ -209,7 +209,7 @@ export async function runBlockchainTest(options: any, testData: any, t: tape.Tes } // 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 witnesss + // Create the block from the JSON block data since the RLP doesn't include the execution witness const block = createBlock( { header: raw.blockHeader, From c5433d1d86030921289b2d5a87d82169fecdcbd6 Mon Sep 17 00:00:00 2001 From: acolytec3 <17355484+acolytec3@users.noreply.github.com> Date: Wed, 30 Oct 2024 11:38:10 -0400 Subject: [PATCH 22/60] typo --- packages/evm/src/verkleAccessWitness.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/evm/src/verkleAccessWitness.ts b/packages/evm/src/verkleAccessWitness.ts index 114e50d77d..d87c953cf6 100644 --- a/packages/evm/src/verkleAccessWitness.ts +++ b/packages/evm/src/verkleAccessWitness.ts @@ -253,7 +253,7 @@ export class VerkleAccessWitness implements VerkleAccessWitnessInterface { } debug( - `${accessedChunkKeyHex}: isWrite=${isWrite} for steamRead=${stemRead} stemWrite=${stemWrite} chunkRead=${chunkRead} chunkWrite=${chunkWrite} chunkFill=${chunkFill}`, + `${accessedChunkKeyHex}: isWrite=${isWrite} for stemRead=${stemRead} stemWrite=${stemWrite} chunkRead=${chunkRead} chunkWrite=${chunkWrite} chunkFill=${chunkFill}`, ) return { stemRead, stemWrite, chunkRead, chunkWrite, chunkFill } } From b137c41b31d9501a172713fa74b360ee928c562c Mon Sep 17 00:00:00 2001 From: acolytec3 <17355484+acolytec3@users.noreply.github.com> Date: Wed, 30 Oct 2024 11:40:58 -0400 Subject: [PATCH 23/60] update logger --- packages/evm/src/verkleAccessWitness.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/evm/src/verkleAccessWitness.ts b/packages/evm/src/verkleAccessWitness.ts index d87c953cf6..3f51d64b4e 100644 --- a/packages/evm/src/verkleAccessWitness.ts +++ b/packages/evm/src/verkleAccessWitness.ts @@ -24,7 +24,7 @@ import type { } from '@ethereumjs/common' import type { Address, PrefixedHexString, VerkleCrypto } from '@ethereumjs/util' -const debug = debugDefault('statemanager:verkle:aw') +const debug = debugDefault('evm:verkle:aw') /** * Tree key constants. From e05d41e642690ac10bb4099c1a2f70405018297f Mon Sep 17 00:00:00 2001 From: acolytec3 <17355484+acolytec3@users.noreply.github.com> Date: Wed, 30 Oct 2024 15:53:50 -0400 Subject: [PATCH 24/60] logic bugs --- packages/statemanager/src/statefulVerkleStateManager.ts | 7 +++---- packages/util/src/verkle.ts | 4 ++-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/packages/statemanager/src/statefulVerkleStateManager.ts b/packages/statemanager/src/statefulVerkleStateManager.ts index 03003ef343..e729778642 100644 --- a/packages/statemanager/src/statefulVerkleStateManager.ts +++ b/packages/statemanager/src/statefulVerkleStateManager.ts @@ -206,7 +206,6 @@ export class StatefulVerkleStateManager implements StateManagerInterface { }, {}) this._postState = postState - this._debug('initVerkleExecutionWitness postState', this._postState) } /** @@ -417,7 +416,7 @@ export class StatefulVerkleStateManager implements StateManagerInterface { } const storageKey = await getVerkleTreeKeyForStorageSlot( address, - bytesToBigInt(key, true), + bytesToBigInt(key), this.verkleCrypto, ) const value = await this._trie.get(storageKey.slice(0, 31), [storageKey[31]]) @@ -432,10 +431,10 @@ export class StatefulVerkleStateManager implements StateManagerInterface { if (this._caches?.storage === undefined) { const storageKey = await getVerkleTreeKeyForStorageSlot( address, - bytesToBigInt(key, true), + bytesToBigInt(key), this.verkleCrypto, ) - await this._trie.put(storageKey.slice(0, 31), [storageKey[31]], [value]) + await this._trie.put(storageKey.slice(0, 31), [storageKey[31]], [setLengthLeft(value, 32)]) } } diff --git a/packages/util/src/verkle.ts b/packages/util/src/verkle.ts index d7beebbb84..cb1c7b7ad4 100644 --- a/packages/util/src/verkle.ts +++ b/packages/util/src/verkle.ts @@ -258,10 +258,10 @@ export const chunkifyCode = (code: Uint8Array) => { if (x >= 0 && slice[x] - 0x5f > 31 - x) { // x >= 0 indicates PUSHn in this chunk // n > 31 - x indicates that PUSHDATA spills over to next chunk - // PUSHDATA overflow = n - 31 - x + 1(i.e. number of elements PUSHed + // PUSHDATA overflow = n - (31 - x) + 1(i.e. number of elements PUSHed // - size of code chunk (31) - position of PUSHn in the previous // code chunk + 1 (since x is zero-indexed)) - const pushDataOverflow = slice[x] - 0x5f - 31 - x + 1 + const pushDataOverflow = slice[x] - 0x5f - (31 - x) + 1 // Put next chunk prepended with number of overflow PUSHDATA bytes chunks.push( concatBytes(Uint8Array.from([pushDataOverflow]), code.slice(i * 31, (i + 1) * 31)), From 66d9f04d68856c93a8aec815d5a3b58ae34ece08 Mon Sep 17 00:00:00 2001 From: acolytec3 <17355484+acolytec3@users.noreply.github.com> Date: Thu, 31 Oct 2024 12:14:45 -0400 Subject: [PATCH 25/60] Move verkle crypto loading out of run function --- packages/vm/test/tester/index.ts | 8 ++++++++ packages/vm/test/tester/runners/BlockchainTestsRunner.ts | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/vm/test/tester/index.ts b/packages/vm/test/tester/index.ts index 65d5a3e79d..55574e2a56 100755 --- a/packages/vm/test/tester/index.ts +++ b/packages/vm/test/tester/index.ts @@ -7,6 +7,7 @@ import * as path from 'path' import * as process from 'process' import { initRustBN } from 'rustbn-wasm' import * as tape from 'tape' +import { loadVerkleCrypto } from 'verkle-cryptography-wasm' import { DEFAULT_FORK_CONFIG, @@ -23,6 +24,7 @@ import { getTestFromSource, getTestsFromArgs } from './testLoader.js' import type { Common } from '@ethereumjs/common' import type { EVMBLSInterface, EVMBN254Interface } from '@ethereumjs/evm' +import type { VerkleCrypto } from '@ethereumjs/util' /** * Test runner @@ -123,6 +125,11 @@ async function runTests() { bn254 = new NobleBN254() } + let verkleCrypto: VerkleCrypto + if (FORK_CONFIG === 'verkle') { + verkleCrypto = await loadVerkleCrypto() + } + /** * Run-time configuration */ @@ -157,6 +164,7 @@ async function runTests() { profile: RUN_PROFILER, bn254, stateManager: argv.stateManager, + verkleCrypto, } /** diff --git a/packages/vm/test/tester/runners/BlockchainTestsRunner.ts b/packages/vm/test/tester/runners/BlockchainTestsRunner.ts index 3d6ded0678..21bb3f0454 100644 --- a/packages/vm/test/tester/runners/BlockchainTestsRunner.ts +++ b/packages/vm/test/tester/runners/BlockchainTestsRunner.ts @@ -52,7 +52,7 @@ export async function runBlockchainTest(options: any, testData: any, t: tape.Tes let stateManager: StateManagerInterface if (options.stateManager === 'verkle') { - const verkleCrypto = await loadVerkleCrypto() + const verkleCrypto = options.verkleCrypto stateTree = await createVerkleTree() stateManager = new StatefulVerkleStateManager({ verkleCrypto, From e6dcdad5f050d086b0f7a0e23fcd73561ca2bdd7 Mon Sep 17 00:00:00 2001 From: Gabriel Rocheleau Date: Thu, 31 Oct 2024 18:57:55 -0700 Subject: [PATCH 26/60] vm: re add validation logic for stateful verkle --- packages/vm/src/runBlock.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/vm/src/runBlock.ts b/packages/vm/src/runBlock.ts index 4ccb52434e..17a0e96c58 100644 --- a/packages/vm/src/runBlock.ts +++ b/packages/vm/src/runBlock.ts @@ -270,8 +270,8 @@ export async function runBlock(vm: VM, opts: RunBlockOpts): Promise Date: Sun, 3 Nov 2024 15:46:01 -0700 Subject: [PATCH 27/60] verkle:improve type of createDefaultLeafVerkleValues --- packages/verkle/src/node/util.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/verkle/src/node/util.ts b/packages/verkle/src/node/util.ts index 80d8459ac8..4213197aa7 100644 --- a/packages/verkle/src/node/util.ts +++ b/packages/verkle/src/node/util.ts @@ -41,7 +41,7 @@ export function isInternalVerkleNode(node: VerkleNode): node is InternalVerkleNo export const createZeroesLeafValue = () => new Uint8Array(32) -export const createDefaultLeafVerkleValues = () => new Array(256).fill(0) +export const createDefaultLeafVerkleValues: () => number[] = () => new Array(256).fill(0) /*** * Converts 128 32byte values of a leaf node into an array of 256 32 byte values representing From a0c35362b18f803daa7c15ce317f557fc0766e5e Mon Sep 17 00:00:00 2001 From: acolytec3 <17355484+acolytec3@users.noreply.github.com> Date: Mon, 4 Nov 2024 14:17:46 -0500 Subject: [PATCH 28/60] Add new util method for dumping node hashes --- packages/verkle/src/util.ts | 45 ++++++++++++++- packages/verkle/test/util.spec.ts | 90 +++++++++++++++++++++++++++++ packages/verkle/test/verkle.spec.ts | 67 +-------------------- 3 files changed, 135 insertions(+), 67 deletions(-) create mode 100644 packages/verkle/test/util.spec.ts diff --git a/packages/verkle/src/util.ts b/packages/verkle/src/util.ts index 3327f4388b..c939c67188 100644 --- a/packages/verkle/src/util.ts +++ b/packages/verkle/src/util.ts @@ -5,6 +5,12 @@ import { LeafVerkleNode, LeafVerkleNodeValue, decodeVerkleNode } from './node/in import type { VerkleTree } from './verkleTree.js' import type { PrefixedHexString } from '@ethereumjs/util' +/** + * Recursively walks down the tree from a given starting node and returns all the leaf values + * @param tree - The verkle tree + * @param startingNode - The starting node + * @returns An array of key-value pairs containing the tree keys and associated values + */ export const dumpLeafValues = async ( tree: VerkleTree, startingNode: Uint8Array, @@ -28,9 +34,46 @@ export const dumpLeafValues = async ( } else { const childPaths = node.children .filter((value) => value !== null) - .map((value) => dumpLeafValues(tree, tree['verkleCrypto'].hashCommitment(value.commitment))) + .map((value) => dumpLeafValues(tree, tree['verkleCrypto'].hashCommitment(value!.commitment))) const res = (await Promise.all(childPaths)).filter((val) => val !== undefined) return res.flat(1) as [PrefixedHexString, PrefixedHexString][] } } +/** + * Recursively walks down the tree from a given starting node and returns all the node paths and hashes + * @param tree - The verkle tree + * @param startingNode - The starting node + * @returns An array of key-value pairs containing the tree paths and associated hashes + */ +export const dumpNodeHashes = async ( + tree: VerkleTree, + startingNode: Uint8Array, +): Promise<[PrefixedHexString, PrefixedHexString][] | undefined> => { + let entries: [PrefixedHexString, PrefixedHexString][] = [] + // Retrieve starting node from DB + const rawNode = await tree['_db'].get(startingNode) + if (rawNode === undefined) return + + const node = decodeVerkleNode(rawNode, tree['verkleCrypto']) + // If current node is root, push '0x' for path and node hash for commitment + equalsBytes(startingNode, tree.root()) && entries.push(['0x', bytesToHex(startingNode)]) + if (node instanceof LeafVerkleNode) { + // Leaf node paths/hashes were added in the previous inner node's iteration + return [] + } else { + const children = node.children.filter((value) => value !== null) + for (const child of children) { + entries.push([ + bytesToHex(child!.path), + bytesToHex(tree['verkleCrypto'].hashCommitment(child!.commitment)), + ]) + } + const childPaths = children.map((value) => + dumpNodeHashes(tree, tree['verkleCrypto'].hashCommitment(value!.commitment)), + ) + const res = (await Promise.all(childPaths)).filter((val) => val !== undefined) + entries = [...entries, ...(res.flat(1) as [PrefixedHexString, PrefixedHexString][])] + } + return entries +} diff --git a/packages/verkle/test/util.spec.ts b/packages/verkle/test/util.spec.ts new file mode 100644 index 0000000000..00cd253519 --- /dev/null +++ b/packages/verkle/test/util.spec.ts @@ -0,0 +1,90 @@ +import { bytesToHex, hexToBytes } from '@ethereumjs/util' +import { assert, describe, it } from 'vitest' + +import { createVerkleTree } from '../src/index.js' +import { dumpLeafValues, dumpNodeHashes } from '../src/util.js' + +// Values taken from verkle fixtures +const values = [ + [ + '0x0365b079a274a1808d56484ce5bd97914629907d75767f51439102e22cd50d00', + '0x00000000000000000000000000000001000000000000003635c9adc5de9ccbaa', + ], + [ + '0x0365b079a274a1808d56484ce5bd97914629907d75767f51439102e22cd50d01', + '0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470', + ], + [ + '0x5b5fdfedd6a0e932da408ac7d772a36513d1eee9b9926e52620c43a433aad700', + '0x0000000000000036000000000000000100000000000000000000000000000000', + ], + [ + '0x5b5fdfedd6a0e932da408ac7d772a36513d1eee9b9926e52620c43a433aad701', + '0xdf61faef43babbb1ebde8fd82ab9cb4cb74c240d0025138521477e073f72080a', + ], + [ + '0x5b5fdfedd6a0e932da408ac7d772a36513d1eee9b9926e52620c43a433aad740', + '0x00b2e892fbf04dcdbb33d71633d7cea0722aed27f8a9d0cf9912f97b34f9dadd', + ], + [ + '0x5b5fdfedd6a0e932da408ac7d772a36513d1eee9b9926e52620c43a433aad780', + '0x0060203611603157600143035f35116029575f35612000014311602957612000', + ], + [ + '0x5b5fdfedd6a0e932da408ac7d772a36513d1eee9b9926e52620c43a433aad781', + '0x005f3506545f5260205ff35b5f5f5260205ff35b5f5ffd000000000000000000', + ], + [ + '0x914ec5f0e0c27fe094862fbd89a6abe684939af6940434d8bf218cedb2d62400', + '0x000000000000000000000000000000000000000000000000000000000000f618', + ], + [ + '0x914ec5f0e0c27fe094862fbd89a6abe684939af6940434d8bf218cedb2d62401', + '0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470', + ], + [ + '0xecb505156708480caf702cd85124f67f3ed78ae4bc890a6dcb62574ba9a90c00', + '0x0000000000000000000000000000000000000000000000000000000000000006', + ], + [ + '0xecb505156708480caf702cd85124f67f3ed78ae4bc890a6dcb62574ba9a90c01', + '0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470', + ], +] +describe('dump leaf values', () => { + it('should produce a correct state root', async () => { + const trie = await createVerkleTree() + for (const [key, val] of values) { + const treeKey = hexToBytes(key) + const value = hexToBytes(val) + await trie.put(treeKey.slice(0, 31), [treeKey[31]], [value]) + } + assert.equal( + bytesToHex(trie.root()), + '0x3a36cd08db733a2c1ef113142c754bd88bfea541cb56361ed1825d5883378e71', + ) + const entries = await dumpLeafValues(trie, trie.root()) + assert.deepEqual(entries, values) + }, 10000) +}) + +describe('dump node hashes', () => { + it('should only contain the root node for an empty tree', async () => { + const trie = await createVerkleTree() + const entries = await dumpNodeHashes(trie, trie.root()) + assert.deepEqual(entries, [['0x', bytesToHex(trie.root())]]) + }) + it.only('should contain the paths and hashes of all nodes in the tree', async () => { + const trie = await createVerkleTree() + for (const [key, val] of values) { + const treeKey = hexToBytes(key) + const value = hexToBytes(val) + await trie.put(treeKey.slice(0, 31), [treeKey[31]], [value]) + } + const entries = await dumpNodeHashes(trie, trie.root()) + assert.equal(entries?.length, 5) + for (const entry of entries!.slice(1)) { + assert.equal(entry[0].length, 64) + } + }) +}) diff --git a/packages/verkle/test/verkle.spec.ts b/packages/verkle/test/verkle.spec.ts index c60a77523c..f282c53fb5 100644 --- a/packages/verkle/test/verkle.spec.ts +++ b/packages/verkle/test/verkle.spec.ts @@ -1,4 +1,4 @@ -import { MapDB, bytesToHex, equalsBytes, hexToBytes, matchingBytesLength } from '@ethereumjs/util' +import { MapDB, equalsBytes, hexToBytes, matchingBytesLength } from '@ethereumjs/util' import { loadVerkleCrypto } from 'verkle-cryptography-wasm' import { assert, beforeAll, describe, it } from 'vitest' @@ -10,7 +10,6 @@ import { createVerkleTree, decodeVerkleNode, } from '../src/index.js' -import { dumpLeafValues } from '../src/util.js' import type { VerkleNode } from '../src/index.js' import type { PrefixedHexString, VerkleCrypto } from '@ethereumjs/util' @@ -280,67 +279,3 @@ describe('Verkle tree', () => { assert.deepEqual((res.node as LeafVerkleNode).values[keys[0][31]], LeafVerkleNodeValue.Deleted) }) }) - -describe('dump leaf values', () => { - // Values taken from verkle fixtures - const values = [ - [ - '0x0365b079a274a1808d56484ce5bd97914629907d75767f51439102e22cd50d00', - '0x00000000000000000000000000000001000000000000003635c9adc5de9ccbaa', - ], - [ - '0x0365b079a274a1808d56484ce5bd97914629907d75767f51439102e22cd50d01', - '0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470', - ], - [ - '0x5b5fdfedd6a0e932da408ac7d772a36513d1eee9b9926e52620c43a433aad700', - '0x0000000000000036000000000000000100000000000000000000000000000000', - ], - [ - '0x5b5fdfedd6a0e932da408ac7d772a36513d1eee9b9926e52620c43a433aad701', - '0xdf61faef43babbb1ebde8fd82ab9cb4cb74c240d0025138521477e073f72080a', - ], - [ - '0x5b5fdfedd6a0e932da408ac7d772a36513d1eee9b9926e52620c43a433aad740', - '0x00b2e892fbf04dcdbb33d71633d7cea0722aed27f8a9d0cf9912f97b34f9dadd', - ], - [ - '0x5b5fdfedd6a0e932da408ac7d772a36513d1eee9b9926e52620c43a433aad780', - '0x0060203611603157600143035f35116029575f35612000014311602957612000', - ], - [ - '0x5b5fdfedd6a0e932da408ac7d772a36513d1eee9b9926e52620c43a433aad781', - '0x005f3506545f5260205ff35b5f5f5260205ff35b5f5ffd000000000000000000', - ], - [ - '0x914ec5f0e0c27fe094862fbd89a6abe684939af6940434d8bf218cedb2d62400', - '0x000000000000000000000000000000000000000000000000000000000000f618', - ], - [ - '0x914ec5f0e0c27fe094862fbd89a6abe684939af6940434d8bf218cedb2d62401', - '0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470', - ], - [ - '0xecb505156708480caf702cd85124f67f3ed78ae4bc890a6dcb62574ba9a90c00', - '0x0000000000000000000000000000000000000000000000000000000000000006', - ], - [ - '0xecb505156708480caf702cd85124f67f3ed78ae4bc890a6dcb62574ba9a90c01', - '0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470', - ], - ] - it('should produce a correct state root', async () => { - const trie = await createVerkleTree() - for (const [key, val] of values) { - const treeKey = hexToBytes(key) - const value = hexToBytes(val) - await trie.put(treeKey.slice(0, 31), [treeKey[31]], [value]) - } - assert.equal( - bytesToHex(trie.root()), - '0x3a36cd08db733a2c1ef113142c754bd88bfea541cb56361ed1825d5883378e71', - ) - const entries = await dumpLeafValues(trie, trie.root()) - assert.deepEqual(entries, values) - }, 10000) -}) From d074be60e37a1e70eb7759b6c62da4691cea1ad6 Mon Sep 17 00:00:00 2001 From: acolytec3 <17355484+acolytec3@users.noreply.github.com> Date: Mon, 4 Nov 2024 14:53:47 -0500 Subject: [PATCH 29/60] Partial fix for null child nodes --- .../test/statefulVerkleStateManager.spec.ts | 22 +++++ packages/verkle/src/node/internalNode.ts | 12 ++- packages/verkle/src/verkleTree.ts | 96 +++++++++++++------ packages/verkle/test/verkle.spec.ts | 23 +++++ 4 files changed, 120 insertions(+), 33 deletions(-) diff --git a/packages/statemanager/test/statefulVerkleStateManager.spec.ts b/packages/statemanager/test/statefulVerkleStateManager.spec.ts index f259f013c0..04c6c43f5e 100644 --- a/packages/statemanager/test/statefulVerkleStateManager.spec.ts +++ b/packages/statemanager/test/statefulVerkleStateManager.spec.ts @@ -36,6 +36,28 @@ describe('Verkle Tree API tests', () => { const deletedAccount = await sm.getAccount(address) assert.equal(deletedAccount, undefined) }) + + it('should return same stateRoot when putting and then deleting account', async () => { + const trie = await createVerkleTree() + const sm = new StatefulVerkleStateManager({ trie, verkleCrypto }) + + const address1 = createAddressFromString('0x9e5ef720fa2cdfa5291eb7e711cfd2e62196f4b3') + const account1 = createAccount({ nonce: 3n, balance: 0xfffn }) + const address2 = createAddressFromString('0x9e5ef720fa2cdfa5291eb7e711cfd2e62196f4b4') + const account2 = createAccount({ nonce: 4n, balance: 0xffen }) + + await sm.putAccount(address1, account1) + const stateRootAfterPutAccount1 = await sm.getStateRoot() + + // Put and then delete the account2 + await sm.putAccount(address2, account2) + await sm.deleteAccount(address2) + + // StateRoot should return to the initial stateRoot + const stateRootAfterDeleteAccount = await sm.getStateRoot() + assert.deepEqual(stateRootAfterPutAccount1, stateRootAfterDeleteAccount) + }) + it('should put and get code', async () => { const trie = await createVerkleTree() const sm = new StatefulVerkleStateManager({ trie, verkleCrypto }) diff --git a/packages/verkle/src/node/internalNode.ts b/packages/verkle/src/node/internalNode.ts index d69c4593fe..98486a0662 100644 --- a/packages/verkle/src/node/internalNode.ts +++ b/packages/verkle/src/node/internalNode.ts @@ -7,7 +7,7 @@ import type { ChildNode, VerkleNodeOptions } from './types.js' export class InternalVerkleNode extends BaseVerkleNode { // Array of tuples of uncompressed commitments (i.e. 64 byte Uint8Arrays) to child nodes along with the path to that child (i.e. the partial stem) - public children: Array + public children: Array public type = VerkleNodeType.Internal constructor(options: VerkleNodeOptions[VerkleNodeType.Internal]) { @@ -16,7 +16,7 @@ export class InternalVerkleNode extends BaseVerkleNode } // Updates the commitment value for a child node at the corresponding index - setChild(childIndex: number, child: ChildNode) { + setChild(childIndex: number, child: ChildNode | null) { // Get previous child commitment at `index` const oldChildReference = this.children[childIndex] !== null @@ -26,14 +26,16 @@ export class InternalVerkleNode extends BaseVerkleNode path: new Uint8Array(), } // Updates the commitment to the child node at `index` - this.children[childIndex] = { ...child } + this.children[childIndex] = child !== null ? { ...child } : null // Updates the overall node commitment based on the update to this child this.commitment = this.verkleCrypto.updateCommitment( this.commitment, childIndex, // The hashed child commitments are used when updating the internal node commitment - this.verkleCrypto.hashCommitment(oldChildReference.commitment), - this.verkleCrypto.hashCommitment(child.commitment), + this.verkleCrypto.hashCommitment(oldChildReference!.commitment), + this.verkleCrypto.hashCommitment( + child !== null ? child.commitment : this.verkleCrypto.zeroCommitment, + ), ) } diff --git a/packages/verkle/src/verkleTree.ts b/packages/verkle/src/verkleTree.ts index bc8dc78b8e..c0964a5517 100644 --- a/packages/verkle/src/verkleTree.ts +++ b/packages/verkle/src/verkleTree.ts @@ -167,7 +167,7 @@ export class VerkleTree { } this.DEBUG && this.debug(`Stem: ${bytesToHex(stem)}`, ['put']) - const putStack: [Uint8Array, VerkleNode][] = [] + const putStack: [Uint8Array, VerkleNode | null][] = [] // Find path to nearest node const foundPath = await this.findPath(stem) @@ -217,8 +217,23 @@ export class VerkleTree { ['put'], ) } - // Push new/updated leafNode to putStack - putStack.push([leafNode.hash(), leafNode]) + if ( + equalsBytes(leafNode.c1!, this.verkleCrypto.zeroCommitment) && + equalsBytes(leafNode.c2!, this.verkleCrypto.zeroCommitment) + ) { + // Special case where we've only written `LeafVerkleNodeValue.Untouched`. In this case, the node shouldn't be in the tree at all + if (foundPath.node !== null) { + // If the node previously existed, we need to delete it + this.DEBUG && this.debug(`Deleting leaf node at stem: ${bytesToHex(stem)}`, ['put']) + putStack.push([leafNode.hash(), null]) + } else { + // NOOP + // If the leaf node doesn't exist in the tree, we shouldn't insert it + } + } else { + // Push new/updated leafNode to putStack + putStack.push([leafNode.hash(), leafNode]) + } // `path` is the path to the last node pushed to the `putStack` let lastPath = leafNode.stem @@ -240,29 +255,54 @@ export class VerkleTree { // Compute the child index to be updated on `nextNode` const childIndex = lastPath[matchingBytesLength(lastPath, nextPath)] // Update child reference - nextNode.setChild(childIndex, { - commitment: putStack[putStack.length - 1][1].commitment, - path: lastPath, - }) - this.DEBUG && - this.debug( - `Updating child reference for node with path: ${bytesToHex( - lastPath, - )} at index ${childIndex} in internal node at path ${bytesToHex(nextPath)}`, - ['put'], - ) + const childReference = putStack[putStack.length - 1][1] + + if (childReference !== null) { + nextNode.setChild(childIndex, { + commitment: childReference.commitment, + path: lastPath, + }) + this.DEBUG && + this.debug( + `Updating child reference for node with path: ${bytesToHex( + lastPath, + )} at index ${childIndex} in internal node at path ${bytesToHex(nextPath)}`, + ['put'], + ) + putStack.push([nextNode.hash(), nextNode]) + } else { + nextNode.setChild(childIndex, null) + if (equalsBytes(nextNode.commitment, this.verkleCrypto.zeroCommitment)) { + // If the node's commitment is the zero commitment, it has no child nodes and should be removed from the tree + putStack.push([nextNode.hash(), null]) + } else { + putStack.push([nextNode.hash(), nextNode]) + } + this.DEBUG && + this.debug( + `Deleting child reference for node with path: ${bytesToHex( + lastPath, + )} at index ${childIndex} in internal node at path ${bytesToHex(nextPath)}`, + ['put'], + ) + } // Hold onto `path` to current node for updating next parent node child index lastPath = nextPath - putStack.push([nextNode.hash(), nextNode]) } } // Step 4) Update root node const rootNode = foundPath.stack.pop()![0] as InternalVerkleNode - rootNode.setChild(stem[0], { - commitment: putStack[putStack.length - 1][1].commitment, - path: lastPath, - }) + const childReference = putStack[putStack.length - 1][1] + if (childReference !== null) { + rootNode.setChild(stem[0], { + commitment: childReference.commitment, + path: lastPath, + }) + } else { + // Set child reference to null if the child node was deleted + rootNode.setChild(stem[0], null) + } this.root(this.verkleCrypto.serializeCommitment(rootNode.commitment)) this.DEBUG && this.debug( @@ -370,15 +410,15 @@ export class VerkleTree { let finished = false while (!finished) { // Look up child node by node hash - rawNode = await this._db.get(this.verkleCrypto.hashCommitment(child.commitment)) + rawNode = await this._db.get(this.verkleCrypto.hashCommitment(child!.commitment)) // We should always find the node if the path is specified in child.path - if (rawNode === undefined) throw new Error(`missing node at ${bytesToHex(child.path)}`) + if (rawNode === undefined) throw new Error(`missing node at ${bytesToHex(child!.path)}`) const decodedNode = decodeVerkleNode(rawNode, this.verkleCrypto) // Calculate the index of the last matching byte in the key - const matchingKeyLength = matchingBytesLength(key, child.path) - const foundNode = equalsBytes(key, child.path) - if (foundNode || child.path.length >= key.length || isLeafVerkleNode(decodedNode)) { + const matchingKeyLength = matchingBytesLength(key, child!.path) + const foundNode = equalsBytes(key, child!.path) + if (foundNode || child!.path.length >= key.length || isLeafVerkleNode(decodedNode)) { // If the key and child.path are equal, then we found the node // If the child.path is the same length or longer than the key but doesn't match it // or the found node is a leaf node, we've found another node where this node should @@ -400,7 +440,7 @@ export class VerkleTree { // We found a different node than the one specified by `key` // so the sought node doesn't exist result.remaining = key.slice(matchingKeyLength) - const pathToNearestNode = isLeafVerkleNode(decodedNode) ? decodedNode.stem : child.path + const pathToNearestNode = isLeafVerkleNode(decodedNode) ? decodedNode.stem : child!.path this.DEBUG && this.debug( `Path ${bytesToHex(pathToNearestNode)} - found path to nearest node ${bytesToHex( @@ -457,12 +497,12 @@ export class VerkleTree { * @param putStack - an array of tuples of keys (the partial path of the node in the trie) and nodes (VerkleNodes) */ - async saveStack(putStack: [Uint8Array, VerkleNode][]): Promise { + async saveStack(putStack: [Uint8Array, VerkleNode | null][]): Promise { const opStack = putStack.map(([key, node]) => { return { - type: 'put', + type: node !== null ? 'put' : 'del', key, - value: node.serialize(), + value: node !== null ? node.serialize() : null, } as PutBatch }) await this._db.batch(opStack) diff --git a/packages/verkle/test/verkle.spec.ts b/packages/verkle/test/verkle.spec.ts index f282c53fb5..a2a904b289 100644 --- a/packages/verkle/test/verkle.spec.ts +++ b/packages/verkle/test/verkle.spec.ts @@ -9,6 +9,7 @@ import { VerkleNodeType, createVerkleTree, decodeVerkleNode, + dumpNodeHashes, } from '../src/index.js' import type { VerkleNode } from '../src/index.js' @@ -278,4 +279,26 @@ describe('Verkle tree', () => { assert.ok(res.node !== null) assert.deepEqual((res.node as LeafVerkleNode).values[keys[0][31]], LeafVerkleNodeValue.Deleted) }) + it.only('should remove null child nodes and roots should match', async () => { + const keys = [ + // Two keys with the same stem but different suffixes + '0x318dea512b6f3237a2d4763cf49bf26de3b617fb0cabe38a97807a5549df4d01', + // A key with a partially matching stem 0x318d to above 2 keys + '0x318dfa512b6f3237a2d4763cf49bf26de3b617fb0cabe38a97807a5549df4d02', + // A key with a partially matching stem 0x318dfa51 to above key + '0x318dfa513b6f3237a2d4763cf49bf26de3b617fb0cabe38a97807a5549df4d02', + ].map((key) => hexToBytes(key as PrefixedHexString)) + const values = [ + '0x320122e8584be00d000000000000000000000000000000000000000000000000', + '0x0000000000000000000000000000000000000000000000000000000000000001', + ].map((key) => hexToBytes(key as PrefixedHexString)) + const trie = await createVerkleTree({ verkleCrypto }) + await trie.put(keys[0].slice(0, 31), [keys[0][31]], [values[0]]) + const root1 = trie.root() + await trie.put(keys[1].slice(0, 31), [keys[1][31]], [values[1]]) + const root2 = trie.root() + await trie.put(keys[2].slice(0, 31), [keys[2][31]], [values[1]]) + await trie.put(keys[2].slice(0, 31), [keys[2][31]], [LeafVerkleNodeValue.Untouched]) + assert.deepEqual(trie.root(), root2) + }) }) From 2f76957c596072e351df4881bcdd879a61326559 Mon Sep 17 00:00:00 2001 From: acolytec3 <17355484+acolytec3@users.noreply.github.com> Date: Mon, 4 Nov 2024 20:52:44 -0500 Subject: [PATCH 30/60] Collapse internal nodes when deleting values --- packages/verkle/src/verkleTree.ts | 63 ++++++++++++++++++++--------- packages/verkle/test/verkle.spec.ts | 6 ++- 2 files changed, 49 insertions(+), 20 deletions(-) diff --git a/packages/verkle/src/verkleTree.ts b/packages/verkle/src/verkleTree.ts index c0964a5517..c653307044 100644 --- a/packages/verkle/src/verkleTree.ts +++ b/packages/verkle/src/verkleTree.ts @@ -217,18 +217,15 @@ export class VerkleTree { ['put'], ) } - if ( - equalsBytes(leafNode.c1!, this.verkleCrypto.zeroCommitment) && - equalsBytes(leafNode.c2!, this.verkleCrypto.zeroCommitment) - ) { - // Special case where we've only written `LeafVerkleNodeValue.Untouched`. In this case, the node shouldn't be in the tree at all + if (leafNode.values.filter((val) => val !== LeafVerkleNodeValue.Untouched).length === 0) { + // If all of the values are "untouched", this node should be deleted if it was previously created if (foundPath.node !== null) { // If the node previously existed, we need to delete it this.DEBUG && this.debug(`Deleting leaf node at stem: ${bytesToHex(stem)}`, ['put']) putStack.push([leafNode.hash(), null]) } else { - // NOOP - // If the leaf node doesn't exist in the tree, we shouldn't insert it + // If the leaf node doesn't exist in the tree, we shouldn't insert it and should just return + return } } else { // Push new/updated leafNode to putStack @@ -245,9 +242,11 @@ export class VerkleTree { const nearestNodeTuple = foundPath.stack.pop()! const nearestNode = nearestNodeTuple[0] lastPath = nearestNodeTuple[1] - const updatedParentTuple = this.updateParent(leafNode, nearestNode, lastPath) - putStack.push([updatedParentTuple.node.hash(), updatedParentTuple.node]) - lastPath = updatedParentTuple.lastPath + const updatedParentTuple = await this.updateParent(leafNode, nearestNode, lastPath) + if (updatedParentTuple !== undefined) { + putStack.push([updatedParentTuple.node.hash(), updatedParentTuple.node]) + lastPath = updatedParentTuple.lastPath + } // Step 3) Walk up trie and update child references in parent internal nodes while (foundPath.stack.length > 1) { @@ -328,15 +327,18 @@ export class VerkleTree { * @param pathToNode the path to `nearestNode` * @returns a tuple of the updated parent node and the path to that parent (i.e. the partial stem of the leaf node that leads to the parent) */ - updateParent( + async updateParent( leafNode: LeafVerkleNode, nearestNode: VerkleNode, pathToNode: Uint8Array, - ): { node: InternalVerkleNode; lastPath: Uint8Array } { + ): Promise<{ node: VerkleNode; lastPath: Uint8Array } | undefined> { + const leafNodeWasDeleted = + leafNode.values.filter((val) => val !== LeafVerkleNodeValue.Untouched).length === 0 + // Compute the portion of leafNode.stem and nearestNode.path that match (i.e. the partial path closest to leafNode.stem) const partialMatchingStemIndex = matchingBytesLength(leafNode.stem, pathToNode) let internalNode: InternalVerkleNode - if (isLeafVerkleNode(nearestNode)) { + if (isLeafVerkleNode(nearestNode) && !leafNodeWasDeleted) { // We need to create a new internal node and set nearestNode and leafNode as child nodes of it // Create new internal node internalNode = InternalVerkleNode.create(this.verkleCrypto) @@ -353,14 +355,36 @@ export class VerkleTree { pathToNode = leafNode.stem.slice(0, partialMatchingStemIndex) this.DEBUG && this.debug(`Creating new internal node at path ${bytesToHex(pathToNode)}`, ['put']) - } else { + } else if (!isLeafVerkleNode(nearestNode)) { // Nearest node is an internal node. We need to update the appropriate child reference // to the new leaf node internalNode = nearestNode - internalNode.setChild(leafNode.stem[partialMatchingStemIndex], { - commitment: leafNode.commitment, - path: leafNode.stem, - }) + internalNode.setChild( + leafNode.stem[partialMatchingStemIndex], + leafNodeWasDeleted + ? null + : { + commitment: leafNode.commitment, + path: leafNode.stem, + }, + ) + if (leafNodeWasDeleted) { + // Check to see if the internal node has only one other child node + const children = internalNode.children.filter((el) => el !== null) + if (children.length === 1) { + // If the internal node has only one child, we can replace the internal node with its child + const rawNode = await this._db.get( + this.verkleCrypto.hashCommitment(children[0]!.commitment), + ) + if (rawNode === undefined) + throw new Error(`missing node in DB at ${bytesToHex(children[0]!.path)}`) + return { + node: decodeVerkleNode(rawNode, this.verkleCrypto) as VerkleNode, + lastPath: children[0]!.path, + } + } + } + this.DEBUG && this.debug( `Updating child reference for leaf node with stem: ${bytesToHex( @@ -372,6 +396,9 @@ export class VerkleTree { )}`, ['put'], ) + } else { + // Nearest node is a leaf node and new leaf node is actually being deleted + return } return { node: internalNode, lastPath: pathToNode } } diff --git a/packages/verkle/test/verkle.spec.ts b/packages/verkle/test/verkle.spec.ts index a2a904b289..f5316e3703 100644 --- a/packages/verkle/test/verkle.spec.ts +++ b/packages/verkle/test/verkle.spec.ts @@ -9,6 +9,7 @@ import { VerkleNodeType, createVerkleTree, decodeVerkleNode, + dumpLeafValues, dumpNodeHashes, } from '../src/index.js' @@ -279,7 +280,7 @@ describe('Verkle tree', () => { assert.ok(res.node !== null) assert.deepEqual((res.node as LeafVerkleNode).values[keys[0][31]], LeafVerkleNodeValue.Deleted) }) - it.only('should remove null child nodes and roots should match', async () => { + it('should remove null child nodes and roots should match', async () => { const keys = [ // Two keys with the same stem but different suffixes '0x318dea512b6f3237a2d4763cf49bf26de3b617fb0cabe38a97807a5549df4d01', @@ -294,11 +295,12 @@ describe('Verkle tree', () => { ].map((key) => hexToBytes(key as PrefixedHexString)) const trie = await createVerkleTree({ verkleCrypto }) await trie.put(keys[0].slice(0, 31), [keys[0][31]], [values[0]]) - const root1 = trie.root() await trie.put(keys[1].slice(0, 31), [keys[1][31]], [values[1]]) const root2 = trie.root() await trie.put(keys[2].slice(0, 31), [keys[2][31]], [values[1]]) + await trie.put(keys[2].slice(0, 31), [keys[2][31]], [LeafVerkleNodeValue.Untouched]) + assert.deepEqual(trie.root(), root2) }) }) From 117863307db39cfc0cc3b8ca97a418a2ea97bdba Mon Sep 17 00:00:00 2001 From: acolytec3 <17355484+acolytec3@users.noreply.github.com> Date: Wed, 6 Nov 2024 16:02:00 -0500 Subject: [PATCH 31/60] update packages --- package-lock.json | 167 +++++++++++++++++++++++++++++++++++---- packages/vm/package.json | 6 +- 2 files changed, 155 insertions(+), 18 deletions(-) diff --git a/package-lock.json b/package-lock.json index c8523c35a5..b210ea9319 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15803,6 +15803,7 @@ "version": "0.4.8", "resolved": "https://registry.npmjs.org/verkle-cryptography-wasm/-/verkle-cryptography-wasm-0.4.8.tgz", "integrity": "sha512-ssom3p39wVRP63IMU/I7dx0mHZvb7svK8MAoMNlAzX/6CqA7FhtokSBt81RszXs3JXa0k1E40Qr1JPHW0aNkgQ==", + "dev": true, "dependencies": { "@scure/base": "^1.1.5" }, @@ -17337,7 +17338,6 @@ "micro-eth-signer": "^0.12.1", "prom-client": "^15.1.0", "rustbn-wasm": "^0.4.0", - "verkle-cryptography-wasm": "^0.4.8", "winston": "^3.3.3", "winston-daily-rotate-file": "^4.5.5", "yargs": "^17.7.1" @@ -17712,9 +17712,7 @@ "devDependencies": { "@ethereumjs/block": "^6.0.0-alpha.1", "@ethereumjs/genesis": "^0.3.0-alpha.1", - "@types/debug": "^4.1.9", - "rustbn-wasm": "^0.4.0", - "verkle-cryptography-wasm": "^0.4.8" + "@types/debug": "^4.1.9" } }, "packages/statemanager/node_modules/lru-cache": { @@ -17879,8 +17877,7 @@ "devDependencies": { "@paulmillr/trusted-setups": "^0.1.2", "kzg-wasm": "^0.5.0", - "micro-eth-signer": "^0.12.1", - "verkle-cryptography-wasm": "^0.4.8" + "micro-eth-signer": "^0.12.1" }, "engines": { "node": ">=18" @@ -17895,8 +17892,7 @@ "@ethereumjs/util": "^10.0.0-alpha.1", "debug": "^4.3.4", "lru-cache": "10.1.0", - "micro-eth-signer": "0.12.1", - "verkle-cryptography-wasm": "^0.4.8" + "micro-eth-signer": "0.12.1" }, "engines": { "node": ">=18" @@ -17928,9 +17924,9 @@ "eventemitter3": "^5.0.1" }, "devDependencies": { - "@ethereumjs/blockchain": "^7.3.0", - "@ethereumjs/ethash": "^3.0.3", - "@ethereumjs/verkle": "0.1.0", + "@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", @@ -17950,7 +17946,7 @@ "node": ">=18" } }, - "packages/vm/node_modules/@ethereumjs/ethash/node_modules/@ethereumjs/block": { + "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==", @@ -17967,7 +17963,7 @@ "node": ">=18" } }, - "packages/vm/node_modules/@ethereumjs/ethash/node_modules/@ethereumjs/common": { + "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==", @@ -17976,7 +17972,7 @@ "@ethereumjs/util": "^9.1.0" } }, - "packages/vm/node_modules/@ethereumjs/ethash/node_modules/@ethereumjs/rlp": { + "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==", @@ -17988,7 +17984,7 @@ "node": ">=18" } }, - "packages/vm/node_modules/@ethereumjs/ethash/node_modules/@ethereumjs/tx": { + "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==", @@ -18003,6 +17999,60 @@ "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==", + "dev": 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==", + "dev": 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", + "integrity": "sha512-cyphdEB/ywIERqWLRHdAS6muTkAcd6BibMOp6XmGbeWgvtIhe4ArxcMDI78MVstJzT/faihvRI4rKQKy+MpdKQ==", + "dev": 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/ethash/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==", + "dev": true, + "bin": { + "rlp": "bin/rlp.cjs" + }, + "engines": { + "node": ">=18" + } + }, "packages/vm/node_modules/@ethereumjs/ethash/node_modules/@ethereumjs/util": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/@ethereumjs/util/-/util-9.1.0.tgz", @@ -18028,6 +18078,84 @@ "@scure/bip39": "1.3.0" } }, + "packages/vm/node_modules/@ethereumjs/verkle/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==", + "dev": 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/verkle/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==", + "dev": true, + "dependencies": { + "@ethereumjs/util": "^9.1.0" + } + }, + "packages/vm/node_modules/@ethereumjs/verkle/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==", + "dev": true, + "bin": { + "rlp": "bin/rlp.cjs" + }, + "engines": { + "node": ">=18" + } + }, + "packages/vm/node_modules/@ethereumjs/verkle/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==", + "dev": 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/verkle/node_modules/@ethereumjs/util": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/util/-/util-9.1.0.tgz", + "integrity": "sha512-XBEKsYqLGXLah9PNJbgdkigthkG7TAGvlD/sH12beMXEyHDyigfcbdvHhmLyDWgDyOJn4QwiQUaF7yeuhnjdog==", + "dev": true, + "dependencies": { + "@ethereumjs/rlp": "^5.0.2", + "ethereum-cryptography": "^2.2.1" + }, + "engines": { + "node": ">=18" + } + }, + "packages/vm/node_modules/@ethereumjs/verkle/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==", + "dev": 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/@noble/curves": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.2.tgz", @@ -18079,6 +18207,15 @@ "url": "https://paulmillr.com/funding/" } }, + "packages/vm/node_modules/lru-cache": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.1.0.tgz", + "integrity": "sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag==", + "dev": true, + "engines": { + "node": "14 || >=16.14" + } + }, "packages/wallet": { "name": "@ethereumjs/wallet", "version": "3.0.0-alpha.1", diff --git a/packages/vm/package.json b/packages/vm/package.json index 5515162ac6..bfc44fddc4 100644 --- a/packages/vm/package.json +++ b/packages/vm/package.json @@ -77,9 +77,9 @@ "eventemitter3": "^5.0.1" }, "devDependencies": { - "@ethereumjs/blockchain": "^7.3.0", - "@ethereumjs/ethash": "^3.0.3", - "@ethereumjs/verkle": "0.1.0", + "@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", From 344979e4aad33fd0474e2753c23e41b978299489 Mon Sep 17 00:00:00 2001 From: acolytec3 <17355484+acolytec3@users.noreply.github.com> Date: Thu, 7 Nov 2024 12:04:11 -0500 Subject: [PATCH 32/60] fix code stem and code chunk suffix generation --- packages/util/src/verkle.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/util/src/verkle.ts b/packages/util/src/verkle.ts index 15fb1607bc..4cdef6b206 100644 --- a/packages/util/src/verkle.ts +++ b/packages/util/src/verkle.ts @@ -362,7 +362,8 @@ export const generateChunkSuffixes = (numChunks: number) => { if (numChunks > VERKLE_CODE_OFFSET) { for (let x = VERKLE_CODE_OFFSET; x < numChunks; x++) { // Fill subsequent chunk suffixes up to 256 and then start over since a single node - chunkSuffixes[x] = x - Math.floor(x / VERKLE_NODE_WIDTH) * VERKLE_NODE_WIDTH + chunkSuffixes[x] = + x - VERKLE_CODE_OFFSET - Math.floor(x / VERKLE_NODE_WIDTH) * VERKLE_NODE_WIDTH } } @@ -388,7 +389,7 @@ export const generateCodeStems = async ( // the first leaf node and 256 chunks in up to 3 additional leaf nodes) // So, instead of computing every single leaf key (which is a heavy async operation), we just compute the stem for the first // chunk in each leaf node and can then know that the chunks in between have tree keys in monotonically increasing order - const numStems = Math.ceil(numChunks / VERKLE_NODE_WIDTH) + const numStems = numChunks > VERKLE_CODE_OFFSET ? Math.ceil(numChunks / VERKLE_NODE_WIDTH) + 1 : 1 const chunkStems = new Array(numStems) // Compute the stem for the initial set of code chunks chunkStems[0] = (await getVerkleTreeKeyForCodeChunk(address, 0, verkleCrypto)).slice(0, 31) From d922a8dbf5a07544a64d4035bc9ce319a44b3f15 Mon Sep 17 00:00:00 2001 From: Gabriel Rocheleau Date: Thu, 7 Nov 2024 11:48:32 -0700 Subject: [PATCH 33/60] vm: update test runners with updated verkle crypto --- packages/vm/test/tester/index.ts | 3 ++- packages/vm/test/tester/runners/BlockchainTestsRunner.ts | 3 ++- packages/vm/test/tester/runners/GeneralStateTestsRunner.ts | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/vm/test/tester/index.ts b/packages/vm/test/tester/index.ts index 55574e2a56..24c9ea9ccc 100755 --- a/packages/vm/test/tester/index.ts +++ b/packages/vm/test/tester/index.ts @@ -2,12 +2,12 @@ import { MCLBLS, NobleBLS, NobleBN254, RustBN254 } from '@ethereumjs/evm' import { trustedSetup } from '@paulmillr/trusted-setups/fast.js' import * as mcl from 'mcl-wasm' import { KZG as microEthKZG } from 'micro-eth-signer/kzg' +import * as verkle from 'micro-eth-signer/verkle' import * as minimist from 'minimist' import * as path from 'path' import * as process from 'process' import { initRustBN } from 'rustbn-wasm' import * as tape from 'tape' -import { loadVerkleCrypto } from 'verkle-cryptography-wasm' import { DEFAULT_FORK_CONFIG, @@ -25,6 +25,7 @@ import { getTestFromSource, getTestsFromArgs } from './testLoader.js' import type { Common } from '@ethereumjs/common' import type { EVMBLSInterface, EVMBN254Interface } from '@ethereumjs/evm' import type { VerkleCrypto } from '@ethereumjs/util' +const loadVerkleCrypto = () => Promise.resolve(verkle) /** * Test runner diff --git a/packages/vm/test/tester/runners/BlockchainTestsRunner.ts b/packages/vm/test/tester/runners/BlockchainTestsRunner.ts index d672195031..ffe561c9f9 100644 --- a/packages/vm/test/tester/runners/BlockchainTestsRunner.ts +++ b/packages/vm/test/tester/runners/BlockchainTestsRunner.ts @@ -15,7 +15,7 @@ import { toBytes, } from '@ethereumjs/util' import { createVerkleTree } from '@ethereumjs/verkle' -import { loadVerkleCrypto } from 'verkle-cryptography-wasm' +import * as verkle from 'micro-eth-signer/verkle' import { buildBlock, createVM, runBlock } from '../../../src/index.js' import { setupPreConditions, verifyPostConditions } from '../../util.js' @@ -26,6 +26,7 @@ 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' +const loadVerkleCrypto = () => Promise.resolve(verkle) function formatBlockHeader(data: any) { const formatted: any = {} diff --git a/packages/vm/test/tester/runners/GeneralStateTestsRunner.ts b/packages/vm/test/tester/runners/GeneralStateTestsRunner.ts index e74286b835..866cb6fef7 100644 --- a/packages/vm/test/tester/runners/GeneralStateTestsRunner.ts +++ b/packages/vm/test/tester/runners/GeneralStateTestsRunner.ts @@ -12,7 +12,7 @@ import { toBytes, } from '@ethereumjs/util' import { createVerkleTree } from '@ethereumjs/verkle' -import { loadVerkleCrypto } from 'verkle-cryptography-wasm' +import * as verkle from 'micro-eth-signer/verkle' import { createVM, runTx } from '../../../src/index.js' import { makeBlockFromEnv, makeTx, setupPreConditions } from '../../util.js' @@ -20,6 +20,7 @@ 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, From 8def8a5ec50d6acf253fa10bed3d94fd37cfc3ca Mon Sep 17 00:00:00 2001 From: Gabriel Rocheleau Date: Thu, 7 Nov 2024 11:49:27 -0700 Subject: [PATCH 34/60] vm: update package lock --- package-lock.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 88c090d73f..5cc36fc55f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17806,7 +17806,7 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/@ethereumjs/block/-/block-5.3.0.tgz", "integrity": "sha512-cyphdEB/ywIERqWLRHdAS6muTkAcd6BibMOp6XmGbeWgvtIhe4ArxcMDI78MVstJzT/faihvRI4rKQKy+MpdKQ==", - "dev": true, + "extraneous": true, "dependencies": { "@ethereumjs/common": "^4.4.0", "@ethereumjs/rlp": "^5.0.2", @@ -17823,7 +17823,7 @@ "version": "4.4.0", "resolved": "https://registry.npmjs.org/@ethereumjs/common/-/common-4.4.0.tgz", "integrity": "sha512-Fy5hMqF6GsE6DpYTyqdDIJPJgUtDn4dL120zKw+Pswuo+iLyBsEYuSyzMw6NVzD2vDzcBG9fE4+qX4X2bPc97w==", - "dev": true, + "extraneous": true, "dependencies": { "@ethereumjs/util": "^9.1.0" } @@ -17832,7 +17832,7 @@ "version": "5.0.2", "resolved": "https://registry.npmjs.org/@ethereumjs/rlp/-/rlp-5.0.2.tgz", "integrity": "sha512-DziebCdg4JpGlEqEdGgXmjqcFoJi+JGulUXwEjsZGAscAQ7MyD/7LE/GVCP29vEQxKc7AAwjT3A2ywHp2xfoCA==", - "dev": true, + "extraneous": true, "bin": { "rlp": "bin/rlp.cjs" }, @@ -17844,7 +17844,7 @@ "version": "5.4.0", "resolved": "https://registry.npmjs.org/@ethereumjs/tx/-/tx-5.4.0.tgz", "integrity": "sha512-SCHnK7m/AouZ7nyoR0MEXw1OO/tQojSbp88t8oxhwes5iZkZCtfFdUrJaiIb72qIpH2FVw6s1k1uP7LXuH7PsA==", - "dev": true, + "extraneous": true, "dependencies": { "@ethereumjs/common": "^4.4.0", "@ethereumjs/rlp": "^5.0.2", @@ -17859,7 +17859,7 @@ "version": "9.1.0", "resolved": "https://registry.npmjs.org/@ethereumjs/util/-/util-9.1.0.tgz", "integrity": "sha512-XBEKsYqLGXLah9PNJbgdkigthkG7TAGvlD/sH12beMXEyHDyigfcbdvHhmLyDWgDyOJn4QwiQUaF7yeuhnjdog==", - "dev": true, + "extraneous": true, "dependencies": { "@ethereumjs/rlp": "^5.0.2", "ethereum-cryptography": "^2.2.1" @@ -17872,7 +17872,7 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.2.1.tgz", "integrity": "sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg==", - "dev": true, + "extraneous": true, "dependencies": { "@noble/curves": "1.4.2", "@noble/hashes": "1.4.0", From c218c8ecfebd03d8809ef385d25adc9c983f6c3f Mon Sep 17 00:00:00 2001 From: acolytec3 <17355484+acolytec3@users.noreply.github.com> Date: Thu, 7 Nov 2024 16:19:29 -0500 Subject: [PATCH 35/60] fix suffix counts --- packages/util/src/verkle.ts | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/packages/util/src/verkle.ts b/packages/util/src/verkle.ts index 4cdef6b206..9b652e5cbc 100644 --- a/packages/util/src/verkle.ts +++ b/packages/util/src/verkle.ts @@ -354,17 +354,12 @@ export function encodeVerkleLeafBasicData(account: Account): Uint8Array { export const generateChunkSuffixes = (numChunks: number) => { if (numChunks === 0) return [] const chunkSuffixes: number[] = new Array(numChunks) - const firstChunksSet = numChunks > VERKLE_CODE_OFFSET ? VERKLE_CODE_OFFSET : numChunks - for (let x = 0; x < firstChunksSet; x++) { - // Fill up to first 128 suffixes - chunkSuffixes[x] = x + VERKLE_CODE_OFFSET - } - if (numChunks > VERKLE_CODE_OFFSET) { - for (let x = VERKLE_CODE_OFFSET; x < numChunks; x++) { - // Fill subsequent chunk suffixes up to 256 and then start over since a single node - chunkSuffixes[x] = - x - VERKLE_CODE_OFFSET - Math.floor(x / VERKLE_NODE_WIDTH) * VERKLE_NODE_WIDTH - } + let currentSuffix = VERKLE_CODE_OFFSET + for (let x = 0; x < numChunks; x++) { + chunkSuffixes[x] = currentSuffix + currentSuffix++ + // Reset suffix to 0 if exceeds VERKLE_NODE_WIDTH + if (currentSuffix >= VERKLE_NODE_WIDTH) currentSuffix = 0 } return chunkSuffixes From 0545b03d646543f6bccfc45256b1034a7b277a2d Mon Sep 17 00:00:00 2001 From: acolytec3 <17355484+acolytec3@users.noreply.github.com> Date: Thu, 14 Nov 2024 09:36:14 -0500 Subject: [PATCH 36/60] fix verkle setup --- packages/common/src/common.ts | 6 +++--- packages/vm/test/tester/config.ts | 7 ++++--- packages/vm/test/tester/runners/BlockchainTestsRunner.ts | 3 +-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/common/src/common.ts b/packages/common/src/common.ts index ce36db6d25..2ecbd2c568 100644 --- a/packages/common/src/common.ts +++ b/packages/common/src/common.ts @@ -317,8 +317,8 @@ 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'] - for (const eip of hfEIPs!) { + const hfEIPs = hfChanges[1].eips ?? [] + for (const eip of hfEIPs) { this._mergeWithParamsCache(this._params[eip] ?? {}) } } @@ -337,7 +337,7 @@ export class Common { for (const [name, hf] of this.HARDFORK_CHANGES) { if (this.gteHardfork(name) && 'eips' in hf) { - this._activatedEIPsCache = this._activatedEIPsCache.concat(hf['eips'] as number[]) + this._activatedEIPsCache = this._activatedEIPsCache.concat(hf.eips as number[]) } } this._activatedEIPsCache = this._activatedEIPsCache.concat(this._eips) diff --git a/packages/vm/test/tester/config.ts b/packages/vm/test/tester/config.ts index f66fd2df66..6115f93d5a 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 verkleCrypto 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) @@ -324,15 +325,15 @@ function setupCommonForVerkle(network: string, timestamp?: number, kzg?: KZG) { } testHardforks.push({ name: 'verkle', block: 1 }) - const common = createCustomCommon( { hardforks: testHardforks, defaultHardfork: 'verkle', }, Mainnet, - { eips: [3607], customCrypto: { kzg } }, + { eips: [3607], customCrypto: { kzg, verkleCrypto } }, ) + console.log(common.eips()) // Activate EIPs const eips = network.match(/(?<=\+)(.\d+)/g) if (eips) { diff --git a/packages/vm/test/tester/runners/BlockchainTestsRunner.ts b/packages/vm/test/tester/runners/BlockchainTestsRunner.ts index ffe561c9f9..c0c900a7a4 100644 --- a/packages/vm/test/tester/runners/BlockchainTestsRunner.ts +++ b/packages/vm/test/tester/runners/BlockchainTestsRunner.ts @@ -53,11 +53,10 @@ export async function runBlockchainTest(options: any, testData: any, t: tape.Tes let stateManager: StateManagerInterface if (options.stateManager === 'verkle') { - const verkleCrypto = options.verkleCrypto stateTree = await createVerkleTree() stateManager = new StatefulVerkleStateManager({ - verkleCrypto, trie: stateTree, + common: options.common, }) } else { stateTree = new MerklePatriciaTrie({ useKeyHashing: true, common }) From 6594acf0a2851affee4726dc3dabf80a474aa1dc Mon Sep 17 00:00:00 2001 From: acolytec3 <17355484+acolytec3@users.noreply.github.com> Date: Fri, 15 Nov 2024 14:12:08 -0500 Subject: [PATCH 37/60] Let the robots rewrite chunkifyCode --- packages/util/src/verkle.ts | 63 +++++++++++++++++-------------- packages/util/test/verkle.spec.ts | 27 +++++++++++++ 2 files changed, 61 insertions(+), 29 deletions(-) diff --git a/packages/util/src/verkle.ts b/packages/util/src/verkle.ts index 9b652e5cbc..23ef013a43 100644 --- a/packages/util/src/verkle.ts +++ b/packages/util/src/verkle.ts @@ -238,39 +238,44 @@ export const getVerkleTreeKeyForCodeChunk = async ( return concatBytes(getVerkleStem(verkleCrypto, address, treeIndex), toBytes(subIndex)) } +// This code was written by robots based on the reference implementation in EIP-6800 export const chunkifyCode = (code: Uint8Array) => { - if (code.length === 0) return [] - // Pad code to multiple of VERKLE_CODE_CHUNK_SIZE bytes - if (code.length % VERKLE_CODE_CHUNK_SIZE !== 0) { - const paddingLength = VERKLE_CODE_CHUNK_SIZE - (code.length % VERKLE_CODE_CHUNK_SIZE) - code = setLengthRight(code, code.length + paddingLength) - } - // Put first chunk (leading byte is always 0 since we have no leading PUSHDATA bytes) - const chunks = [concatBytes(new Uint8Array(1), code.subarray(0, 31))] - for (let i = 1; i < Math.floor(code.length / 31); i++) { - const slice = code.slice((i - 1) * 31, i * 31) - let x = 31 - while (x >= 0) { - // Look for last push instruction in code chunk - if (slice[x] > 0x5f && slice[x] < 0x80) break - x-- + const PUSH1 = 0x60 // Assuming PUSH1 is defined as 0x60 + const PUSH32 = 0x7f // Assuming PUSH32 is defined as 0x7f + const PUSH_OFFSET = 0x5f // Assuming PUSH_OFFSET is defined as 0x5f + + // Calculate padding length + const paddingLength = (31 - (code.length % 31)) % 31 + const paddedCode = new Uint8Array(code.length + paddingLength) + paddedCode.set(code) + + // Pre-allocate the bytesToExecData array + const bytesToExecData = new Uint8Array(paddedCode.length + 32) + + let pos = 0 + while (pos < paddedCode.length) { + let pushdataBytes = 0 + if (PUSH1 <= paddedCode[pos] && paddedCode[pos] <= PUSH32) { + pushdataBytes = paddedCode[pos] - PUSH_OFFSET } - if (x >= 0 && slice[x] - 0x5f > 31 - x) { - // x >= 0 indicates PUSHn in this chunk - // n > 31 - x indicates that PUSHDATA spills over to next chunk - // PUSHDATA overflow = n - (31 - x) + 1(i.e. number of elements PUSHed - // - size of code chunk (31) - position of PUSHn in the previous - // code chunk + 1 (since x is zero-indexed)) - const pushDataOverflow = slice[x] - 0x5f - (31 - x) + 1 - // Put next chunk prepended with number of overflow PUSHDATA bytes - chunks.push( - concatBytes(Uint8Array.from([pushDataOverflow]), code.slice(i * 31, (i + 1) * 31)), - ) - } else { - // Put next chunk prepended with 0 (i.e. no overflow PUSHDATA bytes from previous chunk) - chunks.push(concatBytes(new Uint8Array(1), code.slice(i * 31, (i + 1) * 31))) + pos += 1 + for (let x = 0; x < pushdataBytes; x++) { + bytesToExecData[pos + x] = pushdataBytes - x } + pos += pushdataBytes } + + // Pre-allocate the chunks array + const numChunks = Math.ceil(paddedCode.length / 31) + const chunks = new Array(numChunks) + + for (let i = 0, pos = 0; i < numChunks; i++, pos += 31) { + const chunk = new Uint8Array(32) + chunk[0] = Math.min(bytesToExecData[pos], 31) + chunk.set(paddedCode.subarray(pos, pos + 31), 1) + chunks[i] = chunk + } + return chunks } diff --git a/packages/util/test/verkle.spec.ts b/packages/util/test/verkle.spec.ts index caad795885..362a0475fb 100644 --- a/packages/util/test/verkle.spec.ts +++ b/packages/util/test/verkle.spec.ts @@ -136,4 +136,31 @@ describe('should chunkify code, accounting for leading PUSHDATA bytes', () => { } } }) + it('should chunkify code correctly', () => { + const codes = [ + hexToBytes( + '0x73d94f5374fce5edbc8e2a8697c15331677e6ebf0c3173d94f5374fce5edbc8e2a8697c15331677e6ebf0c315f55', + ), + hexToBytes( + '0x6002600101600260010160026001016002600101600260010160026001016002600101600260010160026001016002600101', + ), + ] + const codeChunks = [ + [ + '0x0073d94f5374fce5edbc8e2a8697c15331677e6ebf0c3173d94f5374fce5edbc', + '0x0c8e2a8697c15331677e6ebf0c315f5500000000000000000000000000000000', + ], + [ + '0x0060026001016002600101600260010160026001016002600101600260010160', + '0x0102600101600260010160026001016002600101000000000000000000000000', + ], + ] + for (const [idx, code] of codes.entries()) { + const chunks = chunkifyCode(code) + assert.deepEqual( + chunks.map((chunk) => bytesToHex(chunk)), + codeChunks[idx], + ) + } + }) }) From 1157db0f1a06ec9a2dbfab3af703552f65587bd4 Mon Sep 17 00:00:00 2001 From: Gabriel Rocheleau Date: Sun, 17 Nov 2024 08:53:43 -0700 Subject: [PATCH 38/60] vm: fix test setup --- packages/vm/test/tester/config.ts | 4 ++-- packages/vm/test/tester/index.ts | 6 ++---- packages/vm/test/tester/runners/BlockchainTestsRunner.ts | 3 --- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/packages/vm/test/tester/config.ts b/packages/vm/test/tester/config.ts index 6115f93d5a..a541473604 100644 --- a/packages/vm/test/tester/config.ts +++ b/packages/vm/test/tester/config.ts @@ -1,5 +1,5 @@ import { Common, Hardfork, Mainnet, createCustomCommon } from '@ethereumjs/common' -import * as verkleCrypto from 'micro-eth-signer/verkle' +import * as verkle from 'micro-eth-signer/verkle' import * as path from 'path' import type { HardforkTransitionConfig } from '@ethereumjs/common' @@ -331,7 +331,7 @@ function setupCommonForVerkle(network: string, timestamp?: number, kzg?: KZG) { defaultHardfork: 'verkle', }, Mainnet, - { eips: [3607], customCrypto: { kzg, verkleCrypto } }, + { eips: [3607], customCrypto: { kzg, verkle } }, ) console.log(common.eips()) // Activate EIPs diff --git a/packages/vm/test/tester/index.ts b/packages/vm/test/tester/index.ts index 24c9ea9ccc..e2d58e7d3e 100755 --- a/packages/vm/test/tester/index.ts +++ b/packages/vm/test/tester/index.ts @@ -25,7 +25,6 @@ import { getTestFromSource, getTestsFromArgs } from './testLoader.js' import type { Common } from '@ethereumjs/common' import type { EVMBLSInterface, EVMBN254Interface } from '@ethereumjs/evm' import type { VerkleCrypto } from '@ethereumjs/util' -const loadVerkleCrypto = () => Promise.resolve(verkle) /** * Test runner @@ -126,9 +125,9 @@ async function runTests() { bn254 = new NobleBN254() } - let verkleCrypto: VerkleCrypto + let verkleCrypto: VerkleCrypto | undefined if (FORK_CONFIG === 'verkle') { - verkleCrypto = await loadVerkleCrypto() + verkleCrypto = verkle } /** @@ -165,7 +164,6 @@ async function runTests() { profile: RUN_PROFILER, bn254, stateManager: argv.stateManager, - verkleCrypto, } /** diff --git a/packages/vm/test/tester/runners/BlockchainTestsRunner.ts b/packages/vm/test/tester/runners/BlockchainTestsRunner.ts index c0c900a7a4..3fc515d849 100644 --- a/packages/vm/test/tester/runners/BlockchainTestsRunner.ts +++ b/packages/vm/test/tester/runners/BlockchainTestsRunner.ts @@ -3,7 +3,6 @@ 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, StatefulVerkleStateManager } from '@ethereumjs/statemanager' import { createTxFromRLP } from '@ethereumjs/tx' import { @@ -15,7 +14,6 @@ import { toBytes, } from '@ethereumjs/util' import { createVerkleTree } from '@ethereumjs/verkle' -import * as verkle from 'micro-eth-signer/verkle' import { buildBlock, createVM, runBlock } from '../../../src/index.js' import { setupPreConditions, verifyPostConditions } from '../../util.js' @@ -26,7 +24,6 @@ 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' -const loadVerkleCrypto = () => Promise.resolve(verkle) function formatBlockHeader(data: any) { const formatted: any = {} From 690eb321dd635cab1304f3ecb4330de96f8b18e0 Mon Sep 17 00:00:00 2001 From: Gabriel Rocheleau Date: Wed, 20 Nov 2024 10:55:17 -0700 Subject: [PATCH 39/60] vm: use common instead of verklecrypto --- packages/vm/test/tester/runners/GeneralStateTestsRunner.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vm/test/tester/runners/GeneralStateTestsRunner.ts b/packages/vm/test/tester/runners/GeneralStateTestsRunner.ts index 866cb6fef7..5482c7c4b6 100644 --- a/packages/vm/test/tester/runners/GeneralStateTestsRunner.ts +++ b/packages/vm/test/tester/runners/GeneralStateTestsRunner.ts @@ -91,7 +91,7 @@ async function runTestCase(options: any, testData: any, t: tape.Test) { const verkleCrypto = await loadVerkleCrypto() stateTree = await createVerkleTree({ verkleCrypto, db: new MapDB() }) stateManager = new StatefulVerkleStateManager({ - verkleCrypto, + common, trie: stateTree, }) } else { From 2bb30bc2ef9817631d754525438038458c202b60 Mon Sep 17 00:00:00 2001 From: Gabriel Rocheleau Date: Wed, 20 Nov 2024 22:55:23 -0700 Subject: [PATCH 40/60] vm: fix historyAddress --- packages/common/src/hardforks.ts | 4 ---- packages/evm/src/params.ts | 2 +- packages/vm/src/params.ts | 2 +- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/common/src/hardforks.ts b/packages/common/src/hardforks.ts index f0fa40bdaf..1f3a8fbf8e 100644 --- a/packages/common/src/hardforks.ts +++ b/packages/common/src/hardforks.ts @@ -170,8 +170,4 @@ export const hardforksDict: HardforksDict = { verkle: { eips: [4762, 6800], }, - // TODO: WIP - verkle: { - eips: [2935, 6800], - }, } diff --git a/packages/evm/src/params.ts b/packages/evm/src/params.ts index 397e746e20..6aa237fe62 100644 --- a/packages/evm/src/params.ts +++ b/packages/evm/src/params.ts @@ -272,7 +272,7 @@ export const paramsEVM: ParamsDict = { */ 2935: { // evm - historyStorageAddress: '0x0aae40965e6800cd9b1f4b05ff21581047e3f91e', // The address where the historical blockhashes are stored + historyStorageAddress: '0xfffffffffffffffffffffffffffffffffffffffe', // The address where the historical blockhashes are stored historyServeWindow: 8192, // The amount of blocks to be served by the historical blockhash contract }, /** diff --git a/packages/vm/src/params.ts b/packages/vm/src/params.ts index dfaa021cda..5b59249903 100644 --- a/packages/vm/src/params.ts +++ b/packages/vm/src/params.ts @@ -39,7 +39,7 @@ export const paramsVM: ParamsDict = { */ 2935: { // config - historyStorageAddress: '0x0aae40965e6800cd9b1f4b05ff21581047e3f91e', // The address where the historical blockhashes are stored + historyStorageAddress: '0xfffffffffffffffffffffffffffffffffffffffe', // The address where the historical blockhashes are stored historyServeWindow: 8192, // The amount of blocks to be served by the historical blockhash contract }, /** From 17ce748c3f32027b91cef705cf03bbd51a17a363 Mon Sep 17 00:00:00 2001 From: Gabriel Rocheleau Date: Wed, 20 Nov 2024 22:56:09 -0700 Subject: [PATCH 41/60] vm: add 2935 --- packages/vm/test/tester/config.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/vm/test/tester/config.ts b/packages/vm/test/tester/config.ts index a541473604..289844d17d 100644 --- a/packages/vm/test/tester/config.ts +++ b/packages/vm/test/tester/config.ts @@ -331,8 +331,9 @@ function setupCommonForVerkle(network: string, timestamp?: number, kzg?: KZG) { defaultHardfork: 'verkle', }, Mainnet, - { eips: [3607], customCrypto: { kzg, verkle } }, + { eips: [2935, 3607], customCrypto: { kzg, verkle } }, ) + console.log(common.eips()) // Activate EIPs const eips = network.match(/(?<=\+)(.\d+)/g) From 6d2ff2ebb8be17d1eb0ea4c5722f1d6b459fe4f0 Mon Sep 17 00:00:00 2001 From: Gabriel Rocheleau Date: Sun, 24 Nov 2024 15:17:23 -0700 Subject: [PATCH 42/60] vm: some improvements for 2935 handling --- packages/blockchain/src/helpers.ts | 2 +- packages/client/src/execution/vmexecution.ts | 4 + packages/evm/src/params.ts | 3 +- .../src/statefulVerkleStateManager.ts | 3 + packages/vm/src/params.ts | 8 +- packages/vm/src/runBlock.ts | 112 +++++++++--------- .../tester/runners/GeneralStateTestsRunner.ts | 2 - 7 files changed, 69 insertions(+), 65 deletions(-) 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/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/evm/src/params.ts b/packages/evm/src/params.ts index 6aa237fe62..8204278b06 100644 --- a/packages/evm/src/params.ts +++ b/packages/evm/src/params.ts @@ -272,8 +272,9 @@ export const paramsEVM: ParamsDict = { */ 2935: { // evm - historyStorageAddress: '0xfffffffffffffffffffffffffffffffffffffffe', // The address where the historical blockhashes are stored + historyStorageAddress: '0x0aae40965e6800cd9b1f4b05ff21581047e3f91e', // The address where the historical blockhashes are stored historyServeWindow: 8192, // The amount of blocks to be served by the historical blockhash contract + systemAddress: '0xfffffffffffffffffffffffffffffffffffffffe', // The system address }, /** . * BASEFEE opcode diff --git a/packages/statemanager/src/statefulVerkleStateManager.ts b/packages/statemanager/src/statefulVerkleStateManager.ts index f5c9abf8e6..de9bd18bd0 100644 --- a/packages/statemanager/src/statefulVerkleStateManager.ts +++ b/packages/statemanager/src/statefulVerkleStateManager.ts @@ -218,6 +218,9 @@ export class StatefulVerkleStateManager implements StateManagerInterface { }, {}) this._postState = postState + + this._debug(`initVerkleExecutionWitness preState=${JSON.stringify(this._preState)}`) + this._debug(`initVerkleExecutionWitness postState=${JSON.stringify(this._postState)}`) } /** diff --git a/packages/vm/src/params.ts b/packages/vm/src/params.ts index 5b59249903..6016f033e8 100644 --- a/packages/vm/src/params.ts +++ b/packages/vm/src/params.ts @@ -39,8 +39,9 @@ export const paramsVM: ParamsDict = { */ 2935: { // config - historyStorageAddress: '0xfffffffffffffffffffffffffffffffffffffffe', // The address where the historical blockhashes are stored + historyStorageAddress: '0x0aae40965e6800cd9b1f4b05ff21581047e3f91e', // The address where the historical blockhashes are stored historyServeWindow: 8192, // The amount of blocks to be served by the historical blockhash contract + systemAddress: '0xfffffffffffffffffffffffffffffffffffffffe', // The system address }, /** . * Reduction in refunds @@ -67,10 +68,7 @@ export const paramsVM: ParamsDict = { * Ethereum state using a unified verkle tree (experimental) */ 6800: { - // kaustinen 6 current uses this address, however this will be updated to correct address - // in next iteration - // config - historyStorageAddress: '0xfffffffffffffffffffffffffffffffffffffffe', // The address where the historical blockhashes are stored + historyStorageAddress: '0x0aae40965e6800cd9b1f4b05ff21581047e3f91e', // The address where the historical blockhashes are stored }, /** * Execution layer triggerable withdrawals (experimental) diff --git a/packages/vm/src/runBlock.ts b/packages/vm/src/runBlock.ts index 2b561de98f..48147f646e 100644 --- a/packages/vm/src/runBlock.ts +++ b/packages/vm/src/runBlock.ts @@ -18,6 +18,7 @@ import { bytesToHex, concatBytes, createAddressFromString, + createPartialAccount, equalsBytes, getVerkleTreeIndicesForStorageSlot, hexToBytes, @@ -271,55 +272,55 @@ export async function runBlock(vm: VM, opts: RunBlockOpts): Promise { let account = await evm.stateManager.getAccount(address) if (account === undefined) { - if (common?.isActivatedEIP(6800) === true) { + if (common.isActivatedEIP(6800) === true) { if (evm.verkleAccessWitness === undefined) { throw Error(`verkleAccessWitness required if verkle (EIP-6800) is activated`) } @@ -752,7 +752,7 @@ export async function rewardAccount( account.balance += reward await evm.journal.putAccount(address, account) - if (common?.isActivatedEIP(6800) === true) { + if (common.isActivatedEIP(6800) === true) { if (evm.verkleAccessWitness === undefined) { throw Error(`verkleAccessWitness required if verkle (EIP-6800) is activated`) } diff --git a/packages/vm/test/tester/runners/GeneralStateTestsRunner.ts b/packages/vm/test/tester/runners/GeneralStateTestsRunner.ts index 5482c7c4b6..60d0444d7d 100644 --- a/packages/vm/test/tester/runners/GeneralStateTestsRunner.ts +++ b/packages/vm/test/tester/runners/GeneralStateTestsRunner.ts @@ -123,8 +123,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' } From fa157ca1ce8513629d8afc5763df5dfd95258cd2 Mon Sep 17 00:00:00 2001 From: Gabriel Rocheleau Date: Tue, 26 Nov 2024 06:33:36 -0700 Subject: [PATCH 43/60] statemanageR: clarify post state error messages --- packages/statemanager/src/statefulVerkleStateManager.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/statemanager/src/statefulVerkleStateManager.ts b/packages/statemanager/src/statefulVerkleStateManager.ts index de9bd18bd0..41ac914c6a 100644 --- a/packages/statemanager/src/statefulVerkleStateManager.ts +++ b/packages/statemanager/src/statefulVerkleStateManager.ts @@ -650,7 +650,7 @@ export class StatefulVerkleStateManager implements StateManagerInterface { if (computedValue === undefined) { this.DEBUG && this._debug( - `Block accesses missing in canonical address=${address} type=${type} ${extraMeta} chunkKey=${chunkKey}`, + `Missing computed value for address=${address} type=${type} ${extraMeta} chunkKey=${chunkKey}`, ) postFailures++ continue @@ -661,7 +661,7 @@ export class StatefulVerkleStateManager implements StateManagerInterface { if (canonicalValue === undefined) { this.DEBUG && this._debug( - `Block accesses missing in canonical address=${address} type=${type} ${extraMeta} chunkKey=${chunkKey}`, + `Block accesses missing from postState for address=${address} type=${type} ${extraMeta} chunkKey=${chunkKey}`, ) postFailures++ continue From 6ff4943da4aab2aa89c461626b2b9e249e7e64fd Mon Sep 17 00:00:00 2001 From: Gabriel Rocheleau Date: Wed, 27 Nov 2024 10:23:49 -0700 Subject: [PATCH 44/60] evm: access witness methods renaming, addition of system verkle access witness and proper handling for 4762 and 2935 --- packages/common/src/interfaces.ts | 6 +- packages/evm/src/evm.ts | 3 +- packages/evm/src/interpreter.ts | 2 +- packages/evm/src/opcodes/functions.ts | 2 +- packages/evm/src/opcodes/gas.ts | 4 +- packages/evm/src/types.ts | 1 + packages/evm/src/verkleAccessWitness.ts | 23 +++-- packages/vm/src/runBlock.ts | 114 +++++++++++++----------- 8 files changed, 88 insertions(+), 67 deletions(-) diff --git a/packages/common/src/interfaces.ts b/packages/common/src/interfaces.ts index fe17f65564..edaabfcd14 100644 --- a/packages/common/src/interfaces.ts +++ b/packages/common/src/interfaces.ts @@ -108,8 +108,8 @@ export interface VerkleAccessWitnessInterface { touchAndChargeContractCreateCompleted(address: Address): bigint touchTxOriginAndComputeGas(origin: Address): bigint touchTxTargetAndComputeGas(target: Address, { sendsValue }: { sendsValue?: boolean }): bigint - touchCodeChunksRangeOnReadAndChargeGas(contact: Address, startPc: number, endPc: number): bigint - touchCodeChunksRangeOnWriteAndChargeGas(contact: Address, startPc: number, endPc: number): bigint + touchCodeChunksRangeOnReadAndComputeGas(contact: Address, startPc: number, endPc: number): bigint + touchCodeChunksRangeOnWriteAndComputeGas(contact: Address, startPc: number, endPc: number): bigint touchAddressOnWriteAndComputeGas( address: Address, treeIndex: number | bigint, @@ -120,7 +120,7 @@ export interface VerkleAccessWitnessInterface { treeIndex: number | bigint, subIndex: number | Uint8Array, ): bigint - touchAddressAndChargeGas( + touchAddressAndComputeGas( address: Address, treeIndex: number | bigint, subIndex: number | Uint8Array, diff --git a/packages/evm/src/evm.ts b/packages/evm/src/evm.ts index 5e86d49d60..a644ea79e7 100644 --- a/packages/evm/src/evm.ts +++ b/packages/evm/src/evm.ts @@ -101,6 +101,7 @@ export class EVM implements EVMInterface { public blockchain: EVMMockBlockchainInterface public journal: Journal public verkleAccessWitness?: VerkleAccessWitness + public systemVerkleAccessWitness?: VerkleAccessWitness public readonly transientStorage: TransientStorage @@ -685,7 +686,7 @@ export class EVM implements EVMInterface { // Add access charges for writing this code to the state if (this.common.isActivatedEIP(6800)) { const byteCodeWriteAccessfee = - message.accessWitness!.touchCodeChunksRangeOnWriteAndChargeGas( + message.accessWitness!.touchCodeChunksRangeOnWriteAndComputeGas( message.to, 0, result.returnValue.length - 1, diff --git a/packages/evm/src/interpreter.ts b/packages/evm/src/interpreter.ts index 9d5be3f8e1..43776061f7 100644 --- a/packages/evm/src/interpreter.ts +++ b/packages/evm/src/interpreter.ts @@ -378,7 +378,7 @@ export class Interpreter { if (this.common.isActivatedEIP(6800) && this._env.chargeCodeAccesses === true) { const contract = this._runState.interpreter.getAddress() const statelessGas = - this._runState.env.accessWitness!.touchCodeChunksRangeOnReadAndChargeGas( + this._runState.env.accessWitness!.touchCodeChunksRangeOnReadAndComputeGas( contract, this._runState.programCounter, this._runState.programCounter, diff --git a/packages/evm/src/opcodes/functions.ts b/packages/evm/src/opcodes/functions.ts index 6b7ef88852..7989eb3ad0 100644 --- a/packages/evm/src/opcodes/functions.ts +++ b/packages/evm/src/opcodes/functions.ts @@ -968,7 +968,7 @@ export const handlers: Map = new Map([ const contract = runState.interpreter.getAddress() const startOffset = Math.min(runState.code.length, runState.programCounter + 1) const endOffset = Math.min(runState.code.length, startOffset + numToPush - 1) - const statelessGas = runState.env.accessWitness!.touchCodeChunksRangeOnReadAndChargeGas( + const statelessGas = runState.env.accessWitness!.touchCodeChunksRangeOnReadAndComputeGas( contract, startOffset, endOffset, diff --git a/packages/evm/src/opcodes/gas.ts b/packages/evm/src/opcodes/gas.ts index a85868c214..028c844752 100644 --- a/packages/evm/src/opcodes/gas.ts +++ b/packages/evm/src/opcodes/gas.ts @@ -154,7 +154,7 @@ export const dynamicGasHandlers: Map events?: EventEmitter verkleAccessWitness?: VerkleAccessWitnessInterface + systemVerkleAccessWitness?: VerkleAccessWitnessInterface } export type EVMProfilerOpts = { diff --git a/packages/evm/src/verkleAccessWitness.ts b/packages/evm/src/verkleAccessWitness.ts index 65a9f77ad7..225242d07d 100644 --- a/packages/evm/src/verkleAccessWitness.ts +++ b/packages/evm/src/verkleAccessWitness.ts @@ -161,16 +161,20 @@ export class VerkleAccessWitness implements VerkleAccessWitnessInterface { return gas } - touchCodeChunksRangeOnReadAndChargeGas(contact: Address, startPc: number, endPc: number): bigint { + touchCodeChunksRangeOnReadAndComputeGas( + contract: Address, + startPc: number, + endPc: number, + ): bigint { let gas = BIGINT_0 for (let chunkNum = Math.floor(startPc / 31); chunkNum <= Math.floor(endPc / 31); chunkNum++) { const { treeIndex, subIndex } = getVerkleTreeIndicesForCodeChunk(chunkNum) - gas += this.touchAddressOnReadAndComputeGas(contact, treeIndex, subIndex) + gas += this.touchAddressOnReadAndComputeGas(contract, treeIndex, subIndex) } return gas } - touchCodeChunksRangeOnWriteAndChargeGas( + touchCodeChunksRangeOnWriteAndComputeGas( contact: Address, startPc: number, endPc: number, @@ -188,7 +192,7 @@ export class VerkleAccessWitness implements VerkleAccessWitnessInterface { treeIndex: number | bigint, subIndex: number | Uint8Array, ): bigint { - return this.touchAddressAndChargeGas(address, treeIndex, subIndex, { + return this.touchAddressAndComputeGas(address, treeIndex, subIndex, { isWrite: true, }) } @@ -198,12 +202,12 @@ export class VerkleAccessWitness implements VerkleAccessWitnessInterface { treeIndex: number | bigint, subIndex: number | Uint8Array, ): bigint { - return this.touchAddressAndChargeGas(address, treeIndex, subIndex, { + return this.touchAddressAndComputeGas(address, treeIndex, subIndex, { isWrite: false, }) } - touchAddressAndChargeGas( + touchAddressAndComputeGas( address: Address, treeIndex: number | bigint, subIndex: number | Uint8Array, @@ -236,7 +240,7 @@ export class VerkleAccessWitness implements VerkleAccessWitnessInterface { } debug( - `touchAddressAndChargeGas=${gas} address=${address} treeIndex=${treeIndex} subIndex=${subIndex}`, + `touchAddressAndComputeGas=${gas} address=${address} treeIndex=${treeIndex} subIndex=${subIndex}`, ) return gas @@ -297,6 +301,11 @@ export class VerkleAccessWitness implements VerkleAccessWitnessInterface { return { stemRead, stemWrite, chunkRead, chunkWrite, chunkFill } } + clear(): void { + this.stems.clear() + this.chunks.clear() + } + merge(accessWitness: VerkleAccessWitness): void { for (const [chunkKey, chunkValue] of accessWitness.chunks.entries()) { const stemKey = chunkKey.slice(0, chunkKey.length - 2) as PrefixedHexString diff --git a/packages/vm/src/runBlock.ts b/packages/vm/src/runBlock.ts index 48147f646e..ca5d0f3ea0 100644 --- a/packages/vm/src/runBlock.ts +++ b/packages/vm/src/runBlock.ts @@ -144,6 +144,9 @@ export async function runBlock(vm: VM, opts: RunBlockOpts): Promise Date: Tue, 3 Dec 2024 15:33:22 -0700 Subject: [PATCH 45/60] evm: minor verkle testnet related adjustments --- packages/client/bin/cli.ts | 1 - packages/common/src/hardforks.ts | 2 +- packages/evm/src/evm.ts | 6 +++--- packages/evm/src/params.ts | 2 +- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/packages/client/bin/cli.ts b/packages/client/bin/cli.ts index 60969b8fb5..95b890e2df 100755 --- a/packages/client/bin/cli.ts +++ b/packages/client/bin/cli.ts @@ -1169,7 +1169,6 @@ async function run() { dnsNetworks: args.dnsNetworks, extIP: args.extIP, key, - logger, maxPeers: args.maxPeers, maxPerRequest: args.maxPerRequest, diff --git a/packages/common/src/hardforks.ts b/packages/common/src/hardforks.ts index 1f3a8fbf8e..f6c169319b 100644 --- a/packages/common/src/hardforks.ts +++ b/packages/common/src/hardforks.ts @@ -168,6 +168,6 @@ export const hardforksDict: HardforksDict = { * Status : Final */ verkle: { - eips: [4762, 6800], + eips: [2935, 4762, 6800], }, } diff --git a/packages/evm/src/evm.ts b/packages/evm/src/evm.ts index a644ea79e7..dd33ce451d 100644 --- a/packages/evm/src/evm.ts +++ b/packages/evm/src/evm.ts @@ -834,11 +834,11 @@ export class EVM implements EVMInterface { let callerAccount if (!message) { this._block = opts.block ?? defaultBlock() + const caller = opts.caller ?? createZeroAddress() this._tx = { gasPrice: opts.gasPrice ?? BIGINT_0, - origin: opts.origin ?? opts.caller ?? createZeroAddress(), + origin: opts.origin ?? caller, } - const caller = opts.caller ?? createZeroAddress() const value = opts.value ?? BIGINT_0 if (opts.skipBalance === true) { @@ -916,7 +916,7 @@ export class EVM implements EVMInterface { result = await this._executeCall(message as MessageWithTo) } else { if (this.DEBUG) { - debug(`Message CREATE execution (to undefined)`) + debug(`Message CREATE execution (to: undefined)`) } result = await this._executeCreate(message) } diff --git a/packages/evm/src/params.ts b/packages/evm/src/params.ts index 8204278b06..50e6fa0c37 100644 --- a/packages/evm/src/params.ts +++ b/packages/evm/src/params.ts @@ -272,7 +272,7 @@ export const paramsEVM: ParamsDict = { */ 2935: { // evm - historyStorageAddress: '0x0aae40965e6800cd9b1f4b05ff21581047e3f91e', // The address where the historical blockhashes are stored + historyStorageAddress: '0xfffffffffffffffffffffffffffffffffffffffe', // The address where the historical blockhashes are stored historyServeWindow: 8192, // The amount of blocks to be served by the historical blockhash contract systemAddress: '0xfffffffffffffffffffffffffffffffffffffffe', // The system address }, From 3ece78d30213677cc6d339f6a6644f70740e3348 Mon Sep 17 00:00:00 2001 From: Gabriel Rocheleau Date: Wed, 4 Dec 2024 12:48:33 -0700 Subject: [PATCH 46/60] evm: remove unused clear method2 --- packages/evm/src/verkleAccessWitness.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/packages/evm/src/verkleAccessWitness.ts b/packages/evm/src/verkleAccessWitness.ts index 225242d07d..639562ecf9 100644 --- a/packages/evm/src/verkleAccessWitness.ts +++ b/packages/evm/src/verkleAccessWitness.ts @@ -301,11 +301,6 @@ export class VerkleAccessWitness implements VerkleAccessWitnessInterface { return { stemRead, stemWrite, chunkRead, chunkWrite, chunkFill } } - clear(): void { - this.stems.clear() - this.chunks.clear() - } - merge(accessWitness: VerkleAccessWitness): void { for (const [chunkKey, chunkValue] of accessWitness.chunks.entries()) { const stemKey = chunkKey.slice(0, chunkKey.length - 2) as PrefixedHexString From 4ec2a97ee8c9045977fc76d22e3a96c59cac41f0 Mon Sep 17 00:00:00 2001 From: Gabriel Rocheleau Date: Sat, 7 Dec 2024 13:26:10 -0700 Subject: [PATCH 47/60] evm: typo --- packages/common/src/interfaces.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/common/src/interfaces.ts b/packages/common/src/interfaces.ts index edaabfcd14..7537b70f7c 100644 --- a/packages/common/src/interfaces.ts +++ b/packages/common/src/interfaces.ts @@ -108,8 +108,12 @@ export interface VerkleAccessWitnessInterface { touchAndChargeContractCreateCompleted(address: Address): bigint touchTxOriginAndComputeGas(origin: Address): bigint touchTxTargetAndComputeGas(target: Address, { sendsValue }: { sendsValue?: boolean }): bigint - touchCodeChunksRangeOnReadAndComputeGas(contact: Address, startPc: number, endPc: number): bigint - touchCodeChunksRangeOnWriteAndComputeGas(contact: Address, startPc: number, endPc: number): bigint + touchCodeChunksRangeOnReadAndComputeGas(contract: Address, startPc: number, endPc: number): bigint + touchCodeChunksRangeOnWriteAndComputeGas( + contract: Address, + startPc: number, + endPc: number, + ): bigint touchAddressOnWriteAndComputeGas( address: Address, treeIndex: number | bigint, From 74647c6e61641d7dd5fe3a85b0e25896240ad0e3 Mon Sep 17 00:00:00 2001 From: acolytec3 <17355484+acolytec3@users.noreply.github.com> Date: Mon, 9 Dec 2024 07:36:27 -0500 Subject: [PATCH 48/60] Add simple cache to access witness (#3810) * Add simple cache to access witness * flush cache at end of tx * Flush system AW cache * add tests * fix verkle cache flushing bug * address feedback --- packages/common/src/interfaces.ts | 2 + packages/evm/src/chunkCache.ts | 35 ++++++++++++++ packages/evm/src/evm.ts | 24 ++++++++-- packages/evm/src/interpreter.ts | 3 ++ packages/evm/src/stemCache.ts | 44 +++++++++++++++++ packages/evm/src/verkleAccessWitness.ts | 37 +++++++++++--- packages/evm/test/verkle.spec.ts | 64 +++++++++++++++++++++++++ packages/verkle/test/verkle.spec.ts | 2 - packages/vm/src/runBlock.ts | 23 ++++----- packages/vm/src/runTx.ts | 4 ++ packages/vm/test/tester/index.ts | 7 --- 11 files changed, 215 insertions(+), 30 deletions(-) create mode 100644 packages/evm/src/chunkCache.ts create mode 100644 packages/evm/src/stemCache.ts diff --git a/packages/common/src/interfaces.ts b/packages/common/src/interfaces.ts index 7537b70f7c..31adfa6461 100644 --- a/packages/common/src/interfaces.ts +++ b/packages/common/src/interfaces.ts @@ -137,6 +137,8 @@ export interface VerkleAccessWitnessInterface { { isWrite }: { isWrite?: boolean }, ): AccessEventFlags merge(accessWitness: VerkleAccessWitnessInterface): void + commit(): void + revert(): void } /* diff --git a/packages/evm/src/chunkCache.ts b/packages/evm/src/chunkCache.ts new file mode 100644 index 0000000000..5831e75a09 --- /dev/null +++ b/packages/evm/src/chunkCache.ts @@ -0,0 +1,35 @@ +import type { ChunkAccessEvent } from './verkleAccessWitness.js' +import type { PrefixedHexString } from '@ethereumjs/util' +export class ChunkCache { + cache: Map + + constructor() { + this.cache = new Map() + } + + set(stemKey: PrefixedHexString, accessedStem: ChunkAccessEvent) { + this.cache.set(stemKey, accessedStem) + } + + get(stemHex: PrefixedHexString): ChunkAccessEvent | undefined { + return this.cache.get(stemHex) + } + + del(stemHex: PrefixedHexString): void { + this.cache.delete(stemHex) + } + + commit(): [PrefixedHexString, ChunkAccessEvent][] { + const items: [PrefixedHexString, ChunkAccessEvent][] = Array.from(this.cache.entries()) + this.clear() + return items + } + + clear(): void { + this.cache.clear() + } + + size() { + return this.cache.size + } +} diff --git a/packages/evm/src/evm.ts b/packages/evm/src/evm.ts index dd33ce451d..32240c1abb 100644 --- a/packages/evm/src/evm.ts +++ b/packages/evm/src/evm.ts @@ -287,8 +287,10 @@ export class EVM implements EVMInterface { if (this.DEBUG) { debugGas(`callAccessGas charged(${callAccessGas}) caused OOG (-> ${gasLimit})`) } + message.accessWitness.revert() return { execResult: OOGResult(message.gasLimit) } } else { + message.accessWitness.commit() if (this.DEBUG) { debugGas(`callAccessGas used (${callAccessGas} gas (-> ${gasLimit}))`) } @@ -318,6 +320,7 @@ export class EVM implements EVMInterface { ) gasLimit -= absenceProofAccessGas if (gasLimit < BIGINT_0) { + message.accessWitness?.revert() if (this.DEBUG) { debugGas( `Proof of absence access charged(${absenceProofAccessGas}) caused OOG (-> ${gasLimit})`, @@ -325,6 +328,7 @@ export class EVM implements EVMInterface { } return { execResult: OOGResult(message.gasLimit) } } else { + message.accessWitness?.commit() if (this.DEBUG) { debugGas(`Proof of absence access used (${absenceProofAccessGas} gas (-> ${gasLimit}))`) } @@ -465,6 +469,7 @@ export class EVM implements EVMInterface { ) gasLimit -= contractCreateAccessGas if (gasLimit < BIGINT_0) { + message.accessWitness?.revert() if (this.DEBUG) { debugGas( `ContractCreateInit charge(${contractCreateAccessGas}) caused OOG (-> ${gasLimit})`, @@ -472,6 +477,7 @@ export class EVM implements EVMInterface { } return { execResult: OOGResult(message.gasLimit) } } else { + message.accessWitness?.commit() if (this.DEBUG) { debugGas(`ContractCreateInit charged (${contractCreateAccessGas} gas (-> ${gasLimit}))`) } @@ -548,6 +554,7 @@ export class EVM implements EVMInterface { message.accessWitness!.touchAndChargeContractCreateCompleted(message.to) gasLimit -= createCompleteAccessGas if (gasLimit < BIGINT_0) { + message.accessWitness?.revert() if (this.DEBUG) { debug( `ContractCreateComplete access gas (${createCompleteAccessGas}) caused OOG (-> ${gasLimit})`, @@ -555,9 +562,12 @@ export class EVM implements EVMInterface { } return { execResult: OOGResult(message.gasLimit) } } else { - debug( - `ContractCreateComplete access used (${createCompleteAccessGas}) gas (-> ${gasLimit})`, - ) + message.accessWitness?.commit() + if (this.DEBUG) { + debug( + `ContractCreateComplete access used (${createCompleteAccessGas}) gas (-> ${gasLimit})`, + ) + } } } @@ -632,6 +642,7 @@ export class EVM implements EVMInterface { } result = { ...result, ...CodesizeExceedsMaximumError(message.gasLimit) } } else { + message.accessWitness?.revert() if (this.DEBUG) { debug(`Contract creation: out of gas`) } @@ -640,6 +651,7 @@ export class EVM implements EVMInterface { } else { // we are in Frontier if (totalGas - returnFee <= message.gasLimit) { + message.accessWitness?.revert() // we cannot pay the code deposit fee (but the deposit code actually did run) if (this.DEBUG) { debug(`Not enough gas to pay the code deposit fee (Frontier)`) @@ -647,6 +659,7 @@ export class EVM implements EVMInterface { result = { ...result, ...COOGResult(totalGas - returnFee) } CodestoreOOG = true } else { + message.accessWitness?.revert() if (this.DEBUG) { debug(`Contract creation: out of gas`) } @@ -663,6 +676,7 @@ export class EVM implements EVMInterface { ) gasLimit -= createCompleteAccessGas if (gasLimit < BIGINT_0) { + message.accessWitness?.revert() if (this.DEBUG) { debug( `ContractCreateComplete access gas (${createCompleteAccessGas}) caused OOG (-> ${gasLimit})`, @@ -693,6 +707,7 @@ export class EVM implements EVMInterface { ) gasLimit -= byteCodeWriteAccessfee if (gasLimit < BIGINT_0) { + message.accessWitness?.revert() if (this.DEBUG) { debug( `byteCodeWrite access gas (${byteCodeWriteAccessfee}) caused OOG (-> ${gasLimit})`, @@ -700,6 +715,7 @@ export class EVM implements EVMInterface { } result = { ...result, ...OOGResult(message.gasLimit) } } else { + message.accessWitness?.commit() debug(`byteCodeWrite access used (${byteCodeWriteAccessfee}) gas (-> ${gasLimit})`) result.executionGasUsed += byteCodeWriteAccessfee } @@ -802,6 +818,7 @@ export class EVM implements EVMInterface { } } + message.accessWitness?.commit() return { ...result, runState: { @@ -962,6 +979,7 @@ export class EVM implements EVMInterface { this.performanceLogger.stopTimer(timer!, 0) } + message.accessWitness?.commit() return result } diff --git a/packages/evm/src/interpreter.ts b/packages/evm/src/interpreter.ts index 43776061f7..12a937278b 100644 --- a/packages/evm/src/interpreter.ts +++ b/packages/evm/src/interpreter.ts @@ -327,6 +327,8 @@ export class Interpreter { this.performanceLogger.unpauseTimer(overheadTimer) } } catch (e: any) { + // Revert access witness changes if we revert - per EIP-4762 + this._runState.env.accessWitness?.revert() if (overheadTimer !== undefined) { this.performanceLogger.unpauseTimer(overheadTimer) } @@ -412,6 +414,7 @@ export class Interpreter { } else { opFn.apply(null, [this._runState, this.common]) } + this._runState.env.accessWitness?.commit() } finally { if (this.profilerOpts?.enabled === true) { this.performanceLogger.stopTimer( diff --git a/packages/evm/src/stemCache.ts b/packages/evm/src/stemCache.ts new file mode 100644 index 0000000000..67710025bb --- /dev/null +++ b/packages/evm/src/stemCache.ts @@ -0,0 +1,44 @@ +import type { StemAccessEvent, StemMeta } from './verkleAccessWitness.js' +import type { PrefixedHexString } from '@ethereumjs/util' +export class StemCache { + cache: Map + + constructor() { + this.cache = new Map() + } + + set(stemKey: PrefixedHexString, accessedStem: StemAccessEvent & StemMeta) { + this.cache.set(stemKey, accessedStem) + } + + get(stemHex: PrefixedHexString): (StemAccessEvent & StemMeta) | undefined { + return this.cache.get(stemHex) + } + + del(stemHex: PrefixedHexString): void { + this.cache.delete(stemHex) + } + + commit(): [PrefixedHexString, StemAccessEvent & StemMeta][] { + const items: [PrefixedHexString, StemAccessEvent & StemMeta][] = Array.from( + this.cache.entries(), + ) + this.clear() + return items + } + + /** + * Clear cache + */ + clear(): void { + this.cache.clear() + } + + /** + * Returns the size of the cache + * @returns + */ + size() { + return this.cache.size + } +} diff --git a/packages/evm/src/verkleAccessWitness.ts b/packages/evm/src/verkleAccessWitness.ts index 639562ecf9..b05a439e59 100644 --- a/packages/evm/src/verkleAccessWitness.ts +++ b/packages/evm/src/verkleAccessWitness.ts @@ -15,6 +15,9 @@ import { } from '@ethereumjs/util' import debugDefault from 'debug' +import { ChunkCache } from './chunkCache.js' +import { StemCache } from './stemCache.js' + import type { AccessEventFlags, RawVerkleAccessedState, @@ -36,13 +39,13 @@ const WitnessChunkWriteCost = BigInt(500) const WitnessChunkFillCost = BigInt(6200) // read is a default access event if stem or chunk is present -type StemAccessEvent = { write?: boolean } +export type StemAccessEvent = { write?: boolean } // chunk fill access event is not being charged right now in kaustinen but will be rectified // in upcoming iterations -type ChunkAccessEvent = StemAccessEvent & { fill?: boolean } +export type ChunkAccessEvent = StemAccessEvent & { fill?: boolean } // Since stem is pedersen hashed, it is useful to maintain the reverse relationship -type StemMeta = { address: Address; treeIndex: number | bigint } +export type StemMeta = { address: Address; treeIndex: number | bigint } export function decodeAccessedState( treeIndex: number | bigint, @@ -82,6 +85,8 @@ export function decodeAccessedState( export class VerkleAccessWitness implements VerkleAccessWitnessInterface { stems: Map chunks: Map + stemCache: StemCache = new StemCache() + chunkCache: ChunkCache = new ChunkCache() verkleCrypto: VerkleCrypto constructor(opts: { verkleCrypto: VerkleCrypto @@ -262,11 +267,11 @@ export class VerkleAccessWitness implements VerkleAccessWitnessInterface { const accessedStemKey = getVerkleStem(this.verkleCrypto, address, treeIndex) const accessedStemHex = bytesToHex(accessedStemKey) - let accessedStem = this.stems.get(accessedStemHex) + let accessedStem = this.stemCache.get(accessedStemHex) ?? this.stems.get(accessedStemHex) if (accessedStem === undefined) { stemRead = true accessedStem = { address, treeIndex } - this.stems.set(accessedStemHex, accessedStem) + this.stemCache.set(accessedStemHex, accessedStem) } const accessedChunkKey = getVerkleKey( @@ -274,11 +279,12 @@ export class VerkleAccessWitness implements VerkleAccessWitnessInterface { typeof subIndex === 'number' ? intToBytes(subIndex) : subIndex, ) const accessedChunkKeyHex = bytesToHex(accessedChunkKey) - let accessedChunk = this.chunks.get(accessedChunkKeyHex) + let accessedChunk = + this.chunkCache.get(accessedChunkKeyHex) ?? this.chunks.get(accessedChunkKeyHex) if (accessedChunk === undefined) { chunkRead = true accessedChunk = {} - this.chunks.set(accessedChunkKeyHex, accessedChunk) + this.chunkCache.set(accessedChunkKeyHex, accessedChunk) } if (isWrite === true) { @@ -326,6 +332,23 @@ export class VerkleAccessWitness implements VerkleAccessWitnessInterface { } } + commit(): void { + const cachedStems = this.stemCache.commit() + for (const [stemKey, stemValue] of cachedStems) { + this.stems.set(stemKey, stemValue) + } + + const cachedChunks = this.chunkCache.commit() + for (const [chunkKey, chunkValue] of cachedChunks) { + this.chunks.set(chunkKey, chunkValue) + } + } + + revert(): void { + this.stemCache.clear() + this.chunkCache.clear() + } + *rawAccesses(): Generator { for (const chunkKey of this.chunks.keys()) { // drop the last byte diff --git a/packages/evm/test/verkle.spec.ts b/packages/evm/test/verkle.spec.ts index 67452ba197..c3c317f0fe 100644 --- a/packages/evm/test/verkle.spec.ts +++ b/packages/evm/test/verkle.spec.ts @@ -42,4 +42,68 @@ describe('verkle tests', () => { const retrievedValue = await sm.getStorage(address, setLengthLeft(bigIntToBytes(2n), 32)) assert.deepEqual(retrievedValue, setLengthLeft(bigIntToBytes(1n), 32)) }) + it('should revert and access witness should not contain a write access due to OOG', async () => { + // This tests executes some very simple bytecode that stores the value 1 in slot 2 + const common = new Common({ + chain: Mainnet, + customCrypto: { verkle }, + eips: [6800], + hardfork: Hardfork.Cancun, + }) + const trie = await createVerkleTree() + const sm = new StatefulVerkleStateManager({ common, trie }) + const address = createAddressFromString('0x9e5ef720fa2cdfa5291eb7e711cfd2e62196f4b3') + const account = createAccount({ nonce: 3n, balance: 0xffffffffn }) + await sm.putAccount(address, account) + const evm = await createEVM({ common, stateManager: sm }) + // Initialize verkleAccess Witness manually (in real context, it is done by the VM, but we are bypassing that here) + evm.verkleAccessWitness = new VerkleAccessWitness({ + verkleCrypto: verkle, + }) + const code = hexToBytes('0x6001600255') // PUSH1 01 PUSH1 02 SSTORE + const res = await evm.runCall({ + code, + caller: address, + to: address, + gasLimit: BigInt(5), // too little gas for bytecode + gasPrice: BigInt(1), + }) + const writtenChunks = Array.from(evm.verkleAccessWitness.chunks.entries()).filter( + ([_, chunk]) => chunk.write !== undefined, + ) + assert.ok(writtenChunks.length === 0) + assert.equal(res.execResult.exceptionError?.error, 'out of gas') + }) + it('access witness should contain a write access', async () => { + // This tests executes some very simple bytecode that stores the value 1 in slot 2 + const common = new Common({ + chain: Mainnet, + customCrypto: { verkle }, + eips: [6800], + hardfork: Hardfork.Cancun, + }) + const trie = await createVerkleTree() + const sm = new StatefulVerkleStateManager({ common, trie }) + const address = createAddressFromString('0x9e5ef720fa2cdfa5291eb7e711cfd2e62196f4b3') + const account = createAccount({ nonce: 3n, balance: 0xffffffffn }) + await sm.putAccount(address, account) + const evm = await createEVM({ common, stateManager: sm }) + // Initialize verkleAccess Witness manually (in real context, it is done by the VM, but we are bypassing that here) + evm.verkleAccessWitness = new VerkleAccessWitness({ + verkleCrypto: verkle, + }) + const code = hexToBytes('0x6001600255') // PUSH1 01 PUSH1 02 SSTORE + const res = await evm.runCall({ + code, + caller: address, + to: address, + gasLimit: BigInt(21000), // sufficient gas for bytecode + gasPrice: BigInt(1), + }) + const writtenChunks = Array.from(evm.verkleAccessWitness.chunks.entries()).filter( + ([_, chunk]) => chunk.write !== undefined, + ) + assert.ok(writtenChunks.length === 1) + assert.equal(res.execResult.exceptionError?.error, undefined) + }) }) diff --git a/packages/verkle/test/verkle.spec.ts b/packages/verkle/test/verkle.spec.ts index ce6e883cc5..afc5914280 100644 --- a/packages/verkle/test/verkle.spec.ts +++ b/packages/verkle/test/verkle.spec.ts @@ -9,8 +9,6 @@ import { VerkleNodeType, createVerkleTree, decodeVerkleNode, - dumpLeafValues, - dumpNodeHashes, } from '../src/index.js' import type { VerkleNode } from '../src/index.js' diff --git a/packages/vm/src/runBlock.ts b/packages/vm/src/runBlock.ts index ca5d0f3ea0..24e895342b 100644 --- a/packages/vm/src/runBlock.ts +++ b/packages/vm/src/runBlock.ts @@ -277,17 +277,17 @@ export async function runBlock(vm: VM, opts: RunBlockOpts): Promise { ) } + if (vm.common.isActivatedEIP(6800)) { + // commit all access witness changes + vm.evm.verkleAccessWitness?.commit() + } return results } diff --git a/packages/vm/test/tester/index.ts b/packages/vm/test/tester/index.ts index e2d58e7d3e..65d5a3e79d 100755 --- a/packages/vm/test/tester/index.ts +++ b/packages/vm/test/tester/index.ts @@ -2,7 +2,6 @@ import { MCLBLS, NobleBLS, NobleBN254, RustBN254 } from '@ethereumjs/evm' import { trustedSetup } from '@paulmillr/trusted-setups/fast.js' import * as mcl from 'mcl-wasm' import { KZG as microEthKZG } from 'micro-eth-signer/kzg' -import * as verkle from 'micro-eth-signer/verkle' import * as minimist from 'minimist' import * as path from 'path' import * as process from 'process' @@ -24,7 +23,6 @@ import { getTestFromSource, getTestsFromArgs } from './testLoader.js' import type { Common } from '@ethereumjs/common' import type { EVMBLSInterface, EVMBN254Interface } from '@ethereumjs/evm' -import type { VerkleCrypto } from '@ethereumjs/util' /** * Test runner @@ -125,11 +123,6 @@ async function runTests() { bn254 = new NobleBN254() } - let verkleCrypto: VerkleCrypto | undefined - if (FORK_CONFIG === 'verkle') { - verkleCrypto = verkle - } - /** * Run-time configuration */ From 2e8015befc05f093efc32b9c8c5f1a296147645b Mon Sep 17 00:00:00 2001 From: Gabriel Rocheleau Date: Tue, 17 Dec 2024 11:06:02 -0700 Subject: [PATCH 49/60] evm: debug verkle access witness cost helper --- packages/common/src/interfaces.ts | 1 + packages/evm/src/verkleAccessWitness.ts | 33 +++++++++++++++++++++++++ packages/vm/src/runBlock.ts | 6 +++++ 3 files changed, 40 insertions(+) diff --git a/packages/common/src/interfaces.ts b/packages/common/src/interfaces.ts index 31adfa6461..f4181cc698 100644 --- a/packages/common/src/interfaces.ts +++ b/packages/common/src/interfaces.ts @@ -101,6 +101,7 @@ export type VerkleAccessedStateWithAddress = VerkleAccessedState & { export interface VerkleAccessWitnessInterface { accesses(): Generator rawAccesses(): Generator + debugWitnessCost(): void touchAndChargeProofOfAbsence(address: Address): bigint touchAndChargeMessageCall(address: Address): bigint touchAndChargeValueTransfer(target: Address): bigint diff --git a/packages/evm/src/verkleAccessWitness.ts b/packages/evm/src/verkleAccessWitness.ts index b05a439e59..b65577bd9f 100644 --- a/packages/evm/src/verkleAccessWitness.ts +++ b/packages/evm/src/verkleAccessWitness.ts @@ -349,6 +349,39 @@ export class VerkleAccessWitness implements VerkleAccessWitnessInterface { this.chunkCache.clear() } + debugWitnessCost(): void { + // Calculate the aggregate gas cost for verkle access witness per type + let stemReads = 0, + stemWrites = 0, + chunkReads = 0, + chunkWrites = 0 + + for (const [_, { write }] of this.stems.entries()) { + stemReads++ + if (write === true) { + stemWrites++ + } + } + for (const [_, { write }] of this.chunks.entries()) { + chunkReads++ + if (write === true) { + chunkWrites++ + } + } + debug( + `${stemReads} stem reads, totalling ${BigInt(stemReads) * WitnessBranchReadCost} gas units`, + ) + debug( + `${stemWrites} stem writes, totalling ${BigInt(stemWrites) * WitnessBranchWriteCost} gas units`, + ) + debug( + `${chunkReads} chunk reads, totalling ${BigInt(chunkReads) * WitnessChunkReadCost} gas units`, + ) + debug( + `${chunkWrites} chunk writes, totalling ${BigInt(chunkWrites) * WitnessChunkWriteCost} gas units`, + ) + } + *rawAccesses(): Generator { for (const chunkKey of this.chunks.keys()) { // drop the last byte diff --git a/packages/vm/src/runBlock.ts b/packages/vm/src/runBlock.ts index 24e895342b..372a0049fc 100644 --- a/packages/vm/src/runBlock.ts +++ b/packages/vm/src/runBlock.ts @@ -495,6 +495,12 @@ async function applyBlock(vm: VM, block: Block, opts: RunBlockOpts): Promise Date: Thu, 19 Dec 2024 10:07:28 -0500 Subject: [PATCH 50/60] Fix step logging so dynamic gas doesn't include state accesses --- packages/evm/src/interpreter.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/evm/src/interpreter.ts b/packages/evm/src/interpreter.ts index 12a937278b..9ba11f900b 100644 --- a/packages/evm/src/interpreter.ts +++ b/packages/evm/src/interpreter.ts @@ -377,6 +377,13 @@ export class Interpreter { // It needs the base fee, for correct gas limit calculation for the CALL opcodes gas = await opEntry.gasHandler(this._runState, gas, this.common) } + + if (this._evm.events.listenerCount('step') > 0 || this._evm.DEBUG) { + // Only run this stepHook function if there is an event listener (e.g. test runner) + // or if the vm is running in debug mode (to display opcode debug logs) + await this._runStepHook(gas, this.getGasLeft()) + } + if (this.common.isActivatedEIP(6800) && this._env.chargeCodeAccesses === true) { const contract = this._runState.interpreter.getAddress() const statelessGas = @@ -389,12 +396,6 @@ export class Interpreter { debugGas(`codechunk accessed statelessGas=${statelessGas} (-> ${gas})`) } - if (this._evm.events.listenerCount('step') > 0 || this._evm.DEBUG) { - // Only run this stepHook function if there is an event listener (e.g. test runner) - // or if the vm is running in debug mode (to display opcode debug logs) - await this._runStepHook(gas, this.getGasLeft()) - } - // Check for invalid opcode if (opInfo.isInvalid) { throw new EvmError(ERROR.INVALID_OPCODE) From d93016e73cda3b2e349756ccd4cb80782307f9f2 Mon Sep 17 00:00:00 2001 From: Gabriel Rocheleau Date: Sat, 21 Dec 2024 12:14:06 -0700 Subject: [PATCH 51/60] statemanager: fix getCode logic for last chunk --- .../src/statefulVerkleStateManager.ts | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/packages/statemanager/src/statefulVerkleStateManager.ts b/packages/statemanager/src/statefulVerkleStateManager.ts index 41ac914c6a..80e645e84e 100644 --- a/packages/statemanager/src/statefulVerkleStateManager.ts +++ b/packages/statemanager/src/statefulVerkleStateManager.ts @@ -318,7 +318,7 @@ export class StatefulVerkleStateManager implements StateManagerInterface { chunkStems[0], chunkSuffixes.slice( 0, - codeChunks.length <= VERKLE_CODE_OFFSET ? codeChunks.length : VERKLE_CODE_OFFSET, + chunkSuffixes.length <= VERKLE_CODE_OFFSET ? chunkSuffixes.length : VERKLE_CODE_OFFSET, ), codeChunks.slice( 0, @@ -398,13 +398,16 @@ export class StatefulVerkleStateManager implements StateManagerInterface { for (let x = 0; x < chunks.length; x++) { if (chunks[x] === undefined) throw new Error(`expected code chunk at ID ${x}, got undefined`) + let lastChunkByteIndex = VERKLE_CODE_CHUNK_SIZE // Determine code ending byte (if we're on the last chunk) - let sliceEnd = 32 if (x === chunks.length - 1) { - // On the last chunk, the end of the slice is either codeSize (if only one chunk) or codeSize % chunkSize - sliceEnd = (x === 0 ? codeSize : codeSize % VERKLE_CODE_CHUNK_SIZE) + 1 + // On the last chunk, the slice either ends on a partial chunk (if codeSize doesn't exactly fit in full chunks), or a full chunk + lastChunkByteIndex = codeSize % VERKLE_CODE_CHUNK_SIZE || VERKLE_CODE_CHUNK_SIZE } - code.set(chunks[x]!.slice(1, sliceEnd), code.byteOffset + x * VERKLE_CODE_CHUNK_SIZE) + code.set( + chunks[x]!.slice(1, lastChunkByteIndex + 1), + code.byteOffset + x * VERKLE_CODE_CHUNK_SIZE, + ) } this._caches?.code?.put(address, code) @@ -598,10 +601,14 @@ export class StatefulVerkleStateManager implements StateManagerInterface { // we can only compare the actual code because to compare the first byte would // be very tricky and impossible in certain scenarios like when the previous code chunk // was not accessed and hence not even provided in the witness + // We are left-padding with two zeroes to get a 32-byte length, but these bytes should not be considered reliable return bytesToHex( - setLengthRight( - code.slice(codeOffset, codeOffset + VERKLE_CODE_CHUNK_SIZE), - VERKLE_CODE_CHUNK_SIZE, + setLengthLeft( + setLengthRight( + code.slice(codeOffset, codeOffset + VERKLE_CODE_CHUNK_SIZE), + VERKLE_CODE_CHUNK_SIZE, + ), + VERKLE_CODE_CHUNK_SIZE + 1, ), ) } @@ -645,7 +652,7 @@ export class StatefulVerkleStateManager implements StateManagerInterface { const { chunkKey } = accessedState accessedChunks.set(chunkKey, true) - const computedValue: PrefixedHexString | null | undefined = + let computedValue: PrefixedHexString | null | undefined = await this.getComputedValue(accessedState) if (computedValue === undefined) { this.DEBUG && @@ -670,7 +677,7 @@ export class StatefulVerkleStateManager implements StateManagerInterface { // if the access type is code, then we can't match the first byte because since the computed value // doesn't has the first byte for push data since previous chunk code itself might not be available if (accessedState.type === VerkleAccessedStateType.Code) { - // computedValue = computedValue !== null ? `0x${computedValue.slice(4)}` : null + computedValue = computedValue !== null ? `0x${computedValue.slice(4)}` : null canonicalValue = canonicalValue !== null ? `0x${canonicalValue.slice(4)}` : null } else if ( accessedState.type === VerkleAccessedStateType.Storage && From 2d3a02f4eeb4d4660a8dee911fb833117ca61ce9 Mon Sep 17 00:00:00 2001 From: Gabriel Rocheleau Date: Sat, 21 Dec 2024 13:14:49 -0700 Subject: [PATCH 52/60] evm: access witness withdrawals handling --- packages/vm/src/runBlock.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/vm/src/runBlock.ts b/packages/vm/src/runBlock.ts index 4e8101a1da..b819dc4b41 100644 --- a/packages/vm/src/runBlock.ts +++ b/packages/vm/src/runBlock.ts @@ -759,10 +759,10 @@ export async function rewardAccount( let account = await evm.stateManager.getAccount(address) if (account === undefined) { if (common.isActivatedEIP(6800) === true) { - if (evm.verkleAccessWitness === undefined) { + if (evm.systemVerkleAccessWitness === undefined) { throw Error(`verkleAccessWitness required if verkle (EIP-6800) is activated`) } - evm.verkleAccessWitness.touchAndChargeProofOfAbsence(address) + evm.systemVerkleAccessWitness.touchAndChargeProofOfAbsence(address) } account = new Account() } @@ -770,11 +770,11 @@ export async function rewardAccount( await evm.journal.putAccount(address, account) if (common.isActivatedEIP(6800) === true) { - if (evm.verkleAccessWitness === undefined) { + 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, }) } From 216650badf76c98dc5ad83f0e955daf9b8db5dee Mon Sep 17 00:00:00 2001 From: Gabriel Rocheleau Date: Sun, 22 Dec 2024 21:27:41 -0700 Subject: [PATCH 53/60] vm: don't include access witness if miner value is 0 --- packages/vm/src/runTx.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/vm/src/runTx.ts b/packages/vm/src/runTx.ts index 691233141a..e54953472e 100644 --- a/packages/vm/src/runTx.ts +++ b/packages/vm/src/runTx.ts @@ -676,7 +676,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`) } From 6449b5e6a84202da2d1e20472e1817452d0c3b57 Mon Sep 17 00:00:00 2001 From: Gabriel Rocheleau Date: Sun, 22 Dec 2024 22:13:53 -0700 Subject: [PATCH 54/60] evm: fix witness skipping for precompiles and system contracts --- packages/evm/src/opcodes/gas.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/evm/src/opcodes/gas.ts b/packages/evm/src/opcodes/gas.ts index d6af6993b0..400af20db0 100644 --- a/packages/evm/src/opcodes/gas.ts +++ b/packages/evm/src/opcodes/gas.ts @@ -173,7 +173,8 @@ export const dynamicGasHandlers: Map Date: Tue, 24 Dec 2024 15:13:24 -0500 Subject: [PATCH 55/60] Add miner basic data and codehash to system access witness --- packages/vm/src/runTx.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/packages/vm/src/runTx.ts b/packages/vm/src/runTx.ts index e54953472e..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) From cc351fd20fcc3adcbbe5f5b97d6165c7e3db65f4 Mon Sep 17 00:00:00 2001 From: Gabriel Rocheleau Date: Tue, 24 Dec 2024 13:56:42 -0700 Subject: [PATCH 56/60] evm: minor fixes --- packages/evm/src/opcodes/gas.ts | 10 ++-- packages/vm/src/runBlock.ts | 98 ++++++++++++++++----------------- 2 files changed, 54 insertions(+), 54 deletions(-) diff --git a/packages/evm/src/opcodes/gas.ts b/packages/evm/src/opcodes/gas.ts index 400af20db0..2b968f46af 100644 --- a/packages/evm/src/opcodes/gas.ts +++ b/packages/evm/src/opcodes/gas.ts @@ -597,7 +597,6 @@ export const dynamicGasHandlers: Map { let account = await evm.stateManager.getAccount(address) if (account === undefined) { - if (common.isActivatedEIP(6800) === true) { + if (common.isActivatedEIP(6800) === true && reward !== BIGINT_0) { if (evm.systemVerkleAccessWitness === undefined) { throw Error(`verkleAccessWitness required if verkle (EIP-6800) is activated`) } @@ -769,7 +769,7 @@ export async function rewardAccount( account.balance += reward await evm.journal.putAccount(address, account) - if (common.isActivatedEIP(6800) === true) { + if (common.isActivatedEIP(6800) === true && reward !== BIGINT_0) { if (evm.systemVerkleAccessWitness === undefined) { throw Error(`verkleAccessWitness required if verkle (EIP-6800) is activated`) } From ee00f50e89df47ddf7f20d00ac33b59f1e92365b Mon Sep 17 00:00:00 2001 From: acolytec3 <17355484+acolytec3@users.noreply.github.com> Date: Tue, 24 Dec 2024 15:58:54 -0500 Subject: [PATCH 57/60] fix rewardAccount to write account into trie --- packages/vm/src/runBlock.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/vm/src/runBlock.ts b/packages/vm/src/runBlock.ts index b819dc4b41..52eaa3435e 100644 --- a/packages/vm/src/runBlock.ts +++ b/packages/vm/src/runBlock.ts @@ -13,6 +13,8 @@ import { BIGINT_8, GWEI_TO_WEI, KECCAK256_RLP, + VERKLE_BASIC_DATA_LEAF_KEY, + VERKLE_CODE_HASH_LEAF_KEY, bigIntToAddressBytes, bigIntToBytes, bytesToHex, @@ -762,7 +764,16 @@ export async function rewardAccount( if (evm.systemVerkleAccessWitness === undefined) { throw Error(`verkleAccessWitness required if verkle (EIP-6800) is activated`) } - evm.systemVerkleAccessWitness.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() } From e2d2cb7603a53a0fa518c70c81c8a14e8dabfbe0 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Thu, 26 Dec 2024 22:06:58 +0100 Subject: [PATCH 58/60] evm/verkle: fix charging base gas for address accessing opcodes --- packages/evm/src/opcodes/EIP2929.ts | 4 ++++ 1 file changed, 4 insertions(+) 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) { From a1e1c4bf0da58ccf25c1f18b024132f9b9a22cdb Mon Sep 17 00:00:00 2001 From: acolytec3 <17355484+acolytec3@users.noreply.github.com> Date: Tue, 31 Dec 2024 06:12:31 -0500 Subject: [PATCH 59/60] Properly await putAccount --- packages/evm/src/evm.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/evm/src/evm.ts b/packages/evm/src/evm.ts index 5f20d8e3c6..44f75cc39f 100644 --- a/packages/evm/src/evm.ts +++ b/packages/evm/src/evm.ts @@ -1105,11 +1105,10 @@ export class EVM implements EVMInterface { } toAccount.balance = newBalance // putAccount as the nonce may have changed for contract creation - const result = this.journal.putAccount(message.to, toAccount) + await this.journal.putAccount(message.to, toAccount) if (this.DEBUG) { debug(`Added toAccount (${message.to}) balance (-> ${toAccount.balance})`) } - return result } /** From c8834e393592faa703d789c7e1924ed00136f50c Mon Sep 17 00:00:00 2001 From: Gabriel Rocheleau Date: Fri, 10 Jan 2025 19:14:14 -0700 Subject: [PATCH 60/60] evm: fix codehash handling in verkle access witness --- packages/evm/src/evm.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/evm/src/evm.ts b/packages/evm/src/evm.ts index 21e5201e2b..01caa3e1a3 100644 --- a/packages/evm/src/evm.ts +++ b/packages/evm/src/evm.ts @@ -467,7 +467,9 @@ export class EVM implements EVMInterface { } if (this.common.isActivatedEIP(6800)) { - const contractCreateAccessGas = message.accessWitness!.writeAccountBasicData(message.to) + const contractCreateAccessGas = + message.accessWitness!.writeAccountBasicData(message.to) + + message.accessWitness!.readAccountCodeHash(message.to) gasLimit -= contractCreateAccessGas if (gasLimit < BIGINT_0) { if (this.DEBUG) {