github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/x/evm/types/statedb_mpt.go (about) 1 package types 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 8 ethcmn "github.com/ethereum/go-ethereum/common" 9 "github.com/ethereum/go-ethereum/core/rawdb" 10 ethstate "github.com/ethereum/go-ethereum/core/state" 11 "github.com/ethereum/go-ethereum/core/types" 12 "github.com/ethereum/go-ethereum/crypto" 13 "github.com/ethereum/go-ethereum/rlp" 14 "github.com/ethereum/go-ethereum/trie" 15 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/store/mpt" 16 sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types" 17 "github.com/fibonacci-chain/fbc/libs/tendermint/libs/log" 18 tmtypes "github.com/fibonacci-chain/fbc/libs/tendermint/types" 19 ) 20 21 func (csdb *CommitStateDB) CommitMpt(prefetcher *mpt.TriePrefetcher) (ethcmn.Hash, error) { 22 // Commit objects to the trie, measuring the elapsed time 23 codeWriter := csdb.db.TrieDB().DiskDB().NewBatch() 24 usedAddrs := make([][]byte, 0, len(csdb.stateObjectsPending)) 25 26 for addr := range csdb.stateObjectsDirty { 27 if obj := csdb.stateObjects[addr]; !obj.deleted { 28 // Write any contract code associated with the state object 29 if obj.code != nil && obj.dirtyCode { 30 rawdb.WriteCode(codeWriter, ethcmn.BytesToHash(obj.CodeHash()), obj.code) 31 obj.dirtyCode = false 32 } 33 34 // Write any storage changes in the state object to its storage trie 35 if err := obj.CommitTrie(csdb.db); err != nil { 36 return ethcmn.Hash{}, err 37 } 38 39 csdb.UpdateAccountStorageInfo(obj) 40 } else { 41 csdb.DeleteAccountStorageInfo(obj) 42 } 43 44 usedAddrs = append(usedAddrs, ethcmn.CopyBytes(addr[:])) // Copy needed for closure 45 } 46 if prefetcher != nil { 47 prefetcher.Used(csdb.originalRoot, usedAddrs) 48 } 49 50 if len(csdb.stateObjectsDirty) > 0 { 51 csdb.stateObjectsDirty = make(map[ethcmn.Address]struct{}) 52 } 53 54 if codeWriter.ValueSize() > 0 { 55 if err := codeWriter.Write(); err != nil { 56 csdb.SetError(fmt.Errorf("failed to commit dirty codes: %s", err.Error())) 57 } 58 } 59 60 return ethcmn.Hash{}, nil 61 } 62 63 func (csdb *CommitStateDB) ForEachStorageMpt(so *stateObject, cb func(key, value ethcmn.Hash) (stop bool)) error { 64 it := trie.NewIterator(so.getTrie(csdb.db).NodeIterator(nil)) 65 for it.Next() { 66 key := ethcmn.BytesToHash(so.trie.GetKey(it.Key)) 67 if value, dirty := so.dirtyStorage[key]; dirty { 68 if cb(key, value) { 69 return nil 70 } 71 continue 72 } 73 74 if len(it.Value) > 0 { 75 _, content, _, err := rlp.Split(it.Value) 76 if err != nil { 77 return err 78 } 79 if cb(key, ethcmn.BytesToHash(content)) { 80 return nil 81 } 82 } 83 } 84 85 return nil 86 } 87 88 func (csdb *CommitStateDB) UpdateAccountStorageInfo(so *stateObject) { 89 if bytes.Equal(so.CodeHash(), emptyCodeHash) { 90 return 91 } 92 93 // Encode the account and update the account trie 94 addr := so.Address() 95 if err := csdb.trie.TryUpdate(addr[:], so.stateRoot.Bytes()); err != nil { 96 csdb.SetError(fmt.Errorf("updateStateObject (%x) error: %v", addr[:], err)) 97 } 98 } 99 100 func (csdb *CommitStateDB) DeleteAccountStorageInfo(so *stateObject) { 101 // Delete the account from the trie 102 addr := so.Address() 103 if err := csdb.trie.TryDelete(addr[:]); err != nil { 104 csdb.SetError(fmt.Errorf("deleteStateObject (%x) error: %v", addr[:], err)) 105 } 106 } 107 108 func (csdb *CommitStateDB) GetStateByKeyMpt(addr ethcmn.Address, key ethcmn.Hash) ethcmn.Hash { 109 var ( 110 enc []byte 111 err error 112 ) 113 114 tmpKey := key 115 if TrieUseCompositeKey { 116 tmpKey = GetStorageByAddressKey(addr.Bytes(), key.Bytes()) 117 } 118 if enc, err = csdb.StorageTrie(addr).TryGet(tmpKey.Bytes()); err != nil { 119 return ethcmn.Hash{} 120 } 121 122 var value ethcmn.Hash 123 if len(enc) > 0 { 124 _, content, _, err := rlp.Split(enc) 125 if err != nil { 126 return ethcmn.Hash{} 127 } 128 value.SetBytes(content) 129 } 130 131 return value 132 } 133 134 func (csdb *CommitStateDB) GetCodeByHashInRawDB(hash ethcmn.Hash) []byte { 135 code, err := csdb.db.ContractCode(ethcmn.Hash{}, hash) 136 if err != nil { 137 return nil 138 } 139 140 return code 141 } 142 143 func (csdb *CommitStateDB) setHeightHashInRawDB(height uint64, hash ethcmn.Hash) { 144 key := AppendHeightHashKey(height) 145 csdb.db.TrieDB().DiskDB().Put(key, hash.Bytes()) 146 } 147 148 func (csdb *CommitStateDB) getHeightHashInRawDB(height uint64) ethcmn.Hash { 149 key := AppendHeightHashKey(height) 150 bz, err := csdb.db.TrieDB().DiskDB().Get(key) 151 if err != nil { 152 return ethcmn.Hash{} 153 } 154 return ethcmn.BytesToHash(bz) 155 } 156 157 // getDeletedStateObject is similar to getStateObject, but instead of returning 158 // nil for a deleted state object, it returns the actual object with the deleted 159 // flag set. This is needed by the state journal to revert to the correct s- 160 // destructed object instead of wiping all knowledge about the state object. 161 func (csdb *CommitStateDB) getDeletedStateObject(addr ethcmn.Address) *stateObject { 162 // Prefer live objects if any is available 163 if obj := csdb.stateObjects[addr]; obj != nil { 164 if _, ok := csdb.updatedAccount[addr]; ok { 165 delete(csdb.updatedAccount, addr) 166 if err := obj.UpdateAccInfo(); err != nil { 167 csdb.SetError(err) 168 return nil 169 } 170 } 171 return obj 172 } 173 174 // otherwise, attempt to fetch the account from the account mapper 175 acc := csdb.accountKeeper.GetAccount(csdb.ctx, sdk.AccAddress(addr.Bytes())) 176 if acc == nil { 177 csdb.SetError(fmt.Errorf("no account found for address: %s", addr.String())) 178 return nil 179 } 180 181 storageRoot := types.EmptyRootHash 182 if tmtypes.HigherThanMars(csdb.ctx.BlockHeight()) || mpt.TrieWriteAhead { 183 root, err := csdb.loadContractStorageRoot(addr) 184 if err != nil { 185 csdb.SetError(err) 186 return nil 187 } 188 storageRoot = root 189 } 190 191 // insert the state object into the live set 192 so := newStateObject(csdb, acc, storageRoot) 193 csdb.setStateObject(so) 194 195 return so 196 } 197 198 func (csdb *CommitStateDB) loadContractStorageRoot(addr ethcmn.Address) (ethcmn.Hash, error) { 199 enc, err := csdb.trie.TryGet(addr.Bytes()) 200 if err != nil { 201 return types.EmptyRootHash, err 202 } 203 204 var storageRoot ethcmn.Hash 205 if len(enc) == 0 { 206 // means the account is a normal account, not a contract account 207 storageRoot = types.EmptyRootHash 208 } else { 209 storageRoot.SetBytes(enc) 210 } 211 212 return storageRoot, nil 213 } 214 215 func (csdb *CommitStateDB) MarkUpdatedAcc(addList []ethcmn.Address) { 216 for _, addr := range addList { 217 csdb.updatedAccount[addr] = struct{}{} 218 } 219 } 220 221 // ---------------------------------------------------------------------------- 222 // Proof related 223 // ---------------------------------------------------------------------------- 224 225 // GetProof returns the Merkle proof for a given account. 226 func (csdb *CommitStateDB) GetProof(addr ethcmn.Address) ([][]byte, error) { 227 return csdb.GetProofByHash(crypto.Keccak256Hash(addr.Bytes())) 228 } 229 230 // GetProofByHash returns the Merkle proof for a given account. 231 func (csdb *CommitStateDB) GetProofByHash(addrHash ethcmn.Hash) ([][]byte, error) { 232 var proof mpt.ProofList 233 err := csdb.trie.Prove(addrHash[:], 0, &proof) 234 return proof, err 235 } 236 237 // GetStorageProof returns the Merkle proof for given storage slot. 238 func (csdb *CommitStateDB) GetStorageProof(a ethcmn.Address, key ethcmn.Hash) ([][]byte, error) { 239 var proof mpt.ProofList 240 addrTrie := csdb.StorageTrie(a) 241 if addrTrie == nil { 242 return proof, errors.New("storage trie for requested address does not exist") 243 } 244 err := addrTrie.Prove(crypto.Keccak256(key.Bytes()), 0, &proof) 245 return proof, err 246 } 247 248 func (csdb *CommitStateDB) Logger() log.Logger { 249 return csdb.ctx.Logger().With("module", ModuleName) 250 } 251 252 // StartPrefetcher initializes a new trie prefetcher to pull in nodes from the 253 // state trie concurrently while the state is mutated so that when we reach the 254 // commit phase, most of the needed data is already hot. 255 func (csdb *CommitStateDB) StartPrefetcher(namespace string) { 256 if !tmtypes.HigherThanMars(csdb.ctx.BlockHeight()) { 257 return 258 } 259 260 if csdb.prefetcher != nil { 261 csdb.prefetcher.Close() 262 csdb.prefetcher = nil 263 } 264 265 csdb.prefetcher = mpt.NewTriePrefetcher(csdb.db, csdb.originalRoot, namespace) 266 } 267 268 // StopPrefetcher terminates a running prefetcher and reports any leftover stats 269 // from the gathered metrics. 270 func (csdb *CommitStateDB) StopPrefetcher() { 271 if csdb.prefetcher != nil { 272 csdb.prefetcher.Close() 273 csdb.prefetcher = nil 274 } 275 } 276 277 func (csdb *CommitStateDB) GetRootTrie() ethstate.Trie { 278 return csdb.trie 279 }