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

Refactor Trie #2355

Open
wants to merge 44 commits into
base: weiihann/improve-state-trie
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
cebbeb9
add bytes benchmark
weiihann Dec 13, 2024
e53ac85
add Rsh test
weiihann Dec 14, 2024
4cc9075
add Truncate
weiihann Dec 14, 2024
c4f7583
all tests passed
weiihann Dec 17, 2024
97ea74b
improve comments
weiihann Dec 18, 2024
44ba4c5
minor chore
weiihann Dec 18, 2024
dcec3a5
improvements
weiihann Dec 19, 2024
60fe040
Implement trie nodes
weiihann Dec 26, 2024
d801429
Update() works on TrieD
weiihann Dec 30, 2024
42f4785
add docs
weiihann Dec 31, 2024
63649cc
Add hasher
weiihann Jan 2, 2025
a391cbf
add tracer
weiihann Jan 7, 2025
1b44cdc
Add package `trienode`
weiihann Jan 9, 2025
47466e2
Add collector
weiihann Jan 9, 2025
47c5e9b
Add Trie.Commit
weiihann Jan 9, 2025
0cf80f4
add Clear() to OrderedSet
weiihann Jan 15, 2025
1b5c7a5
implement triedb
weiihann Jan 15, 2025
b1c5ff2
rename trie/utils to trie/trieutils
weiihann Jan 15, 2025
e945538
minor changes on trienode
weiihann Jan 15, 2025
0262038
add trie id
weiihann Jan 15, 2025
efe4bb2
...a bunch of changes
weiihann Jan 15, 2025
d48eac6
bitarray changes
weiihann Jan 17, 2025
e2c3672
bitarray changes
weiihann Jan 20, 2025
81ed086
at this point range proof tests pass 50%
weiihann Jan 20, 2025
245a389
still failing
weiihann Jan 21, 2025
6db9aef
pass base test but got nil dereference bug
weiihann Jan 21, 2025
35ac499
fix invalid non existent proof
weiihann Jan 21, 2025
0d31b31
fix hasRightElement
weiihann Jan 21, 2025
68e949c
create TrieDB interface
weiihann Jan 21, 2025
09ea3be
range proof test cases all pass
weiihann Jan 21, 2025
6e33a7a
hasRightElement fix edgeNode case
weiihann Jan 21, 2025
aa57d85
remove test
weiihann Jan 22, 2025
20b713a
fix node encoding and add tests
weiihann Jan 22, 2025
252375c
everything pass!!!!!
weiihann Jan 22, 2025
e13fd3a
linter
weiihann Jan 23, 2025
4ed10a1
add comments
weiihann Jan 23, 2025
cf9562a
fix rebase
weiihann Jan 23, 2025
7f70307
remove proof_test changes
weiihann Jan 23, 2025
598fd2c
add bitarray_test.go
weiihann Jan 23, 2025
1d0f868
linter
weiihann Jan 23, 2025
dcbe815
add comments
weiihann Jan 23, 2025
7a851a8
Remove leading zero bytes from bit array
weiihann Feb 6, 2025
5f183de
comments
weiihann Feb 12, 2025
18b6b2a
Fix encoding bug
weiihann Feb 17, 2025
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
120 changes: 120 additions & 0 deletions core/trie2/collector.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package trie2

import (
"fmt"
"sync"

"github.com/NethermindEth/juno/core/felt"
"github.com/NethermindEth/juno/core/trie2/trienode"
)

// Used as a tool to collect all dirty nodes in a NodeSet
type collector struct {
nodes *trienode.NodeSet
}

func newCollector(nodes *trienode.NodeSet) *collector {
return &collector{nodes: nodes}
}

// Collects the nodes in the node set and collapses a node into a hash node
func (c *collector) Collect(n node, parallel bool) *hashNode {
return c.collect(new(Path), n, parallel).(*hashNode)
}

func (c *collector) collect(path *Path, n node, parallel bool) node {
// This path has not been modified, just return the cache
hash, dirty := n.cache()
if hash != nil && !dirty {
return hash
}

// Collect children and then parent
switch cn := n.(type) {
case *edgeNode:
collapsed := cn.copy()

// If the child is a binary node, recurse into it.
// Otherwise, it can only be a hashNode or valueNode.
// Combination of edge (parent) + edge (child) is not possible.
collapsed.child = c.collect(new(Path).Append(path, cn.path), cn.child, parallel)
return c.store(path, collapsed)
case *binaryNode:
collapsed := cn.copy()
collapsed.children = c.collectChildren(path, cn, parallel)
return c.store(path, collapsed)
case *hashNode:
return cn
case *valueNode: // each leaf node is stored as a single entry in the node set
return c.store(path, cn)
default:
panic(fmt.Sprintf("unknown node type: %T", cn))

Check warning on line 51 in core/trie2/collector.go

View check run for this annotation

Codecov / codecov/patch

core/trie2/collector.go#L50-L51

Added lines #L50 - L51 were not covered by tests
}
}

// Collects the children of a binary node, may apply parallel processing if configured
func (c *collector) collectChildren(path *Path, n *binaryNode, parallel bool) [2]node {
children := [2]node{}

// Helper function to process a single child
processChild := func(i int) {
child := n.children[i]
// Return early if it's already a hash node
if hn, ok := child.(*hashNode); ok {
children[i] = hn
return
}

// Create child path
childPath := new(Path).AppendBit(path, uint8(i))

if !parallel {
children[i] = c.collect(childPath, child, parallel)
return
}

// Parallel processing
childSet := trienode.NewNodeSet(c.nodes.Owner)
childCollector := newCollector(childSet)
children[i] = childCollector.collect(childPath, child, parallel)
c.nodes.MergeSet(childSet) //nolint:errcheck // guaranteed to succeed because same owner
}

if !parallel {
// Sequential processing
processChild(0)
processChild(1)
return children
}

// Parallel processing
var wg sync.WaitGroup
var mu sync.Mutex

for i := range 2 {
wg.Add(1)
go func(idx int) {
defer wg.Done()
mu.Lock()
processChild(idx)
mu.Unlock()
}(i)
}
wg.Wait()

return children
}

// Stores the node in the node set and returns the hash node
func (c *collector) store(path *Path, n node) node {
hash, _ := n.cache()

blob := nodeToBytes(n)
if hash == nil { // this is a value node
c.nodes.Add(*path, trienode.NewNode(felt.Felt{}, blob))
return n
}

c.nodes.Add(*path, trienode.NewNode(hash.Felt, blob))
return hash
}
8 changes: 8 additions & 0 deletions core/trie2/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package trie2

import "errors"

var (
ErrCommitted = errors.New("trie is committed")
ErrEmptyRange = errors.New("empty range")
)
117 changes: 117 additions & 0 deletions core/trie2/hasher.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package trie2

import (
"fmt"
"sync"

"github.com/NethermindEth/juno/core/crypto"
)

// A tool for shashing nodes in the trie. It supports both sequential and parallel
// hashing modes.
type hasher struct {
hashFn crypto.HashFn // The hash function to use
parallel bool // Whether to hash binary node children in parallel
}

func newHasher(hash crypto.HashFn, parallel bool) hasher {
return hasher{
hashFn: hash,
parallel: parallel,
}
}

// Computes the hash of a node and returns both the hash node and a cached
// version of the original node. If the node already has a cached hash, returns
// that instead of recomputing.
func (h *hasher) hash(n node) (node, node) {
if hash, _ := n.cache(); hash != nil {
return hash, n
}

switch n := n.(type) {
case *edgeNode:
collapsed, cached := h.hashEdgeChild(n)
hn := &hashNode{Felt: *collapsed.hash(h.hashFn)}
cached.flags.hash = hn
return hn, cached
case *binaryNode:
collapsed, cached := h.hashBinaryChildren(n)
hn := &hashNode{Felt: *collapsed.hash(h.hashFn)}
cached.flags.hash = hn
return hn, cached
case *valueNode, *hashNode:
return n, n
default:
panic(fmt.Sprintf("unknown node type: %T", n))

Check warning on line 46 in core/trie2/hasher.go

View check run for this annotation

Codecov / codecov/patch

core/trie2/hasher.go#L45-L46

Added lines #L45 - L46 were not covered by tests
}
}

func (h *hasher) hashEdgeChild(n *edgeNode) (collapsed, cached *edgeNode) {
collapsed, cached = n.copy(), n.copy()

switch n.child.(type) {
case *edgeNode, *binaryNode:
collapsed.child, cached.child = h.hash(n.child)
}

return collapsed, cached
}

func (h *hasher) hashBinaryChildren(n *binaryNode) (collapsed, cached *binaryNode) {
collapsed, cached = n.copy(), n.copy()

if h.parallel { // TODO(weiihann): double check this parallel strategy
var wg sync.WaitGroup
wg.Add(2)

go func() {
defer wg.Done()
if n.children[0] != nil {
collapsed.children[0], cached.children[0] = h.hash(n.children[0])
} else {
collapsed.children[0], cached.children[0] = nilValueNode, nilValueNode
}

Check warning on line 74 in core/trie2/hasher.go

View check run for this annotation

Codecov / codecov/patch

core/trie2/hasher.go#L73-L74

Added lines #L73 - L74 were not covered by tests
}()

go func() {
defer wg.Done()
if n.children[1] != nil {
collapsed.children[1], cached.children[1] = h.hash(n.children[1])
} else {
collapsed.children[1], cached.children[1] = nilValueNode, nilValueNode
}

Check warning on line 83 in core/trie2/hasher.go

View check run for this annotation

Codecov / codecov/patch

core/trie2/hasher.go#L82-L83

Added lines #L82 - L83 were not covered by tests
}()

wg.Wait()
} else {
if n.children[0] != nil {
collapsed.children[0], cached.children[0] = h.hash(n.children[0])
} else {
collapsed.children[0], cached.children[0] = nilValueNode, nilValueNode
}

if n.children[1] != nil {
collapsed.children[1], cached.children[1] = h.hash(n.children[1])
} else {
collapsed.children[1], cached.children[1] = nilValueNode, nilValueNode
}
}

return collapsed, cached
}

// Construct trie proofs and returns the collapsed node (i.e. nodes with hash children)
// and the hashed node.
func (h *hasher) proofHash(original node) (collapsed, hashed node) {
switch n := original.(type) {
case *edgeNode:
en, _ := h.hashEdgeChild(n)
return en, &hashNode{Felt: *en.hash(h.hashFn)}
case *binaryNode:
bn, _ := h.hashBinaryChildren(n)
return bn, &hashNode{Felt: *bn.hash(h.hashFn)}
default:
return n, n

Check warning on line 115 in core/trie2/hasher.go

View check run for this annotation

Codecov / codecov/patch

core/trie2/hasher.go#L114-L115

Added lines #L114 - L115 were not covered by tests
}
}
61 changes: 61 additions & 0 deletions core/trie2/id.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package trie2

import (
"github.com/NethermindEth/juno/core/felt"
"github.com/NethermindEth/juno/db"
)

type TrieType uint8

const (
Empty TrieType = iota
ClassTrie
ContractTrie
)

// Represents the identifier for uniquely identifying a trie.
type ID struct {
TrieType TrieType
Owner felt.Felt // The contract address which the trie belongs to
}

// Returns the corresponding DB bucket for the trie
func (id *ID) Bucket() db.Bucket {
switch id.TrieType {
case ClassTrie:
return db.ClassTrie
case ContractTrie:
if id.Owner == (felt.Felt{}) {
return db.ContractTrieContract
}
return db.ContractTrieStorage

Check warning on line 31 in core/trie2/id.go

View check run for this annotation

Codecov / codecov/patch

core/trie2/id.go#L25-L31

Added lines #L25 - L31 were not covered by tests
case Empty:
return db.Bucket(0)
default:
panic("invalid trie type")

Check warning on line 35 in core/trie2/id.go

View check run for this annotation

Codecov / codecov/patch

core/trie2/id.go#L34-L35

Added lines #L34 - L35 were not covered by tests
}
}

// Constructs an identifier for a class trie with the provided class trie root hash
func ClassTrieID() *ID {
return &ID{
TrieType: ClassTrie,
Owner: felt.Zero, // class trie does not have an owner
}

Check warning on line 44 in core/trie2/id.go

View check run for this annotation

Codecov / codecov/patch

core/trie2/id.go#L40-L44

Added lines #L40 - L44 were not covered by tests
}

// Constructs an identifier for a contract trie or a contract's storage trie
func ContractTrieID(owner felt.Felt) *ID {
return &ID{
TrieType: ContractTrie,
Owner: owner,
}

Check warning on line 52 in core/trie2/id.go

View check run for this annotation

Codecov / codecov/patch

core/trie2/id.go#L48-L52

Added lines #L48 - L52 were not covered by tests
}

// A general identifier, typically used for temporary trie
func TrieID() *ID {
return &ID{
TrieType: Empty,
Owner: felt.Zero,
}
}
Loading
Loading