Skip to content
2 changes: 2 additions & 0 deletions .github/workflows/browser.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,6 @@ jobs:
- run: npm run test:browser -w=@ethereumjs/statemanager
- run: npm run test:browser -w=@ethereumjs/evm
- run: npm run test:browser -w=@ethereumjs/vm
- run: npm run test:browser -w=@ethereumjs/verkle


23 changes: 16 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/block/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export { Block } from './block.js'
export { executionPayloadFromBeaconPayload } from './from-beacon-payload.js'
export { type BeaconPayloadJson, executionPayloadFromBeaconPayload } from './from-beacon-payload.js'
export { BlockHeader } from './header.js'
export { getDifficulty, valuesArrayToHeaderData } from './helpers.js'
export * from './types.js'
3 changes: 1 addition & 2 deletions packages/client/src/execution/vmexecution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,9 +188,8 @@ export class VMExecution extends Execution {
if (this.verkleVM !== undefined) {
return
}

this.config.logger.info(`Setting up verkleVM`)
const stateManager = new StatelessVerkleStateManager()
const stateManager = await StatelessVerkleStateManager.create()
this.verkleVM = await VM.create({
common: this.config.execCommon,
blockchain: this.chain.blockchain,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,40 +5,51 @@ import * as td from 'testdouble'
import { assert, describe, it } from 'vitest'

import blocks from '../../testdata/blocks/kaustinen4.json'
import genesisJSON from '../../testdata/geth-genesis/kaustinen5.json'
import genesisJSON from '../../testdata/geth-genesis/kaustinen6.json'
import { getRpcClient, setupChain } from '../helpers.js'

import type { Chain } from '../../../src/blockchain'
import type { BeaconPayloadJson } from '@ethereumjs/block'
import type { Common } from '@ethereumjs/common'
import type { HttpClient } from 'jayson/promise'
const genesisVerkleStateRoot = '0x382960711d9ccf58b9db20122e2253eb9bfa99d513f8c9d4e85b55971721f4de'
const genesisVerkleBlockHash = '0x086326f2922364dba375e7c9bed375d622845615c0974ffd1d3be0e34edbfbc3'
const genesisVerkleStateRoot = '0x1fbf85345a3cbba9a6d44f991b721e55620a22397c2a93ee8d5011136ac300ee'
const genesisVerkleBlockHash = '0x3fe165c03e7a77d1e3759362ebeeb16fd964cb411ce11fbe35c7032fab5b9a8a'

/**
* One can run this test in two formats:
* 1. On the saved blocks, comma separated which are limited (353,368,374,467)
* One can run this test in this format:
* 1. Directly pull slots from a kaustinen beacon url
* `TEST_ONLINE_SLOTS=15 PEER_BEACON_URL=https://beacon.verkle-gen-devnet-6.ethpandaops.io DEBUG=ethjs,vm:*,evm:*,statemanager:verkle* npx vitest run test/rpc/engine/kaustinen6.spec.ts`
*
* However there are other ways to run the test with save data and testvectors but they are from old versions but
* can be updated to make it work
*
* a. On the saved blocks, comma separated (were produced for kaustinen4 )
* `TEST_SAVED_NUMBERS=353,368,374,467 npx vitest run test/rpc/engine/kaustinen5.spec.ts`
* 2. Directly pull slots from a kaustinen beacon url
* `TEST_ONLINE_SLOTS=15 PEER_BEACON_URL=https://beacon.verkle-gen-devnet-5.ethpandaops.io DEBUG=ethjs,vm:*,evm:*,statemanager:verkle* npx vitest run test/rpc/engine/kaustinen5.spec.ts`
* 3. Geth produced testvectors
* `TEST_GETH_VEC_DIR=test/testdata/gethk5vecs DEBUG=ethjs,vm:*,evm:*,statemanager:verkle* npx vitest run test/rpc/engine/kaustinen5.spec.ts`
* b. Geth produced testvectors (were produced for kaustinen5)
* `TEST_GETH_VEC_DIR=test/testdata/gethk5vecs DEBUG=ethjs,vm:*,evm:*,statemanager:verkle* npx vitest run test/rpc/engine/kaustinen6.spec.ts`
*/

const originalValidate = (BlockHeader as any).prototype._consensusFormatValidation

async function fetchExecutionPayload(
peerBeaconUrl: string,
slot: number | string
): Promise<BeaconPayloadJson> {
const beaconBlock = await (await fetch(`${peerBeaconUrl}/eth/v2/beacon/blocks/${slot}`)).json()
return beaconBlock.data.message.body.execution_payload
): Promise<BeaconPayloadJson | undefined> {
let beaconPayload: BeaconPayloadJson | undefined = undefined
try {
const beaconBlock = await (await fetch(`${peerBeaconUrl}/eth/v2/beacon/blocks/${slot}`)).json()
beaconPayload = beaconBlock.data.message.body.execution_payload
// eslint-disable-next-line no-empty
} catch (_e) {}

return beaconPayload
}

async function runBlock(
{ chain, rpc, common }: { chain: Chain; rpc: HttpClient; common: Common },
{ execute, parent }: { execute: any; parent: any },
isBeaconData: boolean
isBeaconData: boolean,
context: any
) {
const blockCache = chain.blockCache

Expand All @@ -51,6 +62,11 @@ async function runBlock(
const executePayload =
isBeaconData === true ? executionPayloadFromBeaconPayload(execute as any) : execute
const res = await rpc.request('engine_newPayloadV2', [executePayload])

// if the block was not executed mark as skip so it shows in test
if (res.result.status === 'ACCEPTED') {
context.skip()
}
assert.equal(res.result.status, 'VALID', 'valid status should be received')
}

Expand All @@ -75,21 +91,22 @@ describe(`valid verkle network setup`, async () => {
const savedTestCases = process.env.TEST_SAVED_NUMBERS?.split(',') ?? []

for (const testCase of savedTestCases) {
it(`run saved block ${testCase}`, async () => {
it(`run saved block ${testCase}`, async (context) => {
let testData
let isBeaconData
if (process.env.SAVED_DATA_DIR !== undefined) {
const fileName = `${process.env.SAVED_DATA_DIR}/${testCase}.json`
testData = JSON.parse(readFileSync(fileName))[testCase]
testData = JSON.parse(readFileSync(fileName, 'utf8'))[testCase]
isBeaconData = false
} else {
// @ts-expect-error -- Typescript complains that `testCase` can't index the `blocks` object
testData = blocks[testCase]
isBeaconData = true
}
if (testData === undefined) {
throw Error('unavailable data')
}
await runBlock({ common, chain, rpc }, testData, isBeaconData)
await runBlock({ common, chain, rpc }, testData, isBeaconData, context)
})
}

Expand All @@ -107,9 +124,14 @@ describe(`valid verkle network setup`, async () => {
let parent = await fetchExecutionPayload(process.env.PEER_BEACON_URL, startSlot - 1)
for (let i = startSlot; i <= endSlot; i++) {
const execute = await fetchExecutionPayload(process.env.PEER_BEACON_URL, i)
it(`run fetched block slot: ${i} number: ${execute.block_number}`, async () => {
if (execute === undefined) {
// may be there was no block on this slot
continue
}

it(`run fetched block slot: ${i} number: ${execute.block_number}`, async (context) => {
try {
await runBlock({ common, chain, rpc }, { parent, execute }, true)
await runBlock({ common, chain, rpc }, { parent, execute }, true, context)
} finally {
parent = execute
}
Expand All @@ -123,8 +145,8 @@ describe(`valid verkle network setup`, async () => {
let parent = gethVecs[0]
for (let i = 1; i < gethVecs.length; i++) {
const execute = gethVecs[i]
it(`run geth vector: ${execute.blockNumber}`, async () => {
await runBlock({ common, chain, rpc }, { parent, execute }, false)
it(`run geth vector: ${execute.blockNumber}`, async (context) => {
await runBlock({ common, chain, rpc }, { parent, execute }, false, context)
parent = execute
})
}
Expand All @@ -139,8 +161,8 @@ describe(`valid verkle network setup`, async () => {

async function loadGethVectors(vectorsDirPath: string, opts: { common: Common }) {
// set chain id to 1 for geth vectors
opts.common._chainParams.chainId = BigInt(1)
const stateDiffVec = JSON.parse(readFileSync(`${vectorsDirPath}/statediffs.json`))
opts.common['_chainParams'].chainId = BigInt(1)
const stateDiffVec = JSON.parse(readFileSync(`${vectorsDirPath}/statediffs.json`, 'utf8'))
const executionWitness0 = {
stateDiff: [],
verkleProof: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -897,5 +897,5 @@
"nonce": "0x1234",
"mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"timestamp": "1711712640"
"timestamp": "1712918460"
}
10 changes: 5 additions & 5 deletions packages/common/src/chains.ts
Original file line number Diff line number Diff line change
Expand Up @@ -570,23 +570,23 @@ export const chains: ChainsDict = {
'enrtree://AKA3AM6LPBYEUDMVNU3BSVQJ5AD45Y7YPOHJLEF6W26QOE4VTUDPE@all.holesky.ethdisco.net',
],
},
kaustinen5: {
name: 'kaustinen5',
kaustinen6: {
name: 'kaustinen6',
chainId: 69420,
networkId: 69420,
defaultHardfork: 'prague',
consensus: {
type: 'pos',
algorithm: 'casper',
},
comment: 'Verkle kaustinen testnet 3 (likely temporary, do not hard-wire into production code)',
comment: 'Verkle kaustinen testnet 6 (likely temporary, do not hard-wire into production code)',
url: 'https://github.com/eth-clients/kaustinen/',
genesis: {
difficulty: '0x01',
extraData: '0x',
gasLimit: '0x17D7840',
nonce: '0x0000000000001234',
timestamp: '0x6606a9bc',
timestamp: '0x66190fbc',
},
hardforks: [
{
Expand Down Expand Up @@ -646,7 +646,7 @@ export const chains: ChainsDict = {
{
name: 'prague',
block: null,
timestamp: '1711712640',
timestamp: '1712848500',
},
],
bootstrapNodes: [],
Expand Down
8 changes: 4 additions & 4 deletions packages/common/src/enums.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export enum Chain {
Goerli = 5,
Sepolia = 11155111,
Holesky = 17000,
Kaustinen5 = 69420,
Kaustinen6 = 69420,
}

/**
Expand Down Expand Up @@ -44,10 +44,10 @@ export const ChainGenesis: Record<Chain, GenesisState> = {
blockNumber: BIGINT_0,
stateRoot: hexToBytes('0x69d8c9d72f6fa4ad42d4702b433707212f90db395eb54dc20bc85de253788783'),
},
[Chain.Kaustinen5]: {
name: 'kaustinen5',
[Chain.Kaustinen6]: {
name: 'kaustinen6',
blockNumber: BIGINT_0,
stateRoot: hexToBytes('0x382960711d9ccf58b9db20122e2253eb9bfa99d513f8c9d4e85b55971721f4de'),
stateRoot: hexToBytes('0x1fbf85345a3cbba9a6d44f991b721e55620a22397c2a93ee8d5011136ac300ee'),
},
}

Expand Down
8 changes: 4 additions & 4 deletions packages/evm/src/interpreter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -270,11 +270,11 @@ export class Interpreter {
this._runState.env.chargeCodeAccesses === true
) {
const contract = this._runState.interpreter.getAddress()

if (
!(this._runState.stateManager as StatelessVerkleStateManager).checkChunkWitnessPresent(
contract,
programCounter
)
!(await (
this._runState.stateManager as StatelessVerkleStateManager
).checkChunkWitnessPresent(contract, programCounter))
) {
throw Error(`Invalid witness with missing codeChunk for pc=${programCounter}`)
}
Expand Down
1 change: 0 additions & 1 deletion packages/evm/test/precompiles/0f-bls12-g2mul.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,6 @@ describe('Precompiles: BLS12-G2-MUL', () => {
const evm = await EVM.create({
common,
})
console.log(getActivePrecompiles(common))
const BLS12G2MUL = getActivePrecompiles(common).get('000000000000000000000000000000000000000f')!

for (const testVector of testData) {
Expand Down
2 changes: 1 addition & 1 deletion packages/genesis/test/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ describe('genesis test', () => {
const chainIds = Object.keys(ChainGenesis)
for (const chainId of chainIds) {
// Kaustinen can have an empty genesis state since verkle blocks contain their pre-state
if (Number(chainId) === Chain.Kaustinen5) continue
if (Number(chainId) === Chain.Kaustinen6) continue

const { name, stateRoot: expectedRoot } = ChainGenesis[chainId as unknown as Chain]

Expand Down
3 changes: 2 additions & 1 deletion packages/statemanager/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@
"debug": "^4.3.3",
"ethereum-cryptography": "^2.1.3",
"js-sdsl": "^4.1.4",
"lru-cache": "10.1.0"
"lru-cache": "10.1.0",
"verkle-cryptography-wasm": "^0.4.0"
},
"devDependencies": {
"@ethereumjs/block": "^5.2.0",
Expand Down
20 changes: 15 additions & 5 deletions packages/statemanager/src/accessWitness.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { getKey, getStem } from '@ethereumjs/verkle'
import debugDefault from 'debug'

import type { Address, PrefixedHexString } from '@ethereumjs/util'
import type { VerkleCrypto } from '@ethereumjs/verkle'

const { debug: createDebugLogger } = debugDefault
const debug = createDebugLogger('statemanager:verkle:aw')
Expand Down Expand Up @@ -72,13 +73,18 @@ export type AccessedStateWithAddress = AccessedState & {
export class AccessWitness {
stems: Map<PrefixedHexString, StemAccessEvent & StemMeta>
chunks: Map<PrefixedHexString, ChunkAccessEvent>

verkleCrypto: VerkleCrypto
constructor(
opts: {
verkleCrypto?: VerkleCrypto
stems?: Map<PrefixedHexString, StemAccessEvent & StemMeta>
chunks?: Map<PrefixedHexString, ChunkAccessEvent>
} = {}
) {
if (opts.verkleCrypto === undefined) {
throw new Error('verkle crypto required')
}
this.verkleCrypto = opts.verkleCrypto
this.stems = opts.stems ?? new Map<PrefixedHexString, StemAccessEvent & StemMeta>()
this.chunks = opts.chunks ?? new Map<PrefixedHexString, ChunkAccessEvent>()
}
Expand Down Expand Up @@ -164,7 +170,7 @@ export class AccessWitness {
return gas
}

touchCodeChunksRangeOnReadAndChargeGas(contact: Address, startPc: number, endPc: number) {
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)
Expand All @@ -173,7 +179,11 @@ export class AccessWitness {
return gas
}

touchCodeChunksRangeOnWriteAndChargeGas(contact: Address, startPc: number, endPc: number) {
touchCodeChunksRangeOnWriteAndChargeGas(
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)
Expand Down Expand Up @@ -251,7 +261,7 @@ export class AccessWitness {
// i.e. no fill cost is charged right now
const chunkFill = false

const accessedStemKey = getStem(address, treeIndex)
const accessedStemKey = getStem(this.verkleCrypto, address, treeIndex)
const accessedStemHex = bytesToHex(accessedStemKey)
let accessedStem = this.stems.get(accessedStemHex)
if (accessedStem === undefined) {
Expand Down Expand Up @@ -291,7 +301,7 @@ export class AccessWitness {

/**Create a shallow copy, could clone some caches in future for optimizations */
shallowCopy(): AccessWitness {
return new AccessWitness()
return new AccessWitness({ verkleCrypto: this.verkleCrypto })
}

merge(accessWitness: AccessWitness): void {
Expand Down
Loading