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..8720d715b845 100644 --- a/graft/coreth/plugin/evm/vm_database.go +++ b/graft/coreth/plugin/evm/vm_database.go @@ -4,25 +4,29 @@ package evm import ( + "context" + "errors" + "path/filepath" + "strconv" "time" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/rawdb" + "github.com/ava-labs/libevm/ethdb" "github.com/ava-labs/libevm/log" "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) { - // 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))) +func (vm *VM) initializeDBs(db avalanchedatabase.Database) error { vm.versiondb = versiondb.New(db) vm.acceptedBlockDB = prefixdb.New(acceptedPrefix, vm.versiondb) vm.metadataDB = prefixdb.New(metadataPrefix, vm.versiondb) @@ -30,6 +34,15 @@ 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) + + // newChainDB must be created after acceptedBlockDB because it uses it to + // determine if state sync is enabled. + chaindb, err := vm.newChainDB(db) + if err != nil { + return err + } + vm.chaindb = chaindb + return nil } func (vm *VM) inspectDatabases() error { @@ -80,3 +93,53 @@ func inspectDB(db avalanchedatabase.Database, label string) error { log.Info("Database statistics", "label", label, "total", total.String(), "count", count) return nil } + +// newChainDB creates a new chain database. +// If block database is enabled, it will create a blockdb.Database that +// stores blocks data in separate databases. +// If block database is not enabled but had been previously enabled, it will return an error. +func (vm *VM) newChainDB(db avalanchedatabase.Database) (ethdb.Database, error) { + // Use NewNested rather than New so that the structure of the database + // remains the same regardless of the provided baseDB type. + chainDB := rawdb.NewDatabase(database.New(prefixdb.NewNested(ethDBPrefix, db))) + + // Error if block database has been created and then disabled + metaDB := prefixdb.New(blockDBPrefix, db) + enabled, err := blockdb.IsEnabled(metaDB) + if err != nil { + return nil, err + } + if !vm.config.BlockDatabaseEnabled { + if enabled { + return nil, errors.New("block database should not be disabled after it has been enabled") + } + return chainDB, nil + } + + version := strconv.FormatUint(heightindexdb.IndexFileVersion, 10) + path := filepath.Join(vm.ctx.ChainDataDir, "blockdb", version) + _, lastAcceptedHeight, err := vm.ReadLastAccepted() + if err != nil { + return nil, err + } + stateSyncEnabled := vm.stateSyncEnabled(lastAcceptedHeight) + cfg := heightindexdb.DefaultConfig().WithSyncToDisk(false) + blockDB, initialized, err := blockdb.New( + metaDB, + chainDB, + path, + stateSyncEnabled, + cfg, + vm.ctx.Log, + vm.sdkMetrics, + ) + if err != nil { + return nil, err + } + if initialized && !vm.config.SkipBlockDatabaseAutoMigrate { + if err := blockDB.StartMigration(context.Background()); err != nil { + return nil, err + } + } + return blockDB, 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 {