From 22612f292c69182064f554f04ff02e94035724f7 Mon Sep 17 00:00:00 2001 From: Gabriel Rocheleau Date: Fri, 21 Jun 2024 22:17:50 -0400 Subject: [PATCH] verkle: rename verkle utils and refactor (#3468) * verkle: rename verkle utils * verkle: rename helper functions and remove duplicate helper files * statemanager: rename import * util: remove todo * verkle: port over some tests to util --- packages/evm/src/opcodes/functions.ts | 4 +- packages/evm/src/opcodes/gas.ts | 38 +++---- packages/statemanager/src/accessWitness.ts | 100 +++++++++--------- .../src/statelessVerkleStateManager.ts | 50 +++++---- .../test/statelessVerkleStateManager.spec.ts | 14 +-- packages/util/src/verkle.ts | 83 ++++++--------- packages/util/test/verkle.spec.ts | 77 ++++++++++++++ packages/verkle/src/index.ts | 1 - packages/verkle/src/types.ts | 21 +--- packages/verkle/src/util/crypto.ts | 57 ---------- packages/verkle/src/util/index.ts | 1 - packages/verkle/src/util/keys.ts | 99 ----------------- packages/verkle/src/verkleTree.ts | 3 +- packages/verkle/test/crypto.spec.ts | 51 --------- packages/verkle/test/internalNode.spec.ts | 2 +- packages/verkle/test/keys.spec.ts | 21 ---- packages/vm/src/runBlock.ts | 4 +- 17 files changed, 222 insertions(+), 404 deletions(-) create mode 100644 packages/util/test/verkle.spec.ts delete mode 100644 packages/verkle/src/util/crypto.ts delete mode 100644 packages/verkle/src/util/keys.ts delete mode 100644 packages/verkle/test/crypto.spec.ts delete mode 100644 packages/verkle/test/keys.spec.ts diff --git a/packages/evm/src/opcodes/functions.ts b/packages/evm/src/opcodes/functions.ts index 7807bc82c7..fcaef0b5c2 100644 --- a/packages/evm/src/opcodes/functions.ts +++ b/packages/evm/src/opcodes/functions.ts @@ -26,7 +26,7 @@ import { bytesToHex, concatBytes, ecrecover, - getTreeIndexesForStorageSlot, + getVerkleTreeIndexesForStorageSlot, hexToBytes, publicToAddress, setLengthLeft, @@ -628,7 +628,7 @@ export const handlers: Map = new Map([ const key = setLengthLeft(bigIntToBytes(number % historyServeWindow), 32) if (common.isActivatedEIP(6800)) { - const { treeIndex, subIndex } = getTreeIndexesForStorageSlot(number) + const { treeIndex, subIndex } = getVerkleTreeIndexesForStorageSlot(number) // create witnesses and charge gas const statelessGas = runState.env.accessWitness!.touchAddressOnReadAndComputeGas( historyAddress, diff --git a/packages/evm/src/opcodes/gas.ts b/packages/evm/src/opcodes/gas.ts index 010f996a92..f5610f3d26 100644 --- a/packages/evm/src/opcodes/gas.ts +++ b/packages/evm/src/opcodes/gas.ts @@ -2,17 +2,17 @@ import { Hardfork } from '@ethereumjs/common' import { Account, Address, - BALANCE_LEAF_KEY, BIGINT_0, BIGINT_1, BIGINT_3, BIGINT_31, BIGINT_32, - CODE_HASH_LEAF_KEY, - CODE_SIZE_LEAF_KEY, - VERSION_LEAF_KEY, + VERKLE_BALANCE_LEAF_KEY, + VERKLE_CODE_HASH_LEAF_KEY, + VERKLE_CODE_SIZE_LEAF_KEY, + VERKLE_VERSION_LEAF_KEY, bigIntToBytes, - getTreeIndexesForStorageSlot, + getVerkleTreeIndexesForStorageSlot, setLengthLeft, } from '@ethereumjs/util' @@ -94,7 +94,7 @@ export const dynamicGasHandlers: Map BIGINT_0) { gas += runState.env.accessWitness!.touchAddressOnWriteAndComputeGas( contractAddress, 0, - BALANCE_LEAF_KEY + VERKLE_BALANCE_LEAF_KEY ) } @@ -874,14 +874,14 @@ export const dynamicGasHandlers: Map BIGINT_0) { selfDestructToColdAccessGas += runState.env.accessWitness!.touchAddressOnWriteAndComputeGas( selfdestructToAddress, 0, - BALANCE_LEAF_KEY + VERKLE_BALANCE_LEAF_KEY ) } diff --git a/packages/statemanager/src/accessWitness.ts b/packages/statemanager/src/accessWitness.ts index 4398b3c61b..6ab4f87d4d 100644 --- a/packages/statemanager/src/accessWitness.ts +++ b/packages/statemanager/src/accessWitness.ts @@ -1,19 +1,19 @@ import { - BALANCE_LEAF_KEY, BIGINT_0, - CODE_HASH_LEAF_KEY, - CODE_OFFSET, - CODE_SIZE_LEAF_KEY, - HEADER_STORAGE_OFFSET, - MAIN_STORAGE_OFFSET, - NONCE_LEAF_KEY, + VERKLE_BALANCE_LEAF_KEY, + VERKLE_CODE_HASH_LEAF_KEY, + VERKLE_CODE_OFFSET, + VERKLE_CODE_SIZE_LEAF_KEY, + VERKLE_HEADER_STORAGE_OFFSET, + VERKLE_MAIN_STORAGE_OFFSET, VERKLE_NODE_WIDTH, - VERSION_LEAF_KEY, + VERKLE_NONCE_LEAF_KEY, + VERKLE_VERSION_LEAF_KEY, bytesToBigInt, bytesToHex, - getKey, - getStem, - getTreeIndicesForCodeChunk, + getVerkleKey, + getVerkleStem, + getVerkleTreeIndicesForCodeChunk, hexToBytes, intToBytes, } from '@ethereumjs/util' @@ -90,11 +90,11 @@ export class AccessWitness implements AccessWitnessInterface { 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, CODE_HASH_LEAF_KEY) - gas += this.touchAddressOnReadAndComputeGas(address, 0, NONCE_LEAF_KEY) + gas += this.touchAddressOnReadAndComputeGas(address, 0, VERKLE_VERSION_LEAF_KEY) + gas += this.touchAddressOnReadAndComputeGas(address, 0, VERKLE_BALANCE_LEAF_KEY) + gas += this.touchAddressOnReadAndComputeGas(address, 0, VERKLE_CODE_SIZE_LEAF_KEY) + gas += this.touchAddressOnReadAndComputeGas(address, 0, VERKLE_CODE_HASH_LEAF_KEY) + gas += this.touchAddressOnReadAndComputeGas(address, 0, VERKLE_NONCE_LEAF_KEY) return gas } @@ -102,8 +102,8 @@ export class AccessWitness implements AccessWitnessInterface { 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, VERKLE_VERSION_LEAF_KEY) + gas += this.touchAddressOnReadAndComputeGas(address, 0, VERKLE_CODE_SIZE_LEAF_KEY) return gas } @@ -111,8 +111,8 @@ export class AccessWitness implements AccessWitnessInterface { 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, VERKLE_BALANCE_LEAF_KEY) + gas += this.touchAddressOnWriteAndComputeGas(target, 0, VERKLE_BALANCE_LEAF_KEY) return gas } @@ -120,8 +120,8 @@ export class AccessWitness implements AccessWitnessInterface { 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, VERKLE_VERSION_LEAF_KEY) + gas += this.touchAddressOnWriteAndComputeGas(address, 0, VERKLE_NONCE_LEAF_KEY) return gas } @@ -129,11 +129,11 @@ export class AccessWitness implements AccessWitnessInterface { 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, CODE_HASH_LEAF_KEY) - gas += this.touchAddressOnWriteAndComputeGas(address, 0, NONCE_LEAF_KEY) + gas += this.touchAddressOnWriteAndComputeGas(address, 0, VERKLE_VERSION_LEAF_KEY) + gas += this.touchAddressOnWriteAndComputeGas(address, 0, VERKLE_BALANCE_LEAF_KEY) + gas += this.touchAddressOnWriteAndComputeGas(address, 0, VERKLE_CODE_SIZE_LEAF_KEY) + gas += this.touchAddressOnWriteAndComputeGas(address, 0, VERKLE_CODE_HASH_LEAF_KEY) + gas += this.touchAddressOnWriteAndComputeGas(address, 0, VERKLE_NONCE_LEAF_KEY) return gas } @@ -141,12 +141,12 @@ export class AccessWitness implements AccessWitnessInterface { 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, CODE_HASH_LEAF_KEY) + gas += this.touchAddressOnReadAndComputeGas(origin, 0, VERKLE_VERSION_LEAF_KEY) + gas += this.touchAddressOnReadAndComputeGas(origin, 0, VERKLE_CODE_SIZE_LEAF_KEY) + gas += this.touchAddressOnReadAndComputeGas(origin, 0, VERKLE_CODE_HASH_LEAF_KEY) - gas += this.touchAddressOnWriteAndComputeGas(origin, 0, NONCE_LEAF_KEY) - gas += this.touchAddressOnWriteAndComputeGas(origin, 0, BALANCE_LEAF_KEY) + gas += this.touchAddressOnWriteAndComputeGas(origin, 0, VERKLE_NONCE_LEAF_KEY) + gas += this.touchAddressOnWriteAndComputeGas(origin, 0, VERKLE_BALANCE_LEAF_KEY) return gas } @@ -154,15 +154,15 @@ export class AccessWitness implements AccessWitnessInterface { 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) + gas += this.touchAddressOnReadAndComputeGas(target, 0, VERKLE_VERSION_LEAF_KEY) + gas += this.touchAddressOnReadAndComputeGas(target, 0, VERKLE_CODE_SIZE_LEAF_KEY) + gas += this.touchAddressOnReadAndComputeGas(target, 0, VERKLE_CODE_HASH_LEAF_KEY) + gas += this.touchAddressOnReadAndComputeGas(target, 0, VERKLE_NONCE_LEAF_KEY) if (sendsValue === true) { - gas += this.touchAddressOnWriteAndComputeGas(target, 0, BALANCE_LEAF_KEY) + gas += this.touchAddressOnWriteAndComputeGas(target, 0, VERKLE_BALANCE_LEAF_KEY) } else { - gas += this.touchAddressOnReadAndComputeGas(target, 0, BALANCE_LEAF_KEY) + gas += this.touchAddressOnReadAndComputeGas(target, 0, VERKLE_BALANCE_LEAF_KEY) } return gas @@ -171,7 +171,7 @@ export class AccessWitness implements AccessWitnessInterface { touchCodeChunksRangeOnReadAndChargeGas(contact: 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 } = getTreeIndicesForCodeChunk(chunkNum) + const { treeIndex, subIndex } = getVerkleTreeIndicesForCodeChunk(chunkNum) gas += this.touchAddressOnReadAndComputeGas(contact, treeIndex, subIndex) } return gas @@ -184,7 +184,7 @@ export class AccessWitness implements AccessWitnessInterface { ): bigint { let gas = BIGINT_0 for (let chunkNum = Math.floor(startPc / 31); chunkNum <= Math.floor(endPc / 31); chunkNum++) { - const { treeIndex, subIndex } = getTreeIndicesForCodeChunk(chunkNum) + const { treeIndex, subIndex } = getVerkleTreeIndicesForCodeChunk(chunkNum) gas += this.touchAddressOnWriteAndComputeGas(contact, treeIndex, subIndex) } return gas @@ -259,7 +259,7 @@ export class AccessWitness implements AccessWitnessInterface { // i.e. no fill cost is charged right now const chunkFill = false - const accessedStemKey = getStem(this.verkleCrypto, address, treeIndex) + const accessedStemKey = getVerkleStem(this.verkleCrypto, address, treeIndex) const accessedStemHex = bytesToHex(accessedStemKey) let accessedStem = this.stems.get(accessedStemHex) if (accessedStem === undefined) { @@ -268,7 +268,7 @@ export class AccessWitness implements AccessWitnessInterface { this.stems.set(accessedStemHex, accessedStem) } - const accessedChunkKey = getKey( + const accessedChunkKey = getVerkleKey( accessedStemKey, typeof subIndex === 'number' ? intToBytes(subIndex) : subIndex ) @@ -368,18 +368,18 @@ export function decodeAccessedState(treeIndex: number | bigint, chunkIndex: numb case BigInt(4): return { type: AccessedStateType.CodeSize } default: - if (position < HEADER_STORAGE_OFFSET) { - throw Error(`No attribute yet stored >=5 and <${HEADER_STORAGE_OFFSET}`) + if (position < VERKLE_HEADER_STORAGE_OFFSET) { + throw Error(`No attribute yet stored >=5 and <${VERKLE_HEADER_STORAGE_OFFSET}`) } - if (position >= HEADER_STORAGE_OFFSET && position < CODE_OFFSET) { - const slot = position - BigInt(HEADER_STORAGE_OFFSET) + if (position >= VERKLE_HEADER_STORAGE_OFFSET && position < VERKLE_CODE_OFFSET) { + const slot = position - BigInt(VERKLE_HEADER_STORAGE_OFFSET) return { type: AccessedStateType.Storage, slot } - } else if (position >= CODE_OFFSET && position < MAIN_STORAGE_OFFSET) { - const codeChunkIdx = Number(position) - CODE_OFFSET + } else if (position >= VERKLE_CODE_OFFSET && position < VERKLE_MAIN_STORAGE_OFFSET) { + const codeChunkIdx = Number(position) - VERKLE_CODE_OFFSET return { type: AccessedStateType.Code, codeOffset: codeChunkIdx * 31 } - } else if (position >= MAIN_STORAGE_OFFSET) { - const slot = BigInt(position - MAIN_STORAGE_OFFSET) + } else if (position >= VERKLE_MAIN_STORAGE_OFFSET) { + const slot = BigInt(position - VERKLE_MAIN_STORAGE_OFFSET) return { type: AccessedStateType.Storage, slot } } else { throw Error( diff --git a/packages/statemanager/src/statelessVerkleStateManager.ts b/packages/statemanager/src/statelessVerkleStateManager.ts index 10e534156d..b696af12f9 100644 --- a/packages/statemanager/src/statelessVerkleStateManager.ts +++ b/packages/statemanager/src/statelessVerkleStateManager.ts @@ -2,22 +2,22 @@ import { Account, KECCAK256_NULL, KECCAK256_NULL_S, - LeafType, + VerkleLeafType, bigIntToBytes, bytesToBigInt, bytesToHex, bytesToInt32, - getKey, - getStem, - getTreeKeyForCodeChunk, - getTreeKeyForStorageSlot, + getVerkleKey, + getVerkleStem, + getVerkleTreeKeyForCodeChunk, + getVerkleTreeKeyForStorageSlot, hexToBytes, padToEven, setLengthLeft, setLengthRight, short, toBytes, - verifyProof, + verifyVerkleProof, } from '@ethereumjs/util' import debugDefault from 'debug' import { keccak256 } from 'ethereum-cryptography/keccak.js' @@ -310,7 +310,9 @@ export class StatelessVerkleStateManager implements EVMStateManagerInterface { async checkChunkWitnessPresent(address: Address, codeOffset: number) { const chunkId = codeOffset / 31 - const chunkKey = bytesToHex(await getTreeKeyForCodeChunk(address, chunkId, this.verkleCrypto)) + const chunkKey = bytesToHex( + await getVerkleTreeKeyForCodeChunk(address, chunkId, this.verkleCrypto) + ) return this._state[chunkKey] !== undefined } @@ -381,7 +383,9 @@ export class StatelessVerkleStateManager implements EVMStateManagerInterface { const chunks = Math.floor(codeSize / 31) + 1 for (let chunkId = 0; chunkId < chunks; chunkId++) { - const chunkKey = bytesToHex(await getTreeKeyForCodeChunk(address, chunkId, this.verkleCrypto)) + const chunkKey = bytesToHex( + await getVerkleTreeKeyForCodeChunk(address, chunkId, this.verkleCrypto) + ) const codeChunk = this._state[chunkKey] if (codeChunk === null) { const errorMsg = `Invalid access to a non existent code chunk with chunkKey=${chunkKey}` @@ -453,7 +457,7 @@ export class StatelessVerkleStateManager implements EVMStateManagerInterface { } } - const storageKey = await getTreeKeyForStorageSlot( + const storageKey = await getVerkleTreeKeyForStorageSlot( address, BigInt(bytesToHex(key)), this.verkleCrypto @@ -479,7 +483,7 @@ export class StatelessVerkleStateManager implements EVMStateManagerInterface { this._storageCache!.put(address, key, value) } else { // TODO: Consider refactoring this in a writeContractStorage function? Like in stateManager.ts - const storageKey = await getTreeKeyForStorageSlot( + const storageKey = await getVerkleTreeKeyForStorageSlot( address, BigInt(bytesToHex(key)), this.verkleCrypto @@ -495,8 +499,8 @@ export class StatelessVerkleStateManager implements EVMStateManagerInterface { * @param address - Address to clear the storage of */ async clearContractStorage(address: Address): Promise { - const stem = getStem(this.verkleCrypto, address, 0) - const codeHashKey = getKey(stem, LeafType.CodeHash) + const stem = getVerkleStem(this.verkleCrypto, address, 0) + const codeHashKey = getVerkleKey(stem, VerkleLeafType.CodeHash) this._storageCache?.clearContractStorage(address) // Update codeHash to `c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470` this._state[bytesToHex(codeHashKey)] = KECCAK256_NULL_S @@ -512,12 +516,12 @@ 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 codeHashKey = getKey(stem, LeafType.CodeHash) - const codeSizeKey = getKey(stem, LeafType.CodeSize) + const stem = getVerkleStem(this.verkleCrypto, address, 0) + const versionKey = getVerkleKey(stem, VerkleLeafType.Version) + const balanceKey = getVerkleKey(stem, VerkleLeafType.Balance) + const nonceKey = getVerkleKey(stem, VerkleLeafType.Nonce) + const codeHashKey = getVerkleKey(stem, VerkleLeafType.CodeHash) + const codeSizeKey = getVerkleKey(stem, VerkleLeafType.CodeSize) const versionRaw = this._state[bytesToHex(versionKey)] const balanceRaw = this._state[bytesToHex(balanceKey)] @@ -599,10 +603,10 @@ 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 stem = getVerkleStem(this.verkleCrypto, address, 0) + const balanceKey = getVerkleKey(stem, VerkleLeafType.Balance) + const nonceKey = getVerkleKey(stem, VerkleLeafType.Nonce) + const codeHashKey = getVerkleKey(stem, VerkleLeafType.CodeHash) const balanceBuf = setLengthRight(bigIntToBytes(account.balance, true), 32) const nonceBuf = setLengthRight(bigIntToBytes(account.nonce, true), 32) @@ -663,7 +667,7 @@ export class StatelessVerkleStateManager implements EVMStateManagerInterface { return false } - return verifyProof(this.verkleCrypto, stateRoot, this._executionWitness) + return verifyVerkleProof(this.verkleCrypto, stateRoot, this._executionWitness) } // Verifies that the witness post-state matches the computed post-state diff --git a/packages/statemanager/test/statelessVerkleStateManager.spec.ts b/packages/statemanager/test/statelessVerkleStateManager.spec.ts index 7475e47459..94270c9128 100644 --- a/packages/statemanager/test/statelessVerkleStateManager.spec.ts +++ b/packages/statemanager/test/statelessVerkleStateManager.spec.ts @@ -4,11 +4,11 @@ import { TransactionFactory } from '@ethereumjs/tx' import { Account, Address, - LeafType, + VerkleLeafType, bytesToBigInt, bytesToHex, - getKey, - getStem, + getVerkleKey, + getVerkleStem, hexToBytes, randomBytes, } from '@ethereumjs/util' @@ -115,11 +115,11 @@ describe('StatelessVerkleStateManager: Kaustinen Verkle Block', () => { stateManager.initVerkleExecutionWitness(block.header.number, block.executionWitness) const address = Address.fromString('0x6177843db3138ae69679a54b95cf345ed759450d') - const stem = getStem(stateManager.verkleCrypto, address, 0n) + const stem = getVerkleStem(stateManager.verkleCrypto, address, 0n) - const balanceKey = getKey(stem, LeafType.Balance) - const nonceKey = getKey(stem, LeafType.Nonce) - const codeHashKey = getKey(stem, LeafType.CodeHash) + const balanceKey = getVerkleKey(stem, VerkleLeafType.Balance) + const nonceKey = getVerkleKey(stem, VerkleLeafType.Nonce) + const codeHashKey = getVerkleKey(stem, VerkleLeafType.CodeHash) const balanceRaw = stateManager['_state'][bytesToHex(balanceKey)] const nonceRaw = stateManager['_state'][bytesToHex(nonceKey)] diff --git a/packages/util/src/verkle.ts b/packages/util/src/verkle.ts index a9bd2918ba..959559f701 100644 --- a/packages/util/src/verkle.ts +++ b/packages/util/src/verkle.ts @@ -18,20 +18,7 @@ import type { PrefixedHexString } from './types.js' * Experimental (do not use in production!) */ -// TODO 1: this comes with a deprecation of the respective constants and methods within the -// @ethereumjs/verkle package. Due to ease of parallel work this has not yet been executed upon, -// so this still needs a small follow-up clean up PR. -// -// Along with the PR likely more/additional helper functionality should be moved over -// (basically everything generic not depending on Verkle cryptography) -// -// TODO 2: Basically all these constants and methods need a Verkle or (VERKLE) prefix since -// atm names are too generic to be grasped in a non-Verkle-limited context (e.g. `getKey()`) -// -// Holger Drewes, 2024-06-18 - /* Verkle Crypto */ - export interface VerkleCrypto { getTreeKey: (address: Uint8Array, treeIndex: Uint8Array, subIndex: number) => Uint8Array getTreeKeyHash: (address: Uint8Array, treeIndexLE: Uint8Array) => Uint8Array @@ -55,7 +42,7 @@ export interface VerkleCrypto { * @param treeIndex The index of the tree to generate the key for. Defaults to 0. * @return The 31-bytes verkle tree stem as a Uint8Array. */ -export function getStem( +export function getVerkleStem( ffi: VerkleCrypto, address: Address, treeIndex: number | bigint = 0 @@ -81,7 +68,7 @@ export function getStem( * @param executionWitness The verkle execution witness. * @returns {boolean} Whether or not the executionWitness belongs to the prestateRoot. */ -export function verifyProof( +export function verifyVerkleProof( ffi: VerkleCrypto, prestateRoot: Uint8Array, executionWitness: VerkleExecutionWitness @@ -133,7 +120,7 @@ export interface VerkleExecutionWitness { verkleProof: VerkleProof } -export enum LeafType { +export enum VerkleLeafType { Version = 0, Balance = 1, Nonce = 2, @@ -141,16 +128,16 @@ export enum LeafType { CodeSize = 4, } -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 VERKLE_VERSION_LEAF_KEY = intToBytes(VerkleLeafType.Version) +export const VERKLE_BALANCE_LEAF_KEY = intToBytes(VerkleLeafType.Balance) +export const VERKLE_NONCE_LEAF_KEY = intToBytes(VerkleLeafType.Nonce) +export const VERKLE_CODE_HASH_LEAF_KEY = intToBytes(VerkleLeafType.CodeHash) +export const VERKLE_CODE_SIZE_LEAF_KEY = intToBytes(VerkleLeafType.CodeSize) -export const HEADER_STORAGE_OFFSET = 64 -export const CODE_OFFSET = 128 +export const VERKLE_HEADER_STORAGE_OFFSET = 64 +export const VERKLE_CODE_OFFSET = 128 export const VERKLE_NODE_WIDTH = 256 -export const MAIN_STORAGE_OFFSET = BigInt(256) ** BigInt(31) +export const VERKLE_MAIN_STORAGE_OFFSET = BigInt(256) ** BigInt(31) /** * @dev Returns the tree key for a given verkle tree stem, and sub index. @@ -160,32 +147,32 @@ export const MAIN_STORAGE_OFFSET = BigInt(256) ** BigInt(31) * @return The tree key as a Uint8Array. */ -export const getKey = (stem: Uint8Array, leaf: LeafType | Uint8Array) => { +export const getVerkleKey = (stem: Uint8Array, leaf: VerkleLeafType | 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.CodeHash: - return concatBytes(stem, CODE_HASH_LEAF_KEY) - case LeafType.CodeSize: - return concatBytes(stem, CODE_SIZE_LEAF_KEY) + case VerkleLeafType.Version: + return concatBytes(stem, VERKLE_VERSION_LEAF_KEY) + case VerkleLeafType.Balance: + return concatBytes(stem, VERKLE_BALANCE_LEAF_KEY) + case VerkleLeafType.Nonce: + return concatBytes(stem, VERKLE_NONCE_LEAF_KEY) + case VerkleLeafType.CodeHash: + return concatBytes(stem, VERKLE_CODE_HASH_LEAF_KEY) + case VerkleLeafType.CodeSize: + return concatBytes(stem, VERKLE_CODE_SIZE_LEAF_KEY) default: return concatBytes(stem, leaf) } } -export function getTreeIndexesForStorageSlot(storageKey: bigint): { +export function getVerkleTreeIndexesForStorageSlot(storageKey: bigint): { treeIndex: bigint subIndex: number } { let position: bigint - if (storageKey < CODE_OFFSET - HEADER_STORAGE_OFFSET) { - position = BigInt(HEADER_STORAGE_OFFSET) + storageKey + if (storageKey < VERKLE_CODE_OFFSET - VERKLE_HEADER_STORAGE_OFFSET) { + position = BigInt(VERKLE_HEADER_STORAGE_OFFSET) + storageKey } else { - position = MAIN_STORAGE_OFFSET + storageKey + position = VERKLE_MAIN_STORAGE_OFFSET + storageKey } const treeIndex = position / BigInt(VERKLE_NODE_WIDTH) @@ -194,19 +181,19 @@ export function getTreeIndexesForStorageSlot(storageKey: bigint): { return { treeIndex, subIndex } } -export function getTreeIndicesForCodeChunk(chunkId: number) { - const treeIndex = Math.floor((CODE_OFFSET + chunkId) / VERKLE_NODE_WIDTH) - const subIndex = (CODE_OFFSET + chunkId) % VERKLE_NODE_WIDTH +export function getVerkleTreeIndicesForCodeChunk(chunkId: number) { + const treeIndex = Math.floor((VERKLE_CODE_OFFSET + chunkId) / VERKLE_NODE_WIDTH) + const subIndex = (VERKLE_CODE_OFFSET + chunkId) % VERKLE_NODE_WIDTH return { treeIndex, subIndex } } -export const getTreeKeyForCodeChunk = async ( +export const getVerkleTreeKeyForCodeChunk = async ( address: Address, chunkId: number, verkleCrypto: VerkleCrypto ) => { - const { treeIndex, subIndex } = getTreeIndicesForCodeChunk(chunkId) - return concatBytes(getStem(verkleCrypto, address, treeIndex), toBytes(subIndex)) + const { treeIndex, subIndex } = getVerkleTreeIndicesForCodeChunk(chunkId) + return concatBytes(getVerkleStem(verkleCrypto, address, treeIndex), toBytes(subIndex)) } export const chunkifyCode = (code: Uint8Array) => { @@ -219,12 +206,12 @@ export const chunkifyCode = (code: Uint8Array) => { throw new Error('Not implemented') } -export const getTreeKeyForStorageSlot = async ( +export const getVerkleTreeKeyForStorageSlot = async ( address: Address, storageKey: bigint, verkleCrypto: VerkleCrypto ) => { - const { treeIndex, subIndex } = getTreeIndexesForStorageSlot(storageKey) + const { treeIndex, subIndex } = getVerkleTreeIndexesForStorageSlot(storageKey) - return concatBytes(getStem(verkleCrypto, address, treeIndex), toBytes(subIndex)) + return concatBytes(getVerkleStem(verkleCrypto, address, treeIndex), toBytes(subIndex)) } diff --git a/packages/util/test/verkle.spec.ts b/packages/util/test/verkle.spec.ts new file mode 100644 index 0000000000..3a66f1244a --- /dev/null +++ b/packages/util/test/verkle.spec.ts @@ -0,0 +1,77 @@ +import { loadVerkleCrypto } from 'verkle-cryptography-wasm' +import { assert, beforeAll, describe, it } from 'vitest' + +import * as verkleBlockJSON from '../../statemanager/test/testdata/verkleKaustinen6Block72.json' +import { + Address, + type VerkleCrypto, + type VerkleExecutionWitness, + VerkleLeafType, + bytesToHex, + concatBytes, + getVerkleKey, + getVerkleStem, + hexToBytes, + intToBytes, + randomBytes, + verifyVerkleProof, +} from '../src/index.js' + +describe('Verkle cryptographic helpers', () => { + let verkle: VerkleCrypto + beforeAll(async () => { + verkle = await loadVerkleCrypto() + }) + + it('getVerkleStem(): returns the expected stems', () => { + // Empty address + assert.equal( + bytesToHex( + getVerkleStem(verkle, Address.fromString('0x0000000000000000000000000000000000000000')) + ), + '0x1a100684fd68185060405f3f160e4bb6e034194336b547bdae323f888d5332' + ) + + // Non-empty address + assert.equal( + bytesToHex( + getVerkleStem(verkle, Address.fromString('0x71562b71999873DB5b286dF957af199Ec94617f7')) + ), + '0x1540dfad7755b40be0768c6aa0a5096fbf0215e0e8cf354dd928a178346466' + ) + }) + + it('verifyVerkleProof(): should verify verkle proofs', () => { + // Src: Kaustinen6 testnet, block 71 state root (parent of block 72) + const prestateRoot = hexToBytes( + '0x64e1a647f42e5c2e3c434531ccf529e1b3e93363a40db9fc8eec81f492123510' + ) + const executionWitness = verkleBlockJSON.executionWitness as VerkleExecutionWitness + assert.isTrue(verifyVerkleProof(verkle, prestateRoot, executionWitness)) + }) + + it('verifyVerkleProof(): should return false for invalid verkle proofs', () => { + // Random preStateRoot + const prestateRoot = randomBytes(32) + const executionWitness = verkleBlockJSON.executionWitness as VerkleExecutionWitness + // Modify the proof to make it invalid + assert.isFalse(verifyVerkleProof(verkle, prestateRoot, 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, + ]) { + const key = getVerkleKey(stem, leaf) + assert.equal(key.length, 32) + assert.deepEqual(key, concatBytes(stem, intToBytes(leaf))) + } + }) +}) diff --git a/packages/verkle/src/index.ts b/packages/verkle/src/index.ts index 38e111ce90..d7fde388d7 100644 --- a/packages/verkle/src/index.ts +++ b/packages/verkle/src/index.ts @@ -2,5 +2,4 @@ export * from './db/index.js' export * from './node/index.js' export * from './types.js' export * from './util/index.js' -export * from './util/keys.js' export * from './verkleTree.js' diff --git a/packages/verkle/src/types.ts b/packages/verkle/src/types.ts index 1c1374d3d2..06c09e2024 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 { DB } from '@ethereumjs/util' @@ -60,22 +60,3 @@ export type Checkpoint = { } export const ROOT_DB_KEY = utf8ToBytes('__root__') - -export enum LeafType { - Version = 0, - Balance = 1, - Nonce = 2, - CodeHash = 3, - CodeSize = 4, -} - -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/crypto.ts b/packages/verkle/src/util/crypto.ts deleted file mode 100644 index c4d93d2780..0000000000 --- a/packages/verkle/src/util/crypto.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { - type Address, - bigIntToBytes, - bytesToHex, - int32ToBytes, - setLengthLeft, - setLengthRight, -} from '@ethereumjs/util' - -import type { VerkleCrypto, VerkleExecutionWitness } from '@ethereumjs/util' - -/** - * @dev Returns the 31-bytes verkle tree stem for a given address and tree index. - * @dev Assumes that the verkle node width = 256 - * @param ffi The verkle ffi object from verkle-crypotography-wasm. - * @param address The address to generate the tree key for. - * @param treeIndex The index of the tree to generate the key for. Defaults to 0. - * @return The 31-bytes verkle tree stem as a Uint8Array. - */ -export function getStem( - ffi: VerkleCrypto, - address: Address, - treeIndex: number | bigint = 0 -): Uint8Array { - const address32 = setLengthLeft(address.toBytes(), 32) - - let treeIndexBytes: Uint8Array - if (typeof treeIndex === 'number') { - treeIndexBytes = setLengthRight(int32ToBytes(Number(treeIndex), true), 32) - } else { - treeIndexBytes = setLengthRight(bigIntToBytes(BigInt(treeIndex), true).slice(0, 32), 32) - } - - const treeStem = ffi.getTreeKey(address32, treeIndexBytes, 0).slice(0, 31) - - return treeStem -} - -/** - * 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 const POINT_IDENTITY = new Uint8Array(0) diff --git a/packages/verkle/src/util/index.ts b/packages/verkle/src/util/index.ts index c9131b1392..2da7bbb112 100644 --- a/packages/verkle/src/util/index.ts +++ b/packages/verkle/src/util/index.ts @@ -1,3 +1,2 @@ export * from './bytes.js' -export * from './crypto.js' export * from './tasks.js' diff --git a/packages/verkle/src/util/keys.ts b/packages/verkle/src/util/keys.ts deleted file mode 100644 index 15808396e7..0000000000 --- a/packages/verkle/src/util/keys.ts +++ /dev/null @@ -1,99 +0,0 @@ -import { concatBytes, setLengthRight, toBytes } from '@ethereumjs/util' - -import { - BALANCE_LEAF_KEY, - CODE_HASH_LEAF_KEY, - CODE_OFFSET, - CODE_SIZE_LEAF_KEY, - HEADER_STORAGE_OFFSET, - LeafType, - MAIN_STORAGE_OFFSET, - NONCE_LEAF_KEY, - VERKLE_NODE_WIDTH, - VERSION_LEAF_KEY, -} from '../types.js' - -import { getStem } from './crypto.js' - -import type { Address, VerkleCrypto } from '@ethereumjs/util' - -/** - * @dev Returns the tree key for a given verkle tree stem, and sub index. - * @dev Assumes that the verkle node width = 256 - * @param stem The 31-bytes verkle tree stem as a Uint8Array. - * @param subIndex The sub index of the tree to generate the key for as a Uint8Array. - * @return The tree key as a Uint8Array. - */ - -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.CodeHash: - return concatBytes(stem, CODE_HASH_LEAF_KEY) - case LeafType.CodeSize: - return concatBytes(stem, CODE_SIZE_LEAF_KEY) - default: - return concatBytes(stem, leaf) - } -} - -export function getTreeIndexesForStorageSlot(storageKey: bigint): { - treeIndex: bigint - subIndex: number -} { - let position: bigint - if (storageKey < CODE_OFFSET - HEADER_STORAGE_OFFSET) { - position = BigInt(HEADER_STORAGE_OFFSET) + storageKey - } else { - position = MAIN_STORAGE_OFFSET + storageKey - } - - const treeIndex = position / BigInt(VERKLE_NODE_WIDTH) - const subIndex = Number(position % BigInt(VERKLE_NODE_WIDTH)) - - return { treeIndex, subIndex } -} - -export function getTreeIndicesForCodeChunk(chunkId: number) { - const treeIndex = Math.floor((CODE_OFFSET + chunkId) / VERKLE_NODE_WIDTH) - const subIndex = (CODE_OFFSET + chunkId) % VERKLE_NODE_WIDTH - return { treeIndex, subIndex } -} - -export const getTreeKeyForCodeChunk = async ( - address: Address, - chunkId: number, - verkleCrypto: VerkleCrypto -) => { - const { treeIndex, subIndex } = getTreeIndicesForCodeChunk(chunkId) - return concatBytes(getStem(verkleCrypto, address, treeIndex), toBytes(subIndex)) -} - -export const chunkifyCode = (code: Uint8Array) => { - // Pad code to multiple of 31 bytes - if (code.length % 31 !== 0) { - const paddingLength = 31 - (code.length % 31) - code = setLengthRight(code, code.length + paddingLength) - } - - throw new Error('Not implemented') -} - -export const getTreeKeyForStorageSlot = async ( - address: Address, - storageKey: bigint, - verkleCrypto: VerkleCrypto -) => { - const { treeIndex, subIndex } = getTreeIndexesForStorageSlot(storageKey) - - return concatBytes(getStem(verkleCrypto, address, treeIndex), toBytes(subIndex)) -} - -export const verifyKeyLength = (key: Uint8Array) => { - if (key.length !== 32) throw new Error(`expected key with length 32; got ${key.length}`) -} diff --git a/packages/verkle/src/verkleTree.ts b/packages/verkle/src/verkleTree.ts index 086b501925..0ab096b14e 100644 --- a/packages/verkle/src/verkleTree.ts +++ b/packages/verkle/src/verkleTree.ts @@ -22,7 +22,6 @@ import { type VerkleTreeOptsWithDefaults, } from './types.js' import { matchingBytesLength } from './util/index.js' -import { verifyKeyLength } from './util/keys.js' import type { DB, PutBatch, VerkleCrypto } from '@ethereumjs/util' import type { Debugger } from 'debug' @@ -190,7 +189,7 @@ export class VerkleTree { * @returns A Promise that resolves to `Uint8Array` if a value was found or `undefined` if no value was found. */ async get(key: Uint8Array): Promise { - verifyKeyLength(key) + if (key.length !== 32) throw new Error(`expected key with length 32; got ${key.length}`) const stem = key.slice(0, 31) const suffix = key[key.length - 1] this.DEBUG && this.debug(`Stem: ${bytesToHex(stem)}; Suffix: ${suffix}`, ['GET']) diff --git a/packages/verkle/test/crypto.spec.ts b/packages/verkle/test/crypto.spec.ts deleted file mode 100644 index 3822185aab..0000000000 --- a/packages/verkle/test/crypto.spec.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { - Address, - type VerkleCrypto, - type VerkleExecutionWitness, - bytesToHex, - hexToBytes, - randomBytes, -} from '@ethereumjs/util' -import { loadVerkleCrypto } from 'verkle-cryptography-wasm' -import { assert, beforeAll, describe, it } from 'vitest' - -import * as verkleBlockJSON from '../../statemanager/test/testdata/verkleKaustinen6Block72.json' -import { getStem, verifyProof } from '../src/index.js' - -describe('Verkle cryptographic helpers', () => { - let verkle: VerkleCrypto - beforeAll(async () => { - verkle = await loadVerkleCrypto() - }) - - it('getStem(): returns the expected stems', () => { - // Empty address - assert.equal( - bytesToHex(getStem(verkle, Address.fromString('0x0000000000000000000000000000000000000000'))), - '0x1a100684fd68185060405f3f160e4bb6e034194336b547bdae323f888d5332' - ) - - // Non-empty address - assert.equal( - bytesToHex(getStem(verkle, Address.fromString('0x71562b71999873DB5b286dF957af199Ec94617f7'))), - '0x1540dfad7755b40be0768c6aa0a5096fbf0215e0e8cf354dd928a178346466' - ) - }) - - it('verifyProof(): should verify verkle proofs', () => { - // Src: Kaustinen6 testnet, block 71 state root (parent of block 72) - const prestateRoot = hexToBytes( - '0x64e1a647f42e5c2e3c434531ccf529e1b3e93363a40db9fc8eec81f492123510' - ) - const executionWitness = verkleBlockJSON.executionWitness as VerkleExecutionWitness - assert.isTrue(verifyProof(verkle, prestateRoot, executionWitness)) - }) - - it('verifyProof(): should return false for invalid verkle proofs', () => { - // Random preStateRoot - const prestateRoot = randomBytes(32) - const executionWitness = verkleBlockJSON.executionWitness as VerkleExecutionWitness - // Modify the proof to make it invalid - assert.isFalse(verifyProof(verkle, prestateRoot, executionWitness)) - }) -}) diff --git a/packages/verkle/test/internalNode.spec.ts b/packages/verkle/test/internalNode.spec.ts index c3aa908faa..6aeb88169b 100644 --- a/packages/verkle/test/internalNode.spec.ts +++ b/packages/verkle/test/internalNode.spec.ts @@ -55,7 +55,7 @@ describe('verkle node - internal', () => { commitment: verkleCrypto.zeroCommitment, }) const serialized = node.serialize() - const decoded = decodeNode(serialized, 0, verkleCrypto) + const decoded = decodeNode(serialized, verkleCrypto) assert.deepEqual((decoded as InternalNode).children[0].commitment, child.commitment) }) }) diff --git a/packages/verkle/test/keys.spec.ts b/packages/verkle/test/keys.spec.ts deleted file mode 100644 index 0f41b1996e..0000000000 --- a/packages/verkle/test/keys.spec.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { concatBytes, hexToBytes, intToBytes } from '@ethereumjs/util' -import { assert, describe, it } from 'vitest' - -import { LeafType, getKey } from '../src/index.js' - -describe('should generate valid tree keys', () => { - it('should generate valid keys for each LeafType', () => { - const stem = hexToBytes('0x318dea512b6f3237a2d4763cf49bf26de3b617fb0cabe38a97807a5549df4d') - for (const leaf of [ - LeafType.Version, - LeafType.Balance, - LeafType.Nonce, - LeafType.CodeHash, - LeafType.CodeSize, - ]) { - const key = getKey(stem, leaf) - assert.equal(key.length, 32) - assert.deepEqual(key, concatBytes(stem, intToBytes(leaf))) - } - }) -}) diff --git a/packages/vm/src/runBlock.ts b/packages/vm/src/runBlock.ts index b7873603e9..5c5a15343b 100644 --- a/packages/vm/src/runBlock.ts +++ b/packages/vm/src/runBlock.ts @@ -17,7 +17,7 @@ import { bytesToHex, concatBytes, equalsBytes, - getTreeIndexesForStorageSlot, + getVerkleTreeIndexesForStorageSlot, hexToBytes, intToBytes, setLengthLeft, @@ -513,7 +513,7 @@ export async function accumulateParentBlockHash( // generate access witness if (vm.common.isActivatedEIP(6800)) { - const { treeIndex, subIndex } = getTreeIndexesForStorageSlot(ringKey) + const { treeIndex, subIndex } = getVerkleTreeIndexesForStorageSlot(ringKey) // just create access witnesses without charging for the gas ;( vm.stateManager as StatelessVerkleStateManager