From aaa6b64e45bafd2002ce18a5a573dcc7b0dcfe12 Mon Sep 17 00:00:00 2001 From: Gabriel Rocheleau Date: Fri, 17 May 2024 08:41:20 -0400 Subject: [PATCH 01/13] client: remove initialVerkleStateRoot cli param --- packages/client/bin/cli.ts | 6 ------ packages/client/src/config.ts | 3 --- packages/client/src/execution/vmexecution.ts | 4 +--- packages/statemanager/src/statelessVerkleStateManager.ts | 3 --- 4 files changed, 1 insertion(+), 15 deletions(-) diff --git a/packages/client/bin/cli.ts b/packages/client/bin/cli.ts index aacff167b8..226426aaa5 100755 --- a/packages/client/bin/cli.ts +++ b/packages/client/bin/cli.ts @@ -449,12 +449,6 @@ const args: ClientOpts = yargs boolean: true, hidden: true, }) - .option('initialVerkleStateRoot', { - describe: - 'Provides an initial stateRoot to start the StatelessVerkleStateManager. This is required to bootstrap verkle witness proof verification, since they depend on the stateRoot of the parent block', - string: true, - coerce: (initialVerkleStateRoot: PrefixedHexString) => hexToBytes(initialVerkleStateRoot), - }) .option('useJsCrypto', { describe: 'Use pure Javascript cryptography functions', boolean: true, diff --git a/packages/client/src/config.ts b/packages/client/src/config.ts index 7d0386d0ae..959b89f929 100644 --- a/packages/client/src/config.ts +++ b/packages/client/src/config.ts @@ -338,7 +338,6 @@ export interface ConfigOptions { statelessVerkle?: boolean startExecution?: boolean ignoreStatelessInvalidExecs?: boolean - initialVerkleStateRoot?: Uint8Array /** * Enables Prometheus Metrics that can be collected for monitoring client health @@ -452,7 +451,6 @@ export class Config { public readonly statelessVerkle: boolean public readonly startExecution: boolean public readonly ignoreStatelessInvalidExecs: boolean - public readonly initialVerkleStateRoot: Uint8Array public synchronized: boolean public lastsyncronized?: boolean @@ -546,7 +544,6 @@ export class Config { this.ignoreStatelessInvalidExecs = options.ignoreStatelessInvalidExecs ?? false this.metrics = options.prometheusMetrics - this.initialVerkleStateRoot = options.initialVerkleStateRoot ?? new Uint8Array() // Start it off as synchronized if this is configured to mine or as single node this.synchronized = this.isSingleNode ?? this.mine diff --git a/packages/client/src/execution/vmexecution.ts b/packages/client/src/execution/vmexecution.ts index 2aad9d5ebf..9552bc9649 100644 --- a/packages/client/src/execution/vmexecution.ts +++ b/packages/client/src/execution/vmexecution.ts @@ -190,9 +190,7 @@ export class VMExecution extends Execution { return } this.config.logger.info(`Setting up verkleVM`) - const stateManager = await StatelessVerkleStateManager.create({ - initialStateRoot: this.config.initialVerkleStateRoot, - }) + const stateManager = await StatelessVerkleStateManager.create() this.verkleVM = await VM.create({ common: this.config.execCommon, blockchain: this.chain.blockchain, diff --git a/packages/statemanager/src/statelessVerkleStateManager.ts b/packages/statemanager/src/statelessVerkleStateManager.ts index 2b8d88b12f..d8df71c6ef 100644 --- a/packages/statemanager/src/statelessVerkleStateManager.ts +++ b/packages/statemanager/src/statelessVerkleStateManager.ts @@ -111,7 +111,6 @@ export interface StatelessVerkleStateManagerOpts { codeCacheOpts?: CacheOptions accesses?: AccessWitness verkleCrypto?: VerkleCrypto - initialStateRoot?: Uint8Array } const PUSH_OFFSET = 95 @@ -232,8 +231,6 @@ export class StatelessVerkleStateManager implements EVMStateManagerInterface { }) } - this._cachedStateRoot = opts.initialStateRoot - this.keccakFunction = opts.common?.customCrypto.keccak256 ?? keccak256 if (opts.verkleCrypto === undefined) { From a73f04621d1d5aa92e99f92fb201ca3344e6f5f6 Mon Sep 17 00:00:00 2001 From: Gabriel Rocheleau Date: Fri, 17 May 2024 09:09:23 -0400 Subject: [PATCH 02/13] block: add parentStateRoot field to executionWitness --- packages/block/src/block.ts | 2 ++ packages/block/src/from-beacon-payload.ts | 3 +++ packages/block/src/types.ts | 4 ++++ .../src/statelessVerkleStateManager.ts | 4 ++-- packages/verkle/src/util/crypto.ts | 14 +++----------- packages/vm/src/runBlock.ts | 6 +----- 6 files changed, 15 insertions(+), 18 deletions(-) diff --git a/packages/block/src/block.ts b/packages/block/src/block.ts index 3b914193a3..f481ae3966 100644 --- a/packages/block/src/block.ts +++ b/packages/block/src/block.ts @@ -544,6 +544,8 @@ export class Block { // undefined indicates that the executionWitness should be initialized with the default state if (this.common.isActivatedEIP(6800) && this.executionWitness === undefined) { this.executionWitness = { + // TODO: Evaluate how default parentStateRoot should be handled? + parentStateRoot: '0x', stateDiff: [], verkleProof: { commitmentsByPath: [], diff --git a/packages/block/src/from-beacon-payload.ts b/packages/block/src/from-beacon-payload.ts index 5de5da9f44..4fe0e47495 100644 --- a/packages/block/src/from-beacon-payload.ts +++ b/packages/block/src/from-beacon-payload.ts @@ -75,15 +75,18 @@ type VerkleStateDiffSnakeJson = { } type VerkleExecutionWitnessSnakeJson = { + parent_state_root: PrefixedHexString state_diff: VerkleStateDiffSnakeJson[] verkle_proof: VerkleProofSnakeJson } function parseExecutionWitnessFromSnakeJson({ + parent_state_root, state_diff, verkle_proof, }: VerkleExecutionWitnessSnakeJson): VerkleExecutionWitness { return { + parentStateRoot: parent_state_root, stateDiff: state_diff.map(({ stem, suffix_diffs }) => ({ stem, suffixDiffs: suffix_diffs.map(({ current_value, new_value, suffix }) => ({ diff --git a/packages/block/src/types.ts b/packages/block/src/types.ts index 332911d0af..4a9b84a79d 100644 --- a/packages/block/src/types.ts +++ b/packages/block/src/types.ts @@ -104,6 +104,10 @@ export interface VerkleStateDiff { * An object that provides the state and proof necessary for verkle stateless execution * */ export interface VerkleExecutionWitness { + /** + * The stateRoot of the parent block + */ + parentStateRoot: PrefixedHexString /** * An array of state diffs. * Each item corresponding to state accesses or state modifications of the block. diff --git a/packages/statemanager/src/statelessVerkleStateManager.ts b/packages/statemanager/src/statelessVerkleStateManager.ts index d8df71c6ef..7201889f1e 100644 --- a/packages/statemanager/src/statelessVerkleStateManager.ts +++ b/packages/statemanager/src/statelessVerkleStateManager.ts @@ -664,13 +664,13 @@ export class StatelessVerkleStateManager implements EVMStateManagerInterface { * @param {Uint8Array} stateRoot - The stateRoot to verify the executionWitness against * @returns {boolean} - Returns true if the executionWitness matches the provided stateRoot, otherwise false */ - verifyProof(stateRoot: Uint8Array): boolean { + verifyProof(): boolean { if (this._executionWitness === undefined) { debug('Missing executionWitness') return false } - return verifyProof(this.verkleCrypto, stateRoot, this._executionWitness) + return verifyProof(this.verkleCrypto, this._executionWitness) } // Verifies that the witness post-state matches the computed post-state diff --git a/packages/verkle/src/util/crypto.ts b/packages/verkle/src/util/crypto.ts index 5ba3310159..dc84a0c987 100644 --- a/packages/verkle/src/util/crypto.ts +++ b/packages/verkle/src/util/crypto.ts @@ -1,7 +1,6 @@ import { type Address, bigIntToBytes, - bytesToHex, int32ToBytes, setLengthLeft, setLengthRight, @@ -40,19 +39,12 @@ export function getStem( /** * Verifies that the executionWitness is valid for the given prestateRoot. * @param ffi The verkle ffi object from verkle-crypotography-wasm. - * @param prestateRoot The prestateRoot matching the executionWitness. * @param executionWitness The verkle execution witness. * @returns {boolean} Whether or not the executionWitness belongs to the prestateRoot. */ -export function verifyProof( - ffi: VerkleCrypto, - prestateRoot: Uint8Array, - executionWitness: VerkleExecutionWitness -): boolean { - return ffi.verifyExecutionWitnessPreState( - bytesToHex(prestateRoot), - JSON.stringify(executionWitness) - ) +export function verifyProof(ffi: VerkleCrypto, executionWitness: VerkleExecutionWitness): boolean { + const { parentStateRoot, ...parsedExecutionWitness } = executionWitness + return ffi.verifyExecutionWitnessPreState(parentStateRoot, JSON.stringify(parsedExecutionWitness)) } export const POINT_IDENTITY = new Uint8Array(0) diff --git a/packages/vm/src/runBlock.ts b/packages/vm/src/runBlock.ts index 346a7dd61c..3622a41864 100644 --- a/packages/vm/src/runBlock.ts +++ b/packages/vm/src/runBlock.ts @@ -133,10 +133,6 @@ export async function runBlock(this: VM, opts: RunBlockOpts): Promise Date: Fri, 17 May 2024 10:51:33 -0400 Subject: [PATCH 03/13] verkle: merkle to basic data leaf --- packages/evm/src/opcodes/gas.ts | 41 +++--------- packages/statemanager/src/accessWitness.ts | 40 +++-------- .../src/statelessVerkleStateManager.ts | 61 ++++++----------- packages/verkle/src/constants.ts | 24 +++++++ packages/verkle/src/index.ts | 1 + packages/verkle/src/types.ts | 24 ++----- packages/verkle/src/util/keys.ts | 67 ++++++++++++++----- 7 files changed, 124 insertions(+), 134 deletions(-) create mode 100644 packages/verkle/src/constants.ts diff --git a/packages/evm/src/opcodes/gas.ts b/packages/evm/src/opcodes/gas.ts index 0ed6688aea..74b92a0c6c 100644 --- a/packages/evm/src/opcodes/gas.ts +++ b/packages/evm/src/opcodes/gas.ts @@ -10,13 +10,7 @@ import { bigIntToBytes, setLengthLeft, } from '@ethereumjs/util' -import { - BALANCE_LEAF_KEY, - CODE_HASH_LEAF_KEY, - CODE_SIZE_LEAF_KEY, - VERSION_LEAF_KEY, - getTreeIndexesForStorageSlot, -} from '@ethereumjs/verkle' +import { BASIC_DATA_LEAF_KEY, getTreeIndexesForStorageSlot } from '@ethereumjs/verkle' import { ERROR } from '../exceptions.js' @@ -96,7 +90,7 @@ export const dynamicGasHandlers: Map BIGINT_0) { gas += runState.env.accessWitness!.touchAddressOnWriteAndComputeGas( contractAddress, 0, - BALANCE_LEAF_KEY + BASIC_DATA_LEAF_KEY ) } @@ -876,14 +855,14 @@ export const dynamicGasHandlers: Map BIGINT_0) { selfDestructToColdAccessGas += runState.env.accessWitness!.touchAddressOnWriteAndComputeGas( selfdestructToAddress, 0, - BALANCE_LEAF_KEY + BASIC_DATA_LEAF_KEY ) } diff --git a/packages/statemanager/src/accessWitness.ts b/packages/statemanager/src/accessWitness.ts index 916a922277..5d7ae1c224 100644 --- a/packages/statemanager/src/accessWitness.ts +++ b/packages/statemanager/src/accessWitness.ts @@ -1,14 +1,11 @@ import { BIGINT_0, bytesToBigInt, bytesToHex, hexToBytes, intToBytes } from '@ethereumjs/util' import { - BALANCE_LEAF_KEY, + BASIC_DATA_LEAF_KEY, CODE_HASH_LEAF_KEY, CODE_OFFSET, - CODE_SIZE_LEAF_KEY, HEADER_STORAGE_OFFSET, MAIN_STORAGE_OFFSET, - NONCE_LEAF_KEY, VERKLE_NODE_WIDTH, - VERSION_LEAF_KEY, getKey, getStem, getTreeIndicesForCodeChunk, @@ -94,11 +91,8 @@ export class AccessWitness { touchAndChargeProofOfAbsence(address: Address): bigint { let gas = BIGINT_0 - gas += this.touchAddressOnReadAndComputeGas(address, 0, VERSION_LEAF_KEY) - gas += this.touchAddressOnReadAndComputeGas(address, 0, BALANCE_LEAF_KEY) - gas += this.touchAddressOnReadAndComputeGas(address, 0, CODE_SIZE_LEAF_KEY) + gas += this.touchAddressOnReadAndComputeGas(address, 0, BASIC_DATA_LEAF_KEY) gas += this.touchAddressOnReadAndComputeGas(address, 0, CODE_HASH_LEAF_KEY) - gas += this.touchAddressOnReadAndComputeGas(address, 0, NONCE_LEAF_KEY) return gas } @@ -106,8 +100,7 @@ export class AccessWitness { touchAndChargeMessageCall(address: Address): bigint { let gas = BIGINT_0 - gas += this.touchAddressOnReadAndComputeGas(address, 0, VERSION_LEAF_KEY) - gas += this.touchAddressOnReadAndComputeGas(address, 0, CODE_SIZE_LEAF_KEY) + gas += this.touchAddressOnReadAndComputeGas(address, 0, BASIC_DATA_LEAF_KEY) return gas } @@ -115,8 +108,8 @@ export class AccessWitness { touchAndChargeValueTransfer(caller: Address, target: Address): bigint { let gas = BIGINT_0 - gas += this.touchAddressOnWriteAndComputeGas(caller, 0, BALANCE_LEAF_KEY) - gas += this.touchAddressOnWriteAndComputeGas(target, 0, BALANCE_LEAF_KEY) + gas += this.touchAddressOnWriteAndComputeGas(caller, 0, BASIC_DATA_LEAF_KEY) + gas += this.touchAddressOnWriteAndComputeGas(target, 0, BASIC_DATA_LEAF_KEY) return gas } @@ -124,8 +117,7 @@ export class AccessWitness { touchAndChargeContractCreateInit(address: Address): bigint { let gas = BIGINT_0 - gas += this.touchAddressOnWriteAndComputeGas(address, 0, VERSION_LEAF_KEY) - gas += this.touchAddressOnWriteAndComputeGas(address, 0, NONCE_LEAF_KEY) + gas += this.touchAddressOnWriteAndComputeGas(address, 0, BASIC_DATA_LEAF_KEY) return gas } @@ -133,11 +125,8 @@ export class AccessWitness { touchAndChargeContractCreateCompleted(address: Address): bigint { let gas = BIGINT_0 - gas += this.touchAddressOnWriteAndComputeGas(address, 0, VERSION_LEAF_KEY) - gas += this.touchAddressOnWriteAndComputeGas(address, 0, BALANCE_LEAF_KEY) - gas += this.touchAddressOnWriteAndComputeGas(address, 0, CODE_SIZE_LEAF_KEY) + gas += this.touchAddressOnWriteAndComputeGas(address, 0, BASIC_DATA_LEAF_KEY) gas += this.touchAddressOnWriteAndComputeGas(address, 0, CODE_HASH_LEAF_KEY) - gas += this.touchAddressOnWriteAndComputeGas(address, 0, NONCE_LEAF_KEY) return gas } @@ -145,28 +134,19 @@ export class AccessWitness { touchTxOriginAndComputeGas(origin: Address): bigint { let gas = BIGINT_0 - gas += this.touchAddressOnReadAndComputeGas(origin, 0, VERSION_LEAF_KEY) - gas += this.touchAddressOnReadAndComputeGas(origin, 0, CODE_SIZE_LEAF_KEY) + gas += this.touchAddressOnReadAndComputeGas(origin, 0, BASIC_DATA_LEAF_KEY) gas += this.touchAddressOnReadAndComputeGas(origin, 0, CODE_HASH_LEAF_KEY) - gas += this.touchAddressOnWriteAndComputeGas(origin, 0, NONCE_LEAF_KEY) - gas += this.touchAddressOnWriteAndComputeGas(origin, 0, BALANCE_LEAF_KEY) - return gas } touchTxTargetAndComputeGas(target: Address, { sendsValue }: { sendsValue?: boolean } = {}) { let gas = BIGINT_0 - gas += this.touchAddressOnReadAndComputeGas(target, 0, VERSION_LEAF_KEY) - gas += this.touchAddressOnReadAndComputeGas(target, 0, CODE_SIZE_LEAF_KEY) - gas += this.touchAddressOnReadAndComputeGas(target, 0, CODE_HASH_LEAF_KEY) - gas += this.touchAddressOnReadAndComputeGas(target, 0, NONCE_LEAF_KEY) - if (sendsValue === true) { - gas += this.touchAddressOnWriteAndComputeGas(target, 0, BALANCE_LEAF_KEY) + gas += this.touchAddressOnWriteAndComputeGas(target, 0, BASIC_DATA_LEAF_KEY) } else { - gas += this.touchAddressOnReadAndComputeGas(target, 0, BALANCE_LEAF_KEY) + gas += this.touchAddressOnReadAndComputeGas(target, 0, BASIC_DATA_LEAF_KEY) } return gas diff --git a/packages/statemanager/src/statelessVerkleStateManager.ts b/packages/statemanager/src/statelessVerkleStateManager.ts index 7201889f1e..bbfbab2fc7 100644 --- a/packages/statemanager/src/statelessVerkleStateManager.ts +++ b/packages/statemanager/src/statelessVerkleStateManager.ts @@ -15,6 +15,8 @@ import { } from '@ethereumjs/util' import { LeafType, + decodeLeafBasicData, + encodeLeafBasicData, getKey, getStem, getTreeKeyForCodeChunk, @@ -520,27 +522,16 @@ export class StatelessVerkleStateManager implements EVMStateManagerInterface { } const stem = getStem(this.verkleCrypto, address, 0) - const versionKey = getKey(stem, LeafType.Version) - const balanceKey = getKey(stem, LeafType.Balance) - const nonceKey = getKey(stem, LeafType.Nonce) + const basicDataKey = getKey(stem, LeafType.BasicData) const codeHashKey = getKey(stem, LeafType.CodeHash) - const codeSizeKey = getKey(stem, LeafType.CodeSize) - const versionRaw = this._state[bytesToHex(versionKey)] - const balanceRaw = this._state[bytesToHex(balanceKey)] - const nonceRaw = this._state[bytesToHex(nonceKey)] + const basicDataRaw = this._state[bytesToHex(basicDataKey)] const codeHashRaw = this._state[bytesToHex(codeHashKey)] - const codeSizeRaw = this._state[bytesToHex(codeSizeKey)] // check if the account didn't exist if any of the basic keys have null - if (versionRaw === null || balanceRaw === null || nonceRaw === null || codeHashRaw === null) { + if (basicDataRaw === null || codeHashRaw === null) { // check any of the other key shouldn't have string input available as this account didn't exist - if ( - typeof versionRaw === `string` || - typeof balanceRaw === 'string' || - typeof nonceRaw === 'string' || - typeof codeHashRaw === 'string' - ) { + if (typeof basicDataRaw === `string` || typeof codeHashRaw === 'string') { const errorMsg = `Invalid witness for a non existing address=${address} stem=${bytesToHex( stem )}` @@ -560,32 +551,23 @@ export class StatelessVerkleStateManager implements EVMStateManagerInterface { throw Error(errorMsg) } - if ( - versionRaw === undefined && - balanceRaw === undefined && - nonceRaw === undefined && - codeHashRaw === undefined && - codeSizeRaw === undefined - ) { + if (basicDataRaw === undefined && codeHashRaw === undefined) { const errorMsg = `No witness bundled for address=${address} stem=${bytesToHex(stem)}` debug(errorMsg) throw Error(errorMsg) } + const { version, balance, nonce, codeSize } = decodeLeafBasicData(hexToBytes(basicDataRaw)) + const account = Account.fromPartialAccountData({ - version: typeof versionRaw === 'string' ? bytesToInt32(hexToBytes(versionRaw), true) : null, - balance: typeof balanceRaw === 'string' ? bytesToBigInt(hexToBytes(balanceRaw), true) : null, - nonce: typeof nonceRaw === 'string' ? bytesToBigInt(hexToBytes(nonceRaw), true) : null, + version, + balance, + nonce, codeHash: typeof codeHashRaw === 'string' ? hexToBytes(codeHashRaw) : null, // if codeSizeRaw is null, it means account didnt exist or it was EOA either way codeSize is 0 // if codeSizeRaw is undefined, then we pass in null which in our context of partial account means // not specified - codeSize: - typeof codeSizeRaw === 'string' - ? bytesToInt32(hexToBytes(codeSizeRaw), true) - : codeSizeRaw === null - ? 0 - : null, + codeSize, storageRoot: null, }) @@ -607,16 +589,15 @@ export class StatelessVerkleStateManager implements EVMStateManagerInterface { if (this._accountCacheSettings.deactivate) { const stem = getStem(this.verkleCrypto, address, 0) - const balanceKey = getKey(stem, LeafType.Balance) - const nonceKey = getKey(stem, LeafType.Nonce) - const codeHashKey = getKey(stem, LeafType.CodeHash) - - const balanceBuf = setLengthRight(bigIntToBytes(account.balance, true), 32) - const nonceBuf = setLengthRight(bigIntToBytes(account.nonce, true), 32) + const basicDataKey = getKey(stem, LeafType.BasicData) + const basicDataBytes = encodeLeafBasicData({ + version: account.version, + balance: account.balance, + nonce: account.nonce, + codeSize: account.codeSize, + }) - this._state[bytesToHex(balanceKey)] = bytesToHex(balanceBuf) - this._state[bytesToHex(nonceKey)] = bytesToHex(nonceBuf) - this._state[bytesToHex(codeHashKey)] = bytesToHex(account.codeHash) + this._state[bytesToHex(basicDataKey)] = bytesToHex(basicDataBytes) } else { if (account !== undefined) { this._accountCache!.put(address, account, true) diff --git a/packages/verkle/src/constants.ts b/packages/verkle/src/constants.ts new file mode 100644 index 0000000000..98fc8d133d --- /dev/null +++ b/packages/verkle/src/constants.ts @@ -0,0 +1,24 @@ +import { intToBytes } from '@ethereumjs/util' + +export enum LeafType { + BasicData = 0, + CodeHash = 1, +} + +export const VERSION_OFFSET = 0 +export const NONCE_OFFSET = 4 +export const CODE_SIZE_OFFSET = 12 +export const BALANCE_OFFSET = 16 + +export const VERSION_BYTES_LENGTH = 1 +export const NONCE_BYTES_LENGTH = 8 +export const CODE_SIZE_BYTES_LENGTH = 4 +export const BALANCE_BYTES_LENGTH = 16 + +export const BASIC_DATA_LEAF_KEY = intToBytes(LeafType.BasicData) +export const CODE_HASH_LEAF_KEY = intToBytes(LeafType.CodeHash) + +export const HEADER_STORAGE_OFFSET = 64 +export const CODE_OFFSET = 128 +export const VERKLE_NODE_WIDTH = 256 +export const MAIN_STORAGE_OFFSET = BigInt(256) ** BigInt(31) diff --git a/packages/verkle/src/index.ts b/packages/verkle/src/index.ts index 38e111ce90..bf56186cde 100644 --- a/packages/verkle/src/index.ts +++ b/packages/verkle/src/index.ts @@ -1,3 +1,4 @@ +export * from './constants.js' export * from './db/index.js' export * from './node/index.js' export * from './types.js' diff --git a/packages/verkle/src/types.ts b/packages/verkle/src/types.ts index 81614ae5a0..cf9b4eafe3 100644 --- a/packages/verkle/src/types.ts +++ b/packages/verkle/src/types.ts @@ -1,4 +1,4 @@ -import { intToBytes, utf8ToBytes } from '@ethereumjs/util' +import { utf8ToBytes } from '@ethereumjs/util' import type { VerkleNode } from './node/index.js' import type { WalkController } from './util/walkController.js' @@ -124,21 +124,9 @@ export const ROOT_DB_KEY = utf8ToBytes('__root__') export type VerkleCrypto = VerkleFFI -export enum LeafType { - Version = 0, - Balance = 1, - Nonce = 2, - CodeHash = 3, - CodeSize = 4, +export type VerkleLeafBasicData = { + version: number + nonce: bigint + balance: bigint + codeSize: number } - -export const VERSION_LEAF_KEY = intToBytes(LeafType.Version) -export const BALANCE_LEAF_KEY = intToBytes(LeafType.Balance) -export const NONCE_LEAF_KEY = intToBytes(LeafType.Nonce) -export const CODE_HASH_LEAF_KEY = intToBytes(LeafType.CodeHash) -export const CODE_SIZE_LEAF_KEY = intToBytes(LeafType.CodeSize) - -export const HEADER_STORAGE_OFFSET = 64 -export const CODE_OFFSET = 128 -export const VERKLE_NODE_WIDTH = 256 -export const MAIN_STORAGE_OFFSET = BigInt(256) ** BigInt(31) diff --git a/packages/verkle/src/util/keys.ts b/packages/verkle/src/util/keys.ts index 5f2a3adb7f..06555fb9dc 100644 --- a/packages/verkle/src/util/keys.ts +++ b/packages/verkle/src/util/keys.ts @@ -1,23 +1,66 @@ -import { concatBytes, setLengthRight, toBytes } from '@ethereumjs/util' +import { + bigIntToBytes, + bytesToBigInt, + bytesToInt32, + concatBytes, + int32ToBytes, + setLengthRight, + toBytes, +} from '@ethereumjs/util' import { - BALANCE_LEAF_KEY, + BALANCE_BYTES_LENGTH, + BALANCE_OFFSET, + BASIC_DATA_LEAF_KEY, CODE_HASH_LEAF_KEY, CODE_OFFSET, - CODE_SIZE_LEAF_KEY, + CODE_SIZE_BYTES_LENGTH, + CODE_SIZE_OFFSET, HEADER_STORAGE_OFFSET, LeafType, MAIN_STORAGE_OFFSET, - NONCE_LEAF_KEY, + NONCE_BYTES_LENGTH, + NONCE_OFFSET, VERKLE_NODE_WIDTH, - VERSION_LEAF_KEY, -} from '../types.js' + VERSION_BYTES_LENGTH, +} from '../constants.js' import { getStem } from './crypto.js' -import type { VerkleCrypto } from '../types.js' +import type { VerkleCrypto, VerkleLeafBasicData } from '../types.js' import type { Address } from '@ethereumjs/util' +export function decodeLeafBasicData(encodedBasicData: Uint8Array): VerkleLeafBasicData { + const versionBytes = encodedBasicData.slice(0, VERSION_BYTES_LENGTH) + const nonceBytes = encodedBasicData.slice(NONCE_OFFSET, NONCE_OFFSET + NONCE_BYTES_LENGTH) + const codeSizeBytes = encodedBasicData.slice( + CODE_SIZE_OFFSET, + CODE_SIZE_OFFSET + CODE_SIZE_BYTES_LENGTH + ) + const balanceBytes = encodedBasicData.slice(BALANCE_OFFSET, BALANCE_OFFSET + BALANCE_BYTES_LENGTH) + + const version = bytesToInt32(versionBytes, true) + const nonce = bytesToBigInt(nonceBytes, true) + const codeSize = bytesToInt32(codeSizeBytes, true) + const balance = bytesToBigInt(balanceBytes, true) + + return { version, nonce, codeSize, balance } +} + +export function encodeLeafBasicData(basicData: VerkleLeafBasicData): Uint8Array { + const encodedVersion = setLengthRight(int32ToBytes(basicData.version, true), VERSION_BYTES_LENGTH) + const encodedNonce = setLengthRight(bigIntToBytes(basicData.nonce, true), NONCE_BYTES_LENGTH) + const encodedCodeSize = setLengthRight( + int32ToBytes(basicData.codeSize, true), + CODE_SIZE_BYTES_LENGTH + ) + const encodedBalance = setLengthRight( + bigIntToBytes(basicData.balance, true), + BALANCE_BYTES_LENGTH + ) + return concatBytes(encodedVersion, encodedNonce, encodedCodeSize, encodedBalance) +} + /** * @dev Returns the tree key for a given verkle tree stem, and sub index. * @dev Assumes that the verkle node width = 256 @@ -28,16 +71,10 @@ import type { Address } from '@ethereumjs/util' export const getKey = (stem: Uint8Array, leaf: LeafType | Uint8Array) => { switch (leaf) { - case LeafType.Version: - return concatBytes(stem, VERSION_LEAF_KEY) - case LeafType.Balance: - return concatBytes(stem, BALANCE_LEAF_KEY) - case LeafType.Nonce: - return concatBytes(stem, NONCE_LEAF_KEY) + case LeafType.BasicData: + return concatBytes(stem, BASIC_DATA_LEAF_KEY) case LeafType.CodeHash: return concatBytes(stem, CODE_HASH_LEAF_KEY) - case LeafType.CodeSize: - return concatBytes(stem, CODE_SIZE_LEAF_KEY) default: return concatBytes(stem, leaf) } From 1b6d696b8ed2dd85ec8af40c5fdc1cceeb5c7a7b Mon Sep 17 00:00:00 2001 From: Gabriel Rocheleau Date: Wed, 10 Jul 2024 20:16:10 -0400 Subject: [PATCH 04/13] verkle: address merge conflicts and refactors --- packages/evm/src/opcodes/gas.ts | 2 +- .../src/statelessVerkleStateManager.ts | 10 +-- packages/util/src/verkle.ts | 62 +++++++++++++++++++ packages/verkle/src/constants.ts | 24 ------- packages/verkle/src/index.ts | 1 - packages/verkle/src/node/util.ts | 51 +-------------- 6 files changed, 70 insertions(+), 80 deletions(-) delete mode 100644 packages/verkle/src/constants.ts diff --git a/packages/evm/src/opcodes/gas.ts b/packages/evm/src/opcodes/gas.ts index 0100d38bf0..ade64847b8 100644 --- a/packages/evm/src/opcodes/gas.ts +++ b/packages/evm/src/opcodes/gas.ts @@ -261,7 +261,7 @@ export const dynamicGasHandlers: Map { } return expandedValues } - -export function decodeLeafBasicData(encodedBasicData: Uint8Array): VerkleLeafBasicData { - const versionBytes = encodedBasicData.slice(0, VERSION_BYTES_LENGTH) - const nonceBytes = encodedBasicData.slice(NONCE_OFFSET, NONCE_OFFSET + NONCE_BYTES_LENGTH) - const codeSizeBytes = encodedBasicData.slice( - CODE_SIZE_OFFSET, - CODE_SIZE_OFFSET + CODE_SIZE_BYTES_LENGTH - ) - const balanceBytes = encodedBasicData.slice(BALANCE_OFFSET, BALANCE_OFFSET + BALANCE_BYTES_LENGTH) - - const version = bytesToInt32(versionBytes, true) - const nonce = bytesToBigInt(nonceBytes, true) - const codeSize = bytesToInt32(codeSizeBytes, true) - const balance = bytesToBigInt(balanceBytes, true) - - return { version, nonce, codeSize, balance } -} - -export function encodeLeafBasicData(basicData: VerkleLeafBasicData): Uint8Array { - const encodedVersion = setLengthRight(int32ToBytes(basicData.version, true), VERSION_BYTES_LENGTH) - const encodedNonce = setLengthRight(bigIntToBytes(basicData.nonce, true), NONCE_BYTES_LENGTH) - const encodedCodeSize = setLengthRight( - int32ToBytes(basicData.codeSize, true), - CODE_SIZE_BYTES_LENGTH - ) - const encodedBalance = setLengthRight( - bigIntToBytes(basicData.balance, true), - BALANCE_BYTES_LENGTH - ) - return concatBytes(encodedVersion, encodedNonce, encodedCodeSize, encodedBalance) -} From d787f40b63a76084fc6d08f6484a67cfa4e5560f Mon Sep 17 00:00:00 2001 From: Gabriel Rocheleau Date: Tue, 16 Jul 2024 14:57:24 -0400 Subject: [PATCH 05/13] verkle: use verkle code chunk size constant --- .../src/statelessVerkleStateManager.ts | 19 ++++++++++++------- packages/util/src/verkle.ts | 9 +++++---- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/packages/statemanager/src/statelessVerkleStateManager.ts b/packages/statemanager/src/statelessVerkleStateManager.ts index 7c2eaebf5f..9525d915fe 100644 --- a/packages/statemanager/src/statelessVerkleStateManager.ts +++ b/packages/statemanager/src/statelessVerkleStateManager.ts @@ -2,6 +2,7 @@ import { Account, KECCAK256_NULL, KECCAK256_NULL_S, + VERKLE_CODE_CHUNK_SIZE, VerkleLeafType, bigIntToBytes, bytesToHex, @@ -309,7 +310,7 @@ export class StatelessVerkleStateManager implements EVMStateManagerInterface { } async checkChunkWitnessPresent(address: Address, codeOffset: number) { - const chunkId = codeOffset / 31 + const chunkId = codeOffset / VERKLE_CODE_CHUNK_SIZE const chunkKey = bytesToHex( await getVerkleTreeKeyForCodeChunk(address, chunkId, this.verkleCrypto) ) @@ -379,9 +380,9 @@ export class StatelessVerkleStateManager implements EVMStateManagerInterface { // allocate the code and copy onto it from the available witness chunks const codeSize = account.codeSize // allocate enough to fit the last chunk - const accessedCode = new Uint8Array(codeSize + 31) + const accessedCode = new Uint8Array(codeSize + VERKLE_CODE_CHUNK_SIZE) - const chunks = Math.floor(codeSize / 31) + 1 + const chunks = Math.floor(codeSize / VERKLE_CODE_CHUNK_SIZE) + 1 for (let chunkId = 0; chunkId < chunks; chunkId++) { const chunkKey = bytesToHex( await getVerkleTreeKeyForCodeChunk(address, chunkId, this.verkleCrypto) @@ -393,7 +394,7 @@ export class StatelessVerkleStateManager implements EVMStateManagerInterface { throw Error(errorMsg) } - const codeOffset = chunkId * 31 + const codeOffset = chunkId * VERKLE_CODE_CHUNK_SIZE // if code chunk was accessed as per the provided witnesses copy it over if (codeChunk !== undefined) { // actual code starts from index 1 in chunk, 0th index is if there are any push data bytes @@ -402,7 +403,7 @@ export class StatelessVerkleStateManager implements EVMStateManagerInterface { } else { // else fill this unaccessed segment with invalid opcode since the evm execution shouldn't // end up here - accessedCode.fill(0xfe, codeOffset, 31) + accessedCode.fill(0xfe, codeOffset, VERKLE_CODE_CHUNK_SIZE) } } @@ -808,8 +809,12 @@ export class StatelessVerkleStateManager implements EVMStateManagerInterface { // 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 - const chunkSize = 31 - return bytesToHex(setLengthRight(code.slice(codeOffset, codeOffset + chunkSize), chunkSize)) + return bytesToHex( + setLengthRight( + code.slice(codeOffset, codeOffset + VERKLE_CODE_CHUNK_SIZE), + VERKLE_CODE_CHUNK_SIZE + ) + ) } case AccessedStateType.Storage: { diff --git a/packages/util/src/verkle.ts b/packages/util/src/verkle.ts index eeff673509..9f1ce2b569 100644 --- a/packages/util/src/verkle.ts +++ b/packages/util/src/verkle.ts @@ -146,10 +146,11 @@ export const VERKLE_BALANCE_BYTES_LENGTH = 16 export const VERKLE_BASIC_DATA_LEAF_KEY = intToBytes(VerkleLeafType.BasicData) export const VERKLE_CODE_HASH_LEAF_KEY = intToBytes(VerkleLeafType.CodeHash) +export const VERKLE_CODE_CHUNK_SIZE = 31 export const VERKLE_HEADER_STORAGE_OFFSET = 64 export const VERKLE_CODE_OFFSET = 128 export const VERKLE_NODE_WIDTH = 256 -export const VERKLE_MAIN_STORAGE_OFFSET = BigInt(256) ** BigInt(31) +export const VERKLE_MAIN_STORAGE_OFFSET = BigInt(256) ** BigInt(VERKLE_CODE_CHUNK_SIZE) /** * @dev Returns the tree key for a given verkle tree stem, and sub index. @@ -203,9 +204,9 @@ export const getVerkleTreeKeyForCodeChunk = async ( } export const chunkifyCode = (code: Uint8Array) => { - // Pad code to multiple of 31 bytes - if (code.length % 31 !== 0) { - const paddingLength = 31 - (code.length % 31) + // 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) } From c8dca62ae6119cbbc2b9283a429352eec7892d0f Mon Sep 17 00:00:00 2001 From: Gabriel Rocheleau Date: Tue, 16 Jul 2024 17:46:57 -0400 Subject: [PATCH 06/13] verkle: adjust util tests --- packages/util/test/verkle.spec.ts | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/util/test/verkle.spec.ts b/packages/util/test/verkle.spec.ts index 3a66f1244a..99d08a195c 100644 --- a/packages/util/test/verkle.spec.ts +++ b/packages/util/test/verkle.spec.ts @@ -46,29 +46,29 @@ describe('Verkle cryptographic helpers', () => { const prestateRoot = hexToBytes( '0x64e1a647f42e5c2e3c434531ccf529e1b3e93363a40db9fc8eec81f492123510' ) - const executionWitness = verkleBlockJSON.executionWitness as VerkleExecutionWitness - assert.isTrue(verifyVerkleProof(verkle, prestateRoot, executionWitness)) + const executionWitness = { + ...verkleBlockJSON.default.executionWitness, + parentStateRoot: bytesToHex(prestateRoot), + } as VerkleExecutionWitness + assert.isTrue(verifyVerkleProof(verkle, executionWitness)) }) it('verifyVerkleProof(): should return false for invalid verkle proofs', () => { // Random preStateRoot const prestateRoot = randomBytes(32) - const executionWitness = verkleBlockJSON.executionWitness as VerkleExecutionWitness + const executionWitness = { + ...verkleBlockJSON.default.executionWitness, + parentStateRoot: bytesToHex(prestateRoot), + } as VerkleExecutionWitness // Modify the proof to make it invalid - assert.isFalse(verifyVerkleProof(verkle, prestateRoot, executionWitness)) + assert.isFalse(verifyVerkleProof(verkle, executionWitness)) }) }) describe('should generate valid tree keys', () => { it('should generate valid keys for each VerkleLeafType', () => { const stem = hexToBytes('0x318dea512b6f3237a2d4763cf49bf26de3b617fb0cabe38a97807a5549df4d') - for (const leaf of [ - VerkleLeafType.Version, - VerkleLeafType.Balance, - VerkleLeafType.Nonce, - VerkleLeafType.CodeHash, - VerkleLeafType.CodeSize, - ]) { + for (const leaf of [VerkleLeafType.BasicData, VerkleLeafType.CodeHash]) { const key = getVerkleKey(stem, leaf) assert.equal(key.length, 32) assert.deepEqual(key, concatBytes(stem, intToBytes(leaf))) From 72f659f9bbcfaceb8814667478fd2c7a64c42a3c Mon Sep 17 00:00:00 2001 From: acolytec3 <17355484+acolytec3@users.noreply.github.com> Date: Fri, 30 Aug 2024 11:30:13 -0400 Subject: [PATCH 07/13] Fix build --- 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 a03bfaa9a7..346fee36f3 100644 --- a/packages/vm/src/runBlock.ts +++ b/packages/vm/src/runBlock.ts @@ -150,7 +150,7 @@ export async function runBlock(vm: VM, opts: RunBlockOpts): Promise Date: Fri, 30 Aug 2024 11:33:04 -0400 Subject: [PATCH 08/13] remove stateroot from verifyVerkleProof --- packages/common/src/interfaces.ts | 2 +- packages/statemanager/src/statelessVerkleStateManager.ts | 2 +- packages/vm/src/runBlock.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/common/src/interfaces.ts b/packages/common/src/interfaces.ts index b1832d7428..54e6ec96ef 100644 --- a/packages/common/src/interfaces.ts +++ b/packages/common/src/interfaces.ts @@ -167,7 +167,7 @@ export interface StateManagerInterface { executionWitness?: VerkleExecutionWitness | null, accessWitness?: AccessWitnessInterface, ): void - verifyVerkleProof?(stateRoot: Uint8Array): boolean + verifyVerkleProof?(): boolean verifyPostState?(): boolean checkChunkWitnessPresent?(contract: Address, programCounter: number): Promise getAppliedKey?(address: Uint8Array): Uint8Array // only for preimages diff --git a/packages/statemanager/src/statelessVerkleStateManager.ts b/packages/statemanager/src/statelessVerkleStateManager.ts index 9298052227..94c988103c 100644 --- a/packages/statemanager/src/statelessVerkleStateManager.ts +++ b/packages/statemanager/src/statelessVerkleStateManager.ts @@ -500,7 +500,7 @@ export class StatelessVerkleStateManager implements StateManagerInterface { * @param {Uint8Array} stateRoot - The stateRoot to verify the executionWitness against * @returns {boolean} - Returns true if the executionWitness matches the provided stateRoot, otherwise false */ - verifyVerkleProof(stateRoot: Uint8Array): boolean { + verifyVerkleProof(): boolean { if (this._executionWitness === undefined) { debug('Missing executionWitness') return false diff --git a/packages/vm/src/runBlock.ts b/packages/vm/src/runBlock.ts index 346fee36f3..6f009e055f 100644 --- a/packages/vm/src/runBlock.ts +++ b/packages/vm/src/runBlock.ts @@ -150,7 +150,7 @@ export async function runBlock(vm: VM, opts: RunBlockOpts): Promise Date: Sat, 31 Aug 2024 13:19:26 -0400 Subject: [PATCH 09/13] Add reserved bytes to encodeBasicData function --- packages/util/src/verkle.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/util/src/verkle.ts b/packages/util/src/verkle.ts index 9c0995848a..d00dff9ad3 100644 --- a/packages/util/src/verkle.ts +++ b/packages/util/src/verkle.ts @@ -251,6 +251,8 @@ export function encodeVerkleLeafBasicData(basicData: VerkleLeafBasicData): Uint8 int32ToBytes(basicData.version, true), VERKLE_VERSION_BYTES_LENGTH, ) + // Per EIP-6800, bytes 1-4 are reserved for future use + const reservedBytes = new Uint8Array([0, 0, 0]) const encodedNonce = setLengthRight( bigIntToBytes(basicData.nonce, true), VERKLE_NONCE_BYTES_LENGTH, @@ -263,5 +265,5 @@ export function encodeVerkleLeafBasicData(basicData: VerkleLeafBasicData): Uint8 bigIntToBytes(basicData.balance, true), VERKLE_BALANCE_BYTES_LENGTH, ) - return concatBytes(encodedVersion, encodedNonce, encodedCodeSize, encodedBalance) + return concatBytes(encodedVersion, reservedBytes, encodedNonce, encodedCodeSize, encodedBalance) } From 18b35b61758284c5388f313b55ed5a2905c7ab0e Mon Sep 17 00:00:00 2001 From: acolytec3 <17355484+acolytec3@users.noreply.github.com> Date: Mon, 2 Sep 2024 13:16:47 -0400 Subject: [PATCH 10/13] Merge commit + reserve bytes [no ci] --- packages/trie/README.md | 7 ++- packages/trie/benchmarks/engines/level.ts | 10 ++-- packages/trie/examples/customLevelDB.ts | 7 ++- packages/trie/scripts/view.ts | 20 +++++--- packages/trie/src/trie.ts | 11 ++--- packages/trie/src/util/asyncWalk.ts | 8 ++- packages/trie/test/official.spec.ts | 54 +++++++++++---------- packages/trie/test/proof/range.spec.ts | 24 ++++----- packages/trie/test/trie/secure.spec.ts | 30 +++++------- packages/trie/test/trie/trie.spec.ts | 34 +++---------- packages/trie/test/util/asyncWalk.spec.ts | 59 +++++++++++++---------- packages/vm/src/requests.ts | 56 +++++++++------------ packages/vm/test/tester/index.ts | 1 - 13 files changed, 153 insertions(+), 168 deletions(-) diff --git a/packages/trie/README.md b/packages/trie/README.md index d20cceb06e..f723c2b255 100644 --- a/packages/trie/README.md +++ b/packages/trie/README.md @@ -175,11 +175,10 @@ As an example, to leverage `LevelDB` for all operations then you should create a ```ts // ./examples/customLevelDB.ts#L127-L131 - const trie = new Trie({ db: new LevelDB(new Level('MY_TRIE_DB_LOCATION') as any) }) - console.log(trie.database().db) // LevelDB { ... -} -void main() +const trie = new Trie({ db: new LevelDB(new Level('MY_TRIE_DB_LOCATION')) }) +console.log(trie.database().db) // LevelDB { ... +void main() ``` #### Node Deletion (Pruning) diff --git a/packages/trie/benchmarks/engines/level.ts b/packages/trie/benchmarks/engines/level.ts index 3f80ac704b..16f3f814ab 100644 --- a/packages/trie/benchmarks/engines/level.ts +++ b/packages/trie/benchmarks/engines/level.ts @@ -41,7 +41,7 @@ const getEncodings = (opts: EncodingOpts = {}) => { */ export class LevelDB< TKey extends Uint8Array | string = Uint8Array | string, - TValue extends Uint8Array | string | DBObject = Uint8Array | string | DBObject + TValue extends Uint8Array | string | DBObject = Uint8Array | string | DBObject, > implements DB { _leveldb: AbstractLevel @@ -52,7 +52,7 @@ export class LevelDB< * @param leveldb - An abstract-leveldown compliant store */ constructor( - leveldb?: AbstractLevel + leveldb?: AbstractLevel, ) { this._leveldb = leveldb ?? new MemoryLevel() } @@ -75,7 +75,6 @@ export class LevelDB< throw error } } - // eslint-disable-next-line if (value instanceof Buffer) value = Uint8Array.from(value) return value as TValue } @@ -99,7 +98,10 @@ export class LevelDB< * @inheritDoc */ async batch(opStack: BatchDBOp[]): Promise { - const levelOps = [] + const levelOps: { + keyEncoding: string + valueEncoding: string + }[] = [] for (const op of opStack) { const encodings = getEncodings(op.opts) levelOps.push({ ...op, ...encodings }) diff --git a/packages/trie/examples/customLevelDB.ts b/packages/trie/examples/customLevelDB.ts index 0ed657cb71..dbe92d8de6 100644 --- a/packages/trie/examples/customLevelDB.ts +++ b/packages/trie/examples/customLevelDB.ts @@ -101,7 +101,10 @@ export class LevelDB< * @inheritDoc */ async batch(opStack: BatchDBOp[]): Promise { - const levelOps = [] + const levelOps: { + keyEncoding: string + valueEncoding: string + }[] = [] for (const op of opStack) { const encodings = getEncodings(op.opts) levelOps.push({ ...op, ...encodings }) @@ -124,7 +127,7 @@ export class LevelDB< } async function main() { - const trie = new Trie({ db: new LevelDB(new Level('MY_TRIE_DB_LOCATION') as any) }) + const trie = new Trie({ db: new LevelDB(new Level('MY_TRIE_DB_LOCATION')) }) console.log(trie.database().db) // LevelDB { ... } void main() diff --git a/packages/trie/scripts/view.ts b/packages/trie/scripts/view.ts index ee4e48d487..9ce644f428 100644 --- a/packages/trie/scripts/view.ts +++ b/packages/trie/scripts/view.ts @@ -1,5 +1,11 @@ import { debug as _debug } from 'debug' -import { bytesToHex, equalsBytes, hexToBytes, utf8ToBytes } from '@ethereumjs/util' +import { + bytesToHex, + equalsBytes, + hexToBytes, + PrefixedHexString, + utf8ToBytes, +} from '@ethereumjs/util' import { BranchNode, ExtensionNode, LeafNode } from '../node/index.js' import { Trie } from '../trie.js' @@ -52,15 +58,15 @@ function getNodeType(node: TrieNode): TNode { function logNode(trie: Trie, node: TrieNode, currentKey: number[]): void { delimiter(3) const type = getNodeType(node) - if (equalsBytes((trie as any).hash(node.serialize()), trie.root())) { + if (equalsBytes(trie.hash(node.serialize()), trie.root())) { debugN('rt').extend(type)( - `{ 0x${bytesToHex((trie as any).hash(node.serialize())).slice( + `{ 0x${bytesToHex(trie.hash(node.serialize())).slice( 0, 12, )}... } ---- \uD83D\uDCA5 \u211B \u2134 \u2134 \u0164 \u0147 \u2134 \u0221 \u2211 \u2737`, ) } else { - debugN(type)(`{ 0x${bytesToHex((trie as any).hash(node.serialize())).slice(0, 12)}... } ----`) + debugN(type)(`{ 0x${bytesToHex(trie.hash(node.serialize())).slice(0, 12)}... } ----`) } debugT.extend('Walking')(`from [${currentKey}]`) if ('_nibbles' in node) { @@ -106,8 +112,10 @@ export const view = async (testName: string, inputs: any[], root: string) => { testKeys.set(bytesToHex(input[0]), input[1]) testStrings.set(bytesToHex(input[0]), stringPair) for await (const [key, _val] of testKeys.entries()) { - const retrieved = await trie.get(hexToBytes(key)) - debugT.extend('get')(`[${key}]: ${retrieved && equalsBytes(retrieved, hexToBytes(key))}`) + const retrieved = await trie.get(hexToBytes(key as PrefixedHexString)) + debugT.extend('get')( + `[${key}]: ${retrieved && equalsBytes(retrieved, hexToBytes(key as PrefixedHexString))}`, + ) } } debugT(bytesToHex(trie.root()), expect) diff --git a/packages/trie/src/trie.ts b/packages/trie/src/trie.ts index 27558d341c..4ab3ef7fb9 100644 --- a/packages/trie/src/trie.ts +++ b/packages/trie/src/trie.ts @@ -403,13 +403,11 @@ export class Trie { } } const startingNode = partialPath.stack[partialPath.stack.length - 1] - const start = startingNode !== undefined ? this.hash(startingNode?.serialize()) : this.root() + const start = startingNode !== undefined ? this.hash(startingNode.serialize()) : this.root() try { this.DEBUG && this.debug( - `Walking trie from ${startingNode === undefined ? 'ROOT' : 'NODE'}: ${bytesToHex( - start as Uint8Array, - )}`, + `Walking trie from ${startingNode === undefined ? 'ROOT' : 'NODE'}: ${bytesToHex(start)}`, ['FIND_PATH'], ) await this.walkTrie(start, onFound) @@ -651,8 +649,9 @@ export class Trie { if (branchNode instanceof BranchNode) { // create an extension node // branch->extension->branch - // @ts-ignore Why does this work, and why are we doing this? See issue https://github.com/ethereumjs/ethereumjs-monorepo/issues/3620 - const extensionNode = new ExtensionNode([branchKey], null) + // We push an extension value with a temporarily empty value to the stack. + // It will be replaced later on with the correct value in saveStack + const extensionNode = new ExtensionNode([branchKey], new Uint8Array()) stack.push(extensionNode) key.push(branchKey) } else { diff --git a/packages/trie/src/util/asyncWalk.ts b/packages/trie/src/util/asyncWalk.ts index 28e57586eb..c364c5537e 100644 --- a/packages/trie/src/util/asyncWalk.ts +++ b/packages/trie/src/util/asyncWalk.ts @@ -1,7 +1,5 @@ import { RLP } from '@ethereumjs/rlp' -import { equalsBytes } from '@ethereumjs/util' -// TODO: replace with bytesToHex from @ethereumjs/util -import { toHex } from 'ethereum-cryptography/utils.js' // eslint-disable-line +import { bytesToHex, equalsBytes } from '@ethereumjs/util' import { BranchNode } from '../node/branch.js' import { ExtensionNode } from '../node/extension.js' @@ -36,10 +34,10 @@ export async function* _walkTrie( } try { const node = await this.lookupNode(nodeHash) - if (node === undefined || visited.has(toHex(this.hash(node!.serialize())))) { + if (node === undefined || visited.has(bytesToHex(this.hash(node!.serialize())))) { return } - visited.add(toHex(this.hash(node!.serialize()))) + visited.add(bytesToHex(this.hash(node!.serialize()))) await onFound(node!, currentKey) if (await filter(node!, currentKey)) { yield { node: node!, currentKey } diff --git a/packages/trie/test/official.spec.ts b/packages/trie/test/official.spec.ts index a8581d4108..5431b4ac2a 100644 --- a/packages/trie/test/official.spec.ts +++ b/packages/trie/test/official.spec.ts @@ -8,21 +8,22 @@ import trieTests from './fixtures/trietest.json' describe('official tests', () => { it('should work', async () => { - const testNames = Object.keys(trieTests.tests) + const testNames = Object.keys(trieTests.tests) as (keyof typeof trieTests.tests)[] let trie = new Trie() for (const testName of testNames) { - const inputs = (trieTests as any).tests[testName].in - const expect = (trieTests as any).tests[testName].root + const inputs = trieTests.tests[testName].in + const expect = trieTests.tests[testName].root for (const input of inputs) { - for (let i = 0; i < 2; i++) { - if (typeof input[i] === 'string' && input[i].slice(0, 2) === '0x') { - input[i] = hexToBytes(input[i]) - } else if (typeof input[i] === 'string') { - input[i] = utf8ToBytes(input[i]) + const processedInput = input.map((item) => { + if (item === null) { + return item } - await trie.put(input[0], input[1]) - } + + return isHexString(item) ? hexToBytes(item) : utf8ToBytes(item) + }) as [Uint8Array, Uint8Array | null] + + await trie.put(processedInput[0], processedInput[1]) } assert.equal(bytesToHex(trie.root()), expect) trie = new Trie() @@ -32,28 +33,31 @@ describe('official tests', () => { describe('official tests any order', async () => { it('should work', async () => { - const testNames = Object.keys(trieAnyOrderTests.tests) + const testNames = Object.keys( + trieAnyOrderTests.tests, + ) as (keyof typeof trieAnyOrderTests.tests)[] let trie = new Trie() for (const testName of testNames) { - const test = (trieAnyOrderTests.tests as any)[testName] + const test = trieAnyOrderTests.tests[testName] const keys = Object.keys(test.in) - let key: any - for (key of keys) { - let val = test.in[key] - - if (typeof key === 'string' && isHexString(key)) { - key = hexToBytes(key) - } else if (typeof key === 'string') { - key = utf8ToBytes(key) + for (const stringKey of keys) { + const stringValue: string = test.in[stringKey as keyof typeof test.in] + let key: Uint8Array + let value: Uint8Array + + if (isHexString(stringKey)) { + key = hexToBytes(stringKey) + } else { + key = utf8ToBytes(stringKey) } - if (typeof val === 'string' && isHexString(val)) { - val = hexToBytes(val) - } else if (typeof val === 'string') { - val = utf8ToBytes(val) + if (isHexString(stringValue)) { + value = hexToBytes(stringValue) + } else { + value = utf8ToBytes(stringValue) } - await trie.put(key, val) + await trie.put(key, value) } assert.equal(bytesToHex(trie.root()), test.root) trie = new Trie() diff --git a/packages/trie/test/proof/range.spec.ts b/packages/trie/test/proof/range.spec.ts index ba6fde5d9c..94883f0b10 100644 --- a/packages/trie/test/proof/range.spec.ts +++ b/packages/trie/test/proof/range.spec.ts @@ -160,16 +160,16 @@ describe('simple merkle range proofs generation and verification', () => { try { await verify(trie, entries, start + 5, end, decreasedStartKey) assert.fail() - } catch (err) { - // ignore .. + } catch { + // Ignore } assert.equal(await verify(trie, entries, start, end, startKey, increasedEndKey), true) try { await verify(trie, entries, start, end + 5, startKey, increasedEndKey) assert.fail() - } catch (err) { - // ignore .. + } catch { + // Ignore } }) @@ -269,8 +269,8 @@ describe('simple merkle range proofs generation and verification', () => { try { await cb(trie, entries) result = true - } catch (err) { - // ignore + } catch { + // Ignore } assert.isFalse(result) } @@ -352,8 +352,8 @@ describe('simple merkle range proofs generation and verification', () => { targetRange.map(([, val]) => val), ) result = true - } catch (err) { - // ignore + } catch { + // Ignore } assert.isFalse(result) }) @@ -372,8 +372,8 @@ describe('simple merkle range proofs generation and verification', () => { try { await verify(trie, entries, start, end, decreasedStartKey, decreasedEndKey) result = true - } catch (err) { - // ignore + } catch { + // Ignore } assert.isFalse(result) @@ -384,8 +384,8 @@ describe('simple merkle range proofs generation and verification', () => { try { await verify(trie, entries, start, end, increasedStartKey, increasedEndKey) result = true - } catch (err) { - // ignore + } catch { + // Ignore } assert.isFalse(result) }) diff --git a/packages/trie/test/trie/secure.spec.ts b/packages/trie/test/trie/secure.spec.ts index 228f4d7b87..56af877cca 100644 --- a/packages/trie/test/trie/secure.spec.ts +++ b/packages/trie/test/trie/secure.spec.ts @@ -34,16 +34,16 @@ describe('SecureTrie', () => { it('copy trie (new key prefix / default 0 size cache)', async () => { const keyPrefix = hexToBytes('0x1234') const t = trie.shallowCopy(true, { keyPrefix }) - assert.ok(equalsBytes(t['_opts']['keyPrefix'] as Uint8Array, keyPrefix)) - assert.equal(t['_opts']['cacheSize'] as number, 0) - assert.equal(trie['_opts']['cacheSize'] as number, 0) + assert.ok(equalsBytes(t['_opts']['keyPrefix']!, keyPrefix)) + assert.equal(t['_opts']['cacheSize'], 0) + assert.equal(trie['_opts']['cacheSize'], 0) }) it('copy trie (new cache size)', async () => { const cacheSize = 1000 const t = trie.shallowCopy(true, { cacheSize }) - assert.equal(t['_opts']['cacheSize'] as number, cacheSize) - assert.equal(trie['_opts']['cacheSize'] as number, 0) + assert.equal(t['_opts']['cacheSize'], cacheSize) + assert.equal(trie['_opts']['cacheSize'], 0) }) }) @@ -79,10 +79,7 @@ describe('secure tests', () => { it('empty values', async () => { for (const row of secureTrieTests.tests.emptyValues.in) { - const val = - row[1] !== undefined && row[1] !== null - ? utf8ToBytes(row[1]) - : (null as unknown as Uint8Array) + const val = row[1] !== undefined && row[1] !== null ? utf8ToBytes(row[1]) : null await trie.put(utf8ToBytes(row[0]!), val) } assert.equal(bytesToHex(trie.root()), secureTrieTests.tests.emptyValues.root) @@ -91,27 +88,22 @@ describe('secure tests', () => { it('branchingTests', async () => { trie = new Trie({ useKeyHashing: true, db: new MapDB() }) for (const row of secureTrieTests.tests.branchingTests.in) { - const val = - row[1] !== undefined && row[1] !== null - ? utf8ToBytes(row[1]) - : (null as unknown as Uint8Array) + const val = row[1] !== undefined && row[1] !== null ? utf8ToBytes(row[1]) : null await trie.put(utf8ToBytes(row[0]!), val) } assert.equal(bytesToHex(trie.root()), secureTrieTests.tests.branchingTests.root) }) - /** - TODO: Fix this test it('jeff', async () => { for (const row of secureTrieTests.tests.jeff.in) { - let val = row[1] + let val: string | null | Uint8Array = row[1] if (val !== undefined && val !== null) { - val = hexToBytes(row[1].slice(2)) + val = hexToBytes(`0x${row[1]!.slice(2)}`) } - await trie.put(hexToBytes(row[0].slice(2)), val) + await trie.put(hexToBytes(`0x${row[0]!.slice(2)}`), val) } assert.equal(bytesToHex(trie.root()), secureTrieTests.tests.jeff.root) - })*/ + }) it('put fails if the key is the ROOT_DB_KEY', async () => { const trie = new Trie({ useKeyHashing: true, db: new MapDB(), useRootPersistence: true }) diff --git a/packages/trie/test/trie/trie.spec.ts b/packages/trie/test/trie/trie.spec.ts index b74d13d717..f719b0195f 100644 --- a/packages/trie/test/trie/trie.spec.ts +++ b/packages/trie/test/trie/trie.spec.ts @@ -45,42 +45,24 @@ for (const { constructor, defaults, title } of [ describe(`${title} (Persistence)`, () => { it('creates an instance via createTrie and defaults to `false` with a database', async () => { - // TODO: check this test - assert.isUndefined( - ((await createTrie({ ...defaults, db: new MapDB() })) as any)._useRootPersistence, + assert.isFalse( + (await createTrie({ ...defaults, db: new MapDB() }))['_opts'].useRootPersistence, ) }) - it('creates an instance via createTrie and respects the `useRootPersistence` option with a database', async () => { - // TODO: check this test - assert.isUndefined( - ( - (await createTrie({ - ...defaults, - db: new MapDB(), - useRootPersistence: false, - })) as any - )._useRootPersistence, - ) + it('creates an instance via createTrie and defaults to `false` without a database', async () => { + assert.isFalse((await createTrie({ ...defaults }))['_opts'].useRootPersistence) }) it('creates an instance via createTrie and respects the `useRootPersistence` option with a database', async () => { - // TODO: check this test - assert.isUndefined( + assert.isFalse( ( - (await createTrie({ + await createTrie({ ...defaults, db: new MapDB(), useRootPersistence: false, - })) as any - )._useRootPersistence, - ) - }) - - it('creates an instance via createTrie and defaults to `false` without a database', async () => { - // TODO: check this test - assert.isUndefined( - ((await createTrie({ ...defaults, db: new MapDB() })) as any)._useRootPersistence, + }) + )['_opts'].useRootPersistence, ) }) diff --git a/packages/trie/test/util/asyncWalk.spec.ts b/packages/trie/test/util/asyncWalk.spec.ts index 7b875625bf..83ae68bac7 100644 --- a/packages/trie/test/util/asyncWalk.spec.ts +++ b/packages/trie/test/util/asyncWalk.spec.ts @@ -1,4 +1,4 @@ -import { bytesToHex, equalsBytes, hexToBytes, utf8ToBytes } from '@ethereumjs/util' +import { bytesToHex, equalsBytes, hexToBytes, isHexString, utf8ToBytes } from '@ethereumjs/util' import { assert, describe, it } from 'vitest' import { @@ -15,27 +15,28 @@ import trieTests from '../fixtures/trietest.json' import type { PrefixedHexString } from '@ethereumjs/util' describe('walk the tries from official tests', async () => { - const testNames = Object.keys(trieTests.tests) + const testNames = Object.keys(trieTests.tests) as (keyof typeof trieTests.tests)[] for await (const testName of testNames) { const trie = new Trie() describe(testName, async () => { - const inputs = (trieTests as any).tests[testName].in - const expect = (trieTests as any).tests[testName].root + const inputs = trieTests.tests[testName].in + const expect = trieTests.tests[testName].root const testKeys: Map = new Map() const testStrings: Map = new Map() for await (const [idx, input] of inputs.entries()) { - const stringPair: [string, string] = [inputs[idx][0], inputs[idx][1] ?? 'null'] + const stringPair: [string, string] = [inputs[idx][0]!, inputs[idx][1] ?? 'null'] describe(`put: ${stringPair}`, async () => { - for (let i = 0; i < 2; i++) { - if (typeof input[i] === 'string' && input[i].slice(0, 2) === '0x') { - input[i] = hexToBytes(input[i]) - } else if (typeof input[i] === 'string') { - input[i] = utf8ToBytes(input[i]) + const processedInput = input.map((item) => { + if (item === null) { + return null } - } + + return isHexString(item) ? hexToBytes(item) : utf8ToBytes(item) + }) as [Uint8Array, Uint8Array | null] + try { - await trie.put(input[0], input[1]) + await trie.put(processedInput[0], processedInput[1]) assert(true) } catch (e) { assert(false, (e as any).message) @@ -43,8 +44,8 @@ describe('walk the tries from official tests', async () => { trie.checkpoint() await trie.commit() trie.flushCheckpoints() - testKeys.set(bytesToHex(input[0]), input[1]) - testStrings.set(bytesToHex(input[0]), stringPair) + testKeys.set(bytesToHex(processedInput[0]), processedInput[1]) + testStrings.set(bytesToHex(processedInput[0]), stringPair) describe(`should get all keys`, async () => { for await (const [key, val] of testKeys.entries()) { const retrieved = await trie.get(hexToBytes(key)) @@ -64,26 +65,28 @@ describe('walk the tries from official tests', async () => { describe('walk a sparse trie', async () => { const trie = new Trie() - const inputs = (trieTests as any).tests.jeff.in - const expect = (trieTests as any).tests.jeff.root + const inputs = trieTests.tests.jeff.in + const expect = trieTests.tests.jeff.root // Build a Trie for await (const input of inputs) { - for (let i = 0; i < 2; i++) { - if (typeof input[i] === 'string' && input[i].slice(0, 2) === '0x') { - input[i] = hexToBytes(input[i]) - } else if (typeof input[i] === 'string') { - input[i] = utf8ToBytes(input[i]) + const processedInput = input.map((item) => { + if (item === null) { + return item } - } - await trie.put(input[0], input[1]) + + return isHexString(item) ? hexToBytes(item) : utf8ToBytes(item) + }) as [Uint8Array, Uint8Array | null] + + await trie.put(processedInput[0], processedInput[1]) } // Check the root it(`should have root ${expect}`, async () => { assert.equal(bytesToHex(trie.root()), expect) }) // Generate a proof for inputs[0] - const proofKey = inputs[0][0] + const rawProofKey = inputs[0][0] as string + const proofKey = isHexString(rawProofKey) ? hexToBytes(rawProofKey) : utf8ToBytes(rawProofKey) const proof = await createMerkleProof(trie, proofKey) assert.ok(await verifyTrieProof(proofKey, proof)) @@ -102,7 +105,11 @@ describe('walk a sparse trie', async () => { // The only leaf node should be leaf from the proof const fullKeyNibbles = [...currentKey, ...node._nibbles] assert.deepEqual(fullKeyNibbles, bytesToNibbles(proofKey)) - assert.deepEqual(node.value(), inputs[0][1]) + const rawNodeValue = inputs[0][1] as string + const nodeValue = isHexString(rawNodeValue) + ? hexToBytes(rawNodeValue) + : utf8ToBytes(rawNodeValue) + assert.deepEqual(node.value(), nodeValue) } // Count the nodes...nodes from the proof should be only nodes in the trie found++ @@ -112,7 +119,7 @@ describe('walk a sparse trie', async () => { // Walk the same sparse trie with WalkController try { - await fromProof.walkTrie(fromProof.root(), async (noderef, node, key, wc) => { + await fromProof.walkTrie(fromProof.root(), async (_, node, __, wc) => { wc.allChildren(node!) }) assert.fail('Will throw when it meets a missing node in a sparse trie') diff --git a/packages/vm/src/requests.ts b/packages/vm/src/requests.ts index 781ac01575..54230e18bc 100644 --- a/packages/vm/src/requests.ts +++ b/packages/vm/src/requests.ts @@ -66,18 +66,15 @@ const accumulateEIP7002Requests = async ( ) const withdrawalsAddress = createAddressFromString(bytesToHex(addressBytes)) - const code = await vm.stateManager.getCode(withdrawalsAddress) - - if (code.length === 0) { - throw new Error( - 'Attempt to accumulate EIP-7002 requests failed: the contract does not exist. Ensure the deployment tx has been run, or that the required contract code is stored', - ) - } - const systemAddressBytes = bigIntToAddressBytes(vm.common.param('systemAddress')) const systemAddress = createAddressFromString(bytesToHex(systemAddressBytes)) + const systemAccount = await vm.stateManager.getAccount(systemAddress) + + const originalAccount = await vm.stateManager.getAccount(withdrawalsAddress) - const originalAccount = await vm.stateManager.getAccount(systemAddress) + if (originalAccount === undefined) { + return + } const results = await vm.evm.runCall({ caller: systemAddress, @@ -85,6 +82,12 @@ const accumulateEIP7002Requests = async ( to: withdrawalsAddress, }) + if (systemAccount === undefined) { + await vm.stateManager.deleteAccount(systemAddress) + } else { + await vm.stateManager.putAccount(systemAddress, systemAccount) + } + const resultsBytes = results.execResult.returnValue if (resultsBytes.length > 0) { // Each request is 76 bytes @@ -96,13 +99,6 @@ const accumulateEIP7002Requests = async ( requests.push(createWithdrawalRequest({ sourceAddress, validatorPubkey, amount })) } } - - if (originalAccount === undefined) { - await vm.stateManager.deleteAccount(systemAddress) - } else { - // Restore the original account (the `runCall` updates the nonce) - await vm.stateManager.putAccount(systemAddress, originalAccount) - } } const accumulateEIP7251Requests = async ( @@ -116,18 +112,15 @@ const accumulateEIP7251Requests = async ( ) const consolidationsAddress = createAddressFromString(bytesToHex(addressBytes)) - const code = await vm.stateManager.getCode(consolidationsAddress) - - if (code.length === 0) { - throw new Error( - 'Attempt to accumulate EIP-7251 requests failed: the contract does not exist. Ensure the deployment tx has been run, or that the required contract code is stored', - ) - } - const systemAddressBytes = bigIntToAddressBytes(vm.common.param('systemAddress')) const systemAddress = createAddressFromString(bytesToHex(systemAddressBytes)) + const systemAccount = await vm.stateManager.getAccount(systemAddress) + + const originalAccount = await vm.stateManager.getAccount(consolidationsAddress) - const originalAccount = await vm.stateManager.getAccount(systemAddress) + if (originalAccount === undefined) { + return + } const results = await vm.evm.runCall({ caller: systemAddress, @@ -135,6 +128,12 @@ const accumulateEIP7251Requests = async ( to: consolidationsAddress, }) + if (systemAccount === undefined) { + await vm.stateManager.deleteAccount(systemAddress) + } else { + await vm.stateManager.putAccount(systemAddress, systemAccount) + } + const resultsBytes = results.execResult.returnValue if (resultsBytes.length > 0) { // Each request is 116 bytes @@ -146,13 +145,6 @@ const accumulateEIP7251Requests = async ( requests.push(createConsolidationRequest({ sourceAddress, sourcePubkey, targetPubkey })) } } - - if (originalAccount === undefined) { - await vm.stateManager.deleteAccount(systemAddress) - } else { - // Restore the original account (the `runCall` updates the nonce) - await vm.stateManager.putAccount(systemAddress, originalAccount) - } } const accumulateDeposits = async ( diff --git a/packages/vm/test/tester/index.ts b/packages/vm/test/tester/index.ts index 13061b2e60..20479a2fb7 100755 --- a/packages/vm/test/tester/index.ts +++ b/packages/vm/test/tester/index.ts @@ -52,7 +52,6 @@ import type { EVMBLSInterface, EVMBN254Interface } from '@ethereumjs/evm' * --profile If this flag is passed, the state/blockchain tests will profile */ -//@ts-expect-error Typescript thinks there isn't a default export on minimist but there is const argv = minimist.default(process.argv.slice(2)) async function runTests() { From 90da374a9b9f2e008548738c88a32be422c0f86e Mon Sep 17 00:00:00 2001 From: acolytec3 <17355484+acolytec3@users.noreply.github.com> Date: Mon, 2 Sep 2024 13:18:49 -0400 Subject: [PATCH 11/13] match EIP (bigEndian encoding) [no ci] --- packages/util/src/verkle.ts | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/packages/util/src/verkle.ts b/packages/util/src/verkle.ts index d00dff9ad3..28392d68cd 100644 --- a/packages/util/src/verkle.ts +++ b/packages/util/src/verkle.ts @@ -247,22 +247,16 @@ export function decodeVerkleLeafBasicData(encodedBasicData: Uint8Array): VerkleL } export function encodeVerkleLeafBasicData(basicData: VerkleLeafBasicData): Uint8Array { - const encodedVersion = setLengthRight( - int32ToBytes(basicData.version, true), - VERKLE_VERSION_BYTES_LENGTH, - ) + const encodedVersion = setLengthLeft(int32ToBytes(basicData.version), VERKLE_VERSION_BYTES_LENGTH) // Per EIP-6800, bytes 1-4 are reserved for future use const reservedBytes = new Uint8Array([0, 0, 0]) - const encodedNonce = setLengthRight( - bigIntToBytes(basicData.nonce, true), - VERKLE_NONCE_BYTES_LENGTH, - ) - const encodedCodeSize = setLengthRight( - int32ToBytes(basicData.codeSize, true), + const encodedNonce = setLengthLeft(bigIntToBytes(basicData.nonce), VERKLE_NONCE_BYTES_LENGTH) + const encodedCodeSize = setLengthLeft( + int32ToBytes(basicData.codeSize), VERKLE_CODE_SIZE_BYTES_LENGTH, ) - const encodedBalance = setLengthRight( - bigIntToBytes(basicData.balance, true), + const encodedBalance = setLengthLeft( + bigIntToBytes(basicData.balance), VERKLE_BALANCE_BYTES_LENGTH, ) return concatBytes(encodedVersion, reservedBytes, encodedNonce, encodedCodeSize, encodedBalance) From da61ba46b542285eafd6789de252d333046f8e96 Mon Sep 17 00:00:00 2001 From: acolytec3 <17355484+acolytec3@users.noreply.github.com> Date: Mon, 2 Sep 2024 13:20:02 -0400 Subject: [PATCH 12/13] Switch to basic data packing [no ci] --- .../test/statelessVerkleStateManager.spec.ts | 21 ++++++------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/packages/statemanager/test/statelessVerkleStateManager.spec.ts b/packages/statemanager/test/statelessVerkleStateManager.spec.ts index 6fc153efae..6ee50a37f2 100644 --- a/packages/statemanager/test/statelessVerkleStateManager.spec.ts +++ b/packages/statemanager/test/statelessVerkleStateManager.spec.ts @@ -4,10 +4,10 @@ import { createTxFromSerializedData } from '@ethereumjs/tx' import { Address, VerkleLeafType, - bytesToBigInt, bytesToHex, createAccount, createAddressFromString, + decodeVerkleLeafBasicData, getVerkleKey, getVerkleStem, hexToBytes, @@ -123,26 +123,17 @@ describe('StatelessVerkleStateManager: Kaustinen Verkle Block', () => { const address = createAddressFromString('0x6177843db3138ae69679a54b95cf345ed759450d') const stem = getVerkleStem(stateManager.verkleCrypto, address, 0n) - const balanceKey = getVerkleKey(stem, VerkleLeafType.Balance) - const nonceKey = getVerkleKey(stem, VerkleLeafType.Nonce) + const basicDataKey = getVerkleKey(stem, VerkleLeafType.BasicData) const codeHashKey = getVerkleKey(stem, VerkleLeafType.CodeHash) - const balanceRaw = stateManager['_state'][bytesToHex(balanceKey)] - const nonceRaw = stateManager['_state'][bytesToHex(nonceKey)] + const basicDataRaw = stateManager['_state'][bytesToHex(basicDataKey)] + const basicData = decodeVerkleLeafBasicData(hexToBytes(basicDataRaw!)) const codeHash = stateManager['_state'][bytesToHex(codeHashKey)] const account = await stateManager.getAccount(address) - assert.equal( - account!.balance, - bytesToBigInt(hexToBytes(balanceRaw!), true), - 'should have correct balance', - ) - assert.equal( - account!.nonce, - bytesToBigInt(hexToBytes(nonceRaw!), true), - 'should have correct nonce', - ) + assert.equal(account!.balance, basicData.balance, 'should have correct balance') + assert.equal(account!.nonce, basicData.nonce, 'should have correct nonce') assert.equal(bytesToHex(account!.codeHash), codeHash, 'should have correct codeHash') }) From e0c6fc07a6d432e54b9cf91597ce3cb92ed50f12 Mon Sep 17 00:00:00 2001 From: acolytec3 <17355484+acolytec3@users.noreply.github.com> Date: Mon, 2 Sep 2024 13:25:42 -0400 Subject: [PATCH 13/13] skip tests that need kaustinen7 data --- packages/statemanager/test/statelessVerkleStateManager.spec.ts | 3 ++- packages/vm/test/api/EIPs/eip-6800-verkle.spec.ts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/statemanager/test/statelessVerkleStateManager.spec.ts b/packages/statemanager/test/statelessVerkleStateManager.spec.ts index 6ee50a37f2..5285f5c02a 100644 --- a/packages/statemanager/test/statelessVerkleStateManager.spec.ts +++ b/packages/statemanager/test/statelessVerkleStateManager.spec.ts @@ -48,7 +48,8 @@ describe('StatelessVerkleStateManager: Kaustinen Verkle Block', () => { assert.ok(Object.keys(stateManager['_state']).length !== 0, 'should initialize with state') }) - it('getAccount()', async () => { + // TODO: Turn back on once we have kaustinen7 data + it.skip('getAccount()', async () => { const stateManager = new StatelessVerkleStateManager({ common, verkleCrypto }) stateManager.initVerkleExecutionWitness(block.header.number, block.executionWitness) diff --git a/packages/vm/test/api/EIPs/eip-6800-verkle.spec.ts b/packages/vm/test/api/EIPs/eip-6800-verkle.spec.ts index 8c942b6c34..c3501e23c0 100644 --- a/packages/vm/test/api/EIPs/eip-6800-verkle.spec.ts +++ b/packages/vm/test/api/EIPs/eip-6800-verkle.spec.ts @@ -31,7 +31,8 @@ const block = createBlock({ ...verkleBlockJSON, transactions: decodedTxs } as Bl }) describe('EIP 6800 tests', () => { - it('successfully run transactions statelessly using the block witness', async () => { + // TODO: Turn back on once we have kaustinen7 block data + it.skip('successfully run transactions statelessly using the block witness', async () => { const verkleCrypto = await loadVerkleCrypto() const verkleStateManager = new StatelessVerkleStateManager({ caches: new Caches(),