mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-18 18:02:24 +00:00
refactor: abstract unsupported-method checks out of triedb.Database
Previously, `triedb.Database` would type-assert between hash- and path-based implementations to check for method support. This had two downsides:
1. The repeated type checks were noisy and reduced code clarity; and
2. It was impossible to experiment with different implementations as method support was conditioned on an explicit concrete type.
By moving the methods onto the `backend` interface type, each concrete implementation now declares its own lack of support.
This cleans up the `triedb.Database` method implementations and consigns "chore" code into the `{hash,path}db/unsupported.go` files to be conveniently ignored :). Although not in scope for this PR, these changes allow the `backend` to be embedded in `triedb.Database` to further remove repeated code by exporting `type Backend interface` and entirely removing individual methods.
This commit is contained in:
parent
16bf471151
commit
ab86490b3c
7 changed files with 149 additions and 75 deletions
|
|
@ -17,8 +17,6 @@
|
|||
package triedb
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
|
|
@ -86,6 +84,21 @@ type backend interface {
|
|||
// 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.
|
||||
Reader(root common.Hash) (database.Reader, error)
|
||||
|
||||
// These methods have a one-to-one correspondence with methods exposed by
|
||||
// [Database]. Refer to the respective documentation for their behavior.
|
||||
Cap(limit common.StorageSize) error
|
||||
Reference(root, parent common.Hash) error
|
||||
Dereference(root common.Hash) error
|
||||
Recover(target common.Hash) error
|
||||
Recoverable(root common.Hash) (bool, error)
|
||||
Disable() error
|
||||
Enable(root common.Hash) error
|
||||
Journal(root common.Hash) error
|
||||
SetBufferSize(int) error
|
||||
AccountHistory(_ common.Address, start, end uint64) (*pathdb.HistoryStats, error)
|
||||
StorageHistory(_ common.Address, slot common.Hash, start, end uint64) (*pathdb.HistoryStats, error)
|
||||
HistoryRange() (uint64, uint64, error)
|
||||
}
|
||||
|
||||
// Database is the wrapper of the underlying backend which is shared by different
|
||||
|
|
@ -221,14 +234,10 @@ 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)
|
||||
if !ok {
|
||||
return errors.New("not supported")
|
||||
}
|
||||
if db.preimages != nil {
|
||||
db.preimages.commit(false)
|
||||
}
|
||||
return hdb.Cap(limit)
|
||||
return db.backend.Cap(limit)
|
||||
}
|
||||
|
||||
// Reference adds a new reference from a parent node to a child node. This function
|
||||
|
|
@ -237,23 +246,13 @@ 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)
|
||||
if !ok {
|
||||
return errors.New("not supported")
|
||||
}
|
||||
hdb.Reference(root, parent)
|
||||
return nil
|
||||
return db.backend.Reference(root, parent)
|
||||
}
|
||||
|
||||
// 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)
|
||||
if !ok {
|
||||
return errors.New("not supported")
|
||||
}
|
||||
hdb.Dereference(root)
|
||||
return nil
|
||||
return db.backend.Dereference(root)
|
||||
}
|
||||
|
||||
// Recover rollbacks the database to a specified historical point. The state is
|
||||
|
|
@ -261,22 +260,14 @@ 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)
|
||||
if !ok {
|
||||
return errors.New("not supported")
|
||||
}
|
||||
return pdb.Recover(target)
|
||||
return db.backend.Recover(target)
|
||||
}
|
||||
|
||||
// Recoverable returns the indicator if the specified state is enabled to be
|
||||
// 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)
|
||||
if !ok {
|
||||
return false, errors.New("not supported")
|
||||
}
|
||||
return pdb.Recoverable(root), nil
|
||||
return db.backend.Recoverable(root)
|
||||
}
|
||||
|
||||
// Disable deactivates the database and invalidates all available state layers
|
||||
|
|
@ -285,21 +276,13 @@ 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)
|
||||
if !ok {
|
||||
return errors.New("not supported")
|
||||
}
|
||||
return pdb.Disable()
|
||||
return db.backend.Disable()
|
||||
}
|
||||
|
||||
// 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)
|
||||
if !ok {
|
||||
return errors.New("not supported")
|
||||
}
|
||||
return pdb.Enable(root)
|
||||
return db.backend.Enable(root)
|
||||
}
|
||||
|
||||
// Journal commits an entire diff hierarchy to disk into a single journal entry.
|
||||
|
|
@ -307,22 +290,14 @@ 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)
|
||||
if !ok {
|
||||
return errors.New("not supported")
|
||||
}
|
||||
return pdb.Journal(root)
|
||||
return db.backend.Journal(root)
|
||||
}
|
||||
|
||||
// SetBufferSize sets the node buffer size to the provided value(in bytes).
|
||||
// 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)
|
||||
if !ok {
|
||||
return errors.New("not supported")
|
||||
}
|
||||
return pdb.SetBufferSize(size)
|
||||
return db.backend.SetBufferSize(size)
|
||||
}
|
||||
|
||||
// IsVerkle returns the indicator if the database is holding a verkle tree.
|
||||
|
|
|
|||
|
|
@ -217,12 +217,13 @@ func (db *Database) node(hash common.Hash) ([]byte, error) {
|
|||
// Reference adds a new reference from a parent node to a child node.
|
||||
// This function is used to add reference between internal trie node
|
||||
// and external node(e.g. storage trie root), all internal trie nodes
|
||||
// are referenced together by database itself.
|
||||
func (db *Database) Reference(child common.Hash, parent common.Hash) {
|
||||
// are referenced together by database itself. It always returns a nil error.
|
||||
func (db *Database) Reference(child common.Hash, parent common.Hash) error {
|
||||
db.lock.Lock()
|
||||
defer db.lock.Unlock()
|
||||
|
||||
db.reference(child, parent)
|
||||
return nil
|
||||
}
|
||||
|
||||
// reference is the private locked version of Reference.
|
||||
|
|
@ -250,12 +251,13 @@ func (db *Database) reference(child common.Hash, parent common.Hash) {
|
|||
db.childrenSize += common.HashLength
|
||||
}
|
||||
|
||||
// Dereference removes an existing reference from a root node.
|
||||
func (db *Database) Dereference(root common.Hash) {
|
||||
// Dereference removes an existing reference from a root node. It always returns
|
||||
// a nil error.
|
||||
func (db *Database) Dereference(root common.Hash) error {
|
||||
// Sanity check to ensure that the meta-root is not removed
|
||||
if root == (common.Hash{}) {
|
||||
log.Error("Attempted to dereference the trie cache meta root")
|
||||
return
|
||||
return nil
|
||||
}
|
||||
db.lock.Lock()
|
||||
defer db.lock.Unlock()
|
||||
|
|
@ -273,6 +275,7 @@ func (db *Database) Dereference(root common.Hash) {
|
|||
|
||||
log.Debug("Dereferenced trie from memory database", "nodes", nodes-len(db.dirties), "size", storage-db.dirtiesSize, "time", time.Since(start),
|
||||
"gcnodes", db.gcnodes, "gcsize", db.gcsize, "gctime", db.gctime, "livenodes", len(db.dirties), "livesize", db.dirtiesSize)
|
||||
return nil
|
||||
}
|
||||
|
||||
// dereference is the private locked version of Dereference.
|
||||
|
|
|
|||
60
triedb/hashdb/unsupported.go
Normal file
60
triedb/hashdb/unsupported.go
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
// Copyright 2018 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 hashdb
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/triedb/pathdb"
|
||||
)
|
||||
|
||||
// These methods are implemented by pathdb but not supported by hashdb. They are
|
||||
// included for interface parity to avoid triedb having to check concrete types.
|
||||
|
||||
var errUnsupported = errors.New("method not supported by hashdb")
|
||||
|
||||
// Recover isn't supported and always returns an error.
|
||||
func (*Database) Recover(target common.Hash) error { return errUnsupported }
|
||||
|
||||
// Recoverable isn't supported and always returns an error.
|
||||
func (*Database) Recoverable(root common.Hash) (bool, error) { return false, errUnsupported }
|
||||
|
||||
// Disable isn't supported and always returns an error.
|
||||
func (*Database) Disable() error { return errUnsupported }
|
||||
|
||||
// Enable isn't supported and always returns an error.
|
||||
func (*Database) Enable(root common.Hash) error { return errUnsupported }
|
||||
|
||||
// Journal isn't supported and always returns an error.
|
||||
func (*Database) Journal(root common.Hash) error { return errUnsupported }
|
||||
|
||||
// SetBufferSize isn't supported and always returns an error.
|
||||
func (*Database) SetBufferSize(int) error { return errUnsupported }
|
||||
|
||||
// AccountHistory isn't supported and always returns an error.
|
||||
func (*Database) AccountHistory(_ common.Address, start, end uint64) (*pathdb.HistoryStats, error) {
|
||||
return nil, errUnsupported
|
||||
}
|
||||
|
||||
// StorageHistory isn't supported and always returns an error.
|
||||
func (*Database) StorageHistory(_ common.Address, slot common.Hash, start, end uint64) (*pathdb.HistoryStats, error) {
|
||||
return nil, errUnsupported
|
||||
}
|
||||
|
||||
// HistoryRange isn't supported and always returns an error.
|
||||
func (*Database) HistoryRange() (uint64, uint64, error) { return 0, 0, errUnsupported }
|
||||
|
|
@ -17,8 +17,6 @@
|
|||
package triedb
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/triedb/pathdb"
|
||||
)
|
||||
|
|
@ -33,11 +31,7 @@ import (
|
|||
//
|
||||
// This function is only supported by path mode database.
|
||||
func (db *Database) AccountHistory(address common.Address, start, end uint64) (*pathdb.HistoryStats, error) {
|
||||
pdb, ok := db.backend.(*pathdb.Database)
|
||||
if !ok {
|
||||
return nil, errors.New("not supported")
|
||||
}
|
||||
return pdb.AccountHistory(address, start, end)
|
||||
return db.backend.AccountHistory(address, start, end)
|
||||
}
|
||||
|
||||
// StorageHistory inspects the storage history within the specified range.
|
||||
|
|
@ -52,11 +46,7 @@ func (db *Database) AccountHistory(address common.Address, start, end uint64) (*
|
|||
//
|
||||
// This function is only supported by path mode database.
|
||||
func (db *Database) StorageHistory(address common.Address, slot common.Hash, start uint64, end uint64) (*pathdb.HistoryStats, error) {
|
||||
pdb, ok := db.backend.(*pathdb.Database)
|
||||
if !ok {
|
||||
return nil, errors.New("not supported")
|
||||
}
|
||||
return pdb.StorageHistory(address, slot, start, end)
|
||||
return db.backend.StorageHistory(address, slot, start, end)
|
||||
}
|
||||
|
||||
// HistoryRange returns the block numbers associated with earliest and latest
|
||||
|
|
@ -64,9 +54,5 @@ func (db *Database) StorageHistory(address common.Address, slot common.Hash, sta
|
|||
//
|
||||
// This function is only supported by path mode database.
|
||||
func (db *Database) HistoryRange() (uint64, uint64, error) {
|
||||
pdb, ok := db.backend.(*pathdb.Database)
|
||||
if !ok {
|
||||
return 0, 0, errors.New("not supported")
|
||||
}
|
||||
return pdb.HistoryRange()
|
||||
return db.backend.HistoryRange()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -366,7 +366,7 @@ func (db *Database) Recover(root common.Hash) error {
|
|||
}
|
||||
// Short circuit if the target state is not recoverable.
|
||||
root = types.TrieRootHash(root)
|
||||
if !db.Recoverable(root) {
|
||||
if !db.recoverable(root) {
|
||||
return errStateUnrecoverable
|
||||
}
|
||||
// Apply the state histories upon the disk layer in order.
|
||||
|
|
@ -397,8 +397,15 @@ func (db *Database) Recover(root common.Hash) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Recoverable returns the indicator if the specified state is recoverable.
|
||||
func (db *Database) Recoverable(root common.Hash) bool {
|
||||
// Recoverable returns the indicator if the specified state is recoverable. It
|
||||
// always returns a nil error.
|
||||
func (db *Database) Recoverable(root common.Hash) (bool, error) {
|
||||
return db.recoverable(root), nil
|
||||
}
|
||||
|
||||
// recoverable is the internal implementation of [Database.Recoverable], which
|
||||
// has to return an error for conformity with the triedb.backend interface.
|
||||
func (db *Database) recoverable(root common.Hash) bool {
|
||||
// Ensure the requested state is a known state.
|
||||
root = types.TrieRootHash(root)
|
||||
id := rawdb.ReadStateID(db.diskdb, root)
|
||||
|
|
|
|||
|
|
@ -469,7 +469,13 @@ func TestDatabaseRecoverable(t *testing.T) {
|
|||
{tester.roots[index+1], false},
|
||||
}
|
||||
for i, c := range cases {
|
||||
result := tester.db.Recoverable(c.root)
|
||||
result, err := tester.db.Recoverable(c.root)
|
||||
if err != nil {
|
||||
// Recoverable() always returns nil so this should never happen. The
|
||||
// signature only includes a returned error to conform with a
|
||||
// triedb-internal interface.
|
||||
t.Fatalf("case: %d, %T.Recoverable() got error %v", i, tester.db, err)
|
||||
}
|
||||
if result != c.expect {
|
||||
t.Fatalf("case: %d, unexpected result, want %t, got %t", i, c.expect, result)
|
||||
}
|
||||
|
|
|
|||
37
triedb/pathdb/unsupported.go
Normal file
37
triedb/pathdb/unsupported.go
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
// Copyright 2022 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 pathdb
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
// These methods are implemented by hashdb but not supported by pathdb. They are
|
||||
// included for interface parity to avoid triedb having to check concrete types.
|
||||
|
||||
var errUnsupported = errors.New("method not supported by pathdb")
|
||||
|
||||
// Cap isn't supported and always returns an error.
|
||||
func (*Database) Cap(limit common.StorageSize) error { return errUnsupported }
|
||||
|
||||
// Reference isn't supported and always returns an error.
|
||||
func (*Database) Reference(root, parent common.Hash) error { return errUnsupported }
|
||||
|
||||
// Dereference isn't supported and always returns an error.
|
||||
func (*Database) Dereference(root common.Hash) error { return errUnsupported }
|
||||
Loading…
Reference in a new issue