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 }