diff --git a/graft/coreth/plugin/evm/config/config.go b/graft/coreth/plugin/evm/config/config.go index b333ca7c5802..dd4acbcd4c6e 100644 --- a/graft/coreth/plugin/evm/config/config.go +++ b/graft/coreth/plugin/evm/config/config.go @@ -139,7 +139,11 @@ type Config struct { StateSyncRequestSize uint16 `json:"state-sync-request-size"` // Database Settings - InspectDatabase bool `json:"inspect-database"` // Inspects the database on startup if enabled. + InspectDatabase bool `json:"inspect-database"` // Inspects the database on startup if enabled. + BlockDatabaseEnabled bool `json:"block-database-enabled"` // Use block database for storing block data + // SkipBlockDatabaseAutoMigrate skips auto-migrating block data from key-value + // database to the block database. Only new blocks will be stored in the block database. + SkipBlockDatabaseAutoMigrate bool `json:"skip-block-database-auto-migrate"` // SkipUpgradeCheck disables checking that upgrades must take place before the last // accepted block. Skipping this check is useful when a node operator does not update diff --git a/graft/coreth/plugin/evm/vm.go b/graft/coreth/plugin/evm/vm.go index 44fac1fa0d31..5ba03348be72 100644 --- a/graft/coreth/plugin/evm/vm.go +++ b/graft/coreth/plugin/evm/vm.go @@ -130,6 +130,7 @@ var ( metadataPrefix = []byte("metadata") warpPrefix = []byte("warp") ethDBPrefix = []byte("ethdb") + blockDBPrefix = []byte("blockdb") ) var ( @@ -312,7 +313,9 @@ func (vm *VM) Initialize( } // Initialize the database - vm.initializeDBs(db) + if err := vm.initializeDBs(db); err != nil { + return err + } if vm.config.InspectDatabase { if err := vm.inspectDatabases(); err != nil { return err diff --git a/graft/coreth/plugin/evm/vm_database.go b/graft/coreth/plugin/evm/vm_database.go index a0bc0c6a31e3..2ea19491be8e 100644 --- a/graft/coreth/plugin/evm/vm_database.go +++ b/graft/coreth/plugin/evm/vm_database.go @@ -4,6 +4,10 @@ package evm import ( + "context" + "errors" + "path/filepath" + "strconv" "time" "github.com/ava-labs/libevm/common" @@ -13,13 +17,15 @@ import ( "github.com/ava-labs/avalanchego/database/prefixdb" "github.com/ava-labs/avalanchego/database/versiondb" "github.com/ava-labs/avalanchego/vms/evm/database" + "github.com/ava-labs/avalanchego/vms/evm/database/blockdb" avalanchedatabase "github.com/ava-labs/avalanchego/database" + heightindexdb "github.com/ava-labs/avalanchego/x/blockdb" ) // initializeDBs initializes the databases used by the VM. // coreth always uses the avalanchego provided database. -func (vm *VM) initializeDBs(db avalanchedatabase.Database) { +func (vm *VM) initializeDBs(db avalanchedatabase.Database) error { // Use NewNested rather than New so that the structure of the database // remains the same regardless of the provided baseDB type. vm.chaindb = rawdb.NewDatabase(database.New(prefixdb.NewNested(ethDBPrefix, db))) @@ -30,6 +36,13 @@ func (vm *VM) initializeDBs(db avalanchedatabase.Database) { // that warp signatures are committed to the database atomically with // the last accepted block. vm.warpDB = prefixdb.New(warpPrefix, db) + + // initBlockDB must be called after acceptedBlockDB and chaindb are initialized + // because it uses them. + if err := vm.initBlockDB(db); err != nil { + return err + } + return nil } func (vm *VM) inspectDatabases() error { @@ -80,3 +93,48 @@ func inspectDB(db avalanchedatabase.Database, label string) error { log.Info("Database statistics", "label", label, "total", total.String(), "count", count) return nil } + +// initBlockDB wraps the chaindb with a blockdb.Database that +// stores blocks data in separate databases when enabled. +func (vm *VM) initBlockDB(db avalanchedatabase.Database) error { + // Error if block database has been enabled and then disabled + metaDB := prefixdb.New(blockDBPrefix, db) + enabled, err := blockdb.IsEnabled(metaDB) + if err != nil { + return err + } + if !vm.config.BlockDatabaseEnabled { + if enabled { + return errors.New("block database should not be disabled after it has been enabled") + } + return nil + } + + version := strconv.FormatUint(heightindexdb.IndexFileVersion, 10) + path := filepath.Join(vm.ctx.ChainDataDir, "blockdb", version) + _, height, err := vm.ReadLastAccepted() + if err != nil { + return err + } + ssEnabled := vm.stateSyncEnabled(height) + cfg := heightindexdb.DefaultConfig().WithSyncToDisk(false) + bdb, initialized, err := blockdb.New( + metaDB, + vm.chaindb, + path, + ssEnabled, + cfg, + vm.ctx.Log, + vm.sdkMetrics, + ) + if err != nil { + return err + } + if initialized && !vm.config.SkipBlockDatabaseAutoMigrate { + if err := bdb.StartMigration(context.Background()); err != nil { + return err + } + } + vm.chaindb = bdb + return nil +} diff --git a/graft/coreth/plugin/evm/vm_test.go b/graft/coreth/plugin/evm/vm_test.go index 157ece7c1513..210522ce463c 100644 --- a/graft/coreth/plugin/evm/vm_test.go +++ b/graft/coreth/plugin/evm/vm_test.go @@ -2206,6 +2206,6 @@ func TestInspectDatabases(t *testing.T) { db = memdb.New() ) - vm.initializeDBs(db) + require.NoError(t, vm.initializeDBs(db)) require.NoError(t, vm.inspectDatabases()) } diff --git a/graft/coreth/sync/blocksync/syncer.go b/graft/coreth/sync/blocksync/syncer.go index 2f7340741234..9322708f2dde 100644 --- a/graft/coreth/sync/blocksync/syncer.go +++ b/graft/coreth/sync/blocksync/syncer.go @@ -13,6 +13,8 @@ import ( "github.com/ava-labs/libevm/ethdb" "github.com/ava-labs/libevm/log" + "github.com/ava-labs/avalanchego/vms/evm/database/blockdb" + syncpkg "github.com/ava-labs/avalanchego/graft/coreth/sync" statesyncclient "github.com/ava-labs/avalanchego/graft/coreth/sync/client" ) @@ -75,6 +77,19 @@ func (s *BlockSyncer) Sync(ctx context.Context) error { nextHeight := s.fromHeight blocksToFetch := s.blocksToFetch + // Init blockdb with the first block we will be fetching + firstBlockToFetch := nextHeight - blocksToFetch + 1 + if db, ok := s.db.(*blockdb.Database); ok { + log.Info( + "Initializing block databases on block syncer", + "nextHeight", nextHeight, + "minHeight", firstBlockToFetch, + ) + if err := db.InitBlockDBs(firstBlockToFetch); err != nil { + return err + } + } + // first, check for blocks already available on disk so we don't // request them from peers. for blocksToFetch > 0 {