From ec7eda1b28d7dee2dd7666860763578fc5422e51 Mon Sep 17 00:00:00 2001 From: Darioush Jalali Date: Tue, 8 Oct 2024 17:31:32 -0700 Subject: [PATCH] make backends configurable --- triedb/database.go | 56 ++++++++++++++++++++--------------- triedb/database/database.go | 58 +++++++++++++++++++++++++++++++++++++ triedb/hashdb/database.go | 7 ++++- triedb/pathdb/database.go | 7 ++++- 4 files changed, 103 insertions(+), 25 deletions(-) diff --git a/triedb/database.go b/triedb/database.go index 939a21f1478b..3fb82f31b816 100644 --- a/triedb/database.go +++ b/triedb/database.go @@ -27,15 +27,14 @@ import ( "github.com/ethereum/go-ethereum/trie/triestate" "github.com/ethereum/go-ethereum/triedb/database" "github.com/ethereum/go-ethereum/triedb/hashdb" - "github.com/ethereum/go-ethereum/triedb/pathdb" ) // Config defines all necessary options for database. type Config struct { - Preimages bool // Flag whether the preimage of node key is recorded - IsVerkle bool // Flag whether the db is holding a verkle tree - HashDB *hashdb.Config // Configs for hash-based scheme - PathDB *pathdb.Config // Configs for experimental path-based scheme + Preimages bool // Flag whether the preimage of node key is recorded + IsVerkle bool // Flag whether the db is holding a verkle tree + HashDB hashBackender // Configs for hash-based scheme + PathDB pathBackender // Configs for experimental path-based scheme } // HashDefaults represents a config for using hash-based scheme with @@ -45,6 +44,15 @@ var HashDefaults = &Config{ HashDB: hashdb.Defaults, } +type Backend backend + +type hashBackender interface { + New(diskdb ethdb.Database, resolver hashdb.ChildResolver) database.HashBackend +} +type pathBackender interface { + New(diskdb ethdb.Database) database.PathBackend +} + // backend defines the methods needed to access/update trie nodes in different // state scheme. type backend interface { @@ -76,6 +84,10 @@ type backend interface { // Close closes the trie database backend and releases all held resources. Close() error + + // Reader returns a node reader associated with the specific state. + // An error will be returned if the specified state is not available. + Reader(stateRoot common.Hash) (database.Reader, error) } // Database is the wrapper of the underlying backend which is shared by different @@ -108,7 +120,7 @@ func NewDatabase(diskdb ethdb.Database, config *Config) *Database { log.Crit("Both 'hash' and 'path' mode are configured") } if config.PathDB != nil { - db.backend = pathdb.New(diskdb, config.PathDB) + db.backend = config.PathDB.New(diskdb) } else { var resolver hashdb.ChildResolver if config.IsVerkle { @@ -117,7 +129,11 @@ func NewDatabase(diskdb ethdb.Database, config *Config) *Database { } else { resolver = trie.MerkleResolver{} } - db.backend = hashdb.New(diskdb, config.HashDB, resolver) + if config.HashDB == nil { + // some tests don't set this yet pass a non-nil config + config.HashDB = hashdb.Defaults + } + db.backend = config.HashDB.New(diskdb, resolver) } return db } @@ -125,13 +141,7 @@ func NewDatabase(diskdb ethdb.Database, config *Config) *Database { // Reader returns a reader for accessing all trie nodes with provided state root. // An error will be returned if the requested state is not available. func (db *Database) Reader(blockRoot common.Hash) (database.Reader, error) { - switch b := db.backend.(type) { - case *hashdb.Database: - return b.Reader(blockRoot) - case *pathdb.Database: - return b.Reader(blockRoot) - } - return nil, errors.New("unknown backend") + return db.backend.Reader(blockRoot) } // Update performs a state transition by committing dirty nodes contained in the @@ -221,7 +231,7 @@ func (db *Database) InsertPreimage(preimages map[common.Hash][]byte) { // // It's only supported by hash-based database and will return an error for others. func (db *Database) Cap(limit common.StorageSize) error { - hdb, ok := db.backend.(*hashdb.Database) + hdb, ok := db.backend.(database.HashBackend) if !ok { return errors.New("not supported") } @@ -237,7 +247,7 @@ func (db *Database) Cap(limit common.StorageSize) error { // // It's only supported by hash-based database and will return an error for others. func (db *Database) Reference(root common.Hash, parent common.Hash) error { - hdb, ok := db.backend.(*hashdb.Database) + hdb, ok := db.backend.(database.HashBackend) if !ok { return errors.New("not supported") } @@ -248,7 +258,7 @@ func (db *Database) Reference(root common.Hash, parent common.Hash) error { // Dereference removes an existing reference from a root node. It's only // supported by hash-based database and will return an error for others. func (db *Database) Dereference(root common.Hash) error { - hdb, ok := db.backend.(*hashdb.Database) + hdb, ok := db.backend.(database.HashBackend) if !ok { return errors.New("not supported") } @@ -261,7 +271,7 @@ func (db *Database) Dereference(root common.Hash) error { // corresponding trie histories are existent. It's only supported by path-based // database and will return an error for others. func (db *Database) Recover(target common.Hash) error { - pdb, ok := db.backend.(*pathdb.Database) + pdb, ok := db.backend.(database.PathBackend) if !ok { return errors.New("not supported") } @@ -279,7 +289,7 @@ func (db *Database) Recover(target common.Hash) error { // recovered. It's only supported by path-based database and will return an // error for others. func (db *Database) Recoverable(root common.Hash) (bool, error) { - pdb, ok := db.backend.(*pathdb.Database) + pdb, ok := db.backend.(database.PathBackend) if !ok { return false, errors.New("not supported") } @@ -292,7 +302,7 @@ func (db *Database) Recoverable(root common.Hash) (bool, error) { // // It's only supported by path-based database and will return an error for others. func (db *Database) Disable() error { - pdb, ok := db.backend.(*pathdb.Database) + pdb, ok := db.backend.(database.PathBackend) if !ok { return errors.New("not supported") } @@ -302,7 +312,7 @@ func (db *Database) Disable() error { // Enable activates database and resets the state tree with the provided persistent // state root once the state sync is finished. func (db *Database) Enable(root common.Hash) error { - pdb, ok := db.backend.(*pathdb.Database) + pdb, ok := db.backend.(database.PathBackend) if !ok { return errors.New("not supported") } @@ -314,7 +324,7 @@ func (db *Database) Enable(root common.Hash) error { // flattening everything down (bad for reorgs). It's only supported by path-based // database and will return an error for others. func (db *Database) Journal(root common.Hash) error { - pdb, ok := db.backend.(*pathdb.Database) + pdb, ok := db.backend.(database.PathBackend) if !ok { return errors.New("not supported") } @@ -325,7 +335,7 @@ func (db *Database) Journal(root common.Hash) error { // It's only supported by path-based database and will return an error for // others. func (db *Database) SetBufferSize(size int) error { - pdb, ok := db.backend.(*pathdb.Database) + pdb, ok := db.backend.(database.PathBackend) if !ok { return errors.New("not supported") } diff --git a/triedb/database/database.go b/triedb/database/database.go index 18a8f454e2f4..95a54fd53dad 100644 --- a/triedb/database/database.go +++ b/triedb/database/database.go @@ -18,6 +18,8 @@ package database import ( "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/trie/trienode" + "github.com/ethereum/go-ethereum/trie/triestate" ) // Reader wraps the Node method of a backing trie reader. @@ -46,3 +48,59 @@ type Database interface { // An error will be returned if the specified state is not available. Reader(stateRoot common.Hash) (Reader, error) } + +// Backend defines the methods needed to access/update trie nodes in different +// state scheme. +type Backend interface { + // Scheme returns the identifier of used storage scheme. + Scheme() string + + // Initialized returns an indicator if the state data is already initialized + // according to the state scheme. + Initialized(genesisRoot common.Hash) bool + + // Size returns the current storage size of the diff layers on top of the + // disk layer and the storage size of the nodes cached in the disk layer. + // + // For hash scheme, there is no differentiation between diff layer nodes + // and dirty disk layer nodes, so both are merged into the second return. + Size() (common.StorageSize, common.StorageSize) + + // Update performs a state transition by committing dirty nodes contained + // in the given set in order to update state from the specified parent to + // the specified root. + // + // The passed in maps(nodes, states) will be retained to avoid copying + // everything. Therefore, these maps must not be changed afterwards. + Update(root common.Hash, parent common.Hash, block uint64, nodes *trienode.MergedNodeSet, states *triestate.Set) error + + // Commit writes all relevant trie nodes belonging to the specified state + // to disk. Report specifies whether logs will be displayed in info level. + Commit(root common.Hash, report bool) error + + // Close closes the trie database backend and releases all held resources. + Close() error + + // Reader returns a node reader associated with the specific state. + // An error will be returned if the specified state is not available. + Reader(stateRoot common.Hash) (Reader, error) +} + +type HashBackend interface { + Backend + + Cap(limit common.StorageSize) error + Reference(root common.Hash, parent common.Hash) + Dereference(root common.Hash) +} + +type PathBackend interface { + Backend + + Recover(root common.Hash, loader triestate.TrieLoader) error + Recoverable(root common.Hash) bool + Disable() error + Enable(root common.Hash) error + Journal(root common.Hash) error + SetBufferSize(size int) error +} diff --git a/triedb/hashdb/database.go b/triedb/hashdb/database.go index e45ccdba32ca..671edd8e997b 100644 --- a/triedb/hashdb/database.go +++ b/triedb/hashdb/database.go @@ -33,6 +33,7 @@ import ( "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie/trienode" "github.com/ethereum/go-ethereum/trie/triestate" + "github.com/ethereum/go-ethereum/triedb/database" ) var ( @@ -79,6 +80,10 @@ var Defaults = &Config{ CleanCacheSize: 0, } +func (c *Config) New(diskdb ethdb.Database, resolver ChildResolver) database.HashBackend { + return New(diskdb, c, resolver) +} + // Database is an intermediate write layer between the trie data structures and // the disk database. The aim is to accumulate trie writes in-memory and only // periodically flush a couple tries to disk, garbage collecting the remainder. @@ -631,7 +636,7 @@ func (db *Database) Scheme() string { // Reader retrieves a node reader belonging to the given state root. // An error will be returned if the requested state is not available. -func (db *Database) Reader(root common.Hash) (*reader, error) { +func (db *Database) Reader(root common.Hash) (database.Reader, error) { if _, err := db.node(root); err != nil { return nil, fmt.Errorf("state %#x is not available, %v", root, err) } diff --git a/triedb/pathdb/database.go b/triedb/pathdb/database.go index f2d6cea635a9..c5e2ecff2303 100644 --- a/triedb/pathdb/database.go +++ b/triedb/pathdb/database.go @@ -31,6 +31,7 @@ import ( "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/trie/trienode" "github.com/ethereum/go-ethereum/trie/triestate" + "github.com/ethereum/go-ethereum/triedb/database" ) const ( @@ -92,6 +93,10 @@ type Config struct { ReadOnly bool // Flag whether the database is opened in read only mode. } +func (c *Config) New(diskdb ethdb.Database) database.PathBackend { + return New(diskdb, c) +} + // sanitize checks the provided user configurations and changes anything that's // unreasonable or unworkable. func (c *Config) sanitize() *Config { @@ -208,7 +213,7 @@ func New(diskdb ethdb.Database, config *Config) *Database { } // Reader retrieves a layer belonging to the given state root. -func (db *Database) Reader(root common.Hash) (layer, error) { +func (db *Database) Reader(root common.Hash) (database.Reader, error) { l := db.tree.get(root) if l == nil { return nil, fmt.Errorf("state %#x is not available", root)