diff --git a/triedb/database.go b/triedb/database.go
index 4ce4de54a5d..6ec8b157a74 100644
--- a/triedb/database.go
+++ b/triedb/database.go
@@ -36,6 +36,8 @@ type Config struct {
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
+
+ DBOverride DBConstructor // Injects an arbitrary backend-database implementation
}
// HashDefaults represents a config for using hash-based scheme with
@@ -104,6 +106,9 @@ func NewDatabase(diskdb ethdb.Database, config *Config) *Database {
diskdb: diskdb,
preimages: preimages,
}
+ if db.overrideBackend(diskdb, config) {
+ return db
+ }
if config.HashDB != nil && config.PathDB != nil {
log.Crit("Both 'hash' and 'path' mode are configured")
}
@@ -126,6 +131,8 @@ func NewDatabase(diskdb ethdb.Database, config *Config) *Database {
// 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 ReaderProvider:
+ return b.Reader(blockRoot)
case *hashdb.Database:
return b.Reader(blockRoot)
case *pathdb.Database:
@@ -221,7 +228,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.(HashDB)
if !ok {
return errors.New("not supported")
}
@@ -237,7 +244,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.(HashDB)
if !ok {
return errors.New("not supported")
}
@@ -248,7 +255,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.(HashDB)
if !ok {
return errors.New("not supported")
}
@@ -261,7 +268,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.(PathDB)
if !ok {
return errors.New("not supported")
}
@@ -279,7 +286,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.(PathDB)
if !ok {
return false, errors.New("not supported")
}
@@ -292,7 +299,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.(PathDB)
if !ok {
return errors.New("not supported")
}
@@ -302,7 +309,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.(PathDB)
if !ok {
return errors.New("not supported")
}
@@ -314,7 +321,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.(PathDB)
if !ok {
return errors.New("not supported")
}
@@ -325,7 +332,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.(PathDB)
if !ok {
return errors.New("not supported")
}
diff --git a/triedb/database.libevm.go b/triedb/database.libevm.go
new file mode 100644
index 00000000000..01998d46455
--- /dev/null
+++ b/triedb/database.libevm.go
@@ -0,0 +1,97 @@
+// Copyright 2024 the libevm authors.
+//
+// The libevm additions to go-ethereum are free software: you can redistribute
+// them and/or modify them 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 libevm additions are distributed in the hope that they 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
+// .
+
+package triedb
+
+import (
+ "github.com/ava-labs/libevm/common"
+ "github.com/ava-labs/libevm/ethdb"
+ "github.com/ava-labs/libevm/log"
+ "github.com/ava-labs/libevm/trie/triestate"
+ "github.com/ava-labs/libevm/triedb/database"
+ "github.com/ava-labs/libevm/triedb/hashdb"
+ "github.com/ava-labs/libevm/triedb/pathdb"
+)
+
+// BackendDB defines the intersection of methods shared by [hashdb.Database] and
+// [pathdb.Database]. It is defined to export an otherwise internal type used by
+// the non-libevm geth implementation.
+type BackendDB backend
+
+// A ReaderProvider exposes its underlying Reader as an interface. Both
+// [hashdb.Database] and [pathdb.Database] return concrete types so Go's lack of
+// support for [covariant types] means that this method can't be defined on
+// [BackendDB].
+//
+// [covariant types]: https://go.dev/doc/faq#covariant_types
+type ReaderProvider interface {
+ Reader(common.Hash) (database.Reader, error)
+}
+
+// A DBConstructor constructs alternative backend-database implementations.
+type DBConstructor func(ethdb.Database, *Config) DBOverride
+
+// A DBOverride is an arbitrary implementation of a [Database] backend. It MUST
+// be either a [HashDB] or a [PathDB].
+type DBOverride interface {
+ BackendDB
+ ReaderProvider
+}
+
+func (db *Database) overrideBackend(diskdb ethdb.Database, config *Config) bool {
+ if config.DBOverride == nil {
+ return false
+ }
+ if config.HashDB != nil || config.PathDB != nil {
+ log.Crit("Database override provided when 'hash' or 'path' mode are configured")
+ }
+
+ db.backend = config.DBOverride(diskdb, config)
+ switch db.backend.(type) {
+ case HashDB:
+ case PathDB:
+ default:
+ log.Crit("Database override is neither hash- nor path-based")
+ }
+ return true
+}
+
+var (
+ // If either of these break then the respective interface SHOULD be updated.
+ _ HashDB = (*hashdb.Database)(nil)
+ _ PathDB = (*pathdb.Database)(nil)
+)
+
+// A HashDB mirrors the functionality of a [hashdb.Database].
+type HashDB interface {
+ BackendDB
+
+ Cap(limit common.StorageSize) error
+ Reference(root common.Hash, parent common.Hash)
+ Dereference(root common.Hash)
+}
+
+// A PathDB mirrors the functionality of a [pathdb.Database].
+type PathDB interface {
+ BackendDB
+
+ 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/database.libevm_test.go b/triedb/database.libevm_test.go
new file mode 100644
index 00000000000..6eedcb77a77
--- /dev/null
+++ b/triedb/database.libevm_test.go
@@ -0,0 +1,54 @@
+// Copyright 2024 the libevm authors.
+//
+// The libevm additions to go-ethereum are free software: you can redistribute
+// them and/or modify them 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 libevm additions are distributed in the hope that they 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
+// .
+
+package triedb
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/require"
+
+ "github.com/ava-labs/libevm/common"
+ "github.com/ava-labs/libevm/ethdb"
+ "github.com/ava-labs/libevm/triedb/database"
+)
+
+func TestDBOverride(t *testing.T) {
+ config := &Config{
+ DBOverride: func(d ethdb.Database, c *Config) DBOverride {
+ return override{}
+ },
+ }
+
+ db := NewDatabase(nil, config)
+ got, err := db.Reader(common.Hash{})
+ require.NoError(t, err)
+ if _, ok := got.(reader); !ok {
+ t.Errorf("with non-nil %T.DBOverride, %T.Reader() got concrete type %T; want %T", config, db, got, reader{})
+ }
+}
+
+type override struct {
+ PathDB
+}
+
+type reader struct {
+ database.Reader
+}
+
+func (override) Reader(common.Hash) (database.Reader, error) {
+ return reader{}, nil
+}