github.com/ethereum/go-ethereum@v1.16.1/core/state/stateupdate.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 state
    18  
    19  import (
    20  	"maps"
    21  
    22  	"github.com/ethereum/go-ethereum/common"
    23  	"github.com/ethereum/go-ethereum/trie/trienode"
    24  	"github.com/ethereum/go-ethereum/triedb"
    25  )
    26  
    27  // contractCode represents a contract code with associated metadata.
    28  type contractCode struct {
    29  	hash common.Hash // hash is the cryptographic hash of the contract code.
    30  	blob []byte      // blob is the binary representation of the contract code.
    31  }
    32  
    33  // accountDelete represents an operation for deleting an Ethereum account.
    34  type accountDelete struct {
    35  	address common.Address // address is the unique account identifier
    36  	origin  []byte         // origin is the original value of account data in slim-RLP encoding.
    37  
    38  	// storages stores mutated slots, the value should be nil.
    39  	storages map[common.Hash][]byte
    40  
    41  	// storagesOrigin stores the original values of mutated slots in
    42  	// prefix-zero-trimmed RLP format. The map key refers to the **HASH**
    43  	// of the raw storage slot key.
    44  	storagesOrigin map[common.Hash][]byte
    45  }
    46  
    47  // accountUpdate represents an operation for updating an Ethereum account.
    48  type accountUpdate struct {
    49  	address  common.Address         // address is the unique account identifier
    50  	data     []byte                 // data is the slim-RLP encoded account data.
    51  	origin   []byte                 // origin is the original value of account data in slim-RLP encoding.
    52  	code     *contractCode          // code represents mutated contract code; nil means it's not modified.
    53  	storages map[common.Hash][]byte // storages stores mutated slots in prefix-zero-trimmed RLP format.
    54  
    55  	// storagesOriginByKey and storagesOriginByHash both store the original values
    56  	// of mutated slots in prefix-zero-trimmed RLP format. The difference is that
    57  	// storagesOriginByKey uses the **raw** storage slot key as the map ID, while
    58  	// storagesOriginByHash uses the **hash** of the storage slot key instead.
    59  	storagesOriginByKey  map[common.Hash][]byte
    60  	storagesOriginByHash map[common.Hash][]byte
    61  }
    62  
    63  // stateUpdate represents the difference between two states resulting from state
    64  // execution. It contains information about mutated contract codes, accounts,
    65  // and storage slots, along with their original values.
    66  type stateUpdate struct {
    67  	originRoot     common.Hash               // hash of the state before applying mutation
    68  	root           common.Hash               // hash of the state after applying mutation
    69  	accounts       map[common.Hash][]byte    // accounts stores mutated accounts in 'slim RLP' encoding
    70  	accountsOrigin map[common.Address][]byte // accountsOrigin stores the original values of mutated accounts in 'slim RLP' encoding
    71  
    72  	// storages stores mutated slots in 'prefix-zero-trimmed' RLP format.
    73  	// The value is keyed by account hash and **storage slot key hash**.
    74  	storages map[common.Hash]map[common.Hash][]byte
    75  
    76  	// storagesOrigin stores the original values of mutated slots in
    77  	// 'prefix-zero-trimmed' RLP format.
    78  	// (a) the value is keyed by account hash and **storage slot key** if rawStorageKey is true;
    79  	// (b) the value is keyed by account hash and **storage slot key hash** if rawStorageKey is false;
    80  	storagesOrigin map[common.Address]map[common.Hash][]byte
    81  	rawStorageKey  bool
    82  
    83  	codes map[common.Address]contractCode // codes contains the set of dirty codes
    84  	nodes *trienode.MergedNodeSet         // Aggregated dirty nodes caused by state changes
    85  }
    86  
    87  // empty returns a flag indicating the state transition is empty or not.
    88  func (sc *stateUpdate) empty() bool {
    89  	return sc.originRoot == sc.root
    90  }
    91  
    92  // newStateUpdate constructs a state update object by identifying the differences
    93  // between two states through state execution. It combines the specified account
    94  // deletions and account updates to create a complete state update.
    95  //
    96  // rawStorageKey is a flag indicating whether to use the raw storage slot key or
    97  // the hash of the slot key for constructing state update object.
    98  func newStateUpdate(rawStorageKey bool, originRoot common.Hash, root common.Hash, deletes map[common.Hash]*accountDelete, updates map[common.Hash]*accountUpdate, nodes *trienode.MergedNodeSet) *stateUpdate {
    99  	var (
   100  		accounts       = make(map[common.Hash][]byte)
   101  		accountsOrigin = make(map[common.Address][]byte)
   102  		storages       = make(map[common.Hash]map[common.Hash][]byte)
   103  		storagesOrigin = make(map[common.Address]map[common.Hash][]byte)
   104  		codes          = make(map[common.Address]contractCode)
   105  	)
   106  	// Since some accounts might be destroyed and recreated within the same
   107  	// block, deletions must be aggregated first.
   108  	for addrHash, op := range deletes {
   109  		addr := op.address
   110  		accounts[addrHash] = nil
   111  		accountsOrigin[addr] = op.origin
   112  
   113  		// If storage wiping exists, the hash of the storage slot key must be used
   114  		if len(op.storages) > 0 {
   115  			storages[addrHash] = op.storages
   116  		}
   117  		if len(op.storagesOrigin) > 0 {
   118  			storagesOrigin[addr] = op.storagesOrigin
   119  		}
   120  	}
   121  	// Aggregate account updates then.
   122  	for addrHash, op := range updates {
   123  		// Aggregate dirty contract codes if they are available.
   124  		addr := op.address
   125  		if op.code != nil {
   126  			codes[addr] = *op.code
   127  		}
   128  		accounts[addrHash] = op.data
   129  
   130  		// Aggregate the account original value. If the account is already
   131  		// present in the aggregated accountsOrigin set, skip it.
   132  		if _, found := accountsOrigin[addr]; !found {
   133  			accountsOrigin[addr] = op.origin
   134  		}
   135  		// Aggregate the storage mutation list. If a slot in op.storages is
   136  		// already present in aggregated storages set, the value will be
   137  		// overwritten.
   138  		if len(op.storages) > 0 {
   139  			if _, exist := storages[addrHash]; !exist {
   140  				storages[addrHash] = op.storages
   141  			} else {
   142  				maps.Copy(storages[addrHash], op.storages)
   143  			}
   144  		}
   145  		// Aggregate the storage original values. If the slot is already present
   146  		// in aggregated storagesOrigin set, skip it.
   147  		storageOriginSet := op.storagesOriginByHash
   148  		if rawStorageKey {
   149  			storageOriginSet = op.storagesOriginByKey
   150  		}
   151  		if len(storageOriginSet) > 0 {
   152  			origin, exist := storagesOrigin[addr]
   153  			if !exist {
   154  				storagesOrigin[addr] = storageOriginSet
   155  			} else {
   156  				for key, slot := range storageOriginSet {
   157  					if _, found := origin[key]; !found {
   158  						origin[key] = slot
   159  					}
   160  				}
   161  			}
   162  		}
   163  	}
   164  	return &stateUpdate{
   165  		originRoot:     originRoot,
   166  		root:           root,
   167  		accounts:       accounts,
   168  		accountsOrigin: accountsOrigin,
   169  		storages:       storages,
   170  		storagesOrigin: storagesOrigin,
   171  		rawStorageKey:  rawStorageKey,
   172  		codes:          codes,
   173  		nodes:          nodes,
   174  	}
   175  }
   176  
   177  // stateSet converts the current stateUpdate object into a triedb.StateSet
   178  // object. This function extracts the necessary data from the stateUpdate
   179  // struct and formats it into the StateSet structure consumed by the triedb
   180  // package.
   181  func (sc *stateUpdate) stateSet() *triedb.StateSet {
   182  	return &triedb.StateSet{
   183  		Accounts:       sc.accounts,
   184  		AccountsOrigin: sc.accountsOrigin,
   185  		Storages:       sc.storages,
   186  		StoragesOrigin: sc.storagesOrigin,
   187  		RawStorageKey:  sc.rawStorageKey,
   188  	}
   189  }