ethereum / go-ethereum

Go implementation of the Ethereum protocol
https://geth.ethereum.org
GNU Lesser General Public License v3.0
47.43k stars 20.07k forks source link

Statedb abstraction #28616

Closed rjl493456442 closed 1 month ago

rjl493456442 commented 10 months ago

Statedb has two data sources for state access: Trie and Snapshot.

The availability of the state trie is always ensured, allowing it to be utilized for state access and the computation of the post-transition hash.

On the other hand, the availability of the state snapshot is not guaranteed. It can be used exclusively for state access, offering a more efficient method.

Now both trie and snapshot is hardcoded in statedb like

type StateDB struct {
    trie      Trie
    snaps  *snapshot.Tree        // Nil if snapshot is not available
    snap    snapshot.Snapshot // Nil if snapshot is not available
}

Unfortunately, this approach is not flexible enough. In path mode archive design, a new data source is required to access state from state history.

Besides, in the foreseeable future, state snapshot will be merged into underlying pathdb and a comprehensive reader will be offered which can either access node or state.

In light of these considerations, I propose the implementation of a StateReader abstraction to ensure the necessary flexibility.


The StateReader interface will be something like this

// StateReader defines the interface for accessing accounts or storage slots
// associated with a specific state.
type StateReader interface {
    // Account retrieves the account associated with a particular address.
    Account(addr common.Address) (*types.StateAccount, error)

    // Storage retrieves the storage slot associated with a particular account
    // address and slot key.
    Storage(addr common.Address, slot common.Hash) (common.Hash, error)
}

And I can imagine a few corresponding implementations

holiman commented 10 months ago

I fully agree about the need for a StateReader. However:

Therefore, maybe the interface should be more granular, e.g.

// StateReader defines the interface for accessing accounts or storage slots
// associated with a specific state.
type StateReader interface {
    // StateRoot returns the state root that this reader originates from. 
    StateRoot() common.Hash 
    // AccountBalance returns the balance for the account at address addr
    AccountBalance(addr common.Address) (*uint256.Int, error)
    AccountNonce(addr common.Address) (uint64, error)
    AccountRoot(addr common.Address) (common.Hash, error)
holiman commented 10 months ago

The existing statedb has the following methods. Perhaps a good simple first step would be to make these into an interface:

func (s *StateDB) Exist(addr common.Address) bool 
// Empty returns whether the state object is either non-existent
// or empty according to the EIP161 specification (balance = nonce = code = 0)
func (s *StateDB) Empty(addr common.Address) bool 
// GetBalance retrieves the balance from the given address or 0 if object not found
func (s *StateDB) GetBalance(addr common.Address) *uint256.Int 
// GetNonce retrieves the nonce from the given address or 0 if object not found
func (s *StateDB) GetNonce(addr common.Address) uint64 
// GetStorageRoot retrieves the storage root from the given address or empty
// if object not found.
func (s *StateDB) GetStorageRoot(addr common.Address) common.Hash 
func (s *StateDB) GetCode(addr common.Address) []byte 
func (s *StateDB) GetCodeSize(addr common.Address) int 
func (s *StateDB) GetCodeHash(addr common.Address) common.Hash 
// GetState retrieves a value from the given account's storage trie.
func (s *StateDB) GetState(addr common.Address, hash common.Hash) common.Hash 
// GetCommittedState retrieves a value from the given account's committed storage trie.
func (s *StateDB) GetCommittedState(addr common.Address, hash common.Hash) common.Hash 
func (s *StateDB) HasSelfDestructed(addr common.Address) bool 
fjl commented 1 month ago

29761