mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-23 13:16:42 +00:00
core/types, internal, trie: redefine ListHasher interface
This commit is contained in:
parent
f8412c2953
commit
89546e19d9
7 changed files with 92 additions and 46 deletions
|
|
@ -240,7 +240,7 @@ type extblock struct {
|
||||||
//
|
//
|
||||||
// The receipt's bloom must already calculated for the block's bloom to be
|
// The receipt's bloom must already calculated for the block's bloom to be
|
||||||
// correctly calculated.
|
// correctly calculated.
|
||||||
func NewBlock(header *Header, body *Body, receipts []*Receipt, hasher TrieHasher) *Block {
|
func NewBlock(header *Header, body *Body, receipts []*Receipt, hasher ListHasher) *Block {
|
||||||
if body == nil {
|
if body == nil {
|
||||||
body = &Body{}
|
body = &Body{}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -75,14 +75,17 @@ func prefixedRlpHash(prefix byte, x interface{}) (h common.Hash) {
|
||||||
return h
|
return h
|
||||||
}
|
}
|
||||||
|
|
||||||
// TrieHasher is the tool used to calculate the hash of derivable list.
|
// ListHasher defines the interface for computing the hash of a derivable list.
|
||||||
// This is internal, do not use.
|
type ListHasher interface {
|
||||||
type TrieHasher interface {
|
// Reset clears the internal state of the hasher, preparing it for reuse.
|
||||||
Reset()
|
Reset()
|
||||||
Update([]byte, []byte) error
|
|
||||||
// UpdateSafe is identical to Update, except that this method will copy the
|
// Update inserts the given key-value pair into the hasher.
|
||||||
// value slice. The caller is free to modify the value bytes after this method returns.
|
// The implementation must copy the provided slices, allowing the caller
|
||||||
UpdateSafe([]byte, []byte) error
|
// to safely modify them after the call returns.
|
||||||
|
Update(key []byte, value []byte) error
|
||||||
|
|
||||||
|
// Hash computes and returns the final hash of all inserted key-value pairs.
|
||||||
Hash() common.Hash
|
Hash() common.Hash
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -94,6 +97,7 @@ type DerivableList interface {
|
||||||
EncodeIndex(int, *bytes.Buffer)
|
EncodeIndex(int, *bytes.Buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// encodeForDerive encodes the element in the list at the position i into the buffer.
|
||||||
func encodeForDerive(list DerivableList, i int, buf *bytes.Buffer) []byte {
|
func encodeForDerive(list DerivableList, i int, buf *bytes.Buffer) []byte {
|
||||||
buf.Reset()
|
buf.Reset()
|
||||||
list.EncodeIndex(i, buf)
|
list.EncodeIndex(i, buf)
|
||||||
|
|
@ -101,9 +105,12 @@ func encodeForDerive(list DerivableList, i int, buf *bytes.Buffer) []byte {
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeriveSha creates the tree hashes of transactions, receipts, and withdrawals in a block header.
|
// DeriveSha creates the tree hashes of transactions, receipts, and withdrawals in a block header.
|
||||||
func DeriveSha(list DerivableList, hasher TrieHasher) common.Hash {
|
func DeriveSha(list DerivableList, hasher ListHasher) common.Hash {
|
||||||
hasher.Reset()
|
hasher.Reset()
|
||||||
|
|
||||||
|
// Allocate a buffer for value encoding. As the hasher is claimed that all
|
||||||
|
// supplied key value pairs will be copied by hasher and safe to reuse the
|
||||||
|
// encoding buffer.
|
||||||
valueBuf := encodeBufferPool.Get().(*bytes.Buffer)
|
valueBuf := encodeBufferPool.Get().(*bytes.Buffer)
|
||||||
defer encodeBufferPool.Put(valueBuf)
|
defer encodeBufferPool.Put(valueBuf)
|
||||||
|
|
||||||
|
|
@ -117,17 +124,17 @@ func DeriveSha(list DerivableList, hasher TrieHasher) common.Hash {
|
||||||
for i := 1; i < list.Len() && i <= 0x7f; i++ {
|
for i := 1; i < list.Len() && i <= 0x7f; i++ {
|
||||||
indexBuf = rlp.AppendUint64(indexBuf[:0], uint64(i))
|
indexBuf = rlp.AppendUint64(indexBuf[:0], uint64(i))
|
||||||
value := encodeForDerive(list, i, valueBuf)
|
value := encodeForDerive(list, i, valueBuf)
|
||||||
hasher.UpdateSafe(indexBuf, value)
|
hasher.Update(indexBuf, value)
|
||||||
}
|
}
|
||||||
if list.Len() > 0 {
|
if list.Len() > 0 {
|
||||||
indexBuf = rlp.AppendUint64(indexBuf[:0], 0)
|
indexBuf = rlp.AppendUint64(indexBuf[:0], 0)
|
||||||
value := encodeForDerive(list, 0, valueBuf)
|
value := encodeForDerive(list, 0, valueBuf)
|
||||||
hasher.UpdateSafe(indexBuf, value)
|
hasher.Update(indexBuf, value)
|
||||||
}
|
}
|
||||||
for i := 0x80; i < list.Len(); i++ {
|
for i := 0x80; i < list.Len(); i++ {
|
||||||
indexBuf = rlp.AppendUint64(indexBuf[:0], uint64(i))
|
indexBuf = rlp.AppendUint64(indexBuf[:0], uint64(i))
|
||||||
value := encodeForDerive(list, i, valueBuf)
|
value := encodeForDerive(list, i, valueBuf)
|
||||||
hasher.UpdateSafe(indexBuf, value)
|
hasher.Update(indexBuf, value)
|
||||||
}
|
}
|
||||||
return hasher.Hash()
|
return hasher.Hash()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,12 +26,10 @@ import (
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
"github.com/ethereum/go-ethereum/triedb"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestDeriveSha(t *testing.T) {
|
func TestDeriveSha(t *testing.T) {
|
||||||
|
|
@ -40,7 +38,7 @@ func TestDeriveSha(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
for len(txs) < 1000 {
|
for len(txs) < 1000 {
|
||||||
exp := types.DeriveSha(txs, trie.NewEmpty(triedb.NewDatabase(rawdb.NewMemoryDatabase(), nil)))
|
exp := types.DeriveSha(txs, trie.NewListHasher())
|
||||||
got := types.DeriveSha(txs, trie.NewStackTrie(nil))
|
got := types.DeriveSha(txs, trie.NewStackTrie(nil))
|
||||||
if !bytes.Equal(got[:], exp[:]) {
|
if !bytes.Equal(got[:], exp[:]) {
|
||||||
t.Fatalf("%d txs: got %x exp %x", len(txs), got, exp)
|
t.Fatalf("%d txs: got %x exp %x", len(txs), got, exp)
|
||||||
|
|
@ -81,12 +79,12 @@ func BenchmarkDeriveSha200(b *testing.B) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
}
|
}
|
||||||
want := types.DeriveSha(txs, trie.NewEmpty(triedb.NewDatabase(rawdb.NewMemoryDatabase(), nil)))
|
want := types.DeriveSha(txs, trie.NewListHasher())
|
||||||
var have common.Hash
|
var have common.Hash
|
||||||
b.Run("std_trie", func(b *testing.B) {
|
b.Run("std_trie", func(b *testing.B) {
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for b.Loop() {
|
for b.Loop() {
|
||||||
have = types.DeriveSha(txs, trie.NewEmpty(triedb.NewDatabase(rawdb.NewMemoryDatabase(), nil)))
|
have = types.DeriveSha(txs, trie.NewListHasher())
|
||||||
}
|
}
|
||||||
if have != want {
|
if have != want {
|
||||||
b.Errorf("have %x want %x", have, want)
|
b.Errorf("have %x want %x", have, want)
|
||||||
|
|
@ -111,7 +109,7 @@ func TestFuzzDeriveSha(t *testing.T) {
|
||||||
rndSeed := mrand.Int()
|
rndSeed := mrand.Int()
|
||||||
for i := 0; i < 10; i++ {
|
for i := 0; i < 10; i++ {
|
||||||
seed := rndSeed + i
|
seed := rndSeed + i
|
||||||
exp := types.DeriveSha(newDummy(i), trie.NewEmpty(triedb.NewDatabase(rawdb.NewMemoryDatabase(), nil)))
|
exp := types.DeriveSha(newDummy(i), trie.NewListHasher())
|
||||||
got := types.DeriveSha(newDummy(i), trie.NewStackTrie(nil))
|
got := types.DeriveSha(newDummy(i), trie.NewStackTrie(nil))
|
||||||
if !bytes.Equal(got[:], exp[:]) {
|
if !bytes.Equal(got[:], exp[:]) {
|
||||||
printList(t, newDummy(seed))
|
printList(t, newDummy(seed))
|
||||||
|
|
@ -139,7 +137,7 @@ func TestDerivableList(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for i, tc := range tcs[1:] {
|
for i, tc := range tcs[1:] {
|
||||||
exp := types.DeriveSha(flatList(tc), trie.NewEmpty(triedb.NewDatabase(rawdb.NewMemoryDatabase(), nil)))
|
exp := types.DeriveSha(flatList(tc), trie.NewListHasher())
|
||||||
got := types.DeriveSha(flatList(tc), trie.NewStackTrie(nil))
|
got := types.DeriveSha(flatList(tc), trie.NewStackTrie(nil))
|
||||||
if !bytes.Equal(got[:], exp[:]) {
|
if !bytes.Equal(got[:], exp[:]) {
|
||||||
t.Fatalf("case %d: got %x exp %x", i, got, exp)
|
t.Fatalf("case %d: got %x exp %x", i, got, exp)
|
||||||
|
|
@ -235,12 +233,6 @@ func (d *hashToHumanReadable) Update(i []byte, i2 []byte) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateSafe is identical to Update, except that this method will copy the
|
|
||||||
// value slice. The caller is free to modify the value bytes after this method returns.
|
|
||||||
func (d *hashToHumanReadable) UpdateSafe(key, value []byte) error {
|
|
||||||
return d.Update(key, common.CopyBytes(value))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *hashToHumanReadable) Hash() common.Hash {
|
func (d *hashToHumanReadable) Hash() common.Hash {
|
||||||
return common.Hash{}
|
return common.Hash{}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@
|
||||||
package blocktest
|
package blocktest
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"hash"
|
"hash"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
|
@ -48,17 +49,11 @@ func (h *testHasher) Reset() {
|
||||||
|
|
||||||
// Update updates the hash state with the given key and value.
|
// Update updates the hash state with the given key and value.
|
||||||
func (h *testHasher) Update(key, val []byte) error {
|
func (h *testHasher) Update(key, val []byte) error {
|
||||||
h.hasher.Write(key)
|
h.hasher.Write(bytes.Clone(key))
|
||||||
h.hasher.Write(val)
|
h.hasher.Write(bytes.Clone(val))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateSafe is identical to Update, except that this method will copy the
|
|
||||||
// value slice. The caller is free to modify the value bytes after this method returns.
|
|
||||||
func (h *testHasher) UpdateSafe(key, value []byte) error {
|
|
||||||
return h.Update(key, common.CopyBytes(value))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hash returns the hash value.
|
// Hash returns the hash value.
|
||||||
func (h *testHasher) Hash() common.Hash {
|
func (h *testHasher) Hash() common.Hash {
|
||||||
return common.BytesToHash(h.hasher.Sum(nil))
|
return common.BytesToHash(h.hasher.Sum(nil))
|
||||||
|
|
|
||||||
59
trie/list_hasher.go
Normal file
59
trie/list_hasher.go
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
// Copyright 2025 The 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 trie
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ListHasher is the wrapper of Merkle-Patricia-Trie, implementing the
|
||||||
|
// types.ListHasher by always deep-copying the supplied slices.
|
||||||
|
//
|
||||||
|
// This implementation is very inefficient in terms of memory allocation
|
||||||
|
// compared with StackTrie, only for correctness comparison purpose.
|
||||||
|
type ListHasher struct {
|
||||||
|
tr *Trie
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewListHasher initializes the list hasher.
|
||||||
|
func NewListHasher() *ListHasher {
|
||||||
|
return &ListHasher{
|
||||||
|
tr: NewEmpty(nil),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset implements types.ListHasher, clearing the internal state and prepare
|
||||||
|
// it for reuse.
|
||||||
|
func (h *ListHasher) Reset() {
|
||||||
|
h.tr.reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update implements types.ListHasher, inserting the given key-value pair into
|
||||||
|
// the trie. The supplied key-value pair should be deep-copied to satisfy the
|
||||||
|
// interface restriction.
|
||||||
|
func (h *ListHasher) Update(key []byte, value []byte) error {
|
||||||
|
key, value = bytes.Clone(key), bytes.Clone(value)
|
||||||
|
return h.tr.Update(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hash implements types.ListHasher, computing the final hash of all inserted
|
||||||
|
// key-value pairs.
|
||||||
|
func (h *ListHasher) Hash() common.Hash {
|
||||||
|
return h.tr.Hash()
|
||||||
|
}
|
||||||
|
|
@ -28,7 +28,7 @@ import (
|
||||||
var (
|
var (
|
||||||
stPool = sync.Pool{New: func() any { return new(stNode) }}
|
stPool = sync.Pool{New: func() any { return new(stNode) }}
|
||||||
bPool = newBytesPool(32, 100)
|
bPool = newBytesPool(32, 100)
|
||||||
_ = types.TrieHasher((*StackTrie)(nil))
|
_ = types.ListHasher((*StackTrie)(nil))
|
||||||
)
|
)
|
||||||
|
|
||||||
// OnTrieNode is a callback method invoked when a trie node is committed
|
// OnTrieNode is a callback method invoked when a trie node is committed
|
||||||
|
|
@ -75,16 +75,10 @@ func (t *StackTrie) grow(key []byte) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateSafe is identical to Update, except that this method will copy the
|
|
||||||
// value slice. The caller is free to modify the value bytes after this method returns.
|
|
||||||
func (t *StackTrie) UpdateSafe(key, value []byte) error {
|
|
||||||
// The stacktrie always copies the value (is already safe).
|
|
||||||
return t.Update(key, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update inserts a (key, value) pair into the stack trie.
|
// Update inserts a (key, value) pair into the stack trie.
|
||||||
// The value is copied, and the caller is free to modify the value after this
|
//
|
||||||
// method returns.
|
// Note the supplied key value pair is copied and managed internally, they are
|
||||||
|
// safe to be modified after this method returns.
|
||||||
func (t *StackTrie) Update(key, value []byte) error {
|
func (t *StackTrie) Update(key, value []byte) error {
|
||||||
if len(value) == 0 {
|
if len(value) == 0 {
|
||||||
return errors.New("trying to insert empty (deletion)")
|
return errors.New("trying to insert empty (deletion)")
|
||||||
|
|
@ -114,7 +108,6 @@ func (t *StackTrie) Update(key, value []byte) error {
|
||||||
func (t *StackTrie) Reset() {
|
func (t *StackTrie) Reset() {
|
||||||
t.root = stPool.Get().(*stNode)
|
t.root = stPool.Get().(*stNode)
|
||||||
t.last = nil
|
t.last = nil
|
||||||
t.onTrieNode = nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TrieKey returns the internal key representation for the given user key.
|
// TrieKey returns the internal key representation for the given user key.
|
||||||
|
|
|
||||||
|
|
@ -790,8 +790,8 @@ 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.
|
||||||
func (t *Trie) Reset() {
|
func (t *Trie) reset() {
|
||||||
t.root = nil
|
t.root = nil
|
||||||
t.owner = common.Hash{}
|
t.owner = common.Hash{}
|
||||||
t.unhashed = 0
|
t.unhashed = 0
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue