Skip to content

Commit 05552af

Browse files
Verkle Implementation: Build out Trie Processing (#3430)
* add verkle crypto to trie * More spam * Update leafNode.create [no ci] * small removal [no ci] * Update trie.get to check existence first * Update `put` when inserting a new leaf node [no ci] * more halfway stuff [no ci] * Remove unnecessary commit [no ci] * update verkle crypto dep [no ci] * Add helper to create leaf values as 16 bytes * Changes to support c1 and c2 [no ci] * update new leaf node commitment correctly [no ci] * Changes needed to make `put` work [no ci] * Begin fixing internal node implementation [no ci] * move verkleCrypto to verkleNode constructor opts * address feedback * WIP [no ci] * Finish naive findpath implementation * Update internal node layout * WIP [no ci] * Partial implementation of put [no ci] * update verkle crypto [no ci] * Update this.root [no ci] * Clean up db opt typing [no ci] * API clean/comments [no ci] * fix logic bug for nonexistent path [no ci] * Describe logic for updating tree along path [no ci] * Update `put` to use `saveStack` [no ci] * WIP [no ci] * revise leafNode.create API [no ci] * more updates to put/get [no ci] * More wip [no ci] * Fix bug in internalNode deserialization [no ci] * Add more comments [no ci] * remove duplicative function [no ci] * more wip [no ci] * Add code to produce a 2 layer tree [no ci] * wip [no ci] * Add some initial debug logs [no ci] * More progress [no ci] * more half-working fixes [no ci] * Fix typing issues and remove walk controller * Remove walk controller export [no ci] * Add new test to demonstrate putting values in the trie [no ci] * Add comment * Remove obsolete references * lint * Remove references to depth and unused API components * Update packages/verkle/src/node/internalNode.ts Co-authored-by: Gabriel Rocheleau <[email protected]> * Address feedback * fix tests --------- Co-authored-by: Gabriel Rocheleau <[email protected]>
1 parent cf2b211 commit 05552af

17 files changed

+685
-624
lines changed

package-lock.json

Lines changed: 5 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/util/src/verkle.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ export interface VerkleCrypto {
4343
) => Uint8Array // Commitment
4444
zeroCommitment: Uint8Array
4545
verifyExecutionWitnessPreState: (prestateRoot: string, execution_witness_json: string) => boolean
46+
hashCommitment: (commitment: Uint8Array) => Uint8Array
47+
serializeCommitment: (commitment: Uint8Array) => Uint8Array
4648
}
4749

4850
/**

packages/verkle/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,9 @@
5252
"tsc": "../../config/cli/ts-compile.sh"
5353
},
5454
"dependencies": {
55+
"debug": "^4.3.4",
5556
"lru-cache": "10.1.0",
56-
"verkle-cryptography-wasm": "^0.4.2",
57+
"verkle-cryptography-wasm": "^0.4.4",
5758
"@ethereumjs/block": "^5.2.0",
5859
"@ethereumjs/rlp": "^5.0.2",
5960
"@ethereumjs/util": "^9.0.3"

packages/verkle/src/node/baseVerkleNode.ts

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,22 @@ import { RLP } from '@ethereumjs/rlp'
22

33
import { type VerkleNodeInterface, type VerkleNodeOptions, type VerkleNodeType } from './types.js'
44

5+
import type { VerkleCrypto } from 'verkle-cryptography-wasm'
6+
57
export abstract class BaseVerkleNode<T extends VerkleNodeType> implements VerkleNodeInterface {
68
public commitment: Uint8Array
7-
public depth: number
8-
9+
protected verkleCrypto: VerkleCrypto
910
constructor(options: VerkleNodeOptions[T]) {
1011
this.commitment = options.commitment
11-
this.depth = options.depth
12+
this.verkleCrypto = options.verkleCrypto
1213
}
1314

14-
abstract commit(): Uint8Array
15-
1615
// Hash returns the field representation of the commitment.
1716
hash(): Uint8Array {
18-
throw new Error('Not implemented')
17+
return this.verkleCrypto.hashCommitment(this.commitment)
1918
}
2019

21-
abstract insert(key: Uint8Array, value: Uint8Array, nodeResolverFn: () => void): void
22-
20+
// Returns an array of Uint8Arrays containing the values necessary to reconstruct a node from the DB (where we store them in a RLP serialized format)
2321
abstract raw(): Uint8Array[]
2422

2523
/**
Lines changed: 52 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1,119 +1,89 @@
1-
import { equalsBytes } from '@ethereumjs/util'
2-
3-
import { POINT_IDENTITY } from '../util/crypto.js'
1+
import { type VerkleCrypto } from '@ethereumjs/util'
42

53
import { BaseVerkleNode } from './baseVerkleNode.js'
6-
import { LeafNode } from './leafNode.js'
74
import { NODE_WIDTH, VerkleNodeType } from './types.js'
85

9-
import type { VerkleNode, VerkleNodeOptions } from './types.js'
6+
import type { ChildNode, VerkleNodeOptions } from './types.js'
107

118
export class InternalNode extends BaseVerkleNode<VerkleNodeType.Internal> {
12-
// Array of references to children nodes
13-
public children: Array<VerkleNode | null>
14-
public copyOnWrite: Record<string, Uint8Array>
9+
// Array of tuples of uncompressed commitments (i.e. 64 byte Uint8Arrays) to child nodes along with the path to that child (i.e. the partial stem)
10+
public children: Array<ChildNode>
1511
public type = VerkleNodeType.Internal
1612

17-
/* TODO: options.children is not actually used here */
1813
constructor(options: VerkleNodeOptions[VerkleNodeType.Internal]) {
1914
super(options)
20-
this.children = options.children ?? new Array(NODE_WIDTH).fill(null)
21-
this.copyOnWrite = options.copyOnWrite ?? {}
22-
}
23-
24-
commit(): Uint8Array {
25-
throw new Error('Not implemented')
26-
}
27-
28-
cowChild(_: number): void {
29-
// Not implemented yet
15+
this.children =
16+
options.children ??
17+
new Array(256).fill({
18+
commitment: options.verkleCrypto.zeroCommitment,
19+
path: new Uint8Array(),
20+
})
3021
}
3122

32-
setChild(index: number, child: VerkleNode) {
33-
this.children[index] = child
23+
// Updates the commitment value for a child node at the corresponding index
24+
setChild(childIndex: number, child: ChildNode) {
25+
// Get previous child commitment at `index`
26+
const oldChildReference = this.children[childIndex]
27+
// Updates the commitment to the child node at `index`
28+
this.children[childIndex] = { ...child }
29+
// Updates the overall node commitment based on the update to this child
30+
this.commitment = this.verkleCrypto.updateCommitment(
31+
this.commitment,
32+
childIndex,
33+
// The hashed child commitments are used when updating the internal node commitment
34+
this.verkleCrypto.hashCommitment(oldChildReference.commitment),
35+
this.verkleCrypto.hashCommitment(child.commitment)
36+
)
3437
}
3538

36-
static fromRawNode(rawNode: Uint8Array[], depth: number): InternalNode {
39+
static fromRawNode(rawNode: Uint8Array[], verkleCrypto: VerkleCrypto): InternalNode {
3740
const nodeType = rawNode[0][0]
3841
if (nodeType !== VerkleNodeType.Internal) {
3942
throw new Error('Invalid node type')
4043
}
4144

42-
// The length of the rawNode should be the # of children, + 2 for the node type and the commitment
43-
if (rawNode.length !== NODE_WIDTH + 2) {
45+
// The length of the rawNode should be the # of children * 2 (for commitments and paths) + 2 for the node type and the commitment
46+
if (rawNode.length !== NODE_WIDTH * 2 + 2) {
4447
throw new Error('Invalid node length')
4548
}
4649

47-
// TODO: Generate Point from rawNode value
4850
const commitment = rawNode[rawNode.length - 1]
51+
const childrenCommitments = rawNode.slice(1, NODE_WIDTH + 1)
52+
const childrenPaths = rawNode.slice(NODE_WIDTH + 1, NODE_WIDTH * 2 + 1)
4953

50-
return new InternalNode({ commitment, depth })
54+
const children = childrenCommitments.map((commitment, idx) => {
55+
return { commitment, path: childrenPaths[idx] }
56+
})
57+
return new InternalNode({ commitment, verkleCrypto, children })
5158
}
5259

53-
static create(depth: number): InternalNode {
60+
/**
61+
* Generates a new Internal node with default commitment
62+
*/
63+
static create(verkleCrypto: VerkleCrypto): InternalNode {
5464
const node = new InternalNode({
55-
commitment: POINT_IDENTITY,
56-
depth,
65+
commitment: verkleCrypto.zeroCommitment,
66+
verkleCrypto,
5767
})
5868

5969
return node
6070
}
6171

62-
getChildren(index: number): VerkleNode | null {
63-
return this.children?.[index] ?? null
64-
}
65-
66-
insert(key: Uint8Array, value: Uint8Array, resolver: () => void): void {
67-
const values = new Array<Uint8Array>(NODE_WIDTH)
68-
values[key[31]] = value
69-
this.insertStem(key.slice(0, 31), values, resolver)
70-
}
71-
72-
insertStem(stem: Uint8Array, values: Uint8Array[], resolver: () => void): void {
73-
// Index of the child pointed by the next byte in the key
74-
const childIndex = stem[this.depth]
75-
76-
const child = this.children[childIndex]
77-
78-
if (child instanceof LeafNode) {
79-
this.cowChild(childIndex)
80-
if (equalsBytes(child.stem, stem)) {
81-
return child.insertMultiple(stem, values)
82-
}
83-
84-
// A new branch node has to be inserted. Depending
85-
// on the next byte in both keys, a recursion into
86-
// the moved leaf node can occur.
87-
const nextByteInExistingKey = child.stem[this.depth + 1]
88-
const newBranch = InternalNode.create(this.depth + 1)
89-
newBranch.cowChild(nextByteInExistingKey)
90-
this.children[childIndex] = newBranch
91-
newBranch.children[nextByteInExistingKey] = child
92-
child.depth += 1
93-
94-
const nextByteInInsertedKey = stem[this.depth + 1]
95-
if (nextByteInInsertedKey === nextByteInExistingKey) {
96-
return newBranch.insertStem(stem, values, resolver)
97-
}
98-
99-
// Next word differs, so this was the last level.
100-
// Insert it directly into its final slot.
101-
const leafNode = LeafNode.create(stem, values)
102-
103-
leafNode.setDepth(this.depth + 2)
104-
newBranch.cowChild(nextByteInInsertedKey)
105-
newBranch.children[nextByteInInsertedKey] = leafNode
106-
} else if (child instanceof InternalNode) {
107-
this.cowChild(childIndex)
108-
return child.insertStem(stem, values, resolver)
109-
} else {
110-
throw new Error('Invalid node type')
111-
}
72+
/**
73+
*
74+
* @param index The index in the children array to retrieve the child node commitment from
75+
* @returns the uncompressed 64byte commitment for the child node at the `index` position in the children array
76+
*/
77+
getChildren(index: number): ChildNode | null {
78+
return this.children[index]
11279
}
11380

114-
// TODO: go-verkle also adds the bitlist to the raw format.
11581
raw(): Uint8Array[] {
116-
throw new Error('not implemented yet')
117-
// return [new Uint8Array([VerkleNodeType.Internal]), ...this.children, this.commitment]
82+
return [
83+
new Uint8Array([VerkleNodeType.Internal]),
84+
...this.children.map((child) => child.commitment),
85+
...this.children.map((child) => child.path),
86+
this.commitment,
87+
]
11888
}
11989
}

0 commit comments

Comments
 (0)