mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-24 21:56:43 +00:00
Polish binary tree implementations (#551)
* trie: export PrevalueTracer * trie, core/types: polish binary trie * trie/bintrie: update tests * trie/bintrie: fix incorrect node size
This commit is contained in:
parent
931b8fb13e
commit
123723b9da
19 changed files with 219 additions and 137 deletions
|
|
@ -45,4 +45,7 @@ var (
|
||||||
|
|
||||||
// EmptyVerkleHash is the known hash of an empty verkle trie.
|
// EmptyVerkleHash is the known hash of an empty verkle trie.
|
||||||
EmptyVerkleHash = common.Hash{}
|
EmptyVerkleHash = common.Hash{}
|
||||||
|
|
||||||
|
// EmptyBinaryHash is the known hash of an empty binary trie.
|
||||||
|
EmptyBinaryHash = common.Hash{}
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -43,8 +43,7 @@ const (
|
||||||
// BinaryNode is an interface for a binary trie node.
|
// BinaryNode is an interface for a binary trie node.
|
||||||
type BinaryNode interface {
|
type BinaryNode interface {
|
||||||
Get([]byte, NodeResolverFn) ([]byte, error)
|
Get([]byte, NodeResolverFn) ([]byte, error)
|
||||||
Insert([]byte, []byte, NodeResolverFn) (BinaryNode, error)
|
Insert([]byte, []byte, NodeResolverFn, int) (BinaryNode, error)
|
||||||
// Commit() common.Hash
|
|
||||||
Copy() BinaryNode
|
Copy() BinaryNode
|
||||||
Hash() common.Hash
|
Hash() common.Hash
|
||||||
GetValuesAtStem([]byte, NodeResolverFn) ([][]byte, error)
|
GetValuesAtStem([]byte, NodeResolverFn) ([][]byte, error)
|
||||||
|
|
@ -65,7 +64,7 @@ func SerializeNode(node BinaryNode) []byte {
|
||||||
copy(serialized[33:65], n.right.Hash().Bytes())
|
copy(serialized[33:65], n.right.Hash().Bytes())
|
||||||
return serialized[:]
|
return serialized[:]
|
||||||
case *StemNode:
|
case *StemNode:
|
||||||
var serialized [32 + 256*32]byte
|
var serialized [32 + 32 + 256*32]byte
|
||||||
serialized[0] = nodeTypeStem
|
serialized[0] = nodeTypeStem
|
||||||
copy(serialized[1:32], node.(*StemNode).Stem)
|
copy(serialized[1:32], node.(*StemNode).Stem)
|
||||||
bitmap := serialized[32:64]
|
bitmap := serialized[32:64]
|
||||||
|
|
|
||||||
|
|
@ -28,12 +28,13 @@ func (e Empty) Get(_ []byte, _ NodeResolverFn) ([]byte, error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e Empty) Insert(key []byte, value []byte, _ NodeResolverFn) (BinaryNode, error) {
|
func (e Empty) Insert(key []byte, value []byte, _ NodeResolverFn, depth int) (BinaryNode, error) {
|
||||||
var values [256][]byte
|
var values [256][]byte
|
||||||
values[key[31]] = value
|
values[key[31]] = value
|
||||||
return &StemNode{
|
return &StemNode{
|
||||||
Stem: slices.Clone(key[:31]),
|
Stem: slices.Clone(key[:31]),
|
||||||
Values: values[:],
|
Values: values[:],
|
||||||
|
depth: depth,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ func TestEmptyInsert(t *testing.T) {
|
||||||
key[31] = 0x34
|
key[31] = 0x34
|
||||||
value := common.HexToHash("0xabcd").Bytes()
|
value := common.HexToHash("0xabcd").Bytes()
|
||||||
|
|
||||||
newNode, err := node.Insert(key, value, nil)
|
newNode, err := node.Insert(key, value, nil, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to insert: %v", err)
|
t.Fatalf("Failed to insert: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ func (h HashedNode) Get(_ []byte, _ NodeResolverFn) ([]byte, error) {
|
||||||
panic("not implemented") // TODO: Implement
|
panic("not implemented") // TODO: Implement
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h HashedNode) Insert(key []byte, value []byte, resolver NodeResolverFn) (BinaryNode, error) {
|
func (h HashedNode) Insert(key []byte, value []byte, resolver NodeResolverFn, depth int) (BinaryNode, error) {
|
||||||
return nil, errors.New("insert not implemented for hashed node")
|
return nil, errors.New("insert not implemented for hashed node")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ func TestHashedNodeInsert(t *testing.T) {
|
||||||
key := make([]byte, 32)
|
key := make([]byte, 32)
|
||||||
value := make([]byte, 32)
|
value := make([]byte, 32)
|
||||||
|
|
||||||
_, err := node.Insert(key, value, nil)
|
_, err := node.Insert(key, value, nil, 0)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("Expected error for Insert on HashedNode")
|
t.Fatal("Expected error for Insert on HashedNode")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,17 +25,14 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func keyToPath(depth int, key []byte) ([]byte, error) {
|
func keyToPath(depth int, key []byte) ([]byte, error) {
|
||||||
path := make([]byte, 0, depth+1)
|
|
||||||
|
|
||||||
if depth > 31*8 {
|
if depth > 31*8 {
|
||||||
return nil, errors.New("node too deep")
|
return nil, errors.New("node too deep")
|
||||||
}
|
}
|
||||||
|
path := make([]byte, 0, depth+1)
|
||||||
for i := range depth + 1 {
|
for i := range depth + 1 {
|
||||||
bit := key[i/8] >> (7 - (i % 8)) & 1
|
bit := key[i/8] >> (7 - (i % 8)) & 1
|
||||||
path = append(path, bit)
|
path = append(path, bit)
|
||||||
}
|
}
|
||||||
|
|
||||||
return path, nil
|
return path, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -81,16 +78,16 @@ func (bt *InternalNode) GetValuesAtStem(stem []byte, resolver NodeResolverFn) ([
|
||||||
func (bt *InternalNode) Get(key []byte, resolver NodeResolverFn) ([]byte, error) {
|
func (bt *InternalNode) Get(key []byte, resolver NodeResolverFn) ([]byte, error) {
|
||||||
values, err := bt.GetValuesAtStem(key[:31], resolver)
|
values, err := bt.GetValuesAtStem(key[:31], resolver)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Get error: %w", err)
|
return nil, fmt.Errorf("get error: %w", err)
|
||||||
}
|
}
|
||||||
return values[key[31]], nil
|
return values[key[31]], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert inserts a new key-value pair into the trie.
|
// Insert inserts a new key-value pair into the trie.
|
||||||
func (bt *InternalNode) Insert(key []byte, value []byte, resolver NodeResolverFn) (BinaryNode, error) {
|
func (bt *InternalNode) Insert(key []byte, value []byte, resolver NodeResolverFn, depth int) (BinaryNode, error) {
|
||||||
var values [256][]byte
|
var values [256][]byte
|
||||||
values[key[31]] = value
|
values[key[31]] = value
|
||||||
return bt.InsertValuesAtStem(key[:31], values[:], resolver, 0)
|
return bt.InsertValuesAtStem(key[:31], values[:], resolver, depth)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy creates a deep copy of the node.
|
// Copy creates a deep copy of the node.
|
||||||
|
|
@ -121,11 +118,11 @@ func (bt *InternalNode) Hash() common.Hash {
|
||||||
// InsertValuesAtStem inserts a full value group at the given stem in the internal node.
|
// InsertValuesAtStem inserts a full value group at the given stem in the internal node.
|
||||||
// Already-existing values will be overwritten.
|
// Already-existing values will be overwritten.
|
||||||
func (bt *InternalNode) InsertValuesAtStem(stem []byte, values [][]byte, resolver NodeResolverFn, depth int) (BinaryNode, error) {
|
func (bt *InternalNode) InsertValuesAtStem(stem []byte, values [][]byte, resolver NodeResolverFn, depth int) (BinaryNode, error) {
|
||||||
bit := stem[bt.depth/8] >> (7 - (bt.depth % 8)) & 1
|
|
||||||
var (
|
var (
|
||||||
child *BinaryNode
|
child *BinaryNode
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
|
bit := stem[bt.depth/8] >> (7 - (bt.depth % 8)) & 1
|
||||||
if bit == 0 {
|
if bit == 0 {
|
||||||
child = &bt.left
|
child = &bt.left
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -128,7 +128,7 @@ func TestInternalNodeInsert(t *testing.T) {
|
||||||
leftKey[31] = 10
|
leftKey[31] = 10
|
||||||
leftValue := common.HexToHash("0x0101").Bytes()
|
leftValue := common.HexToHash("0x0101").Bytes()
|
||||||
|
|
||||||
newNode, err := node.Insert(leftKey, leftValue, nil)
|
newNode, err := node.Insert(leftKey, leftValue, nil, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to insert: %v", err)
|
t.Fatalf("Failed to insert: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -114,7 +114,7 @@ func (it *binaryNodeIterator) Next(descend bool) bool {
|
||||||
return it.Next(descend)
|
return it.Next(descend)
|
||||||
case HashedNode:
|
case HashedNode:
|
||||||
// resolve the node
|
// resolve the node
|
||||||
data, err := it.trie.FlatdbNodeResolver(it.Path(), common.Hash(node))
|
data, err := it.trie.nodeResolver(it.Path(), common.Hash(node))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
79
trie/bintrie/key_encoding.go
Normal file
79
trie/bintrie/key_encoding.go
Normal file
|
|
@ -0,0 +1,79 @@
|
||||||
|
// Copyright 2025 go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library 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 Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package bintrie
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/sha256"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/holiman/uint256"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
BasicDataLeafKey = 0
|
||||||
|
CodeHashLeafKey = 1
|
||||||
|
BasicDataCodeSizeOffset = 5
|
||||||
|
BasicDataNonceOffset = 8
|
||||||
|
BasicDataBalanceOffset = 16
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
zeroHash = common.Hash{}
|
||||||
|
codeOffset = uint256.NewInt(128)
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetBinaryTreeKey(addr common.Address, key []byte) []byte {
|
||||||
|
hasher := sha256.New()
|
||||||
|
hasher.Write(zeroHash[:12])
|
||||||
|
hasher.Write(addr[:])
|
||||||
|
hasher.Write(key[:31])
|
||||||
|
k := hasher.Sum(nil)
|
||||||
|
k[31] = key[31]
|
||||||
|
return k
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetBinaryTreeKeyCodeHash(addr common.Address) []byte {
|
||||||
|
var k [32]byte
|
||||||
|
k[31] = CodeHashLeafKey
|
||||||
|
return GetBinaryTreeKey(addr, k[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetBinaryTreeKeyStorageSlot(address common.Address, key []byte) []byte {
|
||||||
|
var k [32]byte
|
||||||
|
|
||||||
|
// Case when the key belongs to the account header
|
||||||
|
if bytes.Equal(key[:31], zeroHash[:31]) && key[31] < 64 {
|
||||||
|
k[31] = 64 + key[31]
|
||||||
|
return GetBinaryTreeKey(address, k[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the main storage offset
|
||||||
|
// note that the first 64 bytes of the main offset storage
|
||||||
|
// are unreachable, which is consistent with the spec and
|
||||||
|
// what verkle does.
|
||||||
|
k[0] = 1 // 1 << 248
|
||||||
|
copy(k[1:], key[:31])
|
||||||
|
k[31] = key[31]
|
||||||
|
|
||||||
|
return GetBinaryTreeKey(address, k[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetBinaryTreeKeyCodeChunk(address common.Address, chunknr *uint256.Int) []byte {
|
||||||
|
chunkOffset := new(uint256.Int).Add(codeOffset, chunknr).Bytes()
|
||||||
|
return GetBinaryTreeKey(address, chunkOffset)
|
||||||
|
}
|
||||||
|
|
@ -39,7 +39,7 @@ func (bt *StemNode) Get(key []byte, _ NodeResolverFn) ([]byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert inserts a new key-value pair into the node.
|
// Insert inserts a new key-value pair into the node.
|
||||||
func (bt *StemNode) Insert(key []byte, value []byte, _ NodeResolverFn) (BinaryNode, error) {
|
func (bt *StemNode) Insert(key []byte, value []byte, _ NodeResolverFn, depth int) (BinaryNode, error) {
|
||||||
if !bytes.Equal(bt.Stem, key[:31]) {
|
if !bytes.Equal(bt.Stem, key[:31]) {
|
||||||
bitStem := bt.Stem[bt.depth/8] >> (7 - (bt.depth % 8)) & 1
|
bitStem := bt.Stem[bt.depth/8] >> (7 - (bt.depth % 8)) & 1
|
||||||
|
|
||||||
|
|
@ -59,7 +59,7 @@ func (bt *StemNode) Insert(key []byte, value []byte, _ NodeResolverFn) (BinaryNo
|
||||||
bitKey := key[new.depth/8] >> (7 - (new.depth % 8)) & 1
|
bitKey := key[new.depth/8] >> (7 - (new.depth % 8)) & 1
|
||||||
if bitKey == bitStem {
|
if bitKey == bitStem {
|
||||||
var err error
|
var err error
|
||||||
*child, err = (*child).Insert(key, value, nil)
|
*child, err = (*child).Insert(key, value, nil, depth+1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return new, fmt.Errorf("insert error: %w", err)
|
return new, fmt.Errorf("insert error: %w", err)
|
||||||
}
|
}
|
||||||
|
|
@ -70,16 +70,14 @@ func (bt *StemNode) Insert(key []byte, value []byte, _ NodeResolverFn) (BinaryNo
|
||||||
*other = &StemNode{
|
*other = &StemNode{
|
||||||
Stem: slices.Clone(key[:31]),
|
Stem: slices.Clone(key[:31]),
|
||||||
Values: values[:],
|
Values: values[:],
|
||||||
depth: new.depth + 1,
|
depth: depth + 1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new, nil
|
return new, nil
|
||||||
}
|
}
|
||||||
if len(value) != 32 {
|
if len(value) != 32 {
|
||||||
return bt, errors.New("invalid insertion: value length")
|
return bt, errors.New("invalid insertion: value length")
|
||||||
}
|
}
|
||||||
|
|
||||||
bt.Values[key[31]] = value
|
bt.Values[key[31]] = value
|
||||||
return bt, nil
|
return bt, nil
|
||||||
}
|
}
|
||||||
|
|
@ -181,7 +179,6 @@ func (bt *StemNode) InsertValuesAtStem(key []byte, values [][]byte, _ NodeResolv
|
||||||
depth: new.depth + 1,
|
depth: new.depth + 1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new, nil
|
return new, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ func TestStemNodeInsertSameStem(t *testing.T) {
|
||||||
key[31] = 10
|
key[31] = 10
|
||||||
value := common.HexToHash("0x0202").Bytes()
|
value := common.HexToHash("0x0202").Bytes()
|
||||||
|
|
||||||
newNode, err := node.Insert(key, value, nil)
|
newNode, err := node.Insert(key, value, nil, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to insert: %v", err)
|
t.Fatalf("Failed to insert: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -86,7 +86,7 @@ func TestStemNodeInsertDifferentStem(t *testing.T) {
|
||||||
key[0] = 0x80 // First bit is 1 instead of 0
|
key[0] = 0x80 // First bit is 1 instead of 0
|
||||||
value := common.HexToHash("0x0202").Bytes()
|
value := common.HexToHash("0x0202").Bytes()
|
||||||
|
|
||||||
newNode, err := node.Insert(key, value, nil)
|
newNode, err := node.Insert(key, value, nil, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to insert: %v", err)
|
t.Fatalf("Failed to insert: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -137,7 +137,7 @@ func TestStemNodeInsertInvalidValueLength(t *testing.T) {
|
||||||
copy(key[:31], stem)
|
copy(key[:31], stem)
|
||||||
invalidValue := []byte{1, 2, 3} // Not 32 bytes
|
invalidValue := []byte{1, 2, 3} // Not 32 bytes
|
||||||
|
|
||||||
_, err := node.Insert(key, invalidValue, nil)
|
_, err := node.Insert(key, invalidValue, nil, 0)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("Expected error for invalid value length")
|
t.Fatal("Expected error for invalid value length")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,6 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
"github.com/ethereum/go-ethereum/trie/trienode"
|
"github.com/ethereum/go-ethereum/trie/trienode"
|
||||||
"github.com/ethereum/go-ethereum/trie/utils"
|
|
||||||
"github.com/ethereum/go-ethereum/triedb/database"
|
"github.com/ethereum/go-ethereum/triedb/database"
|
||||||
"github.com/holiman/uint256"
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
@ -39,11 +38,10 @@ func NewBinaryNode() BinaryNode {
|
||||||
return Empty{}
|
return Empty{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// BinaryTrie is a wrapper around VerkleNode that implements the trie.Trie
|
// BinaryTrie is the implementation of https://eips.ethereum.org/EIPS/eip-7864.
|
||||||
// interface so that Verkle trees can be reused verbatim.
|
|
||||||
type BinaryTrie struct {
|
type BinaryTrie struct {
|
||||||
root BinaryNode
|
root BinaryNode
|
||||||
reader *trie.TrieReader
|
reader *trie.Reader
|
||||||
tracer *trie.PrevalueTracer
|
tracer *trie.PrevalueTracer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -55,36 +53,43 @@ func (t *BinaryTrie) ToDot() string {
|
||||||
|
|
||||||
// NewBinaryTrie creates a new binary trie.
|
// NewBinaryTrie creates a new binary trie.
|
||||||
func NewBinaryTrie(root common.Hash, db database.NodeDatabase) (*BinaryTrie, error) {
|
func NewBinaryTrie(root common.Hash, db database.NodeDatabase) (*BinaryTrie, error) {
|
||||||
reader, err := trie.NewTrieReader(root, common.Hash{}, db)
|
reader, err := trie.NewReader(root, common.Hash{}, db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Parse the root verkle node if it's not empty.
|
t := &BinaryTrie{
|
||||||
node := NewBinaryNode()
|
root: NewBinaryNode(),
|
||||||
if root != types.EmptyVerkleHash && root != types.EmptyRootHash {
|
|
||||||
blob, err := reader.Node(nil, common.Hash{})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
node, err = DeserializeNode(blob, 0)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return &BinaryTrie{
|
|
||||||
root: node,
|
|
||||||
reader: reader,
|
reader: reader,
|
||||||
}, nil
|
tracer: trie.NewPrevalueTracer(),
|
||||||
|
}
|
||||||
|
// Parse the root node if it's not empty
|
||||||
|
if root != types.EmptyBinaryHash && root != types.EmptyRootHash {
|
||||||
|
blob, err := t.nodeResolver(nil, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
node, err := DeserializeNode(blob, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
t.root = node
|
||||||
|
}
|
||||||
|
return t, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// FlatdbNodeResolver is a node resolver that reads nodes from the flatdb.
|
// nodeResolver is a node resolver that reads nodes from the flatdb.
|
||||||
func (t *BinaryTrie) FlatdbNodeResolver(path []byte, hash common.Hash) ([]byte, error) {
|
func (t *BinaryTrie) nodeResolver(path []byte, hash common.Hash) ([]byte, error) {
|
||||||
// empty nodes will be serialized as common.Hash{}, so capture
|
// empty nodes will be serialized as common.Hash{}, so capture
|
||||||
// this special use case.
|
// this special use case.
|
||||||
if hash == (common.Hash{}) {
|
if hash == (common.Hash{}) {
|
||||||
return nil, nil // empty node
|
return nil, nil // empty node
|
||||||
}
|
}
|
||||||
return t.reader.Node(path, hash)
|
blob, err := t.reader.Node(path, hash)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
t.tracer.Put(path, blob)
|
||||||
|
return blob, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetKey returns the sha3 preimage of a hashed key that was previously used
|
// GetKey returns the sha3 preimage of a hashed key that was previously used
|
||||||
|
|
@ -93,30 +98,23 @@ func (t *BinaryTrie) GetKey(key []byte) []byte {
|
||||||
return key
|
return key
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get returns the value for key stored in the trie. The value bytes must
|
|
||||||
// not be modified by the caller. If a node was not found in the database, a
|
|
||||||
// trie.MissingNodeError is returned.
|
|
||||||
func (t *BinaryTrie) GetStorage(addr common.Address, key []byte) ([]byte, error) {
|
|
||||||
return t.root.Get(utils.GetBinaryTreeKey(addr, key), t.FlatdbNodeResolver)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetWithHashedKey returns the value, assuming that the key has already
|
// GetWithHashedKey returns the value, assuming that the key has already
|
||||||
// been hashed.
|
// been hashed.
|
||||||
func (t *BinaryTrie) GetWithHashedKey(key []byte) ([]byte, error) {
|
func (t *BinaryTrie) GetWithHashedKey(key []byte) ([]byte, error) {
|
||||||
return t.root.Get(key, t.FlatdbNodeResolver)
|
return t.root.Get(key, t.nodeResolver)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAccount returns the account information for the given address.
|
// GetAccount returns the account information for the given address.
|
||||||
func (t *BinaryTrie) GetAccount(addr common.Address) (*types.StateAccount, error) {
|
func (t *BinaryTrie) GetAccount(addr common.Address) (*types.StateAccount, error) {
|
||||||
acc := &types.StateAccount{}
|
|
||||||
versionkey := utils.GetBinaryTreeKey(addr, zero[:])
|
|
||||||
var (
|
var (
|
||||||
values [][]byte
|
values [][]byte
|
||||||
err error
|
err error
|
||||||
|
acc = &types.StateAccount{}
|
||||||
|
key = GetBinaryTreeKey(addr, zero[:])
|
||||||
)
|
)
|
||||||
switch r := t.root.(type) {
|
switch r := t.root.(type) {
|
||||||
case *InternalNode:
|
case *InternalNode:
|
||||||
values, err = r.GetValuesAtStem(versionkey[:31], t.FlatdbNodeResolver)
|
values, err = r.GetValuesAtStem(key[:31], t.nodeResolver)
|
||||||
case *StemNode:
|
case *StemNode:
|
||||||
values = r.Values
|
values = r.Values
|
||||||
case Empty:
|
case Empty:
|
||||||
|
|
@ -130,47 +128,53 @@ func (t *BinaryTrie) GetAccount(addr common.Address) (*types.StateAccount, error
|
||||||
return nil, fmt.Errorf("GetAccount (%x) error: %v", addr, err)
|
return nil, fmt.Errorf("GetAccount (%x) error: %v", addr, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// The following code is required for the MPT->VKT conversion.
|
// The following code is required for the MPT->Binary conversion.
|
||||||
// An account can be partially migrated, where storage slots were moved to the VKT
|
// An account can be partially migrated, where storage slots were moved to the binary
|
||||||
// but not yet the account. This means some account information as (header) storage slots
|
// but not yet the account. This means some account information as (header) storage slots
|
||||||
// are in the VKT but basic account information must be read in the base tree (MPT).
|
// are in the binary trie but basic account information must be read in the base tree (MPT).
|
||||||
// TODO: we can simplify this logic depending if the conversion is in progress or finished.
|
// TODO: we can simplify this logic depending if the conversion is in progress or finished.
|
||||||
emptyAccount := true
|
emptyAccount := true
|
||||||
|
for i := 0; values != nil && i <= CodeHashLeafKey && emptyAccount; i++ {
|
||||||
for i := 0; values != nil && i <= utils.CodeHashLeafKey && emptyAccount; i++ {
|
|
||||||
emptyAccount = emptyAccount && values[i] == nil
|
emptyAccount = emptyAccount && values[i] == nil
|
||||||
}
|
}
|
||||||
if emptyAccount {
|
if emptyAccount {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the account has been deleted, then values[10] will be 0 and not nil. If it has
|
// If the account has been deleted, then values[10] will be 0 and not nil. If it has
|
||||||
// been recreated after that, then its code keccak will NOT be 0. So return `nil` if
|
// been recreated after that, then its code keccak will NOT be 0. So return `nil` if
|
||||||
// the nonce, and values[10], and code keccak is 0.
|
// the nonce, and values[10], and code keccak is 0.
|
||||||
if bytes.Equal(values[utils.BasicDataLeafKey], zero[:]) && len(values) > 10 && len(values[10]) > 0 && bytes.Equal(values[utils.CodeHashLeafKey], zero[:]) {
|
if bytes.Equal(values[BasicDataLeafKey], zero[:]) && len(values) > 10 && len(values[10]) > 0 && bytes.Equal(values[CodeHashLeafKey], zero[:]) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
acc.Nonce = binary.BigEndian.Uint64(values[utils.BasicDataLeafKey][utils.BasicDataNonceOffset:])
|
acc.Nonce = binary.BigEndian.Uint64(values[BasicDataLeafKey][BasicDataNonceOffset:])
|
||||||
var balance [16]byte
|
var balance [16]byte
|
||||||
copy(balance[:], values[utils.BasicDataLeafKey][utils.BasicDataBalanceOffset:])
|
copy(balance[:], values[BasicDataLeafKey][BasicDataBalanceOffset:])
|
||||||
acc.Balance = new(uint256.Int).SetBytes(balance[:])
|
acc.Balance = new(uint256.Int).SetBytes(balance[:])
|
||||||
acc.CodeHash = values[utils.CodeHashLeafKey]
|
acc.CodeHash = values[CodeHashLeafKey]
|
||||||
|
|
||||||
return acc, nil
|
return acc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetStorage returns the value for key stored in the trie. The value bytes must
|
||||||
|
// not be modified by the caller. If a node was not found in the database, a
|
||||||
|
// trie.MissingNodeError is returned.
|
||||||
|
func (t *BinaryTrie) GetStorage(addr common.Address, key []byte) ([]byte, error) {
|
||||||
|
return t.root.Get(GetBinaryTreeKey(addr, key), t.nodeResolver)
|
||||||
|
}
|
||||||
|
|
||||||
// UpdateAccount updates the account information for the given address.
|
// UpdateAccount updates the account information for the given address.
|
||||||
func (t *BinaryTrie) UpdateAccount(addr common.Address, acc *types.StateAccount, codeLen int) error {
|
func (t *BinaryTrie) UpdateAccount(addr common.Address, acc *types.StateAccount, codeLen int) error {
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
basicData [32]byte
|
basicData [32]byte
|
||||||
values = make([][]byte, NodeWidth)
|
values = make([][]byte, NodeWidth)
|
||||||
stem = utils.GetBinaryTreeKey(addr, zero[:])
|
stem = GetBinaryTreeKey(addr, zero[:])
|
||||||
)
|
)
|
||||||
|
binary.BigEndian.PutUint32(basicData[BasicDataCodeSizeOffset-1:], uint32(codeLen))
|
||||||
|
binary.BigEndian.PutUint64(basicData[BasicDataNonceOffset:], acc.Nonce)
|
||||||
|
|
||||||
binary.BigEndian.PutUint32(basicData[utils.BasicDataCodeSizeOffset-1:], uint32(codeLen))
|
|
||||||
binary.BigEndian.PutUint64(basicData[utils.BasicDataNonceOffset:], acc.Nonce)
|
|
||||||
// Because the balance is a max of 16 bytes, truncate
|
// Because the balance is a max of 16 bytes, truncate
|
||||||
// the extra values. This happens in devmode, where
|
// the extra values. This happens in devmode, where
|
||||||
// 0xff**32 is allocated to the developer account.
|
// 0xff**32 is allocated to the developer account.
|
||||||
|
|
@ -181,33 +185,33 @@ func (t *BinaryTrie) UpdateAccount(addr common.Address, acc *types.StateAccount,
|
||||||
balanceBytes = balanceBytes[16:]
|
balanceBytes = balanceBytes[16:]
|
||||||
}
|
}
|
||||||
copy(basicData[32-len(balanceBytes):], balanceBytes[:])
|
copy(basicData[32-len(balanceBytes):], balanceBytes[:])
|
||||||
values[utils.BasicDataLeafKey] = basicData[:]
|
values[BasicDataLeafKey] = basicData[:]
|
||||||
values[utils.CodeHashLeafKey] = acc.CodeHash[:]
|
values[CodeHashLeafKey] = acc.CodeHash[:]
|
||||||
|
|
||||||
t.root, err = t.root.InsertValuesAtStem(stem, values, t.FlatdbNodeResolver, 0)
|
t.root, err = t.root.InsertValuesAtStem(stem, values, t.nodeResolver, 0)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateStem updates the values for the given stem key.
|
// UpdateStem updates the values for the given stem key.
|
||||||
func (t *BinaryTrie) UpdateStem(key []byte, values [][]byte) error {
|
func (t *BinaryTrie) UpdateStem(key []byte, values [][]byte) error {
|
||||||
var err error
|
var err error
|
||||||
t.root, err = t.root.InsertValuesAtStem(key, values, t.FlatdbNodeResolver, 0)
|
t.root, err = t.root.InsertValuesAtStem(key, values, t.nodeResolver, 0)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update associates key with value in the trie. If value has length zero, any
|
// UpdateStorage associates key with value in the trie. If value has length zero, any
|
||||||
// existing value is deleted from the trie. The value bytes must not be modified
|
// existing value is deleted from the trie. The value bytes must not be modified
|
||||||
// by the caller while they are stored in the trie. If a node was not found in the
|
// by the caller while they are stored in the trie. If a node was not found in the
|
||||||
// database, a trie.MissingNodeError is returned.
|
// database, a trie.MissingNodeError is returned.
|
||||||
func (t *BinaryTrie) UpdateStorage(address common.Address, key, value []byte) error {
|
func (t *BinaryTrie) UpdateStorage(address common.Address, key, value []byte) error {
|
||||||
k := utils.GetBinaryTreeKeyStorageSlot(address, key)
|
k := GetBinaryTreeKeyStorageSlot(address, key)
|
||||||
var v [32]byte
|
var v [32]byte
|
||||||
if len(value) >= 32 {
|
if len(value) >= 32 {
|
||||||
copy(v[:], value[:32])
|
copy(v[:], value[:32])
|
||||||
} else {
|
} else {
|
||||||
copy(v[32-len(value):], value[:])
|
copy(v[32-len(value):], value[:])
|
||||||
}
|
}
|
||||||
root, err := t.root.Insert(k, v[:], t.FlatdbNodeResolver)
|
root, err := t.root.Insert(k, v[:], t.nodeResolver, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("UpdateStorage (%x) error: %v", address, err)
|
return fmt.Errorf("UpdateStorage (%x) error: %v", address, err)
|
||||||
}
|
}
|
||||||
|
|
@ -220,12 +224,12 @@ func (t *BinaryTrie) DeleteAccount(addr common.Address) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete removes any existing value for key from the trie. If a node was not
|
// DeleteStorage removes any existing value for key from the trie. If a node was not
|
||||||
// found in the database, a trie.MissingNodeError is returned.
|
// found in the database, a trie.MissingNodeError is returned.
|
||||||
func (t *BinaryTrie) DeleteStorage(addr common.Address, key []byte) error {
|
func (t *BinaryTrie) DeleteStorage(addr common.Address, key []byte) error {
|
||||||
k := utils.GetBinaryTreeKey(addr, key)
|
k := GetBinaryTreeKey(addr, key)
|
||||||
var zero [32]byte
|
var zero [32]byte
|
||||||
root, err := t.root.Insert(k, zero[:], t.FlatdbNodeResolver)
|
root, err := t.root.Insert(k, zero[:], t.nodeResolver, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("DeleteStorage (%x) error: %v", addr, err)
|
return fmt.Errorf("DeleteStorage (%x) error: %v", addr, err)
|
||||||
}
|
}
|
||||||
|
|
@ -290,6 +294,8 @@ func (t *BinaryTrie) IsVerkle() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateContractCode updates the contract code into the trie.
|
||||||
|
//
|
||||||
// Note: the basic data leaf needs to have been previously created for this to work
|
// Note: the basic data leaf needs to have been previously created for this to work
|
||||||
func (t *BinaryTrie) UpdateContractCode(addr common.Address, codeHash common.Hash, code []byte) error {
|
func (t *BinaryTrie) UpdateContractCode(addr common.Address, codeHash common.Hash, code []byte) error {
|
||||||
var (
|
var (
|
||||||
|
|
@ -304,7 +310,7 @@ func (t *BinaryTrie) UpdateContractCode(addr common.Address, codeHash common.Has
|
||||||
values = make([][]byte, NodeWidth)
|
values = make([][]byte, NodeWidth)
|
||||||
var offset [32]byte
|
var offset [32]byte
|
||||||
binary.LittleEndian.PutUint64(offset[24:], chunknr+128)
|
binary.LittleEndian.PutUint64(offset[24:], chunknr+128)
|
||||||
key = utils.GetBinaryTreeKey(addr, offset[:])
|
key = GetBinaryTreeKey(addr, offset[:])
|
||||||
}
|
}
|
||||||
values[groupOffset] = chunks[i : i+32]
|
values[groupOffset] = chunks[i : i+32]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ var (
|
||||||
|
|
||||||
func TestSingleEntry(t *testing.T) {
|
func TestSingleEntry(t *testing.T) {
|
||||||
tree := NewBinaryNode()
|
tree := NewBinaryNode()
|
||||||
tree, err := tree.Insert(zeroKey[:], oneKey[:], nil)
|
tree, err := tree.Insert(zeroKey[:], oneKey[:], nil, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -52,11 +52,11 @@ func TestSingleEntry(t *testing.T) {
|
||||||
func TestTwoEntriesDiffFirstBit(t *testing.T) {
|
func TestTwoEntriesDiffFirstBit(t *testing.T) {
|
||||||
var err error
|
var err error
|
||||||
tree := NewBinaryNode()
|
tree := NewBinaryNode()
|
||||||
tree, err = tree.Insert(zeroKey[:], oneKey[:], nil)
|
tree, err = tree.Insert(zeroKey[:], oneKey[:], nil, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
tree, err = tree.Insert(common.HexToHash("8000000000000000000000000000000000000000000000000000000000000000").Bytes(), twoKey[:], nil)
|
tree, err = tree.Insert(common.HexToHash("8000000000000000000000000000000000000000000000000000000000000000").Bytes(), twoKey[:], nil, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -71,19 +71,19 @@ func TestTwoEntriesDiffFirstBit(t *testing.T) {
|
||||||
func TestOneStemColocatedValues(t *testing.T) {
|
func TestOneStemColocatedValues(t *testing.T) {
|
||||||
var err error
|
var err error
|
||||||
tree := NewBinaryNode()
|
tree := NewBinaryNode()
|
||||||
tree, err = tree.Insert(common.HexToHash("0000000000000000000000000000000000000000000000000000000000000003").Bytes(), oneKey[:], nil)
|
tree, err = tree.Insert(common.HexToHash("0000000000000000000000000000000000000000000000000000000000000003").Bytes(), oneKey[:], nil, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
tree, err = tree.Insert(common.HexToHash("0000000000000000000000000000000000000000000000000000000000000004").Bytes(), twoKey[:], nil)
|
tree, err = tree.Insert(common.HexToHash("0000000000000000000000000000000000000000000000000000000000000004").Bytes(), twoKey[:], nil, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
tree, err = tree.Insert(common.HexToHash("0000000000000000000000000000000000000000000000000000000000000009").Bytes(), threeKey[:], nil)
|
tree, err = tree.Insert(common.HexToHash("0000000000000000000000000000000000000000000000000000000000000009").Bytes(), threeKey[:], nil, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
tree, err = tree.Insert(common.HexToHash("00000000000000000000000000000000000000000000000000000000000000FF").Bytes(), fourKey[:], nil)
|
tree, err = tree.Insert(common.HexToHash("00000000000000000000000000000000000000000000000000000000000000FF").Bytes(), fourKey[:], nil, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -96,20 +96,20 @@ func TestTwoStemColocatedValues(t *testing.T) {
|
||||||
var err error
|
var err error
|
||||||
tree := NewBinaryNode()
|
tree := NewBinaryNode()
|
||||||
// stem: 0...0
|
// stem: 0...0
|
||||||
tree, err = tree.Insert(common.HexToHash("0000000000000000000000000000000000000000000000000000000000000003").Bytes(), oneKey[:], nil)
|
tree, err = tree.Insert(common.HexToHash("0000000000000000000000000000000000000000000000000000000000000003").Bytes(), oneKey[:], nil, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
tree, err = tree.Insert(common.HexToHash("0000000000000000000000000000000000000000000000000000000000000004").Bytes(), twoKey[:], nil)
|
tree, err = tree.Insert(common.HexToHash("0000000000000000000000000000000000000000000000000000000000000004").Bytes(), twoKey[:], nil, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
// stem: 10...0
|
// stem: 10...0
|
||||||
tree, err = tree.Insert(common.HexToHash("8000000000000000000000000000000000000000000000000000000000000003").Bytes(), oneKey[:], nil)
|
tree, err = tree.Insert(common.HexToHash("8000000000000000000000000000000000000000000000000000000000000003").Bytes(), oneKey[:], nil, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
tree, err = tree.Insert(common.HexToHash("8000000000000000000000000000000000000000000000000000000000000004").Bytes(), twoKey[:], nil)
|
tree, err = tree.Insert(common.HexToHash("8000000000000000000000000000000000000000000000000000000000000004").Bytes(), twoKey[:], nil, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -124,11 +124,11 @@ func TestTwoKeysMatchFirst42Bits(t *testing.T) {
|
||||||
// key1 and key 2 have the same prefix of 42 bits (b0*42+b1+b1) and differ after.
|
// key1 and key 2 have the same prefix of 42 bits (b0*42+b1+b1) and differ after.
|
||||||
key1 := common.HexToHash("0000000000C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0").Bytes()
|
key1 := common.HexToHash("0000000000C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0").Bytes()
|
||||||
key2 := common.HexToHash("0000000000E00000000000000000000000000000000000000000000000000000").Bytes()
|
key2 := common.HexToHash("0000000000E00000000000000000000000000000000000000000000000000000").Bytes()
|
||||||
tree, err = tree.Insert(key1, oneKey[:], nil)
|
tree, err = tree.Insert(key1, oneKey[:], nil, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
tree, err = tree.Insert(key2, twoKey[:], nil)
|
tree, err = tree.Insert(key2, twoKey[:], nil, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -139,11 +139,11 @@ func TestTwoKeysMatchFirst42Bits(t *testing.T) {
|
||||||
func TestInsertDuplicateKey(t *testing.T) {
|
func TestInsertDuplicateKey(t *testing.T) {
|
||||||
var err error
|
var err error
|
||||||
tree := NewBinaryNode()
|
tree := NewBinaryNode()
|
||||||
tree, err = tree.Insert(oneKey[:], oneKey[:], nil)
|
tree, err = tree.Insert(oneKey[:], oneKey[:], nil, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
tree, err = tree.Insert(oneKey[:], twoKey[:], nil)
|
tree, err = tree.Insert(oneKey[:], twoKey[:], nil, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -161,7 +161,7 @@ func TestLargeNumberOfEntries(t *testing.T) {
|
||||||
for i := range 256 {
|
for i := range 256 {
|
||||||
var key [32]byte
|
var key [32]byte
|
||||||
key[0] = byte(i)
|
key[0] = byte(i)
|
||||||
tree, err = tree.Insert(key[:], ffKey[:], nil)
|
tree, err = tree.Insert(key[:], ffKey[:], nil, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -184,7 +184,7 @@ func TestMerkleizeMultipleEntries(t *testing.T) {
|
||||||
for i, key := range keys {
|
for i, key := range keys {
|
||||||
var v [32]byte
|
var v [32]byte
|
||||||
binary.LittleEndian.PutUint64(v[:8], uint64(i))
|
binary.LittleEndian.PutUint64(v[:8], uint64(i))
|
||||||
tree, err = tree.Insert(key, v[:], nil)
|
tree, err = tree.Insert(key, v[:], nil, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -571,7 +571,7 @@ func VerifyRangeProof(rootHash common.Hash, firstKey []byte, keys [][]byte, valu
|
||||||
root: root,
|
root: root,
|
||||||
reader: newEmptyReader(),
|
reader: newEmptyReader(),
|
||||||
opTracer: newOpTracer(),
|
opTracer: newOpTracer(),
|
||||||
prevalueTracer: newPrevalueTracer(),
|
prevalueTracer: NewPrevalueTracer(),
|
||||||
}
|
}
|
||||||
if empty {
|
if empty {
|
||||||
tr.root = nil
|
tr.root = nil
|
||||||
|
|
|
||||||
|
|
@ -104,17 +104,17 @@ type PrevalueTracer struct {
|
||||||
lock sync.RWMutex
|
lock sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// newPrevalueTracer initializes the tracer for capturing resolved trie nodes.
|
// NewPrevalueTracer initializes the tracer for capturing resolved trie nodes.
|
||||||
func newPrevalueTracer() *PrevalueTracer {
|
func NewPrevalueTracer() *PrevalueTracer {
|
||||||
return &PrevalueTracer{
|
return &PrevalueTracer{
|
||||||
data: make(map[string][]byte),
|
data: make(map[string][]byte),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// put tracks the newly loaded trie node and caches its RLP-encoded
|
// Put tracks the newly loaded trie node and caches its RLP-encoded
|
||||||
// blob internally. Do not modify the value outside this function,
|
// blob internally. Do not modify the value outside this function,
|
||||||
// as it is not deep-copied.
|
// as it is not deep-copied.
|
||||||
func (t *PrevalueTracer) put(path []byte, val []byte) {
|
func (t *PrevalueTracer) Put(path []byte, val []byte) {
|
||||||
t.lock.Lock()
|
t.lock.Lock()
|
||||||
defer t.lock.Unlock()
|
defer t.lock.Unlock()
|
||||||
|
|
||||||
|
|
@ -130,9 +130,9 @@ func (t *PrevalueTracer) Get(path []byte) []byte {
|
||||||
return t.data[string(path)]
|
return t.data[string(path)]
|
||||||
}
|
}
|
||||||
|
|
||||||
// hasList returns a list of flags indicating whether the corresponding trie nodes
|
// HasList returns a list of flags indicating whether the corresponding trie nodes
|
||||||
// specified by the path exist in the trie.
|
// specified by the path exist in the trie.
|
||||||
func (t *PrevalueTracer) hasList(list [][]byte) []bool {
|
func (t *PrevalueTracer) HasList(list [][]byte) []bool {
|
||||||
t.lock.RLock()
|
t.lock.RLock()
|
||||||
defer t.lock.RUnlock()
|
defer t.lock.RUnlock()
|
||||||
|
|
||||||
|
|
@ -144,24 +144,24 @@ func (t *PrevalueTracer) hasList(list [][]byte) []bool {
|
||||||
return exists
|
return exists
|
||||||
}
|
}
|
||||||
|
|
||||||
// values returns a list of values of the cached trie nodes.
|
// Values returns a list of values of the cached trie nodes.
|
||||||
func (t *PrevalueTracer) values() map[string][]byte {
|
func (t *PrevalueTracer) Values() map[string][]byte {
|
||||||
t.lock.RLock()
|
t.lock.RLock()
|
||||||
defer t.lock.RUnlock()
|
defer t.lock.RUnlock()
|
||||||
|
|
||||||
return maps.Clone(t.data)
|
return maps.Clone(t.data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// reset resets the cached content in the prevalueTracer.
|
// Reset resets the cached content in the prevalueTracer.
|
||||||
func (t *PrevalueTracer) reset() {
|
func (t *PrevalueTracer) Reset() {
|
||||||
t.lock.Lock()
|
t.lock.Lock()
|
||||||
defer t.lock.Unlock()
|
defer t.lock.Unlock()
|
||||||
|
|
||||||
clear(t.data)
|
clear(t.data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// copy returns a copied prevalueTracer instance.
|
// Copy returns a copied prevalueTracer instance.
|
||||||
func (t *PrevalueTracer) copy() *PrevalueTracer {
|
func (t *PrevalueTracer) Copy() *PrevalueTracer {
|
||||||
t.lock.RLock()
|
t.lock.RLock()
|
||||||
defer t.lock.RUnlock()
|
defer t.lock.RUnlock()
|
||||||
|
|
||||||
|
|
|
||||||
16
trie/trie.go
16
trie/trie.go
|
|
@ -55,7 +55,7 @@ type Trie struct {
|
||||||
uncommitted int
|
uncommitted int
|
||||||
|
|
||||||
// reader is the handler trie can retrieve nodes from.
|
// reader is the handler trie can retrieve nodes from.
|
||||||
reader *TrieReader
|
reader *Reader
|
||||||
|
|
||||||
// Various tracers for capturing the modifications to trie
|
// Various tracers for capturing the modifications to trie
|
||||||
opTracer *opTracer
|
opTracer *opTracer
|
||||||
|
|
@ -77,7 +77,7 @@ func (t *Trie) Copy() *Trie {
|
||||||
uncommitted: t.uncommitted,
|
uncommitted: t.uncommitted,
|
||||||
reader: t.reader,
|
reader: t.reader,
|
||||||
opTracer: t.opTracer.copy(),
|
opTracer: t.opTracer.copy(),
|
||||||
prevalueTracer: t.prevalueTracer.copy(),
|
prevalueTracer: t.prevalueTracer.Copy(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -88,7 +88,7 @@ func (t *Trie) Copy() *Trie {
|
||||||
// empty, otherwise, the root node must be present in database or returns
|
// empty, otherwise, the root node must be present in database or returns
|
||||||
// a MissingNodeError if not.
|
// a MissingNodeError if not.
|
||||||
func New(id *ID, db database.NodeDatabase) (*Trie, error) {
|
func New(id *ID, db database.NodeDatabase) (*Trie, error) {
|
||||||
reader, err := NewTrieReader(id.StateRoot, id.Owner, db)
|
reader, err := NewReader(id.StateRoot, id.Owner, db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -96,7 +96,7 @@ func New(id *ID, db database.NodeDatabase) (*Trie, error) {
|
||||||
owner: id.Owner,
|
owner: id.Owner,
|
||||||
reader: reader,
|
reader: reader,
|
||||||
opTracer: newOpTracer(),
|
opTracer: newOpTracer(),
|
||||||
prevalueTracer: newPrevalueTracer(),
|
prevalueTracer: NewPrevalueTracer(),
|
||||||
}
|
}
|
||||||
if id.Root != (common.Hash{}) && id.Root != types.EmptyRootHash {
|
if id.Root != (common.Hash{}) && id.Root != types.EmptyRootHash {
|
||||||
rootnode, err := trie.resolveAndTrack(id.Root[:], nil)
|
rootnode, err := trie.resolveAndTrack(id.Root[:], nil)
|
||||||
|
|
@ -659,7 +659,7 @@ func (t *Trie) resolveAndTrack(n hashNode, prefix []byte) (node, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
t.prevalueTracer.put(prefix, blob)
|
t.prevalueTracer.Put(prefix, blob)
|
||||||
|
|
||||||
// The returned node blob won't be changed afterward. No need to
|
// The returned node blob won't be changed afterward. No need to
|
||||||
// deep-copy the slice.
|
// deep-copy the slice.
|
||||||
|
|
@ -673,7 +673,7 @@ func (t *Trie) deletedNodes() [][]byte {
|
||||||
var (
|
var (
|
||||||
pos int
|
pos int
|
||||||
list = t.opTracer.deletedList()
|
list = t.opTracer.deletedList()
|
||||||
flags = t.prevalueTracer.hasList(list)
|
flags = t.prevalueTracer.HasList(list)
|
||||||
)
|
)
|
||||||
for i := 0; i < len(list); i++ {
|
for i := 0; i < len(list); i++ {
|
||||||
if flags[i] {
|
if flags[i] {
|
||||||
|
|
@ -753,7 +753,7 @@ func (t *Trie) hashRoot() []byte {
|
||||||
|
|
||||||
// Witness returns a set containing all trie nodes that have been accessed.
|
// Witness returns a set containing all trie nodes that have been accessed.
|
||||||
func (t *Trie) Witness() map[string][]byte {
|
func (t *Trie) Witness() map[string][]byte {
|
||||||
return t.prevalueTracer.values()
|
return t.prevalueTracer.Values()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset drops the referenced root node and cleans all internal state.
|
// Reset drops the referenced root node and cleans all internal state.
|
||||||
|
|
@ -763,6 +763,6 @@ func (t *Trie) Reset() {
|
||||||
t.unhashed = 0
|
t.unhashed = 0
|
||||||
t.uncommitted = 0
|
t.uncommitted = 0
|
||||||
t.opTracer.reset()
|
t.opTracer.reset()
|
||||||
t.prevalueTracer.reset()
|
t.prevalueTracer.Reset()
|
||||||
t.committed = false
|
t.committed = false
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,39 +22,39 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/triedb/database"
|
"github.com/ethereum/go-ethereum/triedb/database"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TrieReader is a wrapper of the underlying node reader. It's not safe
|
// Reader is a wrapper of the underlying database reader. It's not safe
|
||||||
// for concurrent usage.
|
// for concurrent usage.
|
||||||
type TrieReader struct {
|
type Reader struct {
|
||||||
owner common.Hash
|
owner common.Hash
|
||||||
reader database.NodeReader
|
reader database.NodeReader
|
||||||
banned map[string]struct{} // Marker to prevent node from being accessed, for tests
|
banned map[string]struct{} // Marker to prevent node from being accessed, for tests
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTrieReader initializes the trie reader with the given node reader.
|
// NewReader initializes the trie reader with the given database reader.
|
||||||
func NewTrieReader(stateRoot, owner common.Hash, db database.NodeDatabase) (*TrieReader, error) {
|
func NewReader(stateRoot, owner common.Hash, db database.NodeDatabase) (*Reader, error) {
|
||||||
if stateRoot == (common.Hash{}) || stateRoot == types.EmptyRootHash {
|
if stateRoot == (common.Hash{}) || stateRoot == types.EmptyRootHash {
|
||||||
return &TrieReader{owner: owner}, nil
|
return &Reader{owner: owner}, nil
|
||||||
}
|
}
|
||||||
reader, err := db.NodeReader(stateRoot)
|
reader, err := db.NodeReader(stateRoot)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, &MissingNodeError{Owner: owner, NodeHash: stateRoot, err: err}
|
return nil, &MissingNodeError{Owner: owner, NodeHash: stateRoot, err: err}
|
||||||
}
|
}
|
||||||
return &TrieReader{owner: owner, reader: reader}, nil
|
return &Reader{owner: owner, reader: reader}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// newEmptyReader initializes the pure in-memory reader. All read operations
|
// newEmptyReader initializes the pure in-memory reader. All read operations
|
||||||
// should be forbidden and returns the MissingNodeError.
|
// should be forbidden and returns the MissingNodeError.
|
||||||
func newEmptyReader() *TrieReader {
|
func newEmptyReader() *Reader {
|
||||||
return &TrieReader{}
|
return &Reader{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// node retrieves the rlp-encoded trie node with the provided trie node
|
// Node retrieves the rlp-encoded trie node with the provided trie node
|
||||||
// information. An MissingNodeError will be returned in case the node is
|
// information. An MissingNodeError will be returned in case the node is
|
||||||
// not found or any error is encountered.
|
// not found or any error is encountered.
|
||||||
//
|
//
|
||||||
// Don't modify the returned byte slice since it's not deep-copied and
|
// Don't modify the returned byte slice since it's not deep-copied and
|
||||||
// still be referenced by database.
|
// still be referenced by database.
|
||||||
func (r *TrieReader) Node(path []byte, hash common.Hash) ([]byte, error) {
|
func (r *Reader) Node(path []byte, hash common.Hash) ([]byte, error) {
|
||||||
// Perform the logics in tests for preventing trie node access.
|
// Perform the logics in tests for preventing trie node access.
|
||||||
if r.banned != nil {
|
if r.banned != nil {
|
||||||
if _, ok := r.banned[string(path)]; ok {
|
if _, ok := r.banned[string(path)]; ok {
|
||||||
|
|
|
||||||
|
|
@ -41,13 +41,13 @@ var (
|
||||||
type VerkleTrie struct {
|
type VerkleTrie struct {
|
||||||
root verkle.VerkleNode
|
root verkle.VerkleNode
|
||||||
cache *utils.PointCache
|
cache *utils.PointCache
|
||||||
reader *TrieReader
|
reader *Reader
|
||||||
tracer *PrevalueTracer
|
tracer *PrevalueTracer
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewVerkleTrie constructs a verkle tree based on the specified root hash.
|
// NewVerkleTrie constructs a verkle tree based on the specified root hash.
|
||||||
func NewVerkleTrie(root common.Hash, db database.NodeDatabase, cache *utils.PointCache) (*VerkleTrie, error) {
|
func NewVerkleTrie(root common.Hash, db database.NodeDatabase, cache *utils.PointCache) (*VerkleTrie, error) {
|
||||||
reader, err := NewTrieReader(root, common.Hash{}, db)
|
reader, err := NewReader(root, common.Hash{}, db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -55,7 +55,7 @@ func NewVerkleTrie(root common.Hash, db database.NodeDatabase, cache *utils.Poin
|
||||||
root: verkle.New(),
|
root: verkle.New(),
|
||||||
cache: cache,
|
cache: cache,
|
||||||
reader: reader,
|
reader: reader,
|
||||||
tracer: newPrevalueTracer(),
|
tracer: NewPrevalueTracer(),
|
||||||
}
|
}
|
||||||
// Parse the root verkle node if it's not empty.
|
// Parse the root verkle node if it's not empty.
|
||||||
if root != types.EmptyVerkleHash && root != types.EmptyRootHash {
|
if root != types.EmptyVerkleHash && root != types.EmptyRootHash {
|
||||||
|
|
@ -326,7 +326,7 @@ func (t *VerkleTrie) Copy() *VerkleTrie {
|
||||||
root: t.root.Copy(),
|
root: t.root.Copy(),
|
||||||
cache: t.cache,
|
cache: t.cache,
|
||||||
reader: t.reader,
|
reader: t.reader,
|
||||||
tracer: t.tracer.copy(),
|
tracer: t.tracer.Copy(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -451,7 +451,7 @@ func (t *VerkleTrie) nodeResolver(path []byte) ([]byte, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
t.tracer.put(path, blob)
|
t.tracer.Put(path, blob)
|
||||||
return blob, nil
|
return blob, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue