github.com/ethereum/go-ethereum@v1.16.1/triedb/pathdb/execute.go (about)

     1  // Copyright 2024 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  	"errors"
    21  	"fmt"
    22  
    23  	"github.com/ethereum/go-ethereum/common"
    24  	"github.com/ethereum/go-ethereum/core/types"
    25  	"github.com/ethereum/go-ethereum/crypto"
    26  	"github.com/ethereum/go-ethereum/rlp"
    27  	"github.com/ethereum/go-ethereum/trie"
    28  	"github.com/ethereum/go-ethereum/trie/trienode"
    29  	"github.com/ethereum/go-ethereum/triedb/database"
    30  )
    31  
    32  // context wraps all fields for executing state diffs.
    33  type context struct {
    34  	prevRoot      common.Hash
    35  	postRoot      common.Hash
    36  	accounts      map[common.Address][]byte
    37  	storages      map[common.Address]map[common.Hash][]byte
    38  	nodes         *trienode.MergedNodeSet
    39  	rawStorageKey bool
    40  
    41  	// TODO (rjl493456442) abstract out the state hasher
    42  	// for supporting verkle tree.
    43  	accountTrie *trie.Trie
    44  }
    45  
    46  // apply processes the given state diffs, updates the corresponding post-state
    47  // and returns the trie nodes that have been modified.
    48  func apply(db database.NodeDatabase, prevRoot common.Hash, postRoot common.Hash, rawStorageKey bool, accounts map[common.Address][]byte, storages map[common.Address]map[common.Hash][]byte) (map[common.Hash]map[string]*trienode.Node, error) {
    49  	tr, err := trie.New(trie.TrieID(postRoot), db)
    50  	if err != nil {
    51  		return nil, err
    52  	}
    53  	ctx := &context{
    54  		prevRoot:      prevRoot,
    55  		postRoot:      postRoot,
    56  		accounts:      accounts,
    57  		storages:      storages,
    58  		accountTrie:   tr,
    59  		rawStorageKey: rawStorageKey,
    60  		nodes:         trienode.NewMergedNodeSet(),
    61  	}
    62  	for addr, account := range accounts {
    63  		var err error
    64  		if len(account) == 0 {
    65  			err = deleteAccount(ctx, db, addr)
    66  		} else {
    67  			err = updateAccount(ctx, db, addr)
    68  		}
    69  		if err != nil {
    70  			return nil, fmt.Errorf("failed to revert state, err: %w", err)
    71  		}
    72  	}
    73  	root, result := tr.Commit(false)
    74  	if root != prevRoot {
    75  		return nil, fmt.Errorf("failed to revert state, want %#x, got %#x", prevRoot, root)
    76  	}
    77  	if err := ctx.nodes.Merge(result); err != nil {
    78  		return nil, err
    79  	}
    80  	return ctx.nodes.Flatten(), nil
    81  }
    82  
    83  // updateAccount the account was present in prev-state, and may or may not
    84  // existent in post-state. Apply the reverse diff and verify if the storage
    85  // root matches the one in prev-state account.
    86  func updateAccount(ctx *context, db database.NodeDatabase, addr common.Address) error {
    87  	// The account was present in prev-state, decode it from the
    88  	// 'slim-rlp' format bytes.
    89  	addrHash := crypto.Keccak256Hash(addr.Bytes())
    90  	prev, err := types.FullAccount(ctx.accounts[addr])
    91  	if err != nil {
    92  		return err
    93  	}
    94  	// The account may or may not existent in post-state, try to
    95  	// load it and decode if it's found.
    96  	blob, err := ctx.accountTrie.Get(addrHash.Bytes())
    97  	if err != nil {
    98  		return err
    99  	}
   100  	post := types.NewEmptyStateAccount()
   101  	if len(blob) != 0 {
   102  		if err := rlp.DecodeBytes(blob, &post); err != nil {
   103  			return err
   104  		}
   105  	}
   106  	// Apply all storage changes into the post-state storage trie.
   107  	st, err := trie.New(trie.StorageTrieID(ctx.postRoot, addrHash, post.Root), db)
   108  	if err != nil {
   109  		return err
   110  	}
   111  	for key, val := range ctx.storages[addr] {
   112  		tkey := key
   113  		if ctx.rawStorageKey {
   114  			tkey = crypto.Keccak256Hash(key.Bytes())
   115  		}
   116  		var err error
   117  		if len(val) == 0 {
   118  			err = st.Delete(tkey.Bytes())
   119  		} else {
   120  			err = st.Update(tkey.Bytes(), val)
   121  		}
   122  		if err != nil {
   123  			return err
   124  		}
   125  	}
   126  	root, result := st.Commit(false)
   127  	if root != prev.Root {
   128  		return errors.New("failed to reset storage trie")
   129  	}
   130  	// The returned set can be nil if storage trie is not changed
   131  	// at all.
   132  	if result != nil {
   133  		if err := ctx.nodes.Merge(result); err != nil {
   134  			return err
   135  		}
   136  	}
   137  	// Write the prev-state account into the main trie
   138  	full, err := rlp.EncodeToBytes(prev)
   139  	if err != nil {
   140  		return err
   141  	}
   142  	return ctx.accountTrie.Update(addrHash.Bytes(), full)
   143  }
   144  
   145  // deleteAccount the account was not present in prev-state, and is expected
   146  // to be existent in post-state. Apply the reverse diff and verify if the
   147  // account and storage is wiped out correctly.
   148  func deleteAccount(ctx *context, db database.NodeDatabase, addr common.Address) error {
   149  	// The account must be existent in post-state, load the account.
   150  	addrHash := crypto.Keccak256Hash(addr.Bytes())
   151  	blob, err := ctx.accountTrie.Get(addrHash.Bytes())
   152  	if err != nil {
   153  		return err
   154  	}
   155  	if len(blob) == 0 {
   156  		return fmt.Errorf("account is non-existent %#x", addrHash)
   157  	}
   158  	var post types.StateAccount
   159  	if err := rlp.DecodeBytes(blob, &post); err != nil {
   160  		return err
   161  	}
   162  	st, err := trie.New(trie.StorageTrieID(ctx.postRoot, addrHash, post.Root), db)
   163  	if err != nil {
   164  		return err
   165  	}
   166  	for key, val := range ctx.storages[addr] {
   167  		if len(val) != 0 {
   168  			return errors.New("expect storage deletion")
   169  		}
   170  		tkey := key
   171  		if ctx.rawStorageKey {
   172  			tkey = crypto.Keccak256Hash(key.Bytes())
   173  		}
   174  		if err := st.Delete(tkey.Bytes()); err != nil {
   175  			return err
   176  		}
   177  	}
   178  	root, result := st.Commit(false)
   179  	if root != types.EmptyRootHash {
   180  		return errors.New("failed to clear storage trie")
   181  	}
   182  	// The returned set can be nil if storage trie is not changed
   183  	// at all.
   184  	if result != nil {
   185  		if err := ctx.nodes.Merge(result); err != nil {
   186  			return err
   187  		}
   188  	}
   189  	// Delete the post-state account from the main trie.
   190  	return ctx.accountTrie.Delete(addrHash.Bytes())
   191  }