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

StateManager: stateless verkle state manager #3139

Merged
merged 99 commits into from
Dec 6, 2023
Merged
Show file tree
Hide file tree
Changes from 96 commits
Commits
Show all changes
99 commits
Select commit Hold shift + click to select a range
08df5cf
block: add verkle fields
gabrocheleau Nov 4, 2023
c6423d5
block: add verkle tests
gabrocheleau Nov 4, 2023
c0bf498
common: add verkle eip
gabrocheleau Nov 4, 2023
457fcb1
evm: add verkle eip
gabrocheleau Nov 4, 2023
26a73f6
vm: add verkle handling
gabrocheleau Nov 4, 2023
0fe9d3f
vm: add verkle tests
gabrocheleau Nov 4, 2023
2755783
verkle: add missing exports
gabrocheleau Nov 4, 2023
a075969
statemanager: add verkle package dependency
gabrocheleau Nov 4, 2023
e058bc8
statemanager: stateless verkle state manager
gabrocheleau Nov 4, 2023
20f1d45
statemanager: stateless verkle state manager tests
gabrocheleau Nov 4, 2023
04c503e
verkle: export verifyUpdate from wasm
gabrocheleau Nov 4, 2023
083ac89
stateManager: refactor verkle helpers with verkle package
gabrocheleau Nov 4, 2023
870f350
verkle: use npm's rust verkle wasm instead of local one
gabrocheleau Nov 4, 2023
35f3cf7
add praguge hf
g11tech Nov 7, 2023
6dfb263
add clarification comment
g11tech Nov 7, 2023
ccd116d
move executionWitness from header to state
g11tech Nov 9, 2023
775552d
verkle: fix treeIndexBytes length for pedersenHash
gabrocheleau Nov 9, 2023
e0ebeb5
stateManager: update test cases with prefixed hex strings
gabrocheleau Nov 9, 2023
12d82de
setup test stub for kaustinen2 blocks testing
g11tech Nov 9, 2023
75aa645
integrate the generated data in the test and verify verkle genesis root
g11tech Nov 14, 2023
bfe4330
Merge remote-tracking branch 'origin/master' into stateManager/statel…
g11tech Nov 14, 2023
77ecda6
statemanager: adjust getTreeKey usage
gabrocheleau Nov 14, 2023
0543ee1
statemanager: adjust treeKey usage in tests
gabrocheleau Nov 14, 2023
64626f7
erkle: fix treeIndexBytes length
gabrocheleau Nov 14, 2023
c2bb49a
integrate verklestatemanager with vm execution
g11tech Nov 15, 2023
516d491
handle resets across transition
g11tech Nov 15, 2023
0ad52d2
get the client and blockchain to use verkle state roots
g11tech Nov 15, 2023
6069ae5
get the basic kaustinen2 spec up with correct genesis setup
g11tech Nov 16, 2023
13a82f0
fix typo
g11tech Nov 16, 2023
6b3a8ff
setup kaustinen2 spec with kaustinen2 blocks
g11tech Nov 16, 2023
0649760
add trace
g11tech Nov 16, 2023
242583d
util: update byte util
gabrocheleau Nov 16, 2023
3c4f0e3
Merge branch 'master' into stateManager/statelessVerkleStateManager
gabrocheleau Nov 16, 2023
ba97551
vm: comment out failing block from verkle tesT
gabrocheleau Nov 16, 2023
14d5782
Merge branch 'stateManager/statelessVerkleStateManager' of https://gi…
gabrocheleau Nov 16, 2023
1217805
clean up some verkle-related test files
gabrocheleau Nov 16, 2023
9e6933b
vm: check against StatelessVerkleStateManager instead of checking for…
gabrocheleau Nov 16, 2023
18c6413
verkle: fix linting
gabrocheleau Nov 16, 2023
c6b6f1d
add kaustinen network and get it syncing with cl
g11tech Nov 16, 2023
f3fbf21
Merge remote-tracking branch 'origin/stateManager/statelessVerkleStat…
g11tech Nov 16, 2023
cb51826
genesis: add kaustinen to genesis files
gabrocheleau Nov 16, 2023
d03de48
Merge branch 'stateManager/statelessVerkleStateManager' of https://gi…
gabrocheleau Nov 16, 2023
5c4d7a2
genesis: undo temporarily
gabrocheleau Nov 16, 2023
4b172fe
read account fixes
g11tech Nov 17, 2023
ccb0c1e
Merge branch 'stateManager/statelessVerkleStateManager' of https://gi…
gabrocheleau Nov 17, 2023
f9fcbe1
stateManager: fix bigIntToBytes util
gabrocheleau Nov 17, 2023
8b093f3
verkle: remove unnecessary extra verkle test blocks from vm testdata
gabrocheleau Nov 17, 2023
18ca48a
block: update VerkleExecutionWitness to include post state
gabrocheleau Nov 17, 2023
11d1cfb
block: update verklestatediff format
gabrocheleau Nov 17, 2023
b0e958a
statemanageR: populate test data with newvalue
gabrocheleau Nov 17, 2023
143ea80
vm: add verifyPostState to vm and stateManager
gabrocheleau Nov 17, 2023
f36d559
allow blocks to be constructed with unavailable witness
g11tech Nov 18, 2023
3be3628
store and load block data with execution witness
g11tech Nov 18, 2023
4009313
statemanager: partial implementation of clearContractStorage, and cor…
gabrocheleau Nov 18, 2023
8338238
Merge branch 'stateManager/statelessVerkleStateManager' of https://gi…
gabrocheleau Nov 18, 2023
42481b7
update the kaustinen config for relaunched network
g11tech Nov 26, 2023
c284b50
statemanager: minimal implementation of putContractCode
gabrocheleau Nov 26, 2023
c07736b
statemanager: initialize null codehash as keccak256 null
gabrocheleau Nov 26, 2023
b3740b5
add example gas schedule for verkle full list pending
g11tech Nov 26, 2023
95a48e5
statemanager: fix suffix comparison
gabrocheleau Nov 26, 2023
1b93ed2
Merge branch 'stateManager/statelessVerkleStateManager' of https://gi…
gabrocheleau Nov 26, 2023
882f25a
statemanageR: use our own hexToBytes
gabrocheleau Nov 26, 2023
cc835c0
statemanager: undefined balance/nonce handling
gabrocheleau Nov 26, 2023
451e431
block: use bytesToUtf8 helper instead of Buffer.from().toString()
gabrocheleau Dec 2, 2023
6d1b983
block: typo
gabrocheleau Dec 2, 2023
3eea6f5
allow client to skip engine newpayload exec and do only chain execution
g11tech Dec 3, 2023
776f5a8
fix the verkle vmchain execution and skip marking block invalid for v…
g11tech Dec 3, 2023
e0c3dc8
fix writing and hence loading the missing witness for the empty tx bl…
g11tech Dec 3, 2023
e881a4b
remove console.log
g11tech Dec 3, 2023
ce821cd
Merge branch 'stateManager/statelessVerkleStateManager' of https://gi…
gabrocheleau Dec 4, 2023
f759efd
Merge branch 'master' into stateManager/statelessVerkleStateManager
gabrocheleau Dec 4, 2023
0192b67
block: clarify comment and more efficient ordering
gabrocheleau Dec 4, 2023
f9ff242
block: remove unused test data
gabrocheleau Dec 4, 2023
f09e188
blockchain: reorder function declaration to remove eslint ignore rules
gabrocheleau Dec 4, 2023
b2ca454
common: remove fake eip from readme
gabrocheleau Dec 4, 2023
f658767
common: remove duplicate holesky chain enum
gabrocheleau Dec 4, 2023
c0ad152
statemanager: update test
gabrocheleau Dec 4, 2023
1302382
statemanager: clean up kaustinen test data
gabrocheleau Dec 4, 2023
3ae21c9
verkle: reorganize vekrle tests and add getStem tests
gabrocheleau Dec 4, 2023
5e6a6c0
statemangaer: clean up statemanager and refactor getStem tests to ver…
gabrocheleau Dec 4, 2023
59aac4d
block: add description to verkle execution witness
gabrocheleau Dec 5, 2023
eb25956
block: remove todo
gabrocheleau Dec 5, 2023
c7240ab
client: update vm casing
gabrocheleau Dec 5, 2023
e7722d0
client: Vm -> VM
gabrocheleau Dec 5, 2023
6375711
client: update statemanager instanceof check
gabrocheleau Dec 5, 2023
07e6c4b
stateManager: add doc to verkle stateless state manager and cleanup
gabrocheleau Dec 5, 2023
41b80fb
stateManager: add experimental note
gabrocheleau Dec 5, 2023
d984869
block: reorder executionWitness and opts, and do not attempt to decod…
gabrocheleau Dec 6, 2023
ea35a7a
vm: fix tests
gabrocheleau Dec 6, 2023
66b6025
blockchain: fix util ordering
gabrocheleau Dec 6, 2023
39a18a9
genesis: fix genesis test for kaustinen
gabrocheleau Dec 6, 2023
26d6af0
statemanager: attempt to fix browser test
gabrocheleau Dec 6, 2023
5520db7
Merge branch 'master' into stateManager/statelessVerkleStateManager
gabrocheleau Dec 6, 2023
a1dfe01
StateManager: correct browser test stateless verkle SM exclusion path
holgerd77 Dec 6, 2023
42c1269
More experimental notes
holgerd77 Dec 6, 2023
77addfd
apply feedback
g11tech Dec 6, 2023
b2c7a29
statemanager: make verkle package a peer dependency
gabrocheleau Dec 6, 2023
4e16f25
statemanager: mark verkle as optional peerDep
gabrocheleau Dec 6, 2023
510ef25
client: make merkleVm and verkleVM optional instead of nullable
gabrocheleau Dec 6, 2023
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
1 change: 1 addition & 0 deletions package-lock.json

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

96 changes: 87 additions & 9 deletions packages/block/src/block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
Withdrawal,
bigIntToHex,
bytesToHex,
bytesToUtf8,
equalsBytes,
fetchFromProvider,
getProvider,
Expand All @@ -30,6 +31,7 @@ import type {
HeaderData,
JsonBlock,
JsonRpcBlock,
VerkleExecutionWitness,
} from './types.js'
import type { Common } from '@ethereumjs/common'
import type {
Expand All @@ -50,6 +52,13 @@ export class Block {
public readonly withdrawals?: Withdrawal[]
public readonly common: Common

/**
* EIP-6800: Verkle Proof Data (experimental)
* null implies that the non default executionWitness might exist but not available
* and will not lead to execution of the block via vm with verkle stateless manager
*/
public readonly executionWitness?: VerkleExecutionWitness | null

private cache: {
txTrieRoot?: Uint8Array
} = {}
Expand Down Expand Up @@ -92,6 +101,7 @@ export class Block {
transactions: txsData,
uncleHeaders: uhsData,
withdrawals: withdrawalsData,
executionWitness: executionWitnessData,
} = blockData
const header = BlockHeader.fromHeaderData(headerData, opts)

Expand Down Expand Up @@ -125,8 +135,11 @@ export class Block {
}

const withdrawals = withdrawalsData?.map(Withdrawal.fromWithdrawalData)
// The witness data is planned to come in rlp serialized bytes so leave this
// stub till that time
const executionWitness = executionWitnessData

return new Block(header, transactions, uncleHeaders, withdrawals, opts)
return new Block(header, transactions, uncleHeaders, withdrawals, opts, executionWitness)
}

/**
Expand All @@ -152,18 +165,18 @@ export class Block {
* @param opts
*/
public static fromValuesArray(values: BlockBytes, opts?: BlockOptions) {
if (values.length > 4) {
throw new Error('invalid block. More values than expected were received')
if (values.length > 5) {
throw new Error(`invalid block. More values=${values.length} than expected were received`)
}

// First try to load header so that we can use its common (in case of setHardfork being activated)
// to correctly make checks on the hardforks
const [headerData, txsData, uhsData, withdrawalBytes] = values
const [headerData, txsData, uhsData, withdrawalBytes, executionWitnessBytes] = values
const header = BlockHeader.fromValuesArray(headerData, opts)

if (
header.common.isActivatedEIP(4895) &&
(values[3] === undefined || !Array.isArray(values[3]))
(withdrawalBytes === undefined || !Array.isArray(withdrawalBytes))
) {
throw new Error(
'Invalid serialized block input: EIP-4895 is active, and no withdrawals were provided as array'
Expand Down Expand Up @@ -208,7 +221,18 @@ export class Block {
}))
?.map(Withdrawal.fromWithdrawalData)

return new Block(header, transactions, uncleHeaders, withdrawals, opts)
// executionWitness are not part of the EL fetched blocks via eth_ bodies method
// they are currently only available via the engine api constructed blocks
let executionWitness
if (header.common.isActivatedEIP(6800) && executionWitnessBytes !== undefined) {
executionWitness = JSON.parse(bytesToUtf8(RLP.decode(executionWitnessBytes) as Uint8Array))
} else {
// don't assign default witness if eip 6800 is implemented as it leads to incorrect
// assumptions while executing the block. if not present in input implies its unavailable
executionWitness = null
}

return new Block(header, transactions, uncleHeaders, withdrawals, opts, executionWitness)
}

/**
Expand Down Expand Up @@ -300,6 +324,7 @@ export class Block {
feeRecipient: coinbase,
transactions,
withdrawals: withdrawalsData,
executionWitness,
} = payload

const txs = []
Expand Down Expand Up @@ -331,7 +356,16 @@ export class Block {
}

// we are not setting setHardfork as common is already set to the correct hf
const block = Block.fromBlockData({ header, transactions: txs, withdrawals }, options)
const block = Block.fromBlockData(
{ header, transactions: txs, withdrawals, executionWitness },
options
)
if (
block.common.isActivatedEIP(6800) &&
(executionWitness === undefined || executionWitness === null)
) {
throw Error('Missing executionWitness for EIP-6800 activated executionPayload')
}
// Verify blockHash matches payload
if (!equalsBytes(block.hash(), hexToBytes(payload.blockHash))) {
const validationError = `Invalid blockHash, expected: ${
Expand Down Expand Up @@ -366,13 +400,34 @@ export class Block {
transactions: TypedTransaction[] = [],
uncleHeaders: BlockHeader[] = [],
withdrawals?: Withdrawal[],
opts: BlockOptions = {}
opts: BlockOptions = {},
executionWitness?: VerkleExecutionWitness | null
) {
this.header = header ?? BlockHeader.fromHeaderData({}, opts)
this.common = this.header.common

this.transactions = transactions
this.withdrawals = withdrawals ?? (this.common.isActivatedEIP(4895) ? [] : undefined)
this.executionWitness = executionWitness
// null indicates an intentional absence of value or unavailability
// undefined indicates that the executionWitness should be initialized with the default state
if (this.common.isActivatedEIP(6800) && this.executionWitness === undefined) {
this.executionWitness = {
stateDiff: [],
verkleProof: {
commitmentsByPath: [],
d: '0x',
depthExtensionPresent: '0x',
ipaProof: {
cl: [],
cr: [],
finalEvaluation: '0x',
},
otherStems: [],
},
}
}

this.uncleHeaders = uncleHeaders
if (uncleHeaders.length > 0) {
this.validateUncles()
Expand All @@ -394,14 +449,22 @@ export class Block {
throw new Error('Cannot have a withdrawals field if EIP 4895 is not active')
}

if (
!this.common.isActivatedEIP(6800) &&
executionWitness !== undefined &&
executionWitness !== null
) {
throw new Error(`Cannot have executionWitness field if EIP 6800 is not active `)
}

const freeze = opts?.freeze ?? true
if (freeze) {
Object.freeze(this)
}
}

/**
* Returns a Array of the raw Bytes Arays of this block, in order.
* Returns a Array of the raw Bytes Arrays of this block, in order.
*/
raw(): BlockBytes {
const bytesArray = <BlockBytes>[
Expand All @@ -415,6 +478,10 @@ export class Block {
if (withdrawalsRaw) {
bytesArray.push(withdrawalsRaw)
}
if (this.executionWitness !== undefined && this.executionWitness !== null) {
const executionWitnessBytes = RLP.encode(JSON.stringify(this.executionWitness))
bytesArray.push(executionWitnessBytes as any)
}
return bytesArray
}

Expand Down Expand Up @@ -559,6 +626,17 @@ export class Block {
const msg = this._errorMsg('invalid withdrawals trie')
throw new Error(msg)
}

// Validation for Verkle blocks
// Unnecessary in this implementation since we're providing defaults if those fields are undefined
if (this.common.isActivatedEIP(6800)) {
if (this.executionWitness === undefined) {
throw new Error(`Invalid block: missing executionWitness`)
}
if (this.executionWitness === null) {
throw new Error(`Invalid block: ethereumjs stateless client needs executionWitness`)
}
}
}

/**
Expand Down
9 changes: 8 additions & 1 deletion packages/block/src/from-beacon-payload.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { bigIntToHex } from '@ethereumjs/util'

import type { ExecutionPayload } from './types.js'
import type { ExecutionPayload, VerkleExecutionWitness } from './types.js'

type BeaconWithdrawal = {
index: string
Expand Down Expand Up @@ -30,6 +30,8 @@ export type BeaconPayloadJson = {
blob_gas_used?: string
excess_blob_gas?: string
parent_beacon_block_root?: string
// the casing of VerkleExecutionWitness remains same camel case for now
execution_witness?: VerkleExecutionWitness
}

/**
Expand Down Expand Up @@ -72,6 +74,11 @@ export function executionPayloadFromBeaconPayload(payload: BeaconPayloadJson): E
if (payload.parent_beacon_block_root !== undefined && payload.parent_beacon_block_root !== null) {
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
}

return executionPayload
}
10 changes: 9 additions & 1 deletion packages/block/src/header.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import { keccak256 } from 'ethereum-cryptography/keccak.js'
import { CLIQUE_EXTRA_SEAL, CLIQUE_EXTRA_VANITY } from './clique.js'
import { fakeExponential, valuesArrayToHeaderData } from './helpers.js'

import type { BlockHeaderBytes, BlockOptions, HeaderData, JsonHeader } from './types.js'
import type { BlockHeaderBytes, BlockOptions, HeaderData, JsonHeader } from './types'
import type { CliqueConfig } from '@ethereumjs/common'
import type { BigIntLike } from '@ethereumjs/util'

Expand Down Expand Up @@ -647,6 +647,14 @@ export class BlockHeader {
if (this.common.isActivatedEIP(4895) === true) {
rawItems.push(this.withdrawalsRoot!)
}

// in kaunstinen 2 verkle is scheduled after withdrawals, will eventually be post deneb hopefully
if (this.common.isActivatedEIP(6800) === true) {
// execution witness is not mandatory part of the the block so nothing to push here
// but keep this comment segment for clarity regarding the same and move it according as per the
// HF sequence eventually planned
}

if (this.common.isActivatedEIP(4844) === true) {
rawItems.push(bigIntToUnpaddedBytes(this.blobGasUsed!))
rawItems.push(bigIntToUnpaddedBytes(this.excessBlobGas!))
Expand Down
59 changes: 59 additions & 0 deletions packages/block/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,45 @@ export interface BlockOptions {
skipConsensusFormatValidation?: boolean
}

export interface VerkleProof {
commitmentsByPath: PrefixedHexString[]
d: PrefixedHexString
depthExtensionPresent: PrefixedHexString
ipaProof: {
cl: PrefixedHexString[]
cr: PrefixedHexString[]
finalEvaluation: PrefixedHexString
}
otherStems: PrefixedHexString[]
}

export interface VerkleStateDiff {
stem: PrefixedHexString
suffixDiffs: {
currentValue: PrefixedHexString | null
newValue: PrefixedHexString | null
suffix: number
}[]
}

/**
* Experimental, object format could eventual change.
* An object that provides the state and proof necessary for verkle stateless execution
* */
export interface VerkleExecutionWitness {
/**
* An array of state diffs.
* Each item corresponding to state accesses or state modifications of the block.
* In the current design, it also contains the resulting state of the block execution (post-state).
*/
stateDiff: VerkleStateDiff[]
/**
* The verkle proof for the block.
* Proves that the provided stateDiff belongs to the canonical verkle tree.
*/
verkleProof: VerkleProof
}

/**
* A block header's data.
*/
Expand Down Expand Up @@ -108,13 +147,29 @@ export interface BlockData {
transactions?: Array<TxData[TransactionType]>
uncleHeaders?: Array<HeaderData>
withdrawals?: Array<WithdrawalData>
/**
* EIP-6800: Verkle Proof Data (experimental)
*/
executionWitness?: VerkleExecutionWitness | null
}

export type WithdrawalsBytes = WithdrawalBytes[]
export type ExecutionWitnessBytes = Uint8Array

export type BlockBytes =
| [BlockHeaderBytes, TransactionsBytes, UncleHeadersBytes]
| [BlockHeaderBytes, TransactionsBytes, UncleHeadersBytes, WithdrawalsBytes]
| [
BlockHeaderBytes,
TransactionsBytes,
UncleHeadersBytes,
WithdrawalsBytes,
ExecutionWitnessBytes
]

/**
* BlockHeaderBuffer is a Buffer array, except for the Verkle PreState which is an array of prestate arrays.
*/
export type BlockHeaderBytes = Uint8Array[]
export type BlockBodyBytes = [TransactionsBytes, UncleHeadersBytes, WithdrawalsBytes?]
/**
Expand All @@ -134,6 +189,7 @@ export interface JsonBlock {
transactions?: JsonTx[]
uncleHeaders?: JsonHeader[]
withdrawals?: JsonRpcWithdrawal[]
executionWitness?: VerkleExecutionWitness | null
}

/**
Expand Down Expand Up @@ -192,6 +248,7 @@ export interface JsonRpcBlock {
blobGasUsed?: string // If EIP-4844 is enabled for this block, returns the blob gas used for the block
excessBlobGas?: string // If EIP-4844 is enabled for this block, returns the excess blob gas for the block
parentBeaconBlockRoot?: string // If EIP-4788 is enabled for this block, returns parent beacon block root
executionWitness?: VerkleExecutionWitness | null // If Verkle is enabled for this block
}

// Note: all these strings are 0x-prefixed
Expand Down Expand Up @@ -222,4 +279,6 @@ export type ExecutionPayload = {
blobGasUsed?: PrefixedHexString // QUANTITY, 64 Bits
excessBlobGas?: PrefixedHexString // QUANTITY, 64 Bits
parentBeaconBlockRoot?: PrefixedHexString // QUANTITY, 64 Bits
// VerkleExecutionWitness is already a hex serialized object
executionWitness?: VerkleExecutionWitness | null // QUANTITY, 64 Bits, null imples not available
}
Loading