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  }