github.1485827954.workers.dev/ethereum/go-ethereum@v1.14.3/trie/triestate/state.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 triestate 18 19 import ( 20 "errors" 21 "fmt" 22 "sync" 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/rlp" 28 "github.com/ethereum/go-ethereum/trie/trienode" 29 ) 30 31 // Trie is an Ethereum state trie, can be implemented by Ethereum Merkle Patricia 32 // tree or Verkle tree. 33 type Trie interface { 34 // Get returns the value for key stored in the trie. 35 Get(key []byte) ([]byte, error) 36 37 // Update associates key with value in the trie. 38 Update(key, value []byte) error 39 40 // Delete removes any existing value for key from the trie. 41 Delete(key []byte) error 42 43 // Commit the trie and returns a set of dirty nodes generated along with 44 // the new root hash. 45 Commit(collectLeaf bool) (common.Hash, *trienode.NodeSet, error) 46 } 47 48 // TrieLoader wraps functions to load tries. 49 type TrieLoader interface { 50 // OpenTrie opens the main account trie. 51 OpenTrie(root common.Hash) (Trie, error) 52 53 // OpenStorageTrie opens the storage trie of an account. 54 OpenStorageTrie(stateRoot common.Hash, addrHash, root common.Hash) (Trie, error) 55 } 56 57 // Set represents a collection of mutated states during a state transition. 58 // The value refers to the original content of state before the transition 59 // is made. Nil means that the state was not present previously. 60 type Set struct { 61 Accounts map[common.Address][]byte // Mutated account set, nil means the account was not present 62 Storages map[common.Address]map[common.Hash][]byte // Mutated storage set, nil means the slot was not present 63 size common.StorageSize // Approximate size of set 64 } 65 66 // New constructs the state set with provided data. 67 func New(accounts map[common.Address][]byte, storages map[common.Address]map[common.Hash][]byte) *Set { 68 return &Set{ 69 Accounts: accounts, 70 Storages: storages, 71 } 72 } 73 74 // Size returns the approximate memory size occupied by the set. 75 func (s *Set) Size() common.StorageSize { 76 if s.size != 0 { 77 return s.size 78 } 79 for _, account := range s.Accounts { 80 s.size += common.StorageSize(common.AddressLength + len(account)) 81 } 82 for _, slots := range s.Storages { 83 for _, val := range slots { 84 s.size += common.StorageSize(common.HashLength + len(val)) 85 } 86 s.size += common.StorageSize(common.AddressLength) 87 } 88 return s.size 89 } 90 91 // context wraps all fields for executing state diffs. 92 type context struct { 93 prevRoot common.Hash 94 postRoot common.Hash 95 accounts map[common.Address][]byte 96 storages map[common.Address]map[common.Hash][]byte 97 accountTrie Trie 98 nodes *trienode.MergedNodeSet 99 } 100 101 // Apply traverses the provided state diffs, apply them in the associated 102 // post-state and return the generated dirty trie nodes. The state can be 103 // loaded via the provided trie loader. 104 func Apply(prevRoot common.Hash, postRoot common.Hash, accounts map[common.Address][]byte, storages map[common.Address]map[common.Hash][]byte, loader TrieLoader) (map[common.Hash]map[string]*trienode.Node, error) { 105 tr, err := loader.OpenTrie(postRoot) 106 if err != nil { 107 return nil, err 108 } 109 ctx := &context{ 110 prevRoot: prevRoot, 111 postRoot: postRoot, 112 accounts: accounts, 113 storages: storages, 114 accountTrie: tr, 115 nodes: trienode.NewMergedNodeSet(), 116 } 117 for addr, account := range accounts { 118 var err error 119 if len(account) == 0 { 120 err = deleteAccount(ctx, loader, addr) 121 } else { 122 err = updateAccount(ctx, loader, addr) 123 } 124 if err != nil { 125 return nil, fmt.Errorf("failed to revert state, err: %w", err) 126 } 127 } 128 root, result, err := tr.Commit(false) 129 if err != nil { 130 return nil, err 131 } 132 if root != prevRoot { 133 return nil, fmt.Errorf("failed to revert state, want %#x, got %#x", prevRoot, root) 134 } 135 if err := ctx.nodes.Merge(result); err != nil { 136 return nil, err 137 } 138 return ctx.nodes.Flatten(), nil 139 } 140 141 // updateAccount the account was present in prev-state, and may or may not 142 // existent in post-state. Apply the reverse diff and verify if the storage 143 // root matches the one in prev-state account. 144 func updateAccount(ctx *context, loader TrieLoader, addr common.Address) error { 145 // The account was present in prev-state, decode it from the 146 // 'slim-rlp' format bytes. 147 h := newHasher() 148 defer h.release() 149 150 addrHash := h.hash(addr.Bytes()) 151 prev, err := types.FullAccount(ctx.accounts[addr]) 152 if err != nil { 153 return err 154 } 155 // The account may or may not existent in post-state, try to 156 // load it and decode if it's found. 157 blob, err := ctx.accountTrie.Get(addrHash.Bytes()) 158 if err != nil { 159 return err 160 } 161 post := types.NewEmptyStateAccount() 162 if len(blob) != 0 { 163 if err := rlp.DecodeBytes(blob, &post); err != nil { 164 return err 165 } 166 } 167 // Apply all storage changes into the post-state storage trie. 168 st, err := loader.OpenStorageTrie(ctx.postRoot, addrHash, post.Root) 169 if err != nil { 170 return err 171 } 172 for key, val := range ctx.storages[addr] { 173 var err error 174 if len(val) == 0 { 175 err = st.Delete(key.Bytes()) 176 } else { 177 err = st.Update(key.Bytes(), val) 178 } 179 if err != nil { 180 return err 181 } 182 } 183 root, result, err := st.Commit(false) 184 if err != nil { 185 return err 186 } 187 if root != prev.Root { 188 return errors.New("failed to reset storage trie") 189 } 190 // The returned set can be nil if storage trie is not changed 191 // at all. 192 if result != nil { 193 if err := ctx.nodes.Merge(result); err != nil { 194 return err 195 } 196 } 197 // Write the prev-state account into the main trie 198 full, err := rlp.EncodeToBytes(prev) 199 if err != nil { 200 return err 201 } 202 return ctx.accountTrie.Update(addrHash.Bytes(), full) 203 } 204 205 // deleteAccount the account was not present in prev-state, and is expected 206 // to be existent in post-state. Apply the reverse diff and verify if the 207 // account and storage is wiped out correctly. 208 func deleteAccount(ctx *context, loader TrieLoader, addr common.Address) error { 209 // The account must be existent in post-state, load the account. 210 h := newHasher() 211 defer h.release() 212 213 addrHash := h.hash(addr.Bytes()) 214 blob, err := ctx.accountTrie.Get(addrHash.Bytes()) 215 if err != nil { 216 return err 217 } 218 if len(blob) == 0 { 219 return fmt.Errorf("account is non-existent %#x", addrHash) 220 } 221 var post types.StateAccount 222 if err := rlp.DecodeBytes(blob, &post); err != nil { 223 return err 224 } 225 st, err := loader.OpenStorageTrie(ctx.postRoot, addrHash, post.Root) 226 if err != nil { 227 return err 228 } 229 for key, val := range ctx.storages[addr] { 230 if len(val) != 0 { 231 return errors.New("expect storage deletion") 232 } 233 if err := st.Delete(key.Bytes()); err != nil { 234 return err 235 } 236 } 237 root, result, err := st.Commit(false) 238 if err != nil { 239 return err 240 } 241 if root != types.EmptyRootHash { 242 return errors.New("failed to clear storage trie") 243 } 244 // The returned set can be nil if storage trie is not changed 245 // at all. 246 if result != nil { 247 if err := ctx.nodes.Merge(result); err != nil { 248 return err 249 } 250 } 251 // Delete the post-state account from the main trie. 252 return ctx.accountTrie.Delete(addrHash.Bytes()) 253 } 254 255 // hasher is used to compute the sha256 hash of the provided data. 256 type hasher struct{ sha crypto.KeccakState } 257 258 var hasherPool = sync.Pool{ 259 New: func() interface{} { return &hasher{sha: crypto.NewKeccakState()} }, 260 } 261 262 func newHasher() *hasher { 263 return hasherPool.Get().(*hasher) 264 } 265 266 func (h *hasher) hash(data []byte) common.Hash { 267 return crypto.HashData(h.sha, data) 268 } 269 270 func (h *hasher) release() { 271 hasherPool.Put(h) 272 }