From a250a313adabf2788701a82594d4b9c1b05c44d6 Mon Sep 17 00:00:00 2001 From: Draco Date: Tue, 25 Nov 2025 16:46:37 -0500 Subject: [PATCH] feat: add config to enable block database --- graft/coreth/plugin/evm/config/config.go | 6 ++- graft/coreth/plugin/evm/vm.go | 5 +- graft/coreth/plugin/evm/vm_database.go | 69 +++++++++++++++++++++++- graft/coreth/sync/blocksync/syncer.go | 14 ++++- 4 files changed, 90 insertions(+), 4 deletions(-) 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..06d33abfa6cc 100644 --- a/graft/coreth/plugin/evm/vm_database.go +++ b/graft/coreth/plugin/evm/vm_database.go @@ -4,22 +4,30 @@ package evm import ( + "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" ) +const blockDBFolder = "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 +38,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 +97,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 enabled/created and then disabled + stateDB := prefixdb.New(blockDBPrefix, db) + enabled, err := blockdb.IsEnabled(stateDB) + 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) + dbPath := filepath.Join(vm.ctx.ChainDataDir, blockDBFolder, version) + _, lastAcceptedHeight, err := vm.ReadLastAccepted() + if err != nil { + return nil, err + } + stateSyncEnabled := vm.stateSyncEnabled(lastAcceptedHeight) + config := heightindexdb.DefaultConfig().WithSyncToDisk(false) + blockDB, initialized, err := blockdb.New( + stateDB, + chainDB, + dbPath, + stateSyncEnabled, + config, + vm.ctx.Log, + vm.sdkMetrics, + ) + if err != nil { + return nil, err + } + if initialized && !vm.config.SkipBlockDatabaseAutoMigrate { + if err := blockDB.StartMigration(); err != nil { + return nil, err + } + } + return blockDB, nil +} diff --git a/graft/coreth/sync/blocksync/syncer.go b/graft/coreth/sync/blocksync/syncer.go index 2f7340741234..c0cc5d1ba9f5 100644 --- a/graft/coreth/sync/blocksync/syncer.go +++ b/graft/coreth/sync/blocksync/syncer.go @@ -8,6 +8,7 @@ import ( "errors" "fmt" + "github.com/ava-labs/avalanchego/vms/evm/database/blockdb" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/rawdb" "github.com/ava-labs/libevm/ethdb" @@ -75,10 +76,21 @@ 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, + ) + db.InitBlockDBs(firstBlockToFetch) + } + // first, check for blocks already available on disk so we don't // request them from peers. for blocksToFetch > 0 { - blk := rawdb.ReadBlock(s.db, nextHash, nextHeight) + blk := blockdb.ReadBlock(s.db, nextHash, nextHeight) if blk == nil { // block was not found break