all: move trie interface into trie package

This commit is contained in:
Gary Rong 2023-08-16 10:48:09 +08:00
parent fcf77b581c
commit a5ab1bf031
14 changed files with 144 additions and 118 deletions

View file

@ -23,11 +23,9 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/lru"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/trie/trienode"
)
const (
@ -41,13 +39,13 @@ const (
// Database wraps access to tries and contract code.
type Database interface {
// OpenTrie opens the main account trie.
OpenTrie(root common.Hash) (Trie, error)
OpenTrie(root common.Hash) (trie.Trie, error)
// OpenStorageTrie opens the storage trie of an account.
OpenStorageTrie(stateRoot common.Hash, address common.Address, root common.Hash) (Trie, error)
OpenStorageTrie(stateRoot common.Hash, address common.Address, root common.Hash) (trie.Trie, error)
// CopyTrie returns an independent copy of the given trie.
CopyTrie(Trie) Trie
CopyTrie(trie.Trie) trie.Trie
// ContractCode retrieves a particular contract's code.
ContractCode(addr common.Address, codeHash common.Hash) ([]byte, error)
@ -62,76 +60,6 @@ type Database interface {
TrieDB() *trie.Database
}
// Trie is a Ethereum Merkle Patricia trie.
type Trie interface {
// GetKey returns the sha3 preimage of a hashed key that was previously used
// to store a value.
//
// TODO(fjl): remove this when StateTrie is removed
GetKey([]byte) []byte
// GetStorage returns the value for key stored in the trie. The value bytes
// must not be modified by the caller. If a node was not found in the database,
// a trie.MissingNodeError is returned.
GetStorage(addr common.Address, key []byte) ([]byte, error)
// GetAccount abstracts an account read from the trie. It retrieves the
// account blob from the trie with provided account address and decodes it
// with associated decoding algorithm. If the specified account is not in
// the trie, nil will be returned. If the trie is corrupted(e.g. some nodes
// are missing or the account blob is incorrect for decoding), an error will
// be returned.
GetAccount(address common.Address) (*types.StateAccount, error)
// UpdateStorage associates key with value in the trie. If value has length zero,
// any existing value is deleted from the trie. The value bytes must not be modified
// by the caller while they are stored in the trie. If a node was not found in the
// database, a trie.MissingNodeError is returned.
UpdateStorage(addr common.Address, key, value []byte) error
// UpdateAccount abstracts an account write to the trie. It encodes the
// provided account object with associated algorithm and then updates it
// in the trie with provided address.
UpdateAccount(address common.Address, account *types.StateAccount) error
// UpdateContractCode abstracts code write to the trie. It is expected
// to be moved to the stateWriter interface when the latter is ready.
UpdateContractCode(address common.Address, codeHash common.Hash, code []byte) error
// DeleteStorage removes any existing value for key from the trie. If a node
// was not found in the database, a trie.MissingNodeError is returned.
DeleteStorage(addr common.Address, key []byte) error
// DeleteAccount abstracts an account deletion from the trie.
DeleteAccount(address common.Address) error
// Hash returns the root hash of the trie. It does not write to the database and
// can be used even if the trie doesn't have one.
Hash() common.Hash
// Commit collects all dirty nodes in the trie and replace them with the
// corresponding node hash. All collected nodes(including dirty leaves if
// collectLeaf is true) will be encapsulated into a nodeset for return.
// The returned nodeset can be nil if the trie is clean(nothing to commit).
// Once the trie is committed, it's not usable anymore. A new trie must
// be created with new root and updated trie database for following usage
Commit(collectLeaf bool) (common.Hash, *trienode.NodeSet, error)
// NodeIterator returns an iterator that returns nodes of the trie. Iteration
// starts at the key after the given start key. And error will be returned
// if fails to create node iterator.
NodeIterator(startKey []byte) (trie.NodeIterator, error)
// Prove constructs a Merkle proof for key. The result contains all encoded nodes
// on the path to the value at key. The value itself is also included in the last
// node and can be retrieved by verifying the proof.
//
// If the trie does not contain a value for key, the returned proof contains all
// nodes of the longest existing prefix of the key (at least the root), ending
// with the node that proves the absence of the key.
Prove(key []byte, proofDb ethdb.KeyValueWriter) error
}
// NewDatabase creates a backing store for state. The returned database is safe for
// concurrent use, but does not retain any recent trie nodes in memory. To keep some
// historical state in memory, use the NewDatabaseWithConfig constructor.
@ -169,7 +97,7 @@ type cachingDB struct {
}
// OpenTrie opens the main account trie at a specific root hash.
func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) {
func (db *cachingDB) OpenTrie(root common.Hash) (trie.Trie, error) {
tr, err := trie.NewStateTrie(trie.StateTrieID(root), db.triedb)
if err != nil {
return nil, err
@ -178,7 +106,7 @@ func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) {
}
// OpenStorageTrie opens the storage trie of an account.
func (db *cachingDB) OpenStorageTrie(stateRoot common.Hash, address common.Address, root common.Hash) (Trie, error) {
func (db *cachingDB) OpenStorageTrie(stateRoot common.Hash, address common.Address, root common.Hash) (trie.Trie, error) {
tr, err := trie.NewStateTrie(trie.StorageTrieID(stateRoot, crypto.Keccak256Hash(address.Bytes()), root), db.triedb)
if err != nil {
return nil, err
@ -187,7 +115,7 @@ func (db *cachingDB) OpenStorageTrie(stateRoot common.Hash, address common.Addre
}
// CopyTrie returns an independent copy of the given trie.
func (db *cachingDB) CopyTrie(t Trie) Trie {
func (db *cachingDB) CopyTrie(t trie.Trie) trie.Trie {
switch t := t.(type) {
case *trie.StateTrie:
return t.Copy()

View file

@ -28,6 +28,7 @@ import (
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/trie/trienode"
)
@ -68,8 +69,8 @@ type stateObject struct {
data types.StateAccount // Account data with all mutations applied in the scope of block
// Write caches.
trie Trie // storage trie, which becomes non-nil on first access
code Code // contract bytecode, which gets set when code is loaded
trie trie.Trie // storage trie, which becomes non-nil on first access
code Code // contract bytecode, which gets set when code is loaded
originStorage Storage // Storage cache of original entries to dedup rewrites
pendingStorage Storage // Storage entries that need to be flushed to disk, at the end of an entire block
@ -137,7 +138,7 @@ func (s *stateObject) touch() {
// getTrie returns the associated storage trie. The trie will be opened
// if it's not loaded previously. An error will be returned if trie can't
// be loaded.
func (s *stateObject) getTrie() (Trie, error) {
func (s *stateObject) getTrie() (trie.Trie, error) {
if s.trie == nil {
// Try fetching from prefetcher first
if s.data.Root != types.EmptyRootHash && s.db.prefetcher != nil {
@ -267,7 +268,7 @@ func (s *stateObject) finalise(prefetch bool) {
// updateTrie writes cached storage modifications into the object's storage trie.
// It will return nil if the trie has not been loaded and no changes have been
// made. An error will be returned if the trie can't be loaded/updated correctly.
func (s *stateObject) updateTrie() (Trie, error) {
func (s *stateObject) updateTrie() (trie.Trie, error) {
// Make sure all dirty slots are finalized into the pending storage area
s.finalise(false) // Don't prefetch anymore, pull directly if need be
if len(s.pendingStorage) == 0 {

View file

@ -68,7 +68,7 @@ func (n *proofList) Delete(key []byte) error {
type StateDB struct {
db Database
prefetcher *triePrefetcher
trie Trie
trie trie.Trie
hasher crypto.KeccakState
snaps *snapshot.Tree // Nil if snapshot is not available
snap snapshot.Snapshot // Nil if snapshot is not available
@ -385,7 +385,7 @@ func (s *StateDB) Database() Database {
// StorageTrie returns the storage trie of an account. The return value is a copy
// and is nil for non-existent accounts. An error will be returned if storage trie
// is existent but can't be loaded correctly.
func (s *StateDB) StorageTrie(addr common.Address) (Trie, error) {
func (s *StateDB) StorageTrie(addr common.Address) (trie.Trie, error) {
stateObject := s.getStateObject(addr)
if stateObject == nil {
return nil, nil

View file

@ -22,6 +22,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/trie"
)
var (
@ -37,7 +38,7 @@ var (
type triePrefetcher struct {
db Database // Database to fetch trie nodes through
root common.Hash // Root hash of the account trie for metrics
fetches map[string]Trie // Partially or fully fetcher tries
fetches map[string]trie.Trie // Partially or fully fetcher tries
fetchers map[string]*subfetcher // Subfetchers for each trie
deliveryMissMeter metrics.Meter
@ -111,7 +112,7 @@ func (p *triePrefetcher) copy() *triePrefetcher {
copy := &triePrefetcher{
db: p.db,
root: p.root,
fetches: make(map[string]Trie), // Active prefetchers use the fetches map
fetches: make(map[string]trie.Trie), // Active prefetchers use the fetches map
deliveryMissMeter: p.deliveryMissMeter,
accountLoadMeter: p.accountLoadMeter,
@ -158,7 +159,7 @@ func (p *triePrefetcher) prefetch(owner common.Hash, root common.Hash, addr comm
// trie returns the trie matching the root hash, or nil if the prefetcher doesn't
// have it.
func (p *triePrefetcher) trie(owner common.Hash, root common.Hash) Trie {
func (p *triePrefetcher) trie(owner common.Hash, root common.Hash) trie.Trie {
// If the prefetcher is inactive, return from existing deep copies
id := p.trieID(owner, root)
if p.fetches != nil {
@ -210,15 +211,15 @@ type subfetcher struct {
owner common.Hash // Owner of the trie, usually account hash
root common.Hash // Root hash of the trie to prefetch
addr common.Address // Address of the account that the trie belongs to
trie Trie // Trie being populated with nodes
trie trie.Trie // Trie being populated with nodes
tasks [][]byte // Items queued up for retrieval
lock sync.Mutex // Lock protecting the task queue
wake chan struct{} // Wake channel if a new task is scheduled
stop chan struct{} // Channel to interrupt processing
term chan struct{} // Channel to signal interruption
copy chan chan Trie // Channel to request a copy of the current trie
wake chan struct{} // Wake channel if a new task is scheduled
stop chan struct{} // Channel to interrupt processing
term chan struct{} // Channel to signal interruption
copy chan chan trie.Trie // Channel to request a copy of the current trie
seen map[string]struct{} // Tracks the entries already loaded
dups int // Number of duplicate preload tasks
@ -237,7 +238,7 @@ func newSubfetcher(db Database, state common.Hash, owner common.Hash, root commo
wake: make(chan struct{}, 1),
stop: make(chan struct{}),
term: make(chan struct{}),
copy: make(chan chan Trie),
copy: make(chan chan trie.Trie),
seen: make(map[string]struct{}),
}
go sf.loop()
@ -260,8 +261,8 @@ func (sf *subfetcher) schedule(keys [][]byte) {
// peek tries to retrieve a deep copy of the fetcher's trie in whatever form it
// is currently.
func (sf *subfetcher) peek() Trie {
ch := make(chan Trie)
func (sf *subfetcher) peek() trie.Trie {
ch := make(chan trie.Trie)
select {
case sf.copy <- ch:
// Subfetcher still alive, return copy from it

View file

@ -236,7 +236,7 @@ func (api *DebugAPI) StorageRangeAt(ctx context.Context, blockNrOrHash rpc.Block
return storageRangeAt(st, keyStart, maxResult)
}
func storageRangeAt(st state.Trie, start []byte, maxResult int) (StorageRangeResult, error) {
func storageRangeAt(st trie.Trie, start []byte, maxResult int) (StorageRangeResult, error) {
trieIt, err := st.NodeIterator(start)
if err != nil {
return StorageRangeResult{}, err

View file

@ -35,7 +35,7 @@ import (
var dumper = spew.ConfigState{Indent: " "}
func accountRangeTest(t *testing.T, trie *state.Trie, statedb *state.StateDB, start common.Hash, requestedNum int, expectedNum int) state.IteratorDump {
func accountRangeTest(t *testing.T, trie *trie.Trie, statedb *state.StateDB, start common.Hash, requestedNum int, expectedNum int) state.IteratorDump {
result := statedb.IteratorDump(&state.DumpConfig{
SkipCode: true,
SkipStorage: true,

View file

@ -46,6 +46,7 @@ import (
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/trie"
"github.com/tyler-smith/go-bip39"
)
@ -674,7 +675,7 @@ func (s *BlockChainAPI) GetProof(ctx context.Context, address common.Address, st
keys = make([]common.Hash, len(storageKeys))
keyLengths = make([]int, len(storageKeys))
storageProof = make([]StorageResult, len(storageKeys))
storageTrie state.Trie
storageTrie trie.Trie
storageHash = types.EmptyRootHash
codeHash = types.EmptyCodeHash
)

View file

@ -22,7 +22,6 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/txpool"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/light"
@ -411,7 +410,7 @@ func handleGetProofs(msg Decoder) (serveRequestFn, uint64, uint64, error) {
// Open the account or storage trie for the request
statedb := bc.StateCache()
var trie state.Trie
var trie trie.Trie
switch len(request.AccountAddress) {
case 0:
// No account key specified, open an account trie

View file

@ -85,7 +85,7 @@ func (odr *testOdr) Retrieve(ctx context.Context, req OdrRequest) error {
case *TrieRequest:
var (
err error
t state.Trie
t trie.Trie
)
if len(req.Id.AccountAddress) > 0 {
t, err = odr.serverState.OpenStorageTrie(req.Id.StateRoot, common.BytesToAddress(req.Id.AccountAddress), req.Id.Root)

View file

@ -51,15 +51,15 @@ type odrDatabase struct {
backend OdrBackend
}
func (db *odrDatabase) OpenTrie(root common.Hash) (state.Trie, error) {
func (db *odrDatabase) OpenTrie(root common.Hash) (trie.Trie, error) {
return &odrTrie{db: db, id: db.id}, nil
}
func (db *odrDatabase) OpenStorageTrie(stateRoot common.Hash, address common.Address, root common.Hash) (state.Trie, error) {
func (db *odrDatabase) OpenStorageTrie(stateRoot common.Hash, address common.Address, root common.Hash) (trie.Trie, error) {
return &odrTrie{db: db, id: StorageTrieID(db.id, address, root)}, nil
}
func (db *odrDatabase) CopyTrie(t state.Trie) state.Trie {
func (db *odrDatabase) CopyTrie(t trie.Trie) trie.Trie {
switch t := t.(type) {
case *odrTrie:
cpy := &odrTrie{db: t.db, id: t.id}

View file

@ -28,7 +28,6 @@ import (
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/trie"
@ -61,7 +60,7 @@ func TestNodeIterator(t *testing.T) {
}
}
func diffTries(t1, t2 state.Trie) error {
func diffTries(t1, t2 trie.Trie) error {
trieIt1, err := t1.NodeIterator(nil)
if err != nil {
return err

98
trie/interface.go Normal file
View file

@ -0,0 +1,98 @@
// Copyright 2023 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package trie
import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/trie/trienode"
)
// Trie defines the interface that various tree implementations must adhere to.
// These implementations are used by the stateDB to access both account data and
// contract storage data within the Ethereum state. The primary responsibility
// of a Trie is to serve as the underlying structure for managing Ethereum's state.
type Trie interface {
// GetKey returns the sha3 preimage of a hashed key that was previously used
// to store a value.
//
// TODO(fjl): remove this when StateTrie is removed
GetKey([]byte) []byte
// GetAccount abstracts an account read from the trie. It retrieves the
// account blob from the trie with provided account address and decodes it
// with associated decoding algorithm. If the specified account is not in
// the trie, nil will be returned. If the trie is corrupted(e.g. some nodes
// are missing or the account blob is incorrect for decoding), an error will
// be returned.
GetAccount(address common.Address) (*types.StateAccount, error)
// GetStorage returns the value for key stored in the trie. The value bytes
// must not be modified by the caller. If a node was not found in the database,
// a trie.MissingNodeError is returned.
GetStorage(addr common.Address, key []byte) ([]byte, error)
// UpdateAccount abstracts an account write to the trie. It encodes the
// provided account object with associated algorithm and then updates it
// in the trie with provided address.
UpdateAccount(address common.Address, account *types.StateAccount) error
// UpdateStorage associates key with value in the trie. If value has length zero,
// any existing value is deleted from the trie. The value bytes must not be modified
// by the caller while they are stored in the trie. If a node was not found in the
// database, a trie.MissingNodeError is returned.
UpdateStorage(addr common.Address, key, value []byte) error
// DeleteAccount abstracts an account deletion from the trie.
DeleteAccount(address common.Address) error
// DeleteStorage removes any existing value for key from the trie. If a node
// was not found in the database, a trie.MissingNodeError is returned.
DeleteStorage(addr common.Address, key []byte) error
// UpdateContractCode abstracts code write to the trie. It is expected
// to be moved to the stateWriter interface when the latter is ready.
UpdateContractCode(address common.Address, codeHash common.Hash, code []byte) error
// Hash returns the root hash of the trie. It does not write to the database and
// can be used even if the trie doesn't have one.
Hash() common.Hash
// Commit gathers changed nodes in the trie, replacing them with corresponding
// node hashes. It creates a set of these changed nodes, including modified
// leaf if the "collectLeaf" parameter is set to true. This set is returned as
// the "nodeset," which can be empty (nil) if no changes exist.
//
// Once committed, the trie becomes unusable and requires a new trie with a
// fresh root and updated database for further use.
Commit(collectLeaf bool) (common.Hash, *trienode.NodeSet, error)
// NodeIterator returns an iterator that returns nodes of the trie. Iteration
// starts at the key after the given start key. And error will be returned
// if fails to create node iterator.
NodeIterator(startKey []byte) (NodeIterator, error)
// Prove constructs a Merkle proof for key. The result contains all encoded nodes
// on the path to the value at key. The value itself is also included in the last
// node and can be retrieved by verifying the proof.
//
// If the trie does not contain a value for key, the returned proof contains all
// nodes of the longest existing prefix of the key (at least the root), ending
// with the node that proves the absence of the key.
Prove(key []byte, proofDb ethdb.KeyValueWriter) error
}

View file

@ -38,14 +38,13 @@ func NewSecure(stateRoot common.Hash, owner common.Hash, root common.Hash, db *D
return NewStateTrie(id, db)
}
// StateTrie wraps a trie with key hashing. In a stateTrie trie, all
// access operations hash the key using keccak256. This prevents
// calling code from creating long chains of nodes that
// increase the access time.
// StateTrie wraps a merkle patricia trie with key hashing. In a stateTrie trie,
// all access operations hash the key using keccak256. This prevents calling code
// from creating long chains of nodes that increase the access time.
//
// Contrary to a regular trie, a StateTrie can only be created with
// New and must have an attached database. The database also stores
// the preimage of each key if preimage recording is enabled.
// Contrary to a regular trie, a StateTrie can only be created with New and must
// have an attached database. The database also stores the preimage of each key
// if preimage recording is enabled.
//
// StateTrie is not safe for concurrent use.
type StateTrie struct {

View file

@ -34,7 +34,7 @@ import (
// it's not usable anymore. Callers have to re-create the trie with new root
// based on the updated trie database.
//
// Trie is not safe for concurrent use.
// MPT is not safe for concurrent use.
type MPT struct {
root node
owner common.Hash
@ -84,19 +84,19 @@ func New(id *ID, db *Database) (*MPT, error) {
if err != nil {
return nil, err
}
trie := &MPT{
t := &MPT{
owner: id.Owner,
reader: reader,
tracer: newTracer(),
}
if id.Root != (common.Hash{}) && id.Root != types.EmptyRootHash {
rootnode, err := trie.resolveAndTrack(id.Root[:], nil)
rootnode, err := t.resolveAndTrack(id.Root[:], nil)
if err != nil {
return nil, err
}
trie.root = rootnode
t.root = rootnode
}
return trie, nil
return t, nil
}
// NewEmpty is a shortcut to create empty tree. It's mostly used in tests.