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 }