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:
rjl493456442 2025-08-29 17:59:54 +08:00 committed by GitHub
parent 931b8fb13e
commit 123723b9da
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 219 additions and 137 deletions

View file

@ -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{}
) )

View file

@ -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]

View file

@ -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
} }

View file

@ -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)
} }

View file

@ -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")
} }

View file

@ -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")
} }

View file

@ -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 {

View file

@ -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)
} }

View file

@ -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)
} }

View 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)
}

View file

@ -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
} }

View file

@ -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")
} }

View file

@ -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]

View file

@ -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)
} }

View file

@ -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

View file

@ -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()

View file

@ -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
} }

View file

@ -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 {

View file

@ -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
} }