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

Verkle Implementation: Build out Trie Processing #3430

Merged
merged 58 commits into from
Jun 21, 2024
Merged
Show file tree
Hide file tree
Changes from 55 commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
0b03444
add verkle crypto to trie
acolytec3 May 10, 2024
f468ce2
Merge remote-tracking branch 'origin/master' into verkle-trie-build-out
acolytec3 May 13, 2024
c1ce5eb
More spam
acolytec3 May 13, 2024
a317698
Merge remote-tracking branch 'origin/master' into verkle-trie-build-out
acolytec3 May 15, 2024
1670155
Update leafNode.create [no ci]
acolytec3 May 15, 2024
55d644f
Merge remote-tracking branch 'origin/master' into verkle-trie-build-o…
acolytec3 May 16, 2024
974d1ae
small removal [no ci]
acolytec3 May 16, 2024
570f8c4
Merge remote-tracking branch 'origin/master' into verkle-trie-build-o…
acolytec3 May 17, 2024
430d801
Update trie.get to check existence first
acolytec3 May 20, 2024
fa34dde
Update `put` when inserting a new leaf node [no ci]
acolytec3 May 20, 2024
b2806cf
more halfway stuff [no ci]
acolytec3 May 21, 2024
f9e019e
Remove unnecessary commit [no ci]
acolytec3 May 31, 2024
74676b0
update verkle crypto dep [no ci]
acolytec3 Jun 3, 2024
a267be1
Add helper to create leaf values as 16 bytes
acolytec3 Jun 3, 2024
e3ab994
Changes to support c1 and c2 [no ci]
acolytec3 Jun 3, 2024
6e7e91e
update new leaf node commitment correctly [no ci]
acolytec3 Jun 4, 2024
4a716a7
Merge remote-tracking branch 'origin/master' into verkle-trie-build-o…
acolytec3 Jun 4, 2024
a2ed90e
Changes needed to make `put` work [no ci]
acolytec3 Jun 6, 2024
1902ebc
Begin fixing internal node implementation [no ci]
acolytec3 Jun 6, 2024
541240c
move verkleCrypto to verkleNode constructor opts
acolytec3 Jun 7, 2024
76da96c
address feedback
acolytec3 Jun 7, 2024
52d50a8
WIP [no ci]
acolytec3 Jun 7, 2024
86dd9e9
Finish naive findpath implementation
acolytec3 Jun 10, 2024
189b112
Update internal node layout
acolytec3 Jun 11, 2024
60219ae
WIP [no ci]
acolytec3 Jun 11, 2024
bbd0e96
Partial implementation of put [no ci]
acolytec3 Jun 11, 2024
2301d2c
update verkle crypto [no ci]
acolytec3 Jun 12, 2024
dcdd778
Update this.root [no ci]
acolytec3 Jun 12, 2024
cf33476
Clean up db opt typing [no ci]
acolytec3 Jun 12, 2024
b9285e2
API clean/comments [no ci]
acolytec3 Jun 12, 2024
1dcc64d
fix logic bug for nonexistent path [no ci]
acolytec3 Jun 12, 2024
f42ac23
Describe logic for updating tree along path [no ci]
acolytec3 Jun 12, 2024
e4e257a
Update `put` to use `saveStack` [no ci]
acolytec3 Jun 13, 2024
1b1706d
WIP [no ci]
acolytec3 Jun 13, 2024
96f99aa
revise leafNode.create API [no ci]
acolytec3 Jun 13, 2024
e4bcb66
more updates to put/get [no ci]
acolytec3 Jun 13, 2024
27144c0
More wip [no ci]
acolytec3 Jun 13, 2024
7f4ead6
Fix bug in internalNode deserialization [no ci]
acolytec3 Jun 14, 2024
abc442d
Add more comments [no ci]
acolytec3 Jun 14, 2024
eeb2ec8
remove duplicative function [no ci]
acolytec3 Jun 14, 2024
e7e81bf
more wip [no ci]
acolytec3 Jun 14, 2024
991f4d2
Add code to produce a 2 layer tree [no ci]
acolytec3 Jun 15, 2024
58e5298
wip [no ci]
acolytec3 Jun 17, 2024
06350d0
Add some initial debug logs [no ci]
acolytec3 Jun 17, 2024
df22e18
More progress [no ci]
acolytec3 Jun 17, 2024
69267f7
more half-working fixes [no ci]
acolytec3 Jun 18, 2024
911c5b1
Merge remote-tracking branch 'origin/master' into verkle-trie-build-out
acolytec3 Jun 19, 2024
a38cc80
Fix typing issues and remove walk controller
acolytec3 Jun 19, 2024
ce343ae
Remove walk controller export [no ci]
acolytec3 Jun 19, 2024
9c7b214
Add new test to demonstrate putting values in the trie [no ci]
acolytec3 Jun 19, 2024
ae09bdb
Add comment
acolytec3 Jun 19, 2024
05bfebd
Remove obsolete references
acolytec3 Jun 19, 2024
0b4904c
lint
acolytec3 Jun 19, 2024
cd65a9a
Remove references to depth and unused API components
acolytec3 Jun 20, 2024
1ca879e
Merge branch 'master' into verkle-trie-build-out
acolytec3 Jun 20, 2024
3ab0700
Update packages/verkle/src/node/internalNode.ts
acolytec3 Jun 21, 2024
720ab09
Address feedback
acolytec3 Jun 21, 2024
b47fa49
fix tests
acolytec3 Jun 21, 2024
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
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'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should now use the import from verkle in Util, just opened a related issue here ethereumjs/verkle-cryptography-wasm#54

I would go as far and argue that we should also remove the verkle-cryptography-wasm dependency from this package as well and dependency inject, and so to have this wider strategic goal over the head that this should be replaceable and also here optimally a JS implementation being used.

We then also automatically continue to evolve a generic interface around this and keep thinking about what's needed and what not (I e.g. still like the idea that we take the more hashing related parts out directly and move over to the pedersen/poseidon hash usage from Pauls libraries https://github.com/paulmillr/scure-starknet (if applicable) and with this slowly shrink down the surface of what we need to compile over into the WASM stuff.

Feels to me that there is still too much in.

Practical note: if depency injection feels a bit impractical for now for testing and the like we could also decide to do at a later stage.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, but the main argument for direct dependency injection (so, this would mean: having a mandatory crypto constructor argument) is actually that we avoid having the same bundle problems again once/if we have Verkle added somewhere as a dependency.


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
139 changes: 57 additions & 82 deletions packages/verkle/src/node/internalNode.ts
Original file line number Diff line number Diff line change
@@ -1,119 +1,94 @@
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
gabrocheleau marked this conversation as resolved.
Show resolved Hide resolved
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[],
depth: number,
verkleCrypto: VerkleCrypto
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we remove depth from this API?

): 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 {
acolytec3 marked this conversation as resolved.
Show resolved Hide resolved
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
Loading