-
Notifications
You must be signed in to change notification settings - Fork 751
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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 <contact@rockwaterweb.com> * Address feedback * fix tests --------- Co-authored-by: Gabriel Rocheleau <contact@rockwaterweb.com>
- Loading branch information
1 parent
cf2b211
commit 05552af
Showing
17 changed files
with
685 additions
and
624 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, | ||
] | ||
} | ||
} |
Oops, something went wrong.