Skip to content

Commit

Permalink
Verkle Implementation: Build out Trie Processing (#3430)
Browse files Browse the repository at this point in the history
* 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 <contact@rockwaterweb.com>

* Address feedback

* fix tests

---------

Co-authored-by: Gabriel Rocheleau <contact@rockwaterweb.com>
  • Loading branch information
acolytec3 and gabrocheleau committed Jun 21, 2024
1 parent cf2b211 commit 05552af
Show file tree
Hide file tree
Showing 17 changed files with 685 additions and 624 deletions.
9 changes: 5 additions & 4 deletions package-lock.json

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

2 changes: 2 additions & 0 deletions packages/util/src/verkle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ export interface VerkleCrypto {
) => Uint8Array // Commitment
zeroCommitment: Uint8Array
verifyExecutionWitnessPreState: (prestateRoot: string, execution_witness_json: string) => boolean
hashCommitment: (commitment: Uint8Array) => Uint8Array
serializeCommitment: (commitment: Uint8Array) => Uint8Array
}

/**
Expand Down
3 changes: 2 additions & 1 deletion packages/verkle/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,9 @@
"tsc": "../../config/cli/ts-compile.sh"
},
"dependencies": {
"debug": "^4.3.4",
"lru-cache": "10.1.0",
"verkle-cryptography-wasm": "^0.4.2",
"verkle-cryptography-wasm": "^0.4.4",
"@ethereumjs/block": "^5.2.0",
"@ethereumjs/rlp": "^5.0.2",
"@ethereumjs/util": "^9.0.3"
Expand Down
14 changes: 6 additions & 8 deletions packages/verkle/src/node/baseVerkleNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,22 @@ import { RLP } from '@ethereumjs/rlp'

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

import type { VerkleCrypto } from 'verkle-cryptography-wasm'

export abstract class BaseVerkleNode<T extends VerkleNodeType> implements VerkleNodeInterface {
public commitment: Uint8Array
public depth: number

protected verkleCrypto: VerkleCrypto
constructor(options: VerkleNodeOptions[T]) {
this.commitment = options.commitment
this.depth = options.depth
this.verkleCrypto = options.verkleCrypto
}

abstract commit(): Uint8Array

// Hash returns the field representation of the commitment.
hash(): Uint8Array {
throw new Error('Not implemented')
return this.verkleCrypto.hashCommitment(this.commitment)
}

abstract insert(key: Uint8Array, value: Uint8Array, nodeResolverFn: () => void): void

// 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)
abstract raw(): Uint8Array[]

/**
Expand Down
134 changes: 52 additions & 82 deletions packages/verkle/src/node/internalNode.ts
Original file line number Diff line number Diff line change
@@ -1,119 +1,89 @@
import { equalsBytes } from '@ethereumjs/util'

import { POINT_IDENTITY } from '../util/crypto.js'
import { type VerkleCrypto } from '@ethereumjs/util'

import { BaseVerkleNode } from './baseVerkleNode.js'
import { LeafNode } from './leafNode.js'
import { NODE_WIDTH, VerkleNodeType } from './types.js'

import type { VerkleNode, VerkleNodeOptions } from './types.js'
import type { ChildNode, VerkleNodeOptions } from './types.js'

export class InternalNode extends BaseVerkleNode<VerkleNodeType.Internal> {
// Array of references to children nodes
public children: Array<VerkleNode | null>
public copyOnWrite: Record<string, Uint8Array>
// 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)
public children: Array<ChildNode>
public type = VerkleNodeType.Internal

/* TODO: options.children is not actually used here */
constructor(options: VerkleNodeOptions[VerkleNodeType.Internal]) {
super(options)
this.children = options.children ?? new Array(NODE_WIDTH).fill(null)
this.copyOnWrite = options.copyOnWrite ?? {}
}

commit(): Uint8Array {
throw new Error('Not implemented')
}

cowChild(_: number): void {
// Not implemented yet
this.children =
options.children ??
new Array(256).fill({
commitment: options.verkleCrypto.zeroCommitment,
path: new Uint8Array(),
})
}

setChild(index: number, child: VerkleNode) {
this.children[index] = child
// Updates the commitment value for a child node at the corresponding index
setChild(childIndex: number, child: ChildNode) {
// Get previous child commitment at `index`
const oldChildReference = this.children[childIndex]
// Updates the commitment to the child node at `index`
this.children[childIndex] = { ...child }
// Updates the overall node commitment based on the update to this child
this.commitment = this.verkleCrypto.updateCommitment(
this.commitment,
childIndex,
// The hashed child commitments are used when updating the internal node commitment
this.verkleCrypto.hashCommitment(oldChildReference.commitment),
this.verkleCrypto.hashCommitment(child.commitment)
)
}

static fromRawNode(rawNode: Uint8Array[], depth: number): InternalNode {
static fromRawNode(rawNode: Uint8Array[], verkleCrypto: VerkleCrypto): InternalNode {
const nodeType = rawNode[0][0]
if (nodeType !== VerkleNodeType.Internal) {
throw new Error('Invalid node type')
}

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

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

return new InternalNode({ commitment, depth })
const children = childrenCommitments.map((commitment, idx) => {
return { commitment, path: childrenPaths[idx] }
})
return new InternalNode({ commitment, verkleCrypto, children })
}

static create(depth: number): InternalNode {
/**
* Generates a new Internal node with default commitment
*/
static create(verkleCrypto: VerkleCrypto): InternalNode {
const node = new InternalNode({
commitment: POINT_IDENTITY,
depth,
commitment: verkleCrypto.zeroCommitment,
verkleCrypto,
})

return node
}

getChildren(index: number): VerkleNode | null {
return this.children?.[index] ?? null
}

insert(key: Uint8Array, value: Uint8Array, resolver: () => void): void {
const values = new Array<Uint8Array>(NODE_WIDTH)
values[key[31]] = value
this.insertStem(key.slice(0, 31), values, resolver)
}

insertStem(stem: Uint8Array, values: Uint8Array[], resolver: () => void): void {
// Index of the child pointed by the next byte in the key
const childIndex = stem[this.depth]

const child = this.children[childIndex]

if (child instanceof LeafNode) {
this.cowChild(childIndex)
if (equalsBytes(child.stem, stem)) {
return child.insertMultiple(stem, values)
}

// A new branch node has to be inserted. Depending
// on the next byte in both keys, a recursion into
// the moved leaf node can occur.
const nextByteInExistingKey = child.stem[this.depth + 1]
const newBranch = InternalNode.create(this.depth + 1)
newBranch.cowChild(nextByteInExistingKey)
this.children[childIndex] = newBranch
newBranch.children[nextByteInExistingKey] = child
child.depth += 1

const nextByteInInsertedKey = stem[this.depth + 1]
if (nextByteInInsertedKey === nextByteInExistingKey) {
return newBranch.insertStem(stem, values, resolver)
}

// Next word differs, so this was the last level.
// Insert it directly into its final slot.
const leafNode = LeafNode.create(stem, values)

leafNode.setDepth(this.depth + 2)
newBranch.cowChild(nextByteInInsertedKey)
newBranch.children[nextByteInInsertedKey] = leafNode
} else if (child instanceof InternalNode) {
this.cowChild(childIndex)
return child.insertStem(stem, values, resolver)
} else {
throw new Error('Invalid node type')
}
/**
*
* @param index The index in the children array to retrieve the child node commitment from
* @returns the uncompressed 64byte commitment for the child node at the `index` position in the children array
*/
getChildren(index: number): ChildNode | null {
return this.children[index]
}

// TODO: go-verkle also adds the bitlist to the raw format.
raw(): Uint8Array[] {
throw new Error('not implemented yet')
// return [new Uint8Array([VerkleNodeType.Internal]), ...this.children, this.commitment]
return [
new Uint8Array([VerkleNodeType.Internal]),
...this.children.map((child) => child.commitment),
...this.children.map((child) => child.path),
this.commitment,
]
}
}
Loading

0 comments on commit 05552af

Please sign in to comment.