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 20 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
10 changes: 5 additions & 5 deletions package-lock.json

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

2 changes: 1 addition & 1 deletion packages/statemanager/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
"ethereum-cryptography": "^2.1.3",
"js-sdsl": "^4.1.4",
"lru-cache": "10.1.0",
"verkle-cryptography-wasm": "^0.4.2"
"verkle-cryptography-wasm": "^0.4.3"
},
"devDependencies": {
"@ethereumjs/block": "^5.2.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/verkle/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
},
"dependencies": {
"lru-cache": "10.1.0",
"verkle-cryptography-wasm": "^0.4.2",
"verkle-cryptography-wasm": "^0.4.3",
"@ethereumjs/block": "^5.2.0",
"@ethereumjs/rlp": "^5.0.2",
"@ethereumjs/util": "^9.0.3"
Expand Down
7 changes: 5 additions & 2 deletions packages/verkle/src/node/baseVerkleNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,23 @@ 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
Expand Down
89 changes: 68 additions & 21 deletions packages/verkle/src/node/internalNode.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
import { equalsBytes } from '@ethereumjs/util'
import { BIGINT_0, bytesToBigInt, equalsBytes } from '@ethereumjs/util'

import { POINT_IDENTITY } from '../util/crypto.js'

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 { VerkleCrypto } from '../types.js'
import type { VerkleNodeOptions } from './types.js'

export class InternalNode extends BaseVerkleNode<VerkleNodeType.Internal> {
// Array of references to children nodes
public children: Array<VerkleNode | null>
// Array of commitments to child nodes
public children: Uint8Array[]
public copyOnWrite: Record<string, Uint8Array>
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.children = options.children ?? new Array(NODE_WIDTH).fill(new Uint8Array())
this.copyOnWrite = options.copyOnWrite ?? {}
acolytec3 marked this conversation as resolved.
Show resolved Hide resolved
}

Expand All @@ -29,11 +29,17 @@ export class InternalNode extends BaseVerkleNode<VerkleNodeType.Internal> {
// Not implemented yet
}

setChild(index: number, child: VerkleNode) {
// Updates the commitment value for a child node at the corresponding index
setChild(index: number, child: Uint8Array) {
this.children[index] = child
// TODO: Update commitment as well
}
acolytec3 marked this conversation as resolved.
Show resolved Hide resolved

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')
Expand All @@ -47,35 +53,47 @@ export class InternalNode extends BaseVerkleNode<VerkleNodeType.Internal> {
// TODO: Generate Point from rawNode value
const commitment = rawNode[rawNode.length - 1]

return new InternalNode({ commitment, depth })
return new InternalNode({ commitment, depth, verkleCrypto })
}

static create(depth: number): InternalNode {
static create(depth: number, verkleCrypto: VerkleCrypto): InternalNode {
const node = new InternalNode({
commitment: POINT_IDENTITY,
depth,
verkleCrypto,
})

return node
}

getChildren(index: number): VerkleNode | null {
getChildren(index: number): Uint8Array | null {
return this.children?.[index] ?? null
acolytec3 marked this conversation as resolved.
Show resolved Hide resolved
}

insert(key: Uint8Array, value: Uint8Array, resolver: () => void): void {
insert(
key: Uint8Array,
value: Uint8Array,
resolver: () => void,
verkleCrypto?: VerkleCrypto
): void {
acolytec3 marked this conversation as resolved.
Show resolved Hide resolved
const values = new Array<Uint8Array>(NODE_WIDTH)
values[key[31]] = value
this.insertStem(key.slice(0, 31), values, resolver)
this.insertStem(key.slice(0, 31), values, resolver, verkleCrypto!)
}
acolytec3 marked this conversation as resolved.
Show resolved Hide resolved

insertStem(stem: Uint8Array, values: Uint8Array[], resolver: () => void): void {
insertStem(
stem: Uint8Array,
values: Uint8Array[],
resolver: () => void,
verkleCrypto: VerkleCrypto
): void {
acolytec3 marked this conversation as resolved.
Show resolved Hide resolved
// 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) {
// TODO: Understand the intent of what cowChild is suppoded to do
this.cowChild(childIndex)
Copy link
Contributor

Choose a reason for hiding this comment

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

cow stands for "copy-on-write". As far as I recall cowChild is used as a record to mark the children that have been modified, in order to stack the commitment updates at the end since that is way more efficient.

acolytec3 marked this conversation as resolved.
Show resolved Hide resolved
if (equalsBytes(child.stem, stem)) {
return child.insertMultiple(stem, values)
Expand All @@ -85,35 +103,64 @@ export class InternalNode extends BaseVerkleNode<VerkleNodeType.Internal> {
// 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)
const newBranch = InternalNode.create(this.depth + 1, this.verkleCrypto)
newBranch.cowChild(nextByteInExistingKey)
this.children[childIndex] = newBranch
this.children[childIndex] = newBranch.commitment
newBranch.children[nextByteInExistingKey] = child
child.depth += 1

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

Choose a reason for hiding this comment

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

Suggested change
return newBranch.insertStem(stem, values, resolver, verkleCrypto)
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)
// TODO: Fix this following `trie.put` logic
let leafCommitment = verkleCrypto.zeroCommitment
let c1 = verkleCrypto.zeroCommitment
let c2 = verkleCrypto.zeroCommitment
for (const [idx, value] of values.entries()) {
if (bytesToBigInt(value) > BIGINT_0) {
leafCommitment = verkleCrypto.updateCommitment(
leafCommitment,
idx,
new Uint8Array(32),
value
)
if (idx < 128) {
// We multiply the commitment index by 2 here because each 32 byte value in the leaf node is represented as two 16 byte arrays
c1 = verkleCrypto.updateCommitment(c1, idx * 2, new Uint8Array(32), value)
} else {
c2 = verkleCrypto.updateCommitment(c2, (idx - 128) * 2, new Uint8Array(32), value)
}
}
}

const leafNode = LeafNode.create(
stem,
values,
this.depth + 1,
leafCommitment,
c1,
c2,
this.verkleCrypto
)

// TODO - Why is the leaf node set at depth + 2 instead of + 1)?
leafNode.setDepth(this.depth + 2)
Copy link
Contributor

Choose a reason for hiding this comment

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

I think that this is because the leaf is inserted in the position of the suffix level commitment. So we have:

depth n : internal node:
depth n+1: the extension level commitment
depth n+2: the suffix level commitment

See this picture for how that corresponds to the structure:
image

newBranch.cowChild(nextByteInInsertedKey)
newBranch.children[nextByteInInsertedKey] = leafNode
} else if (child instanceof InternalNode) {
this.cowChild(childIndex)
return child.insertStem(stem, values, resolver)
return child.insertStem(stem, values, resolver, verkleCrypto)
} else {
acolytec3 marked this conversation as resolved.
Show resolved Hide resolved
throw new Error('Invalid node type')
}
}

// TODO: go-verkle also adds the bitlist to the raw format.
raw(): Uint8Array[] {
acolytec3 marked this conversation as resolved.
Show resolved Hide resolved
throw new Error('not implemented yet')
// return [new Uint8Array([VerkleNodeType.Internal]), ...this.children, this.commitment]
return [new Uint8Array([VerkleNodeType.Internal]), ...this.children, this.commitment]
}
}
41 changes: 28 additions & 13 deletions packages/verkle/src/node/leafNode.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import { BaseVerkleNode } from './baseVerkleNode.js'
import { NODE_WIDTH, VerkleNodeType } from './types.js'

import type { Point } from '../types.js'
import type { VerkleCrypto } from '../types.js'
import type { VerkleNodeOptions } from './types.js'

export class LeafNode extends BaseVerkleNode<VerkleNodeType.Leaf> {
public stem: Uint8Array
public values: Uint8Array[]
public c1: Point
public c2: Point
public values: Uint8Array[] // Array of 256 possible values represented as 32 byte Uint8Arrays
public c1?: Uint8Array
public c2?: Uint8Array
public type = VerkleNodeType.Leaf

constructor(options: VerkleNodeOptions[VerkleNodeType.Leaf]) {
Expand All @@ -21,11 +20,27 @@ export class LeafNode extends BaseVerkleNode<VerkleNodeType.Leaf> {
this.c2 = options.c2
}

static create(stem: Uint8Array, values: Uint8Array[]): LeafNode {
throw new Error('Not implemented')
static create(
stem: Uint8Array,
values: Uint8Array[],
depth: number,
commitment: Uint8Array,
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 here too?

c1: Uint8Array,
c2: Uint8Array,
verkleCrypto: VerkleCrypto
): LeafNode {
return new LeafNode({
stem,
values,
depth,
commitment,
c1,
c2,
verkleCrypto,
})
}

static fromRawNode(rawNode: Uint8Array[], depth: number): LeafNode {
static fromRawNode(rawNode: Uint8Array[], depth: number, verkleCrypto: VerkleCrypto): LeafNode {
const nodeType = rawNode[0][0]
Copy link
Contributor

Choose a reason for hiding this comment

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

Remove depth here?

if (nodeType !== VerkleNodeType.Leaf) {
throw new Error('Invalid node type')
Expand All @@ -39,11 +54,11 @@ export class LeafNode extends BaseVerkleNode<VerkleNodeType.Leaf> {
const stem = rawNode[1]
// TODO: Convert the rawNode commitments to points
const commitment = rawNode[2]
acolytec3 marked this conversation as resolved.
Show resolved Hide resolved
const c1 = rawNode[3] as unknown as Point
const c2 = rawNode[4] as unknown as Point
const c1 = rawNode[3]
const c2 = rawNode[4]
const values = rawNode.slice(5, rawNode.length)

return new LeafNode({ depth, stem, values, c1, c2, commitment })
return new LeafNode({ depth, stem, values, c1, c2, commitment, verkleCrypto })
}
commit(): Uint8Array {
throw new Error('Not implemented')
Expand Down Expand Up @@ -73,8 +88,8 @@ export class LeafNode extends BaseVerkleNode<VerkleNodeType.Leaf> {
new Uint8Array([VerkleNodeType.Leaf]),
this.stem,
this.commitment,
this.c1.bytes(),
this.c2.bytes(),
this.c1 ?? new Uint8Array(),
this.c2 ?? new Uint8Array(),
...this.values,
]
}
Expand Down
9 changes: 5 additions & 4 deletions packages/verkle/src/node/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { Point } from '../types.js'
import type { InternalNode } from './internalNode.js'
import type { LeafNode } from './leafNode.js'
import type { VerkleCrypto } from 'verkle-cryptography-wasm'

export enum VerkleNodeType {
Internal,
Expand All @@ -16,14 +16,15 @@ export type VerkleNode = TypedVerkleNode[VerkleNodeType]

export interface VerkleNodeInterface {
commit(): Uint8Array
hash(): any
hash(verkleCrypto: VerkleCrypto): any
serialize(): Uint8Array
acolytec3 marked this conversation as resolved.
Show resolved Hide resolved
}

interface BaseVerkleNodeOptions {
// Value of the commitment
commitment: Uint8Array
depth: number
verkleCrypto: VerkleCrypto
}

interface VerkleInternalNodeOptions extends BaseVerkleNodeOptions {
Expand All @@ -37,8 +38,8 @@ interface VerkleInternalNodeOptions extends BaseVerkleNodeOptions {
interface VerkleLeafNodeOptions extends BaseVerkleNodeOptions {
stem: Uint8Array
values: Uint8Array[]
c1: Point
c2: Point
c1?: Uint8Array
c2?: Uint8Array
}

export interface VerkleNodeOptions {
Expand Down
Loading
Loading