diff --git a/graft/coreth/core/blockchain.go b/graft/coreth/core/blockchain.go index f39e82e05bdb..b1f6a70921c6 100644 --- a/graft/coreth/core/blockchain.go +++ b/graft/coreth/core/blockchain.go @@ -40,17 +40,18 @@ import ( "sync/atomic" "time" + "github.com/ava-labs/avalanchego/database" "github.com/ava-labs/avalanchego/graft/coreth/consensus" "github.com/ava-labs/avalanchego/graft/coreth/core/extstate" "github.com/ava-labs/avalanchego/graft/coreth/core/state/snapshot" "github.com/ava-labs/avalanchego/graft/coreth/internal/version" "github.com/ava-labs/avalanchego/graft/coreth/params" - "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/customrawdb" "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/customtypes" "github.com/ava-labs/avalanchego/graft/coreth/triedb/firewood" "github.com/ava-labs/avalanchego/graft/coreth/triedb/hashdb" "github.com/ava-labs/avalanchego/graft/coreth/triedb/pathdb" "github.com/ava-labs/avalanchego/vms/evm/acp176" + "github.com/ava-labs/avalanchego/vms/evm/sync/customrawdb" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/common/lru" "github.com/ava-labs/libevm/consensus/misc/eip4844" @@ -488,7 +489,10 @@ func NewBlockChain( // if txlookup limit is 0 (uindexing disabled), we don't need to repair the tx index tail. if bc.cacheConfig.TransactionHistory != 0 { - latestStateSynced := customrawdb.GetLatestSyncPerformed(bc.db) + latestStateSynced, err := customrawdb.GetLatestSyncPerformed(bc.db) + if err != nil { + return nil, err + } bc.repairTxIndexTail(latestStateSynced) } @@ -1832,7 +1836,11 @@ func (bc *BlockChain) initSnapshot(b *types.Header) { func (bc *BlockChain) reprocessState(current *types.Block, reexec uint64) error { origin := current.NumberU64() acceptorTip, err := customrawdb.ReadAcceptorTip(bc.db) - if err != nil { + // If no acceptor tip exists, treat it as empty hash (not initialized). + switch { + case errors.Is(err, database.ErrNotFound): + acceptorTip = common.Hash{} + case err != nil: return fmt.Errorf("%w: unable to get Acceptor tip", err) } log.Info("Loaded Acceptor tip", "hash", acceptorTip) @@ -2060,7 +2068,7 @@ func (bc *BlockChain) populateMissingTries() error { // Write marker to DB to indicate populate missing tries finished successfully. // Note: writing the marker here means that we do allow consecutive runs of re-populating // missing tries if it does not finish during the prior run. - if err := customrawdb.WritePopulateMissingTries(bc.db); err != nil { + if err := customrawdb.WritePopulateMissingTries(bc.db, time.Now()); err != nil { return fmt.Errorf("failed to write offline pruning success marker: %w", err) } diff --git a/graft/coreth/core/blockchain_repair_test.go b/graft/coreth/core/blockchain_repair_test.go index 0f72012b70d4..817e3f997564 100644 --- a/graft/coreth/core/blockchain_repair_test.go +++ b/graft/coreth/core/blockchain_repair_test.go @@ -37,8 +37,8 @@ import ( "github.com/ava-labs/avalanchego/graft/coreth/consensus/dummy" "github.com/ava-labs/avalanchego/graft/coreth/params" - "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/customrawdb" "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/upgrade/ap3" + "github.com/ava-labs/avalanchego/vms/evm/sync/customrawdb" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/rawdb" "github.com/ava-labs/libevm/core/types" diff --git a/graft/coreth/core/blockchain_test.go b/graft/coreth/core/blockchain_test.go index 64765e2e2c44..7c5b2639c672 100644 --- a/graft/coreth/core/blockchain_test.go +++ b/graft/coreth/core/blockchain_test.go @@ -36,9 +36,9 @@ import ( "github.com/ava-labs/avalanchego/graft/coreth/consensus/dummy" "github.com/ava-labs/avalanchego/graft/coreth/core/state/pruner" "github.com/ava-labs/avalanchego/graft/coreth/params" - "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/customrawdb" "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/upgrade/ap3" "github.com/ava-labs/avalanchego/upgrade" + "github.com/ava-labs/avalanchego/vms/evm/sync/customrawdb" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/rawdb" "github.com/ava-labs/libevm/core/types" diff --git a/graft/coreth/core/genesis_test.go b/graft/coreth/core/genesis_test.go index 3861254fab74..cb8aa520c314 100644 --- a/graft/coreth/core/genesis_test.go +++ b/graft/coreth/core/genesis_test.go @@ -38,12 +38,12 @@ import ( "github.com/ava-labs/avalanchego/graft/coreth/consensus/dummy" "github.com/ava-labs/avalanchego/graft/coreth/params" "github.com/ava-labs/avalanchego/graft/coreth/params/extras" - "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/customrawdb" "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/upgrade/ap3" "github.com/ava-labs/avalanchego/graft/coreth/precompile/contracts/warp" "github.com/ava-labs/avalanchego/graft/coreth/triedb/firewood" "github.com/ava-labs/avalanchego/graft/coreth/triedb/pathdb" "github.com/ava-labs/avalanchego/graft/coreth/utils" + "github.com/ava-labs/avalanchego/vms/evm/sync/customrawdb" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/rawdb" "github.com/ava-labs/libevm/core/types" diff --git a/graft/coreth/core/state/pruner/pruner.go b/graft/coreth/core/state/pruner/pruner.go index c71e91bc36b6..2fa9e51b1125 100644 --- a/graft/coreth/core/state/pruner/pruner.go +++ b/graft/coreth/core/state/pruner/pruner.go @@ -39,7 +39,7 @@ import ( "time" "github.com/ava-labs/avalanchego/graft/coreth/core/state/snapshot" - "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/customrawdb" + "github.com/ava-labs/avalanchego/vms/evm/sync/customrawdb" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/rawdb" "github.com/ava-labs/libevm/core/types" @@ -219,7 +219,7 @@ func prune(maindb ethdb.Database, stateBloom *stateBloom, bloomPath string, star // Write marker to DB to indicate offline pruning finished successfully. We write before calling os.RemoveAll // to guarantee that if the node dies midway through pruning, then this will run during RecoverPruning. - if err := customrawdb.WriteOfflinePruning(maindb); err != nil { + if err := customrawdb.WriteOfflinePruning(maindb, time.Now()); err != nil { return fmt.Errorf("failed to write offline pruning success marker: %w", err) } diff --git a/graft/coreth/core/state/snapshot/disklayer_test.go b/graft/coreth/core/state/snapshot/disklayer_test.go index 035b5cf3c718..73545d9c1b98 100644 --- a/graft/coreth/core/state/snapshot/disklayer_test.go +++ b/graft/coreth/core/state/snapshot/disklayer_test.go @@ -31,7 +31,7 @@ import ( "bytes" "testing" - "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/customrawdb" + "github.com/ava-labs/avalanchego/vms/evm/sync/customrawdb" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/rawdb" "github.com/ava-labs/libevm/ethdb/memorydb" diff --git a/graft/coreth/core/state/snapshot/generate.go b/graft/coreth/core/state/snapshot/generate.go index eb696d1fabaa..dc1020ad41a9 100644 --- a/graft/coreth/core/state/snapshot/generate.go +++ b/graft/coreth/core/state/snapshot/generate.go @@ -32,8 +32,8 @@ import ( "fmt" "time" - "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/customrawdb" "github.com/ava-labs/avalanchego/graft/coreth/utils" + "github.com/ava-labs/avalanchego/vms/evm/sync/customrawdb" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/rawdb" "github.com/ava-labs/libevm/core/types" diff --git a/graft/coreth/core/state/snapshot/journal.go b/graft/coreth/core/state/snapshot/journal.go index c3116160a18c..1de91725de90 100644 --- a/graft/coreth/core/state/snapshot/journal.go +++ b/graft/coreth/core/state/snapshot/journal.go @@ -33,7 +33,7 @@ import ( "fmt" "time" - "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/customrawdb" + "github.com/ava-labs/avalanchego/vms/evm/sync/customrawdb" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/rawdb" "github.com/ava-labs/libevm/ethdb" @@ -59,20 +59,19 @@ type journalGenerator struct { // store. If loading the snapshot from disk is successful, this function also // returns a boolean indicating whether or not the snapshot is fully generated. func loadSnapshot(diskdb ethdb.KeyValueStore, triedb *triedb.Database, cache int, blockHash, root common.Hash, noBuild bool) (snapshot, bool, error) { - // Retrieve the block number and hash of the snapshot, failing if no snapshot - // is present in the database (or crashed mid-update). - baseBlockHash := customrawdb.ReadSnapshotBlockHash(diskdb) - if baseBlockHash == (common.Hash{}) { - return nil, false, errors.New("missing or corrupted snapshot, no snapshot block hash") + // Retrieve the block hash of the snapshot, failing if no snapshot is present. + baseBlockHash, err := customrawdb.ReadSnapshotBlockHash(diskdb) + if err != nil { + return nil, false, fmt.Errorf("missing or corrupted snapshot, no snapshot block hash: %v", err) } if baseBlockHash != blockHash { return nil, false, fmt.Errorf("block hash stored on disk (%#x) does not match last accepted (%#x)", baseBlockHash, blockHash) } baseRoot := rawdb.ReadSnapshotRoot(diskdb) - if baseRoot == (common.Hash{}) { + switch { + case baseRoot == (common.Hash{}): return nil, false, errors.New("missing or corrupted snapshot, no snapshot root") - } - if baseRoot != root { + case baseRoot != root: return nil, false, fmt.Errorf("root stored on disk (%#x) does not match last accepted (%#x)", baseRoot, root) } diff --git a/graft/coreth/core/state/snapshot/snapshot.go b/graft/coreth/core/state/snapshot/snapshot.go index 6bc7a3dc6188..dc66972d5a04 100644 --- a/graft/coreth/core/state/snapshot/snapshot.go +++ b/graft/coreth/core/state/snapshot/snapshot.go @@ -35,7 +35,7 @@ import ( "sync" "time" - "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/customrawdb" + "github.com/ava-labs/avalanchego/vms/evm/sync/customrawdb" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/rawdb" ethsnapshot "github.com/ava-labs/libevm/core/state/snapshot" diff --git a/graft/coreth/core/state/snapshot/wipe.go b/graft/coreth/core/state/snapshot/wipe.go index 5a27ab92ea28..d42723107999 100644 --- a/graft/coreth/core/state/snapshot/wipe.go +++ b/graft/coreth/core/state/snapshot/wipe.go @@ -31,7 +31,7 @@ import ( "bytes" "time" - "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/customrawdb" + "github.com/ava-labs/avalanchego/vms/evm/sync/customrawdb" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/rawdb" "github.com/ava-labs/libevm/ethdb" diff --git a/graft/coreth/core/state/snapshot/wipe_test.go b/graft/coreth/core/state/snapshot/wipe_test.go index 2ff04b74ea10..5cf603cccbb7 100644 --- a/graft/coreth/core/state/snapshot/wipe_test.go +++ b/graft/coreth/core/state/snapshot/wipe_test.go @@ -28,10 +28,12 @@ package snapshot import ( + "errors" "math/rand" "testing" - "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/customrawdb" + "github.com/ava-labs/avalanchego/database" + "github.com/ava-labs/avalanchego/vms/evm/sync/customrawdb" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/rawdb" "github.com/ava-labs/libevm/ethdb/memorydb" @@ -72,8 +74,12 @@ func TestWipe(t *testing.T) { if items := count(); items != 128 { t.Fatalf("snapshot size mismatch: have %d, want %d", items, 128) } - if hash := customrawdb.ReadSnapshotBlockHash(db); hash == (common.Hash{}) { - t.Errorf("snapshot block hash marker mismatch: have %#x, want ", hash) + blockHash, err := customrawdb.ReadSnapshotBlockHash(db) + switch { + case err != nil: + t.Fatalf("failed to read snapshot block hash before wipe: %v", err) + case blockHash == (common.Hash{}): + t.Fatalf("snapshot block hash is empty before wipe") } if hash := rawdb.ReadSnapshotRoot(db); hash == (common.Hash{}) { t.Errorf("snapshot block root marker mismatch: have %#x, want ", hash) @@ -96,8 +102,14 @@ func TestWipe(t *testing.T) { t.Fatalf("misc item count mismatch: have %d, want %d", items, 1000) } - if hash := customrawdb.ReadSnapshotBlockHash(db); hash != (common.Hash{}) { - t.Errorf("snapshot block hash marker remained after wipe: %#x", hash) + // Verify snapshot markers are removed. + blockHash, err = customrawdb.ReadSnapshotBlockHash(db) + switch { + case errors.Is(err, database.ErrNotFound): // Expected: marker was deleted. + case err != nil: + t.Errorf("unexpected error reading snapshot block hash after wipe: %v", err) + case blockHash != (common.Hash{}): + t.Errorf("snapshot block hash marker remained after wipe: %#x", blockHash) } if hash := rawdb.ReadSnapshotRoot(db); hash != (common.Hash{}) { t.Errorf("snapshot block root marker remained after wipe: %#x", hash) diff --git a/graft/coreth/core/state_manager.go b/graft/coreth/core/state_manager.go index f66809b03395..d83302ffbc53 100644 --- a/graft/coreth/core/state_manager.go +++ b/graft/coreth/core/state_manager.go @@ -30,7 +30,7 @@ package core import ( "fmt" - "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/customrawdb" + "github.com/ava-labs/avalanchego/vms/evm/sync/customrawdb" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/types" "github.com/ava-labs/libevm/ethdb" diff --git a/graft/coreth/eth/api_debug.go b/graft/coreth/eth/api_debug.go index f94748e9399e..742da486d195 100644 --- a/graft/coreth/eth/api_debug.go +++ b/graft/coreth/eth/api_debug.go @@ -34,8 +34,8 @@ import ( "time" "github.com/ava-labs/avalanchego/graft/coreth/internal/ethapi" - "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/customrawdb" "github.com/ava-labs/avalanchego/graft/coreth/rpc" + "github.com/ava-labs/avalanchego/vms/evm/sync/customrawdb" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/common/hexutil" "github.com/ava-labs/libevm/core/rawdb" diff --git a/graft/coreth/eth/backend.go b/graft/coreth/eth/backend.go index 5052055b431f..e1cfa77acf4d 100644 --- a/graft/coreth/eth/backend.go +++ b/graft/coreth/eth/backend.go @@ -49,9 +49,9 @@ import ( "github.com/ava-labs/avalanchego/graft/coreth/miner" "github.com/ava-labs/avalanchego/graft/coreth/node" "github.com/ava-labs/avalanchego/graft/coreth/params" - "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/customrawdb" "github.com/ava-labs/avalanchego/graft/coreth/rpc" "github.com/ava-labs/avalanchego/utils/timer/mockable" + "github.com/ava-labs/avalanchego/vms/evm/sync/customrawdb" "github.com/ava-labs/libevm/accounts" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/bloombits" @@ -151,7 +151,7 @@ func New( "snapshot clean", common.StorageSize(config.SnapshotCache)*1024*1024, ) - scheme, err := customrawdb.ParseStateSchemeExt(config.StateScheme, chainDb) + scheme, err := customrawdb.ParseStateScheme(config.StateScheme, chainDb) if err != nil { return nil, err } diff --git a/graft/coreth/eth/filters/filter_system_test.go b/graft/coreth/eth/filters/filter_system_test.go index c5822fefbdf6..8c201efa5340 100644 --- a/graft/coreth/eth/filters/filter_system_test.go +++ b/graft/coreth/eth/filters/filter_system_test.go @@ -43,9 +43,9 @@ import ( "github.com/ava-labs/avalanchego/graft/coreth/core" "github.com/ava-labs/avalanchego/graft/coreth/internal/ethapi" "github.com/ava-labs/avalanchego/graft/coreth/params" - "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/customrawdb" "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/customtypes" "github.com/ava-labs/avalanchego/graft/coreth/rpc" + "github.com/ava-labs/avalanchego/vms/evm/sync/customrawdb" ethereum "github.com/ava-labs/libevm" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/bloombits" diff --git a/graft/coreth/eth/filters/filter_test.go b/graft/coreth/eth/filters/filter_test.go index 95b2c9177d58..086ff4883557 100644 --- a/graft/coreth/eth/filters/filter_test.go +++ b/graft/coreth/eth/filters/filter_test.go @@ -39,8 +39,8 @@ import ( "github.com/ava-labs/avalanchego/graft/coreth/consensus/dummy" "github.com/ava-labs/avalanchego/graft/coreth/core" "github.com/ava-labs/avalanchego/graft/coreth/params" - "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/customrawdb" "github.com/ava-labs/avalanchego/graft/coreth/rpc" + "github.com/ava-labs/avalanchego/vms/evm/sync/customrawdb" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/rawdb" "github.com/ava-labs/libevm/core/types" diff --git a/graft/coreth/eth/state_accessor.go b/graft/coreth/eth/state_accessor.go index be6f4eaa30d5..d3fd5dfb9461 100644 --- a/graft/coreth/eth/state_accessor.go +++ b/graft/coreth/eth/state_accessor.go @@ -36,7 +36,7 @@ import ( "github.com/ava-labs/avalanchego/graft/coreth/core" "github.com/ava-labs/avalanchego/graft/coreth/core/extstate" "github.com/ava-labs/avalanchego/graft/coreth/eth/tracers" - "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/customrawdb" + "github.com/ava-labs/avalanchego/vms/evm/sync/customrawdb" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/rawdb" "github.com/ava-labs/libevm/core/state" diff --git a/graft/coreth/eth/tracers/api_test.go b/graft/coreth/eth/tracers/api_test.go index 44ffb7a20483..782b58bf95e6 100644 --- a/graft/coreth/eth/tracers/api_test.go +++ b/graft/coreth/eth/tracers/api_test.go @@ -43,8 +43,8 @@ import ( "github.com/ava-labs/avalanchego/graft/coreth/core" "github.com/ava-labs/avalanchego/graft/coreth/internal/ethapi" "github.com/ava-labs/avalanchego/graft/coreth/params" - "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/customrawdb" "github.com/ava-labs/avalanchego/graft/coreth/rpc" + "github.com/ava-labs/avalanchego/vms/evm/sync/customrawdb" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/common/hexutil" "github.com/ava-labs/libevm/core/rawdb" diff --git a/graft/coreth/eth/tracers/tracers_test.go b/graft/coreth/eth/tracers/tracers_test.go index 776f1017bdf2..f3a70fdc4baf 100644 --- a/graft/coreth/eth/tracers/tracers_test.go +++ b/graft/coreth/eth/tracers/tracers_test.go @@ -34,9 +34,9 @@ import ( "github.com/ava-labs/avalanchego/graft/coreth/core" "github.com/ava-labs/avalanchego/graft/coreth/params" - "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/customrawdb" "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/customtypes" "github.com/ava-labs/avalanchego/graft/coreth/tests" + "github.com/ava-labs/avalanchego/vms/evm/sync/customrawdb" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/rawdb" "github.com/ava-labs/libevm/core/types" diff --git a/graft/coreth/go.mod b/graft/coreth/go.mod index 8ed6774b6dc6..ea9a4599f48c 100644 --- a/graft/coreth/go.mod +++ b/graft/coreth/go.mod @@ -15,7 +15,7 @@ go 1.24.9 require ( github.com/VictoriaMetrics/fastcache v1.12.1 - github.com/ava-labs/avalanchego v1.14.1-0.20251120155522-df4a8e531761 + github.com/ava-labs/avalanchego v1.14.1-0.20251126155231-ee44f6856445 github.com/ava-labs/firewood-go-ethhash/ffi v0.0.15 github.com/ava-labs/libevm v1.13.15-0.20251016142715-1bccf4f2ddb2 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc diff --git a/graft/coreth/plugin/evm/customrawdb/accessors_metadata_ext.go b/graft/coreth/plugin/evm/customrawdb/accessors_metadata_ext.go deleted file mode 100644 index 709ea157ffdb..000000000000 --- a/graft/coreth/plugin/evm/customrawdb/accessors_metadata_ext.go +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package customrawdb - -import ( - "fmt" - "time" - - "github.com/ava-labs/libevm/common" - "github.com/ava-labs/libevm/ethdb" - "github.com/ava-labs/libevm/rlp" -) - -// writeCurrentTimeMarker writes a marker of the current time in the db at `key`. -func writeCurrentTimeMarker(db ethdb.KeyValueStore, key []byte) error { - data, err := rlp.EncodeToBytes(uint64(time.Now().Unix())) - if err != nil { - return err - } - return db.Put(key, data) -} - -// readTimeMarker reads the timestamp stored at `key` -func readTimeMarker(db ethdb.KeyValueStore, key []byte) (time.Time, error) { - data, err := db.Get(key) - if err != nil { - return time.Time{}, err - } - - var unix uint64 - if err := rlp.DecodeBytes(data, &unix); err != nil { - return time.Time{}, err - } - - return time.Unix(int64(unix), 0), nil -} - -// WriteOfflinePruning writes a time marker of the last attempt to run offline pruning. -// The marker is written when offline pruning completes and is deleted when the node -// is started successfully with offline pruning disabled. This ensures users must -// disable offline pruning and start their node successfully between runs of offline -// pruning. -func WriteOfflinePruning(db ethdb.KeyValueStore) error { - return writeCurrentTimeMarker(db, offlinePruningKey) -} - -// ReadOfflinePruning reads the most recent timestamp of an attempt to run offline -// pruning if present. -func ReadOfflinePruning(db ethdb.KeyValueStore) (time.Time, error) { - return readTimeMarker(db, offlinePruningKey) -} - -// DeleteOfflinePruning deletes any marker of the last attempt to run offline pruning. -func DeleteOfflinePruning(db ethdb.KeyValueStore) error { - return db.Delete(offlinePruningKey) -} - -// WritePopulateMissingTries writes a marker for the current attempt to populate -// missing tries. -func WritePopulateMissingTries(db ethdb.KeyValueStore) error { - return writeCurrentTimeMarker(db, populateMissingTriesKey) -} - -// ReadPopulateMissingTries reads the most recent timestamp of an attempt to -// re-populate missing trie nodes. -func ReadPopulateMissingTries(db ethdb.KeyValueStore) (time.Time, error) { - return readTimeMarker(db, populateMissingTriesKey) -} - -// DeletePopulateMissingTries deletes any marker of the last attempt to -// re-populate missing trie nodes. -func DeletePopulateMissingTries(db ethdb.KeyValueStore) error { - return db.Delete(populateMissingTriesKey) -} - -// WritePruningDisabled writes a marker to track whether the node has ever run -// with pruning disabled. -func WritePruningDisabled(db ethdb.KeyValueStore) error { - return db.Put(pruningDisabledKey, nil) -} - -// HasPruningDisabled returns true if there is a marker present indicating that -// the node has run with pruning disabled at some pooint. -func HasPruningDisabled(db ethdb.KeyValueStore) (bool, error) { - return db.Has(pruningDisabledKey) -} - -// WriteAcceptorTip writes `hash` as the last accepted block that has been fully processed. -func WriteAcceptorTip(db ethdb.KeyValueWriter, hash common.Hash) error { - return db.Put(acceptorTipKey, hash[:]) -} - -// ReadAcceptorTip reads the hash of the last accepted block that was fully processed. -// If there is no value present (the index is being initialized for the first time), then the -// empty hash is returned. -func ReadAcceptorTip(db ethdb.KeyValueReader) (common.Hash, error) { - has, err := db.Has(acceptorTipKey) - if err != nil { - return common.Hash{}, err - } - if !has { - // If the index is not present on disk, the [acceptorTipKey] index has not been initialized yet. - return common.Hash{}, nil - } - h, err := db.Get(acceptorTipKey) - if err != nil { - return common.Hash{}, err - } - if len(h) != common.HashLength { - return common.Hash{}, fmt.Errorf("value has incorrect length %d", len(h)) - } - return common.BytesToHash(h), nil -} diff --git a/graft/coreth/plugin/evm/customrawdb/accessors_snapshot_ext.go b/graft/coreth/plugin/evm/customrawdb/accessors_snapshot_ext.go deleted file mode 100644 index cd1ab1a92651..000000000000 --- a/graft/coreth/plugin/evm/customrawdb/accessors_snapshot_ext.go +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package customrawdb - -import ( - "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" -) - -// ReadSnapshotBlockHash retrieves the hash of the block whose state is contained in -// the persisted snapshot. -func ReadSnapshotBlockHash(db ethdb.KeyValueReader) common.Hash { - data, _ := db.Get(snapshotBlockHashKey) - if len(data) != common.HashLength { - return common.Hash{} - } - return common.BytesToHash(data) -} - -// WriteSnapshotBlockHash stores the root of the block whose state is contained in -// the persisted snapshot. -func WriteSnapshotBlockHash(db ethdb.KeyValueWriter, blockHash common.Hash) { - if err := db.Put(snapshotBlockHashKey, blockHash[:]); err != nil { - log.Crit("Failed to store snapshot block hash", "err", err) - } -} - -// DeleteSnapshotBlockHash deletes the hash of the block whose state is contained in -// the persisted snapshot. Since snapshots are not immutable, this method can -// be used during updates, so a crash or failure will mark the entire snapshot -// invalid. -func DeleteSnapshotBlockHash(db ethdb.KeyValueWriter) { - if err := db.Delete(snapshotBlockHashKey); err != nil { - log.Crit("Failed to remove snapshot block hash", "err", err) - } -} - -// IterateAccountSnapshots returns an iterator for walking all of the accounts in the snapshot -func IterateAccountSnapshots(db ethdb.Iteratee) ethdb.Iterator { - it := db.NewIterator(rawdb.SnapshotAccountPrefix, nil) - keyLen := len(rawdb.SnapshotAccountPrefix) + common.HashLength - return rawdb.NewKeyLengthIterator(it, keyLen) -} diff --git a/graft/coreth/plugin/evm/customrawdb/accessors_state_sync.go b/graft/coreth/plugin/evm/customrawdb/accessors_state_sync.go deleted file mode 100644 index 67f41771c33c..000000000000 --- a/graft/coreth/plugin/evm/customrawdb/accessors_state_sync.go +++ /dev/null @@ -1,223 +0,0 @@ -// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package customrawdb - -import ( - "encoding/binary" - - "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/utils/wrappers" -) - -// ReadSyncRoot reads the root corresponding to the main trie of an in-progress -// sync and returns common.Hash{} if no in-progress sync was found. -func ReadSyncRoot(db ethdb.KeyValueReader) (common.Hash, error) { - has, err := db.Has(syncRootKey) - if err != nil || !has { - return common.Hash{}, err - } - root, err := db.Get(syncRootKey) - if err != nil { - return common.Hash{}, err - } - return common.BytesToHash(root), nil -} - -// WriteSyncRoot writes root as the root of the main trie of the in-progress sync. -func WriteSyncRoot(db ethdb.KeyValueWriter, root common.Hash) error { - return db.Put(syncRootKey, root[:]) -} - -// AddCodeToFetch adds a marker that we need to fetch the code for `hash`. -func AddCodeToFetch(db ethdb.KeyValueWriter, hash common.Hash) { - if err := db.Put(codeToFetchKey(hash), nil); err != nil { - log.Crit("Failed to put code to fetch", "codeHash", hash, "err", err) - } -} - -// DeleteCodeToFetch removes the marker that the code corresponding to `hash` needs to be fetched. -func DeleteCodeToFetch(db ethdb.KeyValueWriter, hash common.Hash) { - if err := db.Delete(codeToFetchKey(hash)); err != nil { - log.Crit("Failed to delete code to fetch", "codeHash", hash, "err", err) - } -} - -// NewCodeToFetchIterator returns a KeyLength iterator over all code -// hashes that are pending syncing. It is the caller's responsibility to -// unpack the key and call Release on the returned iterator. -func NewCodeToFetchIterator(db ethdb.Iteratee) ethdb.Iterator { - return rawdb.NewKeyLengthIterator( - db.NewIterator(CodeToFetchPrefix, nil), - codeToFetchKeyLength, - ) -} - -func codeToFetchKey(hash common.Hash) []byte { - codeToFetchKey := make([]byte, codeToFetchKeyLength) - copy(codeToFetchKey, CodeToFetchPrefix) - copy(codeToFetchKey[len(CodeToFetchPrefix):], hash[:]) - return codeToFetchKey -} - -// NewSyncSegmentsIterator returns a KeyLength iterator over all trie segments -// added for root. It is the caller's responsibility to unpack the key and call -// Release on the returned iterator. -func NewSyncSegmentsIterator(db ethdb.Iteratee, root common.Hash) ethdb.Iterator { - segmentsPrefix := make([]byte, len(syncSegmentsPrefix)+common.HashLength) - copy(segmentsPrefix, syncSegmentsPrefix) - copy(segmentsPrefix[len(syncSegmentsPrefix):], root[:]) - - return rawdb.NewKeyLengthIterator( - db.NewIterator(segmentsPrefix, nil), - syncSegmentsKeyLength, - ) -} - -// WriteSyncSegment adds a trie segment for root at the given start position. -func WriteSyncSegment(db ethdb.KeyValueWriter, root common.Hash, start common.Hash) error { - return db.Put(packSyncSegmentKey(root, start), []byte{0x01}) -} - -// ClearSyncSegments removes segment markers for root from db -func ClearSyncSegments(db ethdb.KeyValueStore, root common.Hash) error { - segmentsPrefix := make([]byte, len(syncSegmentsPrefix)+common.HashLength) - copy(segmentsPrefix, syncSegmentsPrefix) - copy(segmentsPrefix[len(syncSegmentsPrefix):], root[:]) - return clearPrefix(db, segmentsPrefix, syncSegmentsKeyLength) -} - -// ClearAllSyncSegments removes all segment markers from db -func ClearAllSyncSegments(db ethdb.KeyValueStore) error { - return clearPrefix(db, syncSegmentsPrefix, syncSegmentsKeyLength) -} - -// UnpackSyncSegmentKey returns the root and start position for a trie segment -// key returned from NewSyncSegmentsIterator. -func UnpackSyncSegmentKey(keyBytes []byte) (common.Hash, []byte) { - keyBytes = keyBytes[len(syncSegmentsPrefix):] // skip prefix - root := common.BytesToHash(keyBytes[:common.HashLength]) - start := keyBytes[common.HashLength:] - return root, start -} - -// packSyncSegmentKey packs root and account into a key for storage in db. -func packSyncSegmentKey(root common.Hash, start common.Hash) []byte { - bytes := make([]byte, syncSegmentsKeyLength) - copy(bytes, syncSegmentsPrefix) - copy(bytes[len(syncSegmentsPrefix):], root[:]) - copy(bytes[len(syncSegmentsPrefix)+common.HashLength:], start.Bytes()) - return bytes -} - -// NewSyncStorageTriesIterator returns a KeyLength iterator over all storage tries -// added for syncing (beginning at seek). It is the caller's responsibility to unpack -// the key and call Release on the returned iterator. -func NewSyncStorageTriesIterator(db ethdb.Iteratee, seek []byte) ethdb.Iterator { - return rawdb.NewKeyLengthIterator(db.NewIterator(syncStorageTriesPrefix, seek), syncStorageTriesKeyLength) -} - -// WriteSyncStorageTrie adds a storage trie for account (with the given root) to be synced. -func WriteSyncStorageTrie(db ethdb.KeyValueWriter, root common.Hash, account common.Hash) error { - return db.Put(packSyncStorageTrieKey(root, account), []byte{0x01}) -} - -// ClearSyncStorageTrie removes all storage trie accounts (with the given root) from db. -// Intended for use when the trie with root has completed syncing. -func ClearSyncStorageTrie(db ethdb.KeyValueStore, root common.Hash) error { - accountsPrefix := make([]byte, len(syncStorageTriesPrefix)+common.HashLength) - copy(accountsPrefix, syncStorageTriesPrefix) - copy(accountsPrefix[len(syncStorageTriesPrefix):], root[:]) - return clearPrefix(db, accountsPrefix, syncStorageTriesKeyLength) -} - -// ClearAllSyncStorageTries removes all storage tries added for syncing from db -func ClearAllSyncStorageTries(db ethdb.KeyValueStore) error { - return clearPrefix(db, syncStorageTriesPrefix, syncStorageTriesKeyLength) -} - -// UnpackSyncStorageTrieKey returns the root and account for a storage trie -// key returned from NewSyncStorageTriesIterator. -func UnpackSyncStorageTrieKey(keyBytes []byte) (common.Hash, common.Hash) { - keyBytes = keyBytes[len(syncStorageTriesPrefix):] // skip prefix - root := common.BytesToHash(keyBytes[:common.HashLength]) - account := common.BytesToHash(keyBytes[common.HashLength:]) - return root, account -} - -// packSyncStorageTrieKey packs root and account into a key for storage in db. -func packSyncStorageTrieKey(root common.Hash, account common.Hash) []byte { - bytes := make([]byte, 0, syncStorageTriesKeyLength) - bytes = append(bytes, syncStorageTriesPrefix...) - bytes = append(bytes, root[:]...) - bytes = append(bytes, account[:]...) - return bytes -} - -// WriteSyncPerformed logs an entry in `db` indicating the VM state synced to `blockNumber`. -func WriteSyncPerformed(db ethdb.KeyValueWriter, blockNumber uint64) error { - syncPerformedPrefixLen := len(syncPerformedPrefix) - bytes := make([]byte, syncPerformedPrefixLen+wrappers.LongLen) - copy(bytes[:syncPerformedPrefixLen], syncPerformedPrefix) - binary.BigEndian.PutUint64(bytes[syncPerformedPrefixLen:], blockNumber) - return db.Put(bytes, []byte{0x01}) -} - -// NewSyncPerformedIterator returns an iterator over all block numbers the VM -// has state synced to. -func NewSyncPerformedIterator(db ethdb.Iteratee) ethdb.Iterator { - return rawdb.NewKeyLengthIterator(db.NewIterator(syncPerformedPrefix, nil), syncPerformedKeyLength) -} - -// UnpackSyncPerformedKey returns the block number from keys the iterator returned -// from NewSyncPerformedIterator. -func UnpackSyncPerformedKey(key []byte) uint64 { - return binary.BigEndian.Uint64(key[len(syncPerformedPrefix):]) -} - -// GetLatestSyncPerformed returns the latest block number state synced performed to. -func GetLatestSyncPerformed(db ethdb.Iteratee) uint64 { - it := NewSyncPerformedIterator(db) - defer it.Release() - - var latestSyncPerformed uint64 - for it.Next() { - syncPerformed := UnpackSyncPerformedKey(it.Key()) - if syncPerformed > latestSyncPerformed { - latestSyncPerformed = syncPerformed - } - } - return latestSyncPerformed -} - -// clearPrefix removes all keys in db that begin with prefix and match an -// expected key length. `keyLen` must include the length of the prefix. -func clearPrefix(db ethdb.KeyValueStore, prefix []byte, keyLen int) error { - it := db.NewIterator(prefix, nil) - defer it.Release() - - batch := db.NewBatch() - for it.Next() { - key := common.CopyBytes(it.Key()) - if len(key) != keyLen { - continue - } - if err := batch.Delete(key); err != nil { - return err - } - if batch.ValueSize() > ethdb.IdealBatchSize { - if err := batch.Write(); err != nil { - return err - } - batch.Reset() - } - } - if err := it.Error(); err != nil { - return err - } - return batch.Write() -} diff --git a/graft/coreth/plugin/evm/customrawdb/accessors_state_sync_test.go b/graft/coreth/plugin/evm/customrawdb/accessors_state_sync_test.go deleted file mode 100644 index 6a57572e4f0a..000000000000 --- a/graft/coreth/plugin/evm/customrawdb/accessors_state_sync_test.go +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package customrawdb - -import ( - "slices" - "testing" - - "github.com/ava-labs/libevm/common" - "github.com/ava-labs/libevm/core/rawdb" - "github.com/stretchr/testify/require" -) - -func TestClearPrefix(t *testing.T) { - require := require.New(t) - db := rawdb.NewMemoryDatabase() - // add a key that should be cleared - require.NoError(WriteSyncSegment(db, common.Hash{1}, common.Hash{})) - - // add a key that should not be cleared - key := slices.Concat(syncSegmentsPrefix, []byte("foo")) - require.NoError(db.Put(key, []byte("bar"))) - - require.NoError(ClearAllSyncSegments(db)) - - count := 0 - it := db.NewIterator(syncSegmentsPrefix, nil) - defer it.Release() - for it.Next() { - count++ - } - require.NoError(it.Error()) - require.Equal(1, count) -} diff --git a/graft/coreth/plugin/evm/customrawdb/database_ext.go b/graft/coreth/plugin/evm/customrawdb/database_ext.go deleted file mode 100644 index c301a5bfff54..000000000000 --- a/graft/coreth/plugin/evm/customrawdb/database_ext.go +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package customrawdb - -import ( - "bytes" - "fmt" - - "github.com/ava-labs/libevm/common" - "github.com/ava-labs/libevm/core/rawdb" - "github.com/ava-labs/libevm/ethdb" -) - -// InspectDatabase traverses the entire database and checks the size -// of all different categories of data. -func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error { - type stat = rawdb.DatabaseStat - stats := []struct { - name string - keyLen int - keyPrefix []byte - stat *stat - }{ - {"Trie segments", syncSegmentsKeyLength, syncSegmentsPrefix, &stat{}}, - {"Storage tries to fetch", syncStorageTriesKeyLength, syncStorageTriesPrefix, &stat{}}, - {"Code to fetch", codeToFetchKeyLength, CodeToFetchPrefix, &stat{}}, - {"Block numbers synced to", syncPerformedKeyLength, syncPerformedPrefix, &stat{}}, - } - - options := []rawdb.InspectDatabaseOption{ - rawdb.WithDatabaseMetadataKeys(func(key []byte) bool { - return bytes.Equal(key, snapshotBlockHashKey) || - bytes.Equal(key, syncRootKey) - }), - rawdb.WithDatabaseStatRecorder(func(key []byte, size common.StorageSize) bool { - for _, s := range stats { - if len(key) == s.keyLen && bytes.HasPrefix(key, s.keyPrefix) { - s.stat.Add(size) - return true - } - } - return false - }), - rawdb.WithDatabaseStatsTransformer(func(rows [][]string) [][]string { - newRows := make([][]string, 0, len(rows)) - for _, row := range rows { - switch db, cat := row[0], row[1]; { - // Discard rows specific to libevm (geth) but irrelevant to coreth. - case db == "Key-Value store" && (cat == "Difficulties" || cat == "Beacon sync headers"): - case db == "Ancient store (Chain)": - default: - newRows = append(newRows, row) - } - } - for _, s := range stats { - newRows = append(newRows, []string{"State sync", s.name, s.stat.Size(), s.stat.Count()}) - } - return newRows - }), - } - - return rawdb.InspectDatabase(db, keyPrefix, keyStart, options...) -} - -// ParseStateSchemeExt parses the state scheme from the provided string. -func ParseStateSchemeExt(provided string, disk ethdb.Database) (string, error) { - // Check for custom scheme - if provided == FirewoodScheme { - if diskScheme := rawdb.ReadStateScheme(disk); diskScheme != "" { - // Valid scheme on disk mismatched - return "", fmt.Errorf("state scheme %s already set on disk, can't use Firewood", diskScheme) - } - // If no conflicting scheme is found, is valid. - return FirewoodScheme, nil - } - - // Check for valid eth scheme - return rawdb.ParseStateScheme(provided, disk) -} diff --git a/graft/coreth/plugin/evm/customrawdb/database_ext_test.go b/graft/coreth/plugin/evm/customrawdb/database_ext_test.go deleted file mode 100644 index 2b786b7f8177..000000000000 --- a/graft/coreth/plugin/evm/customrawdb/database_ext_test.go +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package customrawdb - -import ( - "fmt" - - "github.com/ava-labs/libevm/common" - "github.com/ava-labs/libevm/core/rawdb" - "github.com/ava-labs/libevm/ethdb" -) - -func ExampleInspectDatabase() { - db := &stubDatabase{ - iterator: &stubIterator{}, - } - - // Extra metadata keys: (17 + 32) + (12 + 32) = 93 bytes - WriteSnapshotBlockHash(db, common.Hash{}) - rawdb.WriteSnapshotRoot(db, common.Hash{}) - // Trie segments: (77 + 2) + 1 = 80 bytes - _ = WriteSyncSegment(db, common.Hash{}, common.Hash{}) - // Storage tries to fetch: 76 + 1 = 77 bytes - _ = WriteSyncStorageTrie(db, common.Hash{}, common.Hash{}) - // Code to fetch: 34 + 0 = 34 bytes - AddCodeToFetch(db, common.Hash{}) - // Block numbers synced to: 22 + 1 = 23 bytes - _ = WriteSyncPerformed(db, 0) - - keyPrefix := []byte(nil) - keyStart := []byte(nil) - - err := InspectDatabase(db, keyPrefix, keyStart) - if err != nil { - fmt.Println(err) - } - // Output: - // +-----------------+-------------------------+----------+-------+ - // | DATABASE | CATEGORY | SIZE | ITEMS | - // +-----------------+-------------------------+----------+-------+ - // | Key-Value store | Headers | 0.00 B | 0 | - // | Key-Value store | Bodies | 0.00 B | 0 | - // | Key-Value store | Receipt lists | 0.00 B | 0 | - // | Key-Value store | Block number->hash | 0.00 B | 0 | - // | Key-Value store | Block hash->number | 0.00 B | 0 | - // | Key-Value store | Transaction index | 0.00 B | 0 | - // | Key-Value store | Bloombit index | 0.00 B | 0 | - // | Key-Value store | Contract codes | 0.00 B | 0 | - // | Key-Value store | Hash trie nodes | 0.00 B | 0 | - // | Key-Value store | Path trie state lookups | 0.00 B | 0 | - // | Key-Value store | Path trie account nodes | 0.00 B | 0 | - // | Key-Value store | Path trie storage nodes | 0.00 B | 0 | - // | Key-Value store | Trie preimages | 0.00 B | 0 | - // | Key-Value store | Account snapshot | 0.00 B | 0 | - // | Key-Value store | Storage snapshot | 0.00 B | 0 | - // | Key-Value store | Clique snapshots | 0.00 B | 0 | - // | Key-Value store | Singleton metadata | 93.00 B | 2 | - // | Light client | CHT trie nodes | 0.00 B | 0 | - // | Light client | Bloom trie nodes | 0.00 B | 0 | - // | State sync | Trie segments | 78.00 B | 1 | - // | State sync | Storage tries to fetch | 77.00 B | 1 | - // | State sync | Code to fetch | 34.00 B | 1 | - // | State sync | Block numbers synced to | 23.00 B | 1 | - // +-----------------+-------------------------+----------+-------+ - // | TOTAL | 305.00 B | | - // +-----------------+-------------------------+----------+-------+ -} - -type stubDatabase struct { - ethdb.Database - iterator *stubIterator -} - -func (s *stubDatabase) NewIterator(_, _ []byte) ethdb.Iterator { - return s.iterator -} - -// AncientSize is used in [InspectDatabase] to determine the ancient sizes. -func (*stubDatabase) AncientSize(string) (uint64, error) { - return 0, nil -} - -func (*stubDatabase) Ancients() (uint64, error) { - return 0, nil -} - -func (*stubDatabase) Tail() (uint64, error) { - return 0, nil -} - -func (s *stubDatabase) Put(key, value []byte) error { - s.iterator.kvs = append(s.iterator.kvs, keyValue{key: key, value: value}) - return nil -} - -func (*stubDatabase) Get([]byte) ([]byte, error) { - return nil, nil -} - -func (*stubDatabase) ReadAncients(func(ethdb.AncientReaderOp) error) error { - return nil -} - -type stubIterator struct { - ethdb.Iterator - i int // see [stubIterator.pos] - kvs []keyValue -} - -type keyValue struct { - key []byte - value []byte -} - -// pos returns the true iterator position, which is otherwise off by one because -// Next() is called _before_ usage. -func (s *stubIterator) pos() int { - return s.i - 1 -} - -func (s *stubIterator) Next() bool { - s.i++ - return s.pos() < len(s.kvs) -} - -func (*stubIterator) Release() {} - -func (s *stubIterator) Key() []byte { - return s.kvs[s.pos()].key -} - -func (s *stubIterator) Value() []byte { - return s.kvs[s.pos()].value -} diff --git a/graft/coreth/plugin/evm/customrawdb/schema_ext.go b/graft/coreth/plugin/evm/customrawdb/schema_ext.go deleted file mode 100644 index ed3f66f6200c..000000000000 --- a/graft/coreth/plugin/evm/customrawdb/schema_ext.go +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package customrawdb - -import ( - "github.com/ava-labs/libevm/common" - - "github.com/ava-labs/avalanchego/utils/wrappers" -) - -var ( - // snapshotBlockHashKey tracks the block hash of the last snapshot. - snapshotBlockHashKey = []byte("SnapshotBlockHash") - // offlinePruningKey tracks runs of offline pruning - offlinePruningKey = []byte("OfflinePruning") - // populateMissingTriesKey tracks runs of trie backfills - populateMissingTriesKey = []byte("PopulateMissingTries") - // pruningDisabledKey tracks whether the node has ever run in archival mode - // to ensure that a user does not accidentally corrupt an archival node. - pruningDisabledKey = []byte("PruningDisabled") - // acceptorTipKey tracks the tip of the last accepted block that has been fully processed. - acceptorTipKey = []byte("AcceptorTipKey") -) - -// State sync progress keys and prefixes -var ( - // syncRootKey indicates the root of the main account trie currently being synced - syncRootKey = []byte("sync_root") - // syncStorageTriesPrefix is the prefix for storage tries that need to be fetched. - // syncStorageTriesPrefix + trie root + account hash: indicates a storage trie must be fetched for the account - syncStorageTriesPrefix = []byte("sync_storage") - // syncSegmentsPrefix is the prefix for segments. - // syncSegmentsPrefix + trie root + 32-byte start key: indicates the trie at root has a segment starting at the specified key - syncSegmentsPrefix = []byte("sync_segments") - // CodeToFetchPrefix is the prefix for code hashes that need to be fetched. - // CodeToFetchPrefix + code hash -> empty value tracks the outstanding code hashes we need to fetch. - CodeToFetchPrefix = []byte("CP") -) - -// State sync progress key lengths -var ( - syncStorageTriesKeyLength = len(syncStorageTriesPrefix) + 2*common.HashLength - syncSegmentsKeyLength = len(syncSegmentsPrefix) + 2*common.HashLength - codeToFetchKeyLength = len(CodeToFetchPrefix) + common.HashLength -) - -// State sync metadata -var ( - syncPerformedPrefix = []byte("sync_performed") - // syncPerformedKeyLength is the length of the key for the sync performed metadata key, - // and is equal to [syncPerformedPrefix] + block number as uint64. - syncPerformedKeyLength = len(syncPerformedPrefix) + wrappers.LongLen -) - -var FirewoodScheme = "firewood" diff --git a/graft/coreth/plugin/evm/vm.go b/graft/coreth/plugin/evm/vm.go index 44fac1fa0d31..63e8642c7f8d 100644 --- a/graft/coreth/plugin/evm/vm.go +++ b/graft/coreth/plugin/evm/vm.go @@ -54,7 +54,6 @@ import ( "github.com/ava-labs/avalanchego/graft/coreth/params" "github.com/ava-labs/avalanchego/graft/coreth/params/extras" "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/config" - "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/customrawdb" "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/extension" "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/gossip" "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/message" @@ -80,6 +79,7 @@ import ( "github.com/ava-labs/avalanchego/vms/components/gas" "github.com/ava-labs/avalanchego/vms/evm/acp176" "github.com/ava-labs/avalanchego/vms/evm/acp226" + "github.com/ava-labs/avalanchego/vms/evm/sync/customrawdb" corethlog "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/log" warpcontract "github.com/ava-labs/avalanchego/graft/coreth/precompile/contracts/warp" diff --git a/graft/coreth/plugin/evm/vm_test.go b/graft/coreth/plugin/evm/vm_test.go index 157ece7c1513..ca4c6bcf655e 100644 --- a/graft/coreth/plugin/evm/vm_test.go +++ b/graft/coreth/plugin/evm/vm_test.go @@ -36,7 +36,6 @@ import ( "github.com/ava-labs/avalanchego/graft/coreth/params" "github.com/ava-labs/avalanchego/graft/coreth/params/paramstest" "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/customheader" - "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/customrawdb" "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/customtypes" "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/extension" "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/message" @@ -56,6 +55,7 @@ import ( "github.com/ava-labs/avalanchego/vms/evm/acp176" "github.com/ava-labs/avalanchego/vms/evm/acp226" "github.com/ava-labs/avalanchego/vms/evm/predicate" + "github.com/ava-labs/avalanchego/vms/evm/sync/customrawdb" "github.com/ava-labs/avalanchego/vms/platformvm/warp/payload" warpcontract "github.com/ava-labs/avalanchego/graft/coreth/precompile/contracts/warp" diff --git a/graft/coreth/plugin/evm/vmtest/test_syncervm.go b/graft/coreth/plugin/evm/vmtest/test_syncervm.go index b39703d99cca..2a4d745cb714 100644 --- a/graft/coreth/plugin/evm/vmtest/test_syncervm.go +++ b/graft/coreth/plugin/evm/vmtest/test_syncervm.go @@ -27,7 +27,6 @@ import ( "github.com/ava-labs/avalanchego/graft/coreth/core" "github.com/ava-labs/avalanchego/graft/coreth/core/coretest" "github.com/ava-labs/avalanchego/graft/coreth/params/paramstest" - "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/customrawdb" "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/customtypes" "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/extension" "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/vmsync" @@ -43,6 +42,7 @@ import ( "github.com/ava-labs/avalanchego/vms/components/chain" "github.com/ava-labs/avalanchego/vms/evm/database" "github.com/ava-labs/avalanchego/vms/evm/predicate" + "github.com/ava-labs/avalanchego/vms/evm/sync/customrawdb" avalancheatomic "github.com/ava-labs/avalanchego/chains/atomic" avalanchedatabase "github.com/ava-labs/avalanchego/database" @@ -497,7 +497,7 @@ func testSyncerVM(t *testing.T, testSyncVMSetup *testSyncVMSetup, test SyncTestP // TODO: this avoids circular dependencies but is not ideal. ethDBPrefix := []byte("ethdb") chaindb := database.New(prefixdb.NewNested(ethDBPrefix, testSyncVMSetup.syncerVM.DB)) - requireSyncPerformedHeights(t, chaindb, map[uint64]struct{}{}) + requireSyncPerformedHeight(t, chaindb, 0) return } require.NoError(err, "state sync failed") @@ -508,7 +508,8 @@ func testSyncerVM(t *testing.T, testSyncVMSetup *testSyncVMSetup, test SyncTestP require.Equal(serverVM.LastAcceptedExtendedBlock().Height(), syncerVM.LastAcceptedExtendedBlock().Height(), "block height mismatch between syncer and server") require.Equal(serverVM.LastAcceptedExtendedBlock().ID(), syncerVM.LastAcceptedExtendedBlock().ID(), "blockID mismatch between syncer and server") require.True(syncerVM.Ethereum().BlockChain().HasState(syncerVM.Ethereum().BlockChain().LastAcceptedBlock().Root()), "unavailable state for last accepted block") - requireSyncPerformedHeights(t, syncerVM.Ethereum().ChainDb(), map[uint64]struct{}{retrievedSummary.Height(): {}}) + expectedHeight := retrievedSummary.Height() + requireSyncPerformedHeight(t, syncerVM.Ethereum().ChainDb(), expectedHeight) lastNumber := syncerVM.Ethereum().BlockChain().LastAcceptedBlock().NumberU64() // check the last block is indexed @@ -653,16 +654,11 @@ func generateAndAcceptBlocks(t *testing.T, vm extension.InnerVM, numBlocks int, vm.Ethereum().BlockChain().DrainAcceptorQueue() } -// requireSyncPerformedHeights iterates over all heights the VM has synced to and -// verifies they all match the heights present in `expected`. -func requireSyncPerformedHeights(t *testing.T, db ethdb.Iteratee, expected map[uint64]struct{}) { - it := customrawdb.NewSyncPerformedIterator(db) - defer it.Release() - - found := make(map[uint64]struct{}, len(expected)) - for it.Next() { - found[customrawdb.UnpackSyncPerformedKey(it.Key())] = struct{}{} - } - require.NoError(t, it.Error()) - require.Equal(t, expected, found) +// requireSyncPerformedHeight verifies the latest sync performed height matches expectations. +// Pass 0 to verify no sync was performed. +func requireSyncPerformedHeight(t *testing.T, db ethdb.KeyValueStore, expected uint64) { + t.Helper() + latest, err := customrawdb.GetLatestSyncPerformed(db) + require.NoError(t, err) + require.Equal(t, expected, latest, "sync performed height mismatch: expected %d, got %d", expected, latest) } diff --git a/graft/coreth/plugin/evm/vmtest/test_vm.go b/graft/coreth/plugin/evm/vmtest/test_vm.go index abea48c7734e..0958fff38da0 100644 --- a/graft/coreth/plugin/evm/vmtest/test_vm.go +++ b/graft/coreth/plugin/evm/vmtest/test_vm.go @@ -15,12 +15,12 @@ import ( "github.com/ava-labs/avalanchego/api/metrics" "github.com/ava-labs/avalanchego/database/prefixdb" - "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/customrawdb" "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/extension" "github.com/ava-labs/avalanchego/snow" "github.com/ava-labs/avalanchego/snow/consensus/snowman" "github.com/ava-labs/avalanchego/snow/engine/enginetest" "github.com/ava-labs/avalanchego/upgrade/upgradetest" + "github.com/ava-labs/avalanchego/vms/evm/sync/customrawdb" avalancheatomic "github.com/ava-labs/avalanchego/chains/atomic" commoneng "github.com/ava-labs/avalanchego/snow/engine/common" diff --git a/graft/coreth/sync/statesync/code_queue.go b/graft/coreth/sync/statesync/code_queue.go index e61ff321f75a..d1d0e4327d4a 100644 --- a/graft/coreth/sync/statesync/code_queue.go +++ b/graft/coreth/sync/statesync/code_queue.go @@ -14,7 +14,7 @@ import ( "github.com/ava-labs/libevm/ethdb" "github.com/ava-labs/libevm/libevm/options" - "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/customrawdb" + "github.com/ava-labs/avalanchego/vms/evm/sync/customrawdb" ) const defaultQueueCapacity = 5000 @@ -136,7 +136,9 @@ func (q *CodeQueue) AddCode(ctx context.Context, codeHashes []common.Hash) error // key rather than growing DB usage. The consumer deletes the marker after // fulfilling the request (or when it detects code is already present). for _, codeHash := range codeHashes { - customrawdb.AddCodeToFetch(batch, codeHash) + if err := customrawdb.WriteCodeToFetch(batch, codeHash); err != nil { + return fmt.Errorf("failed to write code to fetch marker: %w", err) + } } if err := batch.Write(); err != nil { @@ -202,7 +204,9 @@ func recoverUnfetchedCodeHashes(db ethdb.Database) ([]common.Hash, error) { continue } - customrawdb.DeleteCodeToFetch(batch, codeHash) + if err := customrawdb.DeleteCodeToFetch(batch, codeHash); err != nil { + return nil, fmt.Errorf("failed to delete code to fetch marker: %w", err) + } if batch.ValueSize() < ethdb.IdealBatchSize { continue } diff --git a/graft/coreth/sync/statesync/code_queue_test.go b/graft/coreth/sync/statesync/code_queue_test.go index f0f3b6437d5d..d40a40b56682 100644 --- a/graft/coreth/sync/statesync/code_queue_test.go +++ b/graft/coreth/sync/statesync/code_queue_test.go @@ -16,8 +16,8 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/goleak" - "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/customrawdb" "github.com/ava-labs/avalanchego/utils/set" + "github.com/ava-labs/avalanchego/vms/evm/sync/customrawdb" ) func TestCodeQueue(t *testing.T) { @@ -86,7 +86,7 @@ func TestCodeQueue(t *testing.T) { rawdb.WriteCode(db, hash, code) } for hash := range tt.alreadyToFetch { - customrawdb.AddCodeToFetch(db, hash) + require.NoError(t, customrawdb.WriteCodeToFetch(db, hash)) } quit := make(chan struct{}) diff --git a/graft/coreth/sync/statesync/code_syncer.go b/graft/coreth/sync/statesync/code_syncer.go index 1ac8c47b5d71..022b8622c3ea 100644 --- a/graft/coreth/sync/statesync/code_syncer.go +++ b/graft/coreth/sync/statesync/code_syncer.go @@ -14,8 +14,8 @@ import ( "github.com/ava-labs/libevm/libevm/options" "golang.org/x/sync/errgroup" - "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/customrawdb" "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/message" + "github.com/ava-labs/avalanchego/vms/evm/sync/customrawdb" syncpkg "github.com/ava-labs/avalanchego/graft/coreth/sync" statesyncclient "github.com/ava-labs/avalanchego/graft/coreth/sync/client" @@ -145,7 +145,9 @@ func (c *CodeSyncer) work(ctx context.Context) error { if rawdb.HasCode(c.db, codeHash) { // Best-effort cleanup of stale marker. batch := c.db.NewBatch() - customrawdb.DeleteCodeToFetch(batch, codeHash) + if err := customrawdb.DeleteCodeToFetch(batch, codeHash); err != nil { + return fmt.Errorf("failed to delete stale code marker: %w", err) + } if err := batch.Write(); err != nil { return fmt.Errorf("failed to write batch for stale code marker: %w", err) @@ -182,7 +184,9 @@ func (c *CodeSyncer) fulfillCodeRequest(ctx context.Context, codeHashes []common batch := c.db.NewBatch() for i, codeHash := range codeHashes { - customrawdb.DeleteCodeToFetch(batch, codeHash) + if err := customrawdb.DeleteCodeToFetch(batch, codeHash); err != nil { + return fmt.Errorf("failed to delete code to fetch marker: %w", err) + } rawdb.WriteCode(batch, codeHash, codeByteSlices[i]) } diff --git a/graft/coreth/sync/statesync/code_syncer_test.go b/graft/coreth/sync/statesync/code_syncer_test.go index b285f4f4005e..bf77e46db9a9 100644 --- a/graft/coreth/sync/statesync/code_syncer_test.go +++ b/graft/coreth/sync/statesync/code_syncer_test.go @@ -15,10 +15,10 @@ import ( "github.com/ava-labs/libevm/ethdb/memorydb" "github.com/stretchr/testify/require" - "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/customrawdb" "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/message" "github.com/ava-labs/avalanchego/graft/coreth/sync/handlers" "github.com/ava-labs/avalanchego/utils" + "github.com/ava-labs/avalanchego/vms/evm/sync/customrawdb" statesyncclient "github.com/ava-labs/avalanchego/graft/coreth/sync/client" handlerstats "github.com/ava-labs/avalanchego/graft/coreth/sync/handlers/stats" @@ -144,7 +144,7 @@ func TestCodeSyncerAddsInProgressCodeHashes(t *testing.T) { codeBytes := utils.RandomBytes(100) codeHash := crypto.Keccak256Hash(codeBytes) clientDB := rawdb.NewMemoryDatabase() - customrawdb.AddCodeToFetch(clientDB, codeHash) + require.NoError(t, customrawdb.WriteCodeToFetch(clientDB, codeHash)) testCodeSyncer(t, codeSyncerTest{ clientDB: clientDB, codeRequestHashes: nil, @@ -166,7 +166,7 @@ func TestCodeSyncerAddsMoreInProgressThanQueueSize(t *testing.T) { db := rawdb.NewMemoryDatabase() for _, codeHash := range codeHashes { - customrawdb.AddCodeToFetch(db, codeHash) + require.NoError(t, customrawdb.WriteCodeToFetch(db, codeHash)) } testCodeSyncer(t, codeSyncerTest{ diff --git a/graft/coreth/sync/statesync/sync_test.go b/graft/coreth/sync/statesync/sync_test.go index c00483a94d4d..c3a9749ed91b 100644 --- a/graft/coreth/sync/statesync/sync_test.go +++ b/graft/coreth/sync/statesync/sync_test.go @@ -23,10 +23,10 @@ import ( "golang.org/x/sync/errgroup" "github.com/ava-labs/avalanchego/graft/coreth/core/state/snapshot" - "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/customrawdb" "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/message" "github.com/ava-labs/avalanchego/graft/coreth/sync/handlers" "github.com/ava-labs/avalanchego/graft/coreth/sync/statesync/statesynctest" + "github.com/ava-labs/avalanchego/vms/evm/sync/customrawdb" statesyncclient "github.com/ava-labs/avalanchego/graft/coreth/sync/client" handlerstats "github.com/ava-labs/avalanchego/graft/coreth/sync/handlers/stats" @@ -526,7 +526,7 @@ func testSyncerSyncsToNewRoot(t *testing.T, deleteBetweenSyncs func(*testing.T, // Also verifies any code referenced by the EVM state is present in [clientTrieDB] and the hash is correct. func assertDBConsistency(t testing.TB, root common.Hash, clientDB ethdb.Database, serverTrieDB, clientTrieDB *triedb.Database) { numSnapshotAccounts := 0 - accountIt := customrawdb.IterateAccountSnapshots(clientDB) + accountIt := customrawdb.NewAccountSnapshotsIterator(clientDB) defer accountIt.Release() for accountIt.Next() { if !bytes.HasPrefix(accountIt.Key(), rawdb.SnapshotAccountPrefix) || len(accountIt.Key()) != len(rawdb.SnapshotAccountPrefix)+common.HashLength { diff --git a/graft/coreth/sync/statesync/trie_queue.go b/graft/coreth/sync/statesync/trie_queue.go index 8f6e8b0df6c8..749328d03d53 100644 --- a/graft/coreth/sync/statesync/trie_queue.go +++ b/graft/coreth/sync/statesync/trie_queue.go @@ -4,10 +4,13 @@ package statesync import ( + "errors" + "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/ethdb" - "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/customrawdb" + "github.com/ava-labs/avalanchego/database" + "github.com/ava-labs/avalanchego/vms/evm/sync/customrawdb" ) // trieQueue persists storage trie roots with their associated @@ -28,9 +31,14 @@ func NewTrieQueue(db ethdb.Database) *trieQueue { // the persisted root does not match the root we are syncing to. func (t *trieQueue) clearIfRootDoesNotMatch(root common.Hash) error { persistedRoot, err := customrawdb.ReadSyncRoot(t.db) - if err != nil { + // If no sync root exists, treat it as empty hash (no previous sync). + switch { + case errors.Is(err, database.ErrNotFound): + persistedRoot = common.Hash{} + case err != nil: return err } + if persistedRoot != (common.Hash{}) && persistedRoot != root { // if not resuming, clear all progress markers if err := customrawdb.ClearAllSyncStorageTries(t.db); err != nil { @@ -76,7 +84,7 @@ func (t *trieQueue) getNextTrie() (common.Hash, []common.Hash, bool, error) { // Iterate over the keys to find the next storage trie root and all of the account hashes that contain the same storage root. for it.Next() { // Unpack the state root and account hash from the current key - nextRoot, nextAccount := customrawdb.UnpackSyncStorageTrieKey(it.Key()) + nextRoot, nextAccount := customrawdb.ParseSyncStorageTrieKey(it.Key()) // Set the root for the first pass if root == (common.Hash{}) { root = nextRoot @@ -105,7 +113,7 @@ func (t *trieQueue) countTries() (int, error) { ) for it.Next() { - nextRoot, _ := customrawdb.UnpackSyncStorageTrieKey(it.Key()) + nextRoot, _ := customrawdb.ParseSyncStorageTrieKey(it.Key()) if root == (common.Hash{}) || root != nextRoot { root = nextRoot tries++ diff --git a/graft/coreth/sync/statesync/trie_segments.go b/graft/coreth/sync/statesync/trie_segments.go index 29bcec64c48b..61f2068a72bf 100644 --- a/graft/coreth/sync/statesync/trie_segments.go +++ b/graft/coreth/sync/statesync/trie_segments.go @@ -16,10 +16,10 @@ import ( "github.com/ava-labs/libevm/log" "github.com/ava-labs/libevm/trie" - "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/customrawdb" "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/message" "github.com/ava-labs/avalanchego/graft/coreth/utils" "github.com/ava-labs/avalanchego/utils/wrappers" + "github.com/ava-labs/avalanchego/vms/evm/sync/customrawdb" syncclient "github.com/ava-labs/avalanchego/graft/coreth/sync/client" ) @@ -102,7 +102,7 @@ func (t *trieToSync) loadSegments() error { // key immediately prior to the segment we found on disk. // This is because we do not persist the beginning of // the first segment. - _, segmentStart := customrawdb.UnpackSyncSegmentKey(it.Key()) + _, segmentStart := customrawdb.ParseSyncSegmentKey(it.Key()) segmentStartPos := binary.BigEndian.Uint16(segmentStart[:wrappers.ShortLen]) t.addSegment(prevSegmentStart, addPadding(segmentStartPos-1, 0xff)) diff --git a/graft/coreth/tests/state_test_util.go b/graft/coreth/tests/state_test_util.go index 69bd22581b61..fd805da620ae 100644 --- a/graft/coreth/tests/state_test_util.go +++ b/graft/coreth/tests/state_test_util.go @@ -32,10 +32,10 @@ import ( "github.com/ava-labs/avalanchego/graft/coreth/core/extstate" "github.com/ava-labs/avalanchego/graft/coreth/core/state/snapshot" - "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/customrawdb" "github.com/ava-labs/avalanchego/graft/coreth/triedb/firewood" "github.com/ava-labs/avalanchego/graft/coreth/triedb/hashdb" "github.com/ava-labs/avalanchego/graft/coreth/triedb/pathdb" + "github.com/ava-labs/avalanchego/vms/evm/sync/customrawdb" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/rawdb" "github.com/ava-labs/libevm/core/state"