-
Notifications
You must be signed in to change notification settings - Fork 471
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
79fb8fe
commit a9700c1
Showing
9 changed files
with
1,774 additions
and
0 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
// Copyright (C) 2019-2023 Algorand, Inc. | ||
// This file is part of go-algorand | ||
// | ||
// go-algorand is free software: you can redistribute it and/or modify | ||
// it under the terms of the GNU Affero General Public License as | ||
// published by the Free Software Foundation, either version 3 of the | ||
// License, or (at your option) any later version. | ||
// | ||
// go-algorand is distributed in the hope that it will be useful, | ||
// but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
// GNU Affero General Public License for more details. | ||
// | ||
// You should have received a copy of the GNU Affero General Public License | ||
// along with go-algorand. If not, see <https://www.gnu.org/licenses/>. | ||
|
||
package statetrie | ||
|
||
import ( | ||
"sync" | ||
"github.com/algorand/go-algorand/crypto" | ||
"github.com/algorand/go-algorand/crypto/statetrie/nibbles" | ||
) | ||
|
||
// Backing nodes are placeholders for nodes that have been stored in the | ||
// backing store. All we need is the full key of the node and its hash. | ||
type backingNode struct { | ||
key nibbles.Nibbles | ||
hash crypto.Digest | ||
} | ||
|
||
var backingNodePool = sync.Pool{ | ||
New: func() interface{} { | ||
return &backingNode{ | ||
key: make(nibbles.Nibbles, 0), | ||
} | ||
}, | ||
} | ||
|
||
func makeBackingNode(hash crypto.Digest, key nibbles.Nibbles) *backingNode { | ||
stats.makebanodes++ | ||
ba := backingNodePool.Get().(*backingNode) | ||
ba.hash = hash | ||
ba.key = append(ba.key[:0], key...) | ||
return ba | ||
} | ||
func (ba *backingNode) setHash(hash crypto.Digest) { | ||
ba.hash = hash | ||
} | ||
func (ba *backingNode) add(mt *Trie, pathKey nibbles.Nibbles, remainingKey nibbles.Nibbles, valueHash crypto.Digest) (node, error) { | ||
// will be provided in the subsequent backing store PR | ||
return nil, nil | ||
} | ||
func (ba *backingNode) hashing() error { | ||
return nil | ||
} | ||
func (ba *backingNode) getKey() nibbles.Nibbles { | ||
return ba.key | ||
} | ||
func (ba *backingNode) getHash() *crypto.Digest { | ||
return &ba.hash | ||
} | ||
func (ba *backingNode) serialize() ([]byte, error) { | ||
panic("backingNode cannot be serialized") | ||
} |
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 |
---|---|---|
@@ -0,0 +1,174 @@ | ||
// Copyright (C) 2019-2023 Algorand, Inc. | ||
// This file is part of go-algorand | ||
// | ||
// go-algorand is free software: you can redistribute it and/or modify | ||
// it under the terms of the GNU Affero General Public License as | ||
// published by the Free Software Foundation, either version 3 of the | ||
// License, or (at your option) any later version. | ||
// | ||
// go-algorand is distributed in the hope that it will be useful, | ||
// but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
// GNU Affero General Public License for more details. | ||
// | ||
// You should have received a copy of the GNU Affero General Public License | ||
// along with go-algorand. If not, see <https://www.gnu.org/licenses/>. | ||
|
||
package statetrie | ||
|
||
import ( | ||
"bytes" | ||
|
||
"github.com/algorand/go-algorand/crypto" | ||
"github.com/algorand/go-algorand/crypto/statetrie/nibbles" | ||
) | ||
|
||
type branchNode struct { | ||
children [16]node | ||
valueHash crypto.Digest | ||
key nibbles.Nibbles | ||
hash crypto.Digest | ||
} | ||
|
||
// makeBranchNode creates a branch node with the provided children nodes, valueHash, | ||
// and full key. | ||
func makeBranchNode(children [16]node, valueHash crypto.Digest, key nibbles.Nibbles) *branchNode { | ||
stats.makebranches++ | ||
bn := &branchNode{children: children, valueHash: valueHash, key: make(nibbles.Nibbles, len(key))} | ||
copy(bn.key, key) | ||
return bn | ||
} | ||
func (bn *branchNode) add(mt *Trie, pathKey nibbles.Nibbles, remainingKey nibbles.Nibbles, valueHash crypto.Digest) (node, error) { | ||
//Three operational transitions: | ||
// | ||
//- BN.ADD.1: Store the new value in the branch node value slot. This overwrites | ||
// the branch node slot value. | ||
// | ||
//- BN.ADD.2: Make a new leaf node with the new value, and point an available | ||
// branch child slot at it. This stores a new leaf node in a child slot. | ||
// | ||
//- BN.ADD.3: This repoints the child node to a new/existing node resulting from | ||
// performing the Add operation on the child node. | ||
if len(remainingKey) == 0 { | ||
// If we're here, then set the value hash in this node, overwriting the old one. | ||
if bn.valueHash == valueHash { | ||
// If it is the same value, do not zero the hash | ||
return bn, nil | ||
} | ||
|
||
bn.valueHash = valueHash | ||
// transition BN.ADD.1 | ||
bn.hash = crypto.Digest{} | ||
return bn, nil | ||
} | ||
|
||
// Otherwise, shift out the first nibble and check the children for it. | ||
shifted := nibbles.ShiftLeft(remainingKey, 1) | ||
slot := remainingKey[0] | ||
if bn.children[slot] == nil { | ||
// nil children are available. | ||
lnKey := pathKey[:] | ||
lnKey = append(lnKey, slot) | ||
|
||
// transition BN.ADD.2 | ||
bn.hash = crypto.Digest{} | ||
bn.children[slot] = makeLeafNode(shifted, valueHash, lnKey) | ||
} else { | ||
// Not available. Descend down the branch. | ||
replacement, err := bn.children[slot].add(mt, append(pathKey, remainingKey[0]), shifted, valueHash) | ||
if err != nil { | ||
return nil, err | ||
} | ||
// If the replacement hash is zero, zero the branch node hash | ||
if replacement.getHash().IsZero() { | ||
bn.hash = crypto.Digest{} | ||
} | ||
// transition BN.ADD.3 | ||
bn.children[slot] = replacement | ||
} | ||
|
||
return bn, nil | ||
} | ||
|
||
// hashing serializes the node and then hashes it, storing the hash in the node. | ||
func (bn *branchNode) hashing() error { | ||
if bn.hash.IsZero() { | ||
for i := 0; i < 16; i++ { | ||
if bn.children[i] != nil && bn.children[i].getHash().IsZero() { | ||
err := bn.children[i].hashing() | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
} | ||
bytes, err := bn.serialize() | ||
if err != nil { | ||
return err | ||
} | ||
stats.cryptohashes++ | ||
bn.hash = crypto.Hash(bytes) | ||
} | ||
return nil | ||
} | ||
|
||
// deserializeBranchNode turns a data array and its key in the trie into | ||
// a branch node. | ||
func deserializeBranchNode(data []byte, key nibbles.Nibbles) *branchNode { | ||
if data[0] != 5 { | ||
panic("invalid prefix for branch node") | ||
} | ||
if len(data) < (1 + 17*crypto.DigestSize) { | ||
panic("data too short to be a branch node") | ||
} | ||
|
||
var children [16]node | ||
for i := 0; i < 16; i++ { | ||
var hash crypto.Digest | ||
|
||
copy(hash[:], data[1+i*crypto.DigestSize:(1+crypto.DigestSize)+i*crypto.DigestSize]) | ||
if !hash.IsZero() { | ||
chKey := key[:] | ||
chKey = append(chKey, byte(i)) | ||
children[i] = makeBackingNode(hash, chKey) | ||
} | ||
} | ||
var valueHash crypto.Digest | ||
copy(valueHash[:], data[(1+16*crypto.DigestSize):(1+17*crypto.DigestSize)]) | ||
return makeBranchNode(children, valueHash, key) | ||
} | ||
|
||
// setHash sets the value of the hash for the node. | ||
func (bn *branchNode) setHash(hash crypto.Digest) { | ||
bn.hash = hash | ||
} | ||
|
||
var bnbuffer bytes.Buffer | ||
|
||
func (bn *branchNode) serialize() ([]byte, error) { | ||
bnbuffer.Reset() | ||
var empty crypto.Digest | ||
prefix := byte(5) | ||
|
||
bnbuffer.WriteByte(prefix) | ||
for i := 0; i < 16; i++ { | ||
if bn.children[i] != nil { | ||
bnbuffer.Write(bn.children[i].getHash().ToSlice()) | ||
} else { | ||
bnbuffer.Write(empty[:]) | ||
} | ||
} | ||
bnbuffer.Write(bn.valueHash[:]) | ||
return bnbuffer.Bytes(), nil | ||
} | ||
|
||
// getKey gets the nibbles of the full key for this node. | ||
func (bn *branchNode) getKey() nibbles.Nibbles { | ||
return bn.key | ||
} | ||
|
||
// getHash gets the hash for this node. If the hash has not been set by a | ||
// hashing operation like branchNode.hashing, getHash will not calculate it | ||
// (instead it will return the empty hash, crypto.Digest{}) | ||
func (bn *branchNode) getHash() *crypto.Digest { | ||
return &bn.hash | ||
} |
Oops, something went wrong.