Skip to content

Commit 0e2171c

Browse files
committed
trie nodes add oper impl
1 parent e7aa0d4 commit 0e2171c

File tree

9 files changed

+1774
-0
lines changed

9 files changed

+1774
-0
lines changed

crypto/statetrie/README.md

Lines changed: 360 additions & 0 deletions
Large diffs are not rendered by default.

crypto/statetrie/backing.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// Copyright (C) 2019-2023 Algorand, Inc.
2+
// This file is part of go-algorand
3+
//
4+
// go-algorand is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Affero General Public License as
6+
// published by the Free Software Foundation, either version 3 of the
7+
// License, or (at your option) any later version.
8+
//
9+
// go-algorand is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Affero General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Affero General Public License
15+
// along with go-algorand. If not, see <https://www.gnu.org/licenses/>.
16+
17+
package statetrie
18+
19+
import (
20+
"sync"
21+
"github.com/algorand/go-algorand/crypto"
22+
"github.com/algorand/go-algorand/crypto/statetrie/nibbles"
23+
)
24+
25+
// Backing nodes are placeholders for nodes that have been stored in the
26+
// backing store. All we need is the full key of the node and its hash.
27+
type backingNode struct {
28+
key nibbles.Nibbles
29+
hash crypto.Digest
30+
}
31+
32+
var backingNodePool = sync.Pool{
33+
New: func() interface{} {
34+
return &backingNode{
35+
key: make(nibbles.Nibbles, 0),
36+
}
37+
},
38+
}
39+
40+
func makeBackingNode(hash crypto.Digest, key nibbles.Nibbles) *backingNode {
41+
stats.makebanodes++
42+
ba := backingNodePool.Get().(*backingNode)
43+
ba.hash = hash
44+
ba.key = append(ba.key[:0], key...)
45+
return ba
46+
}
47+
func (ba *backingNode) setHash(hash crypto.Digest) {
48+
ba.hash = hash
49+
}
50+
func (ba *backingNode) add(mt *Trie, pathKey nibbles.Nibbles, remainingKey nibbles.Nibbles, valueHash crypto.Digest) (node, error) {
51+
// will be provided in the subsequent backing store PR
52+
return nil, nil
53+
}
54+
func (ba *backingNode) hashing() error {
55+
return nil
56+
}
57+
func (ba *backingNode) getKey() nibbles.Nibbles {
58+
return ba.key
59+
}
60+
func (ba *backingNode) getHash() *crypto.Digest {
61+
return &ba.hash
62+
}
63+
func (ba *backingNode) serialize() ([]byte, error) {
64+
panic("backingNode cannot be serialized")
65+
}

crypto/statetrie/branch.go

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
// Copyright (C) 2019-2023 Algorand, Inc.
2+
// This file is part of go-algorand
3+
//
4+
// go-algorand is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Affero General Public License as
6+
// published by the Free Software Foundation, either version 3 of the
7+
// License, or (at your option) any later version.
8+
//
9+
// go-algorand is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Affero General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Affero General Public License
15+
// along with go-algorand. If not, see <https://www.gnu.org/licenses/>.
16+
17+
package statetrie
18+
19+
import (
20+
"bytes"
21+
22+
"github.com/algorand/go-algorand/crypto"
23+
"github.com/algorand/go-algorand/crypto/statetrie/nibbles"
24+
)
25+
26+
type branchNode struct {
27+
children [16]node
28+
valueHash crypto.Digest
29+
key nibbles.Nibbles
30+
hash crypto.Digest
31+
}
32+
33+
// makeBranchNode creates a branch node with the provided children nodes, valueHash,
34+
// and full key.
35+
func makeBranchNode(children [16]node, valueHash crypto.Digest, key nibbles.Nibbles) *branchNode {
36+
stats.makebranches++
37+
bn := &branchNode{children: children, valueHash: valueHash, key: make(nibbles.Nibbles, len(key))}
38+
copy(bn.key, key)
39+
return bn
40+
}
41+
func (bn *branchNode) add(mt *Trie, pathKey nibbles.Nibbles, remainingKey nibbles.Nibbles, valueHash crypto.Digest) (node, error) {
42+
//Three operational transitions:
43+
//
44+
//- BN.ADD.1: Store the new value in the branch node value slot. This overwrites
45+
// the branch node slot value.
46+
//
47+
//- BN.ADD.2: Make a new leaf node with the new value, and point an available
48+
// branch child slot at it. This stores a new leaf node in a child slot.
49+
//
50+
//- BN.ADD.3: This repoints the child node to a new/existing node resulting from
51+
// performing the Add operation on the child node.
52+
if len(remainingKey) == 0 {
53+
// If we're here, then set the value hash in this node, overwriting the old one.
54+
if bn.valueHash == valueHash {
55+
// If it is the same value, do not zero the hash
56+
return bn, nil
57+
}
58+
59+
bn.valueHash = valueHash
60+
// transition BN.ADD.1
61+
bn.hash = crypto.Digest{}
62+
return bn, nil
63+
}
64+
65+
// Otherwise, shift out the first nibble and check the children for it.
66+
shifted := nibbles.ShiftLeft(remainingKey, 1)
67+
slot := remainingKey[0]
68+
if bn.children[slot] == nil {
69+
// nil children are available.
70+
lnKey := pathKey[:]
71+
lnKey = append(lnKey, slot)
72+
73+
// transition BN.ADD.2
74+
bn.hash = crypto.Digest{}
75+
bn.children[slot] = makeLeafNode(shifted, valueHash, lnKey)
76+
} else {
77+
// Not available. Descend down the branch.
78+
replacement, err := bn.children[slot].add(mt, append(pathKey, remainingKey[0]), shifted, valueHash)
79+
if err != nil {
80+
return nil, err
81+
}
82+
// If the replacement hash is zero, zero the branch node hash
83+
if replacement.getHash().IsZero() {
84+
bn.hash = crypto.Digest{}
85+
}
86+
// transition BN.ADD.3
87+
bn.children[slot] = replacement
88+
}
89+
90+
return bn, nil
91+
}
92+
93+
// hashing serializes the node and then hashes it, storing the hash in the node.
94+
func (bn *branchNode) hashing() error {
95+
if bn.hash.IsZero() {
96+
for i := 0; i < 16; i++ {
97+
if bn.children[i] != nil && bn.children[i].getHash().IsZero() {
98+
err := bn.children[i].hashing()
99+
if err != nil {
100+
return err
101+
}
102+
}
103+
}
104+
bytes, err := bn.serialize()
105+
if err != nil {
106+
return err
107+
}
108+
stats.cryptohashes++
109+
bn.hash = crypto.Hash(bytes)
110+
}
111+
return nil
112+
}
113+
114+
// deserializeBranchNode turns a data array and its key in the trie into
115+
// a branch node.
116+
func deserializeBranchNode(data []byte, key nibbles.Nibbles) *branchNode {
117+
if data[0] != 5 {
118+
panic("invalid prefix for branch node")
119+
}
120+
if len(data) < (1 + 17*crypto.DigestSize) {
121+
panic("data too short to be a branch node")
122+
}
123+
124+
var children [16]node
125+
for i := 0; i < 16; i++ {
126+
var hash crypto.Digest
127+
128+
copy(hash[:], data[1+i*crypto.DigestSize:(1+crypto.DigestSize)+i*crypto.DigestSize])
129+
if !hash.IsZero() {
130+
chKey := key[:]
131+
chKey = append(chKey, byte(i))
132+
children[i] = makeBackingNode(hash, chKey)
133+
}
134+
}
135+
var valueHash crypto.Digest
136+
copy(valueHash[:], data[(1+16*crypto.DigestSize):(1+17*crypto.DigestSize)])
137+
return makeBranchNode(children, valueHash, key)
138+
}
139+
140+
// setHash sets the value of the hash for the node.
141+
func (bn *branchNode) setHash(hash crypto.Digest) {
142+
bn.hash = hash
143+
}
144+
145+
var bnbuffer bytes.Buffer
146+
147+
func (bn *branchNode) serialize() ([]byte, error) {
148+
bnbuffer.Reset()
149+
var empty crypto.Digest
150+
prefix := byte(5)
151+
152+
bnbuffer.WriteByte(prefix)
153+
for i := 0; i < 16; i++ {
154+
if bn.children[i] != nil {
155+
bnbuffer.Write(bn.children[i].getHash().ToSlice())
156+
} else {
157+
bnbuffer.Write(empty[:])
158+
}
159+
}
160+
bnbuffer.Write(bn.valueHash[:])
161+
return bnbuffer.Bytes(), nil
162+
}
163+
164+
// getKey gets the nibbles of the full key for this node.
165+
func (bn *branchNode) getKey() nibbles.Nibbles {
166+
return bn.key
167+
}
168+
169+
// getHash gets the hash for this node. If the hash has not been set by a
170+
// hashing operation like branchNode.hashing, getHash will not calculate it
171+
// (instead it will return the empty hash, crypto.Digest{})
172+
func (bn *branchNode) getHash() *crypto.Digest {
173+
return &bn.hash
174+
}

0 commit comments

Comments
 (0)