Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
20 changes: 10 additions & 10 deletions packages/block/src/block/block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -339,16 +339,16 @@ export class Block {
// Validation for Verkle blocks
// Unnecessary in this implementation since we're providing defaults if those fields are undefined
// TODO: Decide if we should actually require this or not
if (this.common.isActivatedEIP(6800)) {
if (this.executionWitness === undefined) {
throw EthereumJSErrorWithoutCode(`Invalid block: missing executionWitness`)
}
if (this.executionWitness === null) {
throw EthereumJSErrorWithoutCode(
`Invalid block: ethereumjs stateless client needs executionWitness`,
)
}
}
// if (this.common.isActivatedEIP(6800)) {
// if (this.executionWitness === undefined) {
// throw EthereumJSErrorWithoutCode(`Invalid block: missing executionWitness`)
// }
// if (this.executionWitness === null) {
// throw EthereumJSErrorWithoutCode(
// `Invalid block: ethereumjs stateless client needs executionWitness`,
// )
// }
// }
}

/**
Expand Down
10 changes: 5 additions & 5 deletions packages/block/src/block/constructors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,11 +147,11 @@ export function createBlockFromBytesArray(values: BlockBytes, opts?: BlockOption
)
}

if (header.common.isActivatedEIP(6800) && executionWitnessBytes === undefined) {
throw EthereumJSErrorWithoutCode(
'Invalid serialized block input: EIP-6800 is active, and execution witness is undefined',
)
}
// if (header.common.isActivatedEIP(6800) && executionWitnessBytes === undefined) {
// throw EthereumJSErrorWithoutCode(
// 'Invalid serialized block input: EIP-6800 is active, and execution witness is undefined',
// )
// }

// parse transactions
const transactions = []
Expand Down
18 changes: 18 additions & 0 deletions packages/common/src/eips.ts
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,15 @@ export const eipsDict: EIPsDict = {
minimumHardfork: Hardfork.Paris,
requiredEIPs: [4844],
},
/**
* Description : Verkle state transition via an overlay tree
* URL : https://eips.ethereum.org/EIPS/eip-7612
* Status : Draft
*/
7612: {
minimumHardfork: Hardfork.Verkle,
requiredEIPs: [4762, 6800 /* 7545 */],
},
/**
* Description : EOF Contract Creation
* URL : https://github.com/ethereum/EIPs/blob/dd32a34cfe4473bce143641bfffe4fd67e1987ab/EIPS/eip-7620.md
Expand Down Expand Up @@ -470,6 +479,15 @@ export const eipsDict: EIPsDict = {
minimumHardfork: Hardfork.Chainstart,
requiredEIPs: [2935],
},
/**
* Description : State conversion to Verkle Tree
* URL : https://eips.ethereum.org/EIPS/eip-7748
* Status : Draft
*/
7748: {
minimumHardfork: Hardfork.Verkle,
requiredEIPs: [7612],
},
/**
* Description : Ethereum state using a unified binary tree (experimental)
* URL : hhttps://eips.ethereum.org/EIPS/eip-7864
Expand Down
58 changes: 47 additions & 11 deletions packages/mpt/src/mpt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,13 +115,13 @@ export class MerklePatriciaTrie {
typeof window === 'undefined' ? (process?.env?.DEBUG?.includes('ethjs') ?? false) : false
this.debug = this.DEBUG
? (message: string, namespaces: string[] = []) => {
let log = this._debug
for (const name of namespaces) {
log = log.extend(name)
}
log(message)
let log = this._debug
for (const name of namespaces) {
log = log.extend(name)
}
: (..._: any) => {}
log(message)
}
: (..._: any) => { }

this.database(opts?.db ?? new MapDB<string, Uint8Array>(), valueEncoding)

Expand Down Expand Up @@ -332,8 +332,8 @@ export class MerklePatriciaTrie {
partialPath: {
stack: MPTNode[]
} = {
stack: [],
},
stack: [],
},
): Promise<Path> {
const targetKey = bytesToNibbles(key)
const keyLen = targetKey.length
Expand Down Expand Up @@ -401,9 +401,8 @@ export class MerklePatriciaTrie {
`Comparing node key to expected\n|| Node_Key: [${node.key()}]\n|| Expected: [${targetKey.slice(
progress,
progress + node.key().length,
)}]\n|| Matching: [${
targetKey.slice(progress, progress + node.key().length).toString() ===
node.key().toString()
)}]\n|| Matching: [${targetKey.slice(progress, progress + node.key().length).toString() ===
node.key().toString()
}]
`,
['find_path', 'extension_node'],
Expand Down Expand Up @@ -1089,6 +1088,43 @@ export class MerklePatriciaTrie {
this._db.checkpoints = []
}

/**
* Returns the next key with a value in the trie, starting from the given startKey (inclusive),
* along with the subsequent key to continue iteration.
*
* @param startKey - packed key bytes to start scanning from
* @returns
* - key: the first key ≥ startKey that has a value (or null if none)
* - nextKey: the key immediately after `key` (or null if there is no further key)
*/
async getNextValue(
startKey: Uint8Array,
): Promise<{ key: Uint8Array | undefined; nextKey: Uint8Array | undefined }> {
let foundKey: Uint8Array | undefined
let nextKey: Uint8Array | undefined
const startBigInt = bytesToBigInt(startKey)

await this.walkAllValueNodes(async (node: MPTNode, currentKey: number[]) => {
// only care about leaf nodes
if (!(node instanceof LeafMPTNode)) return

// reconstruct the full packed key for this leaf
const packed = nibblesTypeToPackedBytes(currentKey.concat(node.key()))
const v = bytesToBigInt(packed)

if (foundKey === undefined) {
// haven’t hit our startKey yet
if (v < startBigInt) return
foundKey = packed
} else if (nextKey === undefined) {
// this is the very next key after foundKey
nextKey = packed
}
// (no need to return anything; traversal will simply continue but we ignore further nodes)
})

return { key: foundKey, nextKey }
}
/**
* Returns a list of values stored in the trie
* @param startKey first unhashed key in the range to be returned (defaults to 0). Note, all keys must be of the same length or undefined behavior will result
Expand Down
1 change: 1 addition & 0 deletions packages/statemanager/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ export * from './simpleStateManager.ts'
export * from './statefulBinaryTreeStateManager.ts'
export * from './statefulVerkleStateManager.ts'
export * from './statelessVerkleStateManager.ts'
export * from './overlayStateManager.ts'
export * from './types.ts'
25 changes: 19 additions & 6 deletions packages/statemanager/src/merkleStateManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,13 @@ import type { Debugger } from 'debug'
export const CODEHASH_PREFIX = utf8ToBytes('c')

/**
* Default StateManager implementation for the VM.
* Merkle StateManager implementation for the VM.
*
* The state manager abstracts from the underlying data store
* by providing higher level access to accounts, contract code
* and storage slots.
*
* The default state manager implementation uses a
* The merkle state manager implementation uses a
* `@ethereumjs/mpt` trie as a data backend.
*
* Note that there is a `SimpleStateManager` dependency-free state
Expand Down Expand Up @@ -141,10 +141,8 @@ export class MerkleStateManager implements StateManagerInterface {
async putAccount(address: Address, account: Account | undefined): Promise<void> {
if (this.DEBUG) {
this._debug(
`Save account address=${address} nonce=${account?.nonce} balance=${
account?.balance
} contract=${account && account.isContract() ? 'yes' : 'no'} empty=${
account && account.isEmpty() ? 'yes' : 'no'
`Save account address=${address} nonce=${account?.nonce} balance=${account?.balance
} contract=${account && account.isContract() ? 'yes' : 'no'} empty=${account && account.isEmpty() ? 'yes' : 'no'
}`,
)
}
Expand Down Expand Up @@ -537,6 +535,21 @@ export class MerkleStateManager implements StateManagerInterface {
}
}

/**
* Find the next account key in the trie after `startKey`, using lexicographical iteration.
* Returns [key, value, nextKey] where:
* - key: the matched key >= startKey
* - nextKey: the next key after this one (or undefined if none)
*/
async getNextAtKey(startKey: Uint8Array): Promise<{ key: Uint8Array, nextKey: Uint8Array | undefined } | undefined> {
const { key, nextKey } = await this._trie.getNextValue(startKey)
if (key === undefined) {
return undefined
}
return { key, nextKey }

}

/**
* Gets the state-root of the Merkle-Patricia trie representation
* of the state of this StateManager. Will error if there are uncommitted
Expand Down
Loading