Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

common: configure and fix kaustinen4 verkle testnet sync #3269

Merged
merged 53 commits into from
Mar 26, 2024
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
1befc1c
common: configure syncing kaustinen3 verkle testnet
g11tech Feb 3, 2024
594f94c
fix the vmexecution for verkle stateless init
g11tech Feb 3, 2024
e81515f
track and ignore the BLOCKHASH witness to avoid k3 blockhash issues
g11tech Feb 3, 2024
b890167
remove the tx to/from access fees
g11tech Feb 3, 2024
251f388
setup kaunstinen3 block15 stateless test execution for debugging
g11tech Feb 3, 2024
a9e5dc8
fix charging and waiving of tx origin/dest access fees
g11tech Feb 3, 2024
1c5f0b0
configure to run kaustinen4
g11tech Feb 6, 2024
dc1306f
setup to test the failing block 368
g11tech Feb 6, 2024
1c34416
debug and fix the code chunking boundary conditions
g11tech Feb 6, 2024
2cfa736
skip missing chunk lookup on the invalid opcode if code not loaded fr…
g11tech Feb 6, 2024
b17a392
setup to debug the failing block374 in spec
g11tech Feb 6, 2024
d8ffbb2
fix genesis spec
g11tech Feb 6, 2024
7523f65
add block353 to the test spec and help debug nethermind/besu for acce…
g11tech Feb 8, 2024
d55385f
add partial account functionality and set stateless verkle manager to…
g11tech Feb 9, 2024
7222037
load all partial basic data in account and use the same in the get co…
g11tech Feb 9, 2024
d859671
debug and fix block372 working including stem hack for k4
g11tech Feb 9, 2024
5726e1f
sync further and now setup failing block 479 for debugging
g11tech Feb 9, 2024
5b5eaef
change stem formation for addresses less than 20 bytes to match kaust…
g11tech Mar 2, 2024
d5705df
update test to test more than one block
g11tech Mar 2, 2024
d8df57e
add capability to test spec run blocks off beacon url
g11tech Mar 3, 2024
eab6a46
debug and handle if CL returns payload with snake case witness
g11tech Mar 3, 2024
4c0e4ab
fix the comment
g11tech Mar 4, 2024
d2142a9
vm: clear verkle cache in runBlock if opt is true
gabrocheleau Mar 5, 2024
547e092
add functionality to start stateless execution from any block in chain
g11tech Mar 5, 2024
ee9800a
chore: merge with master
gabrocheleau Mar 5, 2024
1385262
configure client to decouple execution and keep syncing despite chain…
g11tech Mar 6, 2024
a9bc254
modify the client to continue syncing ignoring block failures
g11tech Mar 6, 2024
ed1f1bd
save failing blocks if indicated by the flag
g11tech Mar 6, 2024
7e7bf2a
update test to pick generated testcases from dir
g11tech Mar 7, 2024
f8a1db6
debug and add missing data in toexecutionwitness
g11tech Mar 7, 2024
aec1f6f
fix the test spec for various scenarios
g11tech Mar 7, 2024
8067869
add witness errors to the debug log
g11tech Mar 7, 2024
8adb7a9
debug and fix the issue for slot mismatch of a valid block - 521
g11tech Mar 7, 2024
d06b42c
add hack to get over the stem matching issue of kaustinen4 by brutefo…
g11tech Mar 8, 2024
3244d86
allow one to provide witnesses to a rlped block via opts
g11tech Mar 17, 2024
4e27c76
restore stem calc to correct one
g11tech Mar 17, 2024
7ed686c
apply fix for codechunk comparision and better post state mismatches …
g11tech Mar 17, 2024
aaa5839
setup the kaustinen4 spec test to use geth test vectors
g11tech Mar 17, 2024
92e0dc1
remove the code creation cost and fix the code creation write cost an…
g11tech Mar 17, 2024
8c3d7c1
fix charging code read acceses for codecopy if from calldata and remo…
g11tech Mar 17, 2024
a3ca621
debug and match the create opcode gas consumptions
g11tech Mar 18, 2024
bd20bb9
commit new vestors and a fix for the contract create costs to not cha…
g11tech Mar 21, 2024
2e1b158
add blockhash set/get accesses and remove ignoring them from post sta…
g11tech Mar 21, 2024
e9973ef
remove cold cost for sload and sstore
g11tech Mar 21, 2024
b392741
Merge remote-tracking branch 'origin/master' into kaustinen3
g11tech Mar 25, 2024
d5f8a96
debug and fix 3 testcases in verkle stateless manager spec
g11tech Mar 25, 2024
3487ed7
fix rest of spec
g11tech Mar 26, 2024
e6b3f7d
make the get contract size optional in interface
g11tech Mar 26, 2024
326eff0
fix lint
g11tech Mar 26, 2024
357496d
fix vm api test
g11tech Mar 26, 2024
44f06c1
restore kaustinen2 genesis for preimages spec
g11tech Mar 26, 2024
0395441
Merge remote-tracking branch 'origin/master' into kaustinen3
g11tech Mar 26, 2024
d7e2635
client spec and lint fixes
g11tech Mar 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 61 additions & 3 deletions packages/block/src/from-beacon-payload.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { bigIntToHex } from '@ethereumjs/util'

import type { ExecutionPayload, VerkleExecutionWitness } from './types.js'
import type { PrefixedHexString } from '@ethereumjs/util'

type BeaconWithdrawal = {
index: string
Expand Down Expand Up @@ -34,6 +35,59 @@ export type BeaconPayloadJson = {
execution_witness?: VerkleExecutionWitness
}

type VerkleProofSnakeJson = {
commitments_by_path: PrefixedHexString[]
d: PrefixedHexString
depth_extension_present: PrefixedHexString
ipa_proof: {
cl: PrefixedHexString[]
cr: PrefixedHexString[]
final_evaluation: PrefixedHexString
}
other_stems: PrefixedHexString[]
}

type VerkleStateDiffSnakeJson = {
stem: PrefixedHexString
suffix_diffs: {
current_value: PrefixedHexString | null
new_value: PrefixedHexString | null
suffix: number | string
}[]
}

type VerkleExecutionWitnessSnakeJson = {
state_diff: VerkleStateDiffSnakeJson[]
verkle_proof: VerkleProofSnakeJson
}

function parseExecutionWitnessFromSnakeJson({
state_diff,
verkle_proof,
}: VerkleExecutionWitnessSnakeJson): VerkleExecutionWitness {
return {
stateDiff: state_diff.map(({ stem, suffix_diffs }) => ({
stem,
suffixDiffs: suffix_diffs.map(({ current_value, new_value, suffix }) => ({
currentValue: current_value,
newValue: new_value,
suffix,
})),
})),
verkleProof: {
commitmentsByPath: verkle_proof.commitments_by_path,
d: verkle_proof.d,
depthExtensionPresent: verkle_proof.depth_extension_present,
ipaProof: {
cl: verkle_proof.ipa_proof.cl,
cr: verkle_proof.ipa_proof.cr,
finalEvaluation: verkle_proof.ipa_proof.final_evaluation,
},
otherStems: verkle_proof.other_stems,
},
}
}

/**
* Converts a beacon block execution payload JSON object {@link BeaconPayloadJson} to the {@link ExecutionPayload} data needed to construct a {@link Block}.
* The JSON data can be retrieved from a consensus layer (CL) client on this Beacon API `/eth/v2/beacon/blocks/[block number]`
Expand Down Expand Up @@ -75,9 +129,13 @@ export function executionPayloadFromBeaconPayload(payload: BeaconPayloadJson): E
executionPayload.parentBeaconBlockRoot = payload.parent_beacon_block_root
}
if (payload.execution_witness !== undefined && payload.execution_witness !== null) {
// the casing structure in payload is already camel case, might be updated in
// kaustinen relaunch
executionPayload.executionWitness = payload.execution_witness
// the casing structure in payload could be camel case or snake depending upon the CL
executionPayload.executionWitness =
payload.execution_witness.verkleProof !== undefined
? payload.execution_witness
: parseExecutionWitnessFromSnakeJson(
payload.execution_witness as unknown as VerkleExecutionWitnessSnakeJson
)
}

return executionPayload
Expand Down
48 changes: 27 additions & 21 deletions packages/client/src/execution/vmexecution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,26 @@ export class VMExecution extends Execution {
return
}

const blockchain = this.chain.blockchain
if (typeof blockchain.getIteratorHead !== 'function') {
throw new Error('cannot get iterator head: blockchain has no getIteratorHead function')
}
const headBlock = await blockchain.getIteratorHead()
const { number, timestamp, stateRoot } = headBlock.header
this.chainStatus = {
height: number,
status: ExecStatus.VALID,
root: stateRoot,
hash: headBlock.hash(),
}

if (typeof blockchain.getTotalDifficulty !== 'function') {
throw new Error('cannot get iterator head: blockchain has no getTotalDifficulty function')
}
const td = await blockchain.getTotalDifficulty(headBlock.header.hash())
this.config.execCommon.setHardforkBy({ blockNumber: number, td, timestamp })
this.hardfork = this.config.execCommon.hardfork()

if (this.config.execCommon.gteHardfork(Hardfork.Prague)) {
if (!this.config.statelessVerkle) {
throw Error(`Currently stateful verkle execution not supported`)
Expand All @@ -233,33 +253,19 @@ export class VMExecution extends Execution {
this.vm = this.merkleVM!
}

if (typeof this.vm.blockchain.getIteratorHead !== 'function') {
throw new Error('cannot get iterator head: blockchain has no getIteratorHead function')
}
const headBlock = await this.vm.blockchain.getIteratorHead()
const { number, timestamp, stateRoot } = headBlock.header
this.chainStatus = {
height: number,
status: ExecStatus.VALID,
root: stateRoot,
hash: headBlock.hash(),
}
if (number === BIGINT_0) {
const genesisState =
this.chain['_customGenesisState'] ?? getGenesis(Number(this.vm.common.chainId()))
if (!genesisState) {
this.chain['_customGenesisState'] ?? getGenesis(Number(blockchain.common.chainId()))
if (
!genesisState &&
(this.vm instanceof DefaultStateManager || !this.config.statelessVerkle)
) {
throw new Error('genesisState not available')
} else {
await this.vm.stateManager.generateCanonicalGenesis(genesisState)
}
await this.vm.stateManager.generateCanonicalGenesis(genesisState)
}

if (typeof this.vm.blockchain.getTotalDifficulty !== 'function') {
throw new Error('cannot get iterator head: blockchain has no getTotalDifficulty function')
}
const td = await this.vm.blockchain.getTotalDifficulty(headBlock.header.hash())
this.config.execCommon.setHardforkBy({ blockNumber: number, td, timestamp })
this.hardfork = this.config.execCommon.hardfork()

await super.open()
// TODO: Should a run be started to execute any left over blocks?
// void this.run()
Expand Down
67 changes: 0 additions & 67 deletions packages/client/test/rpc/engine/kaustinen2.spec.ts

This file was deleted.

107 changes: 107 additions & 0 deletions packages/client/test/rpc/engine/kaustinen4.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import { Block, BlockHeader, executionPayloadFromBeaconPayload } from '@ethereumjs/block'
import * as td from 'testdouble'
import { assert, describe, it } from 'vitest'

import blocks from '../../testdata/blocks/kaustinen4.json'
import genesisJSON from '../../testdata/geth-genesis/kaustinen4.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 = '0x8493ed97fd4314acb6ed519867b086dc698e25df37ebe8f2bc77313537710744'

/**
* One can run this test in two formats:
* 1. On the saved blocks, comma separated which are limited (345,375,368,467)
* `TEST_SAVED_NUMBERS=345,375,368,467 npx vitest run test/rpc/engine/kaustinen4.spec.ts`
* 2. Directly pull slots from a kaustinen beacon url
* `TEST_ONLINE_SLOTS=345,353..360 PEER_BEACON_URL=https://beacon.verkle-gen-devnet-4.ethpandaops.io npx vitest run test/rpc/engine/kaustinen4.spec.ts`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is so cool, just tested this, works like a charm!!! 🔥 👍 🙂

*/

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
}

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

const parentPayload = executionPayloadFromBeaconPayload(parent as any)
const parentBlock = await Block.fromExecutionPayload(parentPayload, { common })
blockCache.remoteBlocks.set(parentPayload.blockHash.slice(2), parentBlock)
blockCache.executedBlocks.set(parentPayload.blockHash.slice(2), parentBlock)

const executePayload = executionPayloadFromBeaconPayload(execute as any)
const res = await rpc.request('engine_newPayloadV2', [executePayload])
assert.equal(res.result.status, 'VALID', 'valid status should be received')
}

describe(`valid verkle network setup`, async () => {
const { server, chain, common } = await setupChain(genesisJSON, 'post-merge', {
engine: true,
genesisStateRoot: genesisVerkleStateRoot,
})
const rpc = getRpcClient(server)
it('genesis should be correctly setup', async () => {
const res = await rpc.request('eth_getBlockByNumber', ['0x0', false])

const block0 = res.result
assert.equal(block0.hash, genesisVerkleBlockHash)
assert.equal(block0.stateRoot, genesisVerkleStateRoot)
})

// currently it seems the the blocks can't be played one after another as it seems
// to not do clean init of the statemanager. this isn't a problem in sequential
// execution, but need to be fixed up in the stateless random execution
// TEST_SAVED_NUMBERS=353,368,374,479
const savedTestCases = process.env.TEST_SAVED_NUMBERS?.split(',') ?? []

for (const testCase of savedTestCases) {
it(`run saved block ${testCase}`, async () => {
const testData = blocks[testCase]
if (testData === undefined) {
throw Error('unavailable data')
}
await runBlock({ common, chain, rpc }, blocks[testCase])
})
}

// we can also test online slots (not numbers because ELs don't provide witnesses
// directly, so we need to pull payload from beacon)
// TEST_ONLINE_SLOTS=345,375,368..467,
for (const numberOrRange of process.env.TEST_ONLINE_SLOTS?.split(',') ?? []) {
if (process.env.PEER_BEACON_URL === undefined) {
throw Error(`PEER_BEACON_URL env is not defined`)
}

const rangeSplit = numberOrRange.split('..')
const startSlot = parseInt(rangeSplit[0])
const endSlot = parseInt(rangeSplit[1] ?? rangeSplit[0])
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, startSlot)
console.log(execute.execution_witness)
it(`run fetched block slot: ${i} number: ${execute.block_number}`, async () => {
await runBlock({ common, chain, rpc }, { parent, execute })
parent = execute
})
}
}

it(`reset TD`, () => {
server.close()
BlockHeader.prototype['_consensusFormatValidation'] = originalValidate
td.reset()
})
})
Loading