github.1485827954.workers.dev/ethereum/go-ethereum@v1.14.3/triedb/pathdb/testutils.go (about)

     1  // Copyright 2023 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package pathdb
    18  
    19  import (
    20  	"bytes"
    21  	"fmt"
    22  	"slices"
    23  
    24  	"github.com/ethereum/go-ethereum/common"
    25  	"github.com/ethereum/go-ethereum/core/types"
    26  	"github.com/ethereum/go-ethereum/crypto"
    27  	"github.com/ethereum/go-ethereum/trie/trienode"
    28  	"github.com/ethereum/go-ethereum/trie/triestate"
    29  )
    30  
    31  // testHasher is a test utility for computing root hash of a batch of state
    32  // elements. The hash algorithm is to sort all the elements in lexicographical
    33  // order, concat the key and value in turn, and perform hash calculation on
    34  // the concatenated bytes. Except the root hash, a nodeset will be returned
    35  // once Commit is called, which contains all the changes made to hasher.
    36  type testHasher struct {
    37  	owner   common.Hash            // owner identifier
    38  	root    common.Hash            // original root
    39  	dirties map[common.Hash][]byte // dirty states
    40  	cleans  map[common.Hash][]byte // clean states
    41  }
    42  
    43  // newTestHasher constructs a hasher object with provided states.
    44  func newTestHasher(owner common.Hash, root common.Hash, cleans map[common.Hash][]byte) (*testHasher, error) {
    45  	if cleans == nil {
    46  		cleans = make(map[common.Hash][]byte)
    47  	}
    48  	if got, _ := hash(cleans); got != root {
    49  		return nil, fmt.Errorf("state root mismatched, want: %x, got: %x", root, got)
    50  	}
    51  	return &testHasher{
    52  		owner:   owner,
    53  		root:    root,
    54  		dirties: make(map[common.Hash][]byte),
    55  		cleans:  cleans,
    56  	}, nil
    57  }
    58  
    59  // Get returns the value for key stored in the trie.
    60  func (h *testHasher) Get(key []byte) ([]byte, error) {
    61  	hash := common.BytesToHash(key)
    62  	val, ok := h.dirties[hash]
    63  	if ok {
    64  		return val, nil
    65  	}
    66  	return h.cleans[hash], nil
    67  }
    68  
    69  // Update associates key with value in the trie.
    70  func (h *testHasher) Update(key, value []byte) error {
    71  	h.dirties[common.BytesToHash(key)] = common.CopyBytes(value)
    72  	return nil
    73  }
    74  
    75  // Delete removes any existing value for key from the trie.
    76  func (h *testHasher) Delete(key []byte) error {
    77  	h.dirties[common.BytesToHash(key)] = nil
    78  	return nil
    79  }
    80  
    81  // Commit computes the new hash of the states and returns the set with all
    82  // state changes.
    83  func (h *testHasher) Commit(collectLeaf bool) (common.Hash, *trienode.NodeSet, error) {
    84  	var (
    85  		nodes = make(map[common.Hash][]byte)
    86  		set   = trienode.NewNodeSet(h.owner)
    87  	)
    88  	for hash, val := range h.cleans {
    89  		nodes[hash] = val
    90  	}
    91  	for hash, val := range h.dirties {
    92  		nodes[hash] = val
    93  		if bytes.Equal(val, h.cleans[hash]) {
    94  			continue
    95  		}
    96  		// Utilize the hash of the state key as the node path to mitigate
    97  		// potential collisions within the path.
    98  		path := crypto.Keccak256(hash.Bytes())
    99  		if len(val) == 0 {
   100  			set.AddNode(path, trienode.NewDeleted())
   101  		} else {
   102  			set.AddNode(path, trienode.New(crypto.Keccak256Hash(val), val))
   103  		}
   104  	}
   105  	root, blob := hash(nodes)
   106  
   107  	// Include the dirty root node as well.
   108  	if root != types.EmptyRootHash && root != h.root {
   109  		set.AddNode(nil, trienode.New(root, blob))
   110  	}
   111  	if root == types.EmptyRootHash && h.root != types.EmptyRootHash {
   112  		set.AddNode(nil, trienode.NewDeleted())
   113  	}
   114  	return root, set, nil
   115  }
   116  
   117  // hash performs the hash computation upon the provided states.
   118  func hash(states map[common.Hash][]byte) (common.Hash, []byte) {
   119  	var hs []common.Hash
   120  	for hash := range states {
   121  		hs = append(hs, hash)
   122  	}
   123  	slices.SortFunc(hs, common.Hash.Cmp)
   124  
   125  	var input []byte
   126  	for _, hash := range hs {
   127  		if len(states[hash]) == 0 {
   128  			continue
   129  		}
   130  		input = append(input, hash.Bytes()...)
   131  		input = append(input, states[hash]...)
   132  	}
   133  	if len(input) == 0 {
   134  		return types.EmptyRootHash, nil
   135  	}
   136  	return crypto.Keccak256Hash(input), input
   137  }
   138  
   139  type hashLoader struct {
   140  	accounts map[common.Hash][]byte
   141  	storages map[common.Hash]map[common.Hash][]byte
   142  }
   143  
   144  func newHashLoader(accounts map[common.Hash][]byte, storages map[common.Hash]map[common.Hash][]byte) *hashLoader {
   145  	return &hashLoader{
   146  		accounts: accounts,
   147  		storages: storages,
   148  	}
   149  }
   150  
   151  // OpenTrie opens the main account trie.
   152  func (l *hashLoader) OpenTrie(root common.Hash) (triestate.Trie, error) {
   153  	return newTestHasher(common.Hash{}, root, l.accounts)
   154  }
   155  
   156  // OpenStorageTrie opens the storage trie of an account.
   157  func (l *hashLoader) OpenStorageTrie(stateRoot common.Hash, addrHash, root common.Hash) (triestate.Trie, error) {
   158  	return newTestHasher(addrHash, root, l.storages[addrHash])
   159  }