From a8b8d625acc1e81918305485e2b6f0e1ae996b9c Mon Sep 17 00:00:00 2001 From: Piers Powlesland Date: Wed, 26 Jun 2024 13:25:12 +0100 Subject: [PATCH 1/3] Fixed migration for datadirs without ancients The script was assuming that ancients would have been migrated and was considering the numAncients-1 to be the next block to migrate but when numAncients is zero that's a problem. Also remved logic for picking up where db migration left of for the level db since it was complicating the logic and that process takes a few seconds, which is nothing compared with the minutes taken to migrate the ancients. --- op-chain-ops/cmd/celo-migrate/db.go | 27 +--------- op-chain-ops/cmd/celo-migrate/main.go | 41 +++++++-------- op-chain-ops/cmd/celo-migrate/non-ancients.go | 51 +++++++------------ 3 files changed, 37 insertions(+), 82 deletions(-) diff --git a/op-chain-ops/cmd/celo-migrate/db.go b/op-chain-ops/cmd/celo-migrate/db.go index e7f685909d53..2b833b4e4007 100644 --- a/op-chain-ops/cmd/celo-migrate/db.go +++ b/op-chain-ops/cmd/celo-migrate/db.go @@ -14,9 +14,8 @@ import ( // Constants for the database const ( - DBCache = 1024 // size of the cache in MB - DBHandles = 60 // number of handles - LastMigratedNonAncientBlockKey = "celoLastMigratedNonAncientBlock" + DBCache = 1024 // size of the cache in MB + DBHandles = 60 // number of handles ) var ( @@ -35,28 +34,6 @@ func headerKey(number uint64, hash common.Hash) []byte { return append(append(headerPrefix, encodeBlockNumber(number)...), hash.Bytes()...) } -// readLastMigratedNonAncientBlock returns the last migration number. If it doesn't exist, it returns 0. -func readLastMigratedNonAncientBlock(db ethdb.KeyValueReader) uint64 { - data, err := db.Get([]byte(LastMigratedNonAncientBlockKey)) - if err != nil { - return 0 - } - number := binary.BigEndian.Uint64(data) - return number -} - -// writeLastMigratedNonAncientBlock stores the last migration number. -func writeLastMigratedNonAncientBlock(db ethdb.KeyValueWriter, number uint64) error { - enc := make([]byte, 8) - binary.BigEndian.PutUint64(enc, number) - return db.Put([]byte(LastMigratedNonAncientBlockKey), enc) -} - -// deleteLastMigratedNonAncientBlock removes the last migration number. -func deleteLastMigratedNonAncientBlock(db ethdb.KeyValueWriter) error { - return db.Delete([]byte(LastMigratedNonAncientBlockKey)) -} - // openDB opens the chaindata database at the given path. Note this path is below the datadir func openDB(chaindataPath string) (ethdb.Database, error) { if _, err := os.Stat(chaindataPath); errors.Is(err, os.ErrNotExist) { diff --git a/op-chain-ops/cmd/celo-migrate/main.go b/op-chain-ops/cmd/celo-migrate/main.go index b7d27791bc4b..4dc59f2fcafe 100644 --- a/op-chain-ops/cmd/celo-migrate/main.go +++ b/op-chain-ops/cmd/celo-migrate/main.go @@ -82,10 +82,6 @@ var ( Name: "clear-all", Usage: "Use this to start with a fresh new db, deleting all data including ancients. CAUTION: Re-migrating ancients takes time.", } - keepNonAncientsFlag = &cli.BoolFlag{ - Name: "keep-non-ancients", - Usage: "CAUTION: Not recommended for production. Use to keep all data in the new db as is, including any partially migrated non-ancient blocks and state data. If non-ancient blocks are partially migrated, the script will attempt to resume the migration.", - } onlyAncientsFlag = &cli.BoolFlag{ Name: "only-ancients", Usage: "Use to only migrate ancient blocks. Ignored when running full migration", @@ -99,7 +95,6 @@ var ( bufferSizeFlag, memoryLimitFlag, clearAllFlag, - keepNonAncientsFlag, } stateMigrationFlags = []cli.Flag{ newDBPathFlag, @@ -115,14 +110,13 @@ var ( ) type blockMigrationOptions struct { - oldDBPath string - newDBPath string - batchSize uint64 - bufferSize uint64 - memoryLimit int64 - clearAll bool - keepNonAncients bool - onlyAncients bool + oldDBPath string + newDBPath string + batchSize uint64 + bufferSize uint64 + memoryLimit int64 + clearAll bool + onlyAncients bool } type stateMigrationOptions struct { @@ -137,14 +131,13 @@ type stateMigrationOptions struct { func parseBlockMigrationOptions(ctx *cli.Context) blockMigrationOptions { return blockMigrationOptions{ - oldDBPath: ctx.String(oldDBPathFlag.Name), - newDBPath: ctx.String(newDBPathFlag.Name), - batchSize: ctx.Uint64(batchSizeFlag.Name), - bufferSize: ctx.Uint64(bufferSizeFlag.Name), - memoryLimit: ctx.Int64(memoryLimitFlag.Name), - clearAll: ctx.Bool(clearAllFlag.Name), - keepNonAncients: ctx.Bool(keepNonAncientsFlag.Name), - onlyAncients: ctx.Bool(onlyAncientsFlag.Name), + oldDBPath: ctx.String(oldDBPathFlag.Name), + newDBPath: ctx.String(newDBPathFlag.Name), + batchSize: ctx.Uint64(batchSizeFlag.Name), + bufferSize: ctx.Uint64(bufferSizeFlag.Name), + memoryLimit: ctx.Int64(memoryLimitFlag.Name), + clearAll: ctx.Bool(clearAllFlag.Name), + onlyAncients: ctx.Bool(onlyAncientsFlag.Name), } } @@ -232,7 +225,7 @@ func runBlockMigration(opts blockMigrationOptions) error { debug.SetMemoryLimit(opts.memoryLimit * 1 << 20) // Set memory limit, converting from MiB to bytes - log.Info("Block Migration Started", "oldDBPath", opts.oldDBPath, "newDBPath", opts.newDBPath, "batchSize", opts.batchSize, "memoryLimit", opts.memoryLimit, "clearAll", opts.clearAll, "keepNonAncients", opts.keepNonAncients, "onlyAncients", opts.onlyAncients) + log.Info("Block Migration Started", "oldDBPath", opts.oldDBPath, "newDBPath", opts.newDBPath, "batchSize", opts.batchSize, "memoryLimit", opts.memoryLimit, "clearAll", opts.clearAll, "onlyAncients", opts.onlyAncients) var err error @@ -244,7 +237,7 @@ func runBlockMigration(opts blockMigrationOptions) error { if err = os.RemoveAll(opts.newDBPath); err != nil { return fmt.Errorf("failed to remove new database: %w", err) } - } else if !opts.keepNonAncients { + } else { if err = cleanupNonAncientDb(opts.newDBPath); err != nil { return fmt.Errorf("failed to reset non-ancient database: %w", err) } @@ -258,7 +251,7 @@ func runBlockMigration(opts blockMigrationOptions) error { var numNonAncients uint64 if !opts.onlyAncients { - if numNonAncients, err = migrateNonAncientsDb(opts.oldDBPath, opts.newDBPath, numAncientsNewAfter-1, opts.batchSize); err != nil { + if numNonAncients, err = migrateNonAncientsDb(opts.oldDBPath, opts.newDBPath, numAncientsNewAfter, opts.batchSize); err != nil { return fmt.Errorf("failed to migrate non-ancients database: %w", err) } } else { diff --git a/op-chain-ops/cmd/celo-migrate/non-ancients.go b/op-chain-ops/cmd/celo-migrate/non-ancients.go index bab92aacd3f1..2d2c1726ac18 100644 --- a/op-chain-ops/cmd/celo-migrate/non-ancients.go +++ b/op-chain-ops/cmd/celo-migrate/non-ancients.go @@ -10,7 +10,7 @@ import ( "github.com/ethereum/go-ethereum/log" ) -func migrateNonAncientsDb(oldDbPath, newDbPath string, lastAncientBlock, batchSize uint64) (uint64, error) { +func migrateNonAncientsDb(oldDbPath, newDbPath string, numAncients, batchSize uint64) (uint64, error) { // First copy files from old database to new database log.Info("Copy files from old database (excluding ancients)", "process", "non-ancients") @@ -51,22 +51,10 @@ func migrateNonAncientsDb(oldDbPath, newDbPath string, lastAncientBlock, batchSi // get the last block number hash := rawdb.ReadHeadHeaderHash(newDB) lastBlock := *rawdb.ReadHeaderNumber(newDB, hash) - lastMigratedNonAncientBlock := readLastMigratedNonAncientBlock(newDB) // returns 0 if not found - // if migration was interrupted, start from the last migrated block - fromBlock := max(lastAncientBlock, lastMigratedNonAncientBlock) + 1 + log.Info("Non-Ancient Block Migration Started", "process", "non-ancients", "startBlock", numAncients, "endBlock", lastBlock, "count", lastBlock-numAncients, "lastAncientBlock", numAncients) - if fromBlock >= lastBlock { - log.Info("Non-Ancient Block Migration Skipped", "process", "non-ancients", "lastAncientBlock", lastAncientBlock, "endBlock", lastBlock, "lastMigratedNonAncientBlock", lastMigratedNonAncientBlock) - if lastMigratedNonAncientBlock != lastBlock { - return 0, fmt.Errorf("migration range empty but last migrated block is not the last block in the database") - } - return 0, nil - } - - log.Info("Non-Ancient Block Migration Started", "process", "non-ancients", "startBlock", fromBlock, "endBlock", lastBlock, "count", lastBlock-fromBlock, "lastAncientBlock", lastAncientBlock, "lastMigratedNonAncientBlock", lastMigratedNonAncientBlock) - - for i := fromBlock; i <= lastBlock; i += batchSize { + for i := numAncients; i <= lastBlock; i += batchSize { numbersHash := rawdb.ReadAllHashesInRange(newDB, i, i+batchSize-1) log.Info("Processing Block Range", "process", "non-ancients", "from", i, "to(inclusve)", i+batchSize-1, "count", len(numbersHash)) @@ -94,30 +82,27 @@ func migrateNonAncientsDb(oldDbPath, newDbPath string, lastAncientBlock, batchSi batch := newDB.NewBatch() rawdb.WriteBodyRLP(batch, numberHash.Hash, numberHash.Number, newBody) _ = batch.Put(headerKey(numberHash.Number, numberHash.Hash), newHeader) - _ = writeLastMigratedNonAncientBlock(batch, numberHash.Number) if err := batch.Write(); err != nil { return 0, fmt.Errorf("failed to write header and body: block %d - %x: %w", numberHash.Number, numberHash.Hash, err) } } } - toBeRemoved := rawdb.ReadAllHashesInRange(newDB, 1, lastAncientBlock) - log.Info("Removing frozen blocks", "process", "non-ancients", "count", len(toBeRemoved)) - batch := newDB.NewBatch() - for _, numberHash := range toBeRemoved { - rawdb.DeleteBlockWithoutNumber(batch, numberHash.Hash, numberHash.Number) - rawdb.DeleteCanonicalHash(batch, numberHash.Number) - } - if err := batch.Write(); err != nil { - return 0, fmt.Errorf("failed to delete frozen blocks: %w", err) - } - - // if migration finished, remove the last migration number - if err := deleteLastMigratedNonAncientBlock(newDB); err != nil { - return 0, fmt.Errorf("failed to delete last migration number: %w", err) + if numAncients > 0 { + toBeRemoved := rawdb.ReadAllHashesInRange(newDB, 1, numAncients) + log.Info("Removing frozen blocks", "process", "non-ancients", "count", len(toBeRemoved)) + batch := newDB.NewBatch() + for _, numberHash := range toBeRemoved { + rawdb.DeleteBlockWithoutNumber(batch, numberHash.Hash, numberHash.Number) + rawdb.DeleteCanonicalHash(batch, numberHash.Number) + } + if err := batch.Write(); err != nil { + return 0, fmt.Errorf("failed to delete frozen blocks: %w", err) + } + log.Info("Removed frozen blocks, still in leveldb", "process", "non-ancients", "removedBlocks", len(toBeRemoved)) } + migratedCount := lastBlock - numAncients + 1 + log.Info("Non-Ancient Block Migration Ended", "process", "non-ancients", "migratedBlocks", migratedCount) - log.Info("Non-Ancient Block Migration Ended", "process", "non-ancients", "migratedBlocks", lastBlock-fromBlock+1, "removedBlocks", len(toBeRemoved)) - - return lastBlock - fromBlock + 1, nil + return migratedCount, nil } From 74b8139283b1c84b8294eb108c03cc140a8cc9b0 Mon Sep 17 00:00:00 2001 From: Piers Powlesland Date: Wed, 26 Jun 2024 14:17:22 +0100 Subject: [PATCH 2/3] Ensure that we set gas limit if migrating at pre-gingerbread point --- op-chain-ops/cmd/celo-migrate/state.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/op-chain-ops/cmd/celo-migrate/state.go b/op-chain-ops/cmd/celo-migrate/state.go index 67be76047d98..53396f237a7f 100644 --- a/op-chain-ops/cmd/celo-migrate/state.go +++ b/op-chain-ops/cmd/celo-migrate/state.go @@ -124,6 +124,11 @@ func applyStateMigrationChanges(config *genesis.DeployConfig, genesis *core.Gene migrationBlockTime = uint64(time.Now().Unix()) } + // If gas limit was zero at the transition point use a default of 30M. + gasLimit := header.GasLimit + if gasLimit == 0 { + gasLimit = 30e6 + } // Create the header for the Cel2 transition block. cel2Header := &types.Header{ ParentHash: header.Hash(), @@ -135,7 +140,7 @@ func applyStateMigrationChanges(config *genesis.DeployConfig, genesis *core.Gene Bloom: types.Bloom{}, Difficulty: new(big.Int).Set(common.Big0), Number: migrationBlock, - GasLimit: header.GasLimit, + GasLimit: gasLimit, GasUsed: 0, Time: migrationBlockTime, Extra: []byte("CeL2 migration"), From 6cedcfa552fba00c293f1b18af7ee0f3a518d804 Mon Sep 17 00:00:00 2001 From: Piers Powlesland Date: Mon, 1 Jul 2024 09:53:27 +0100 Subject: [PATCH 3/3] Add note on use of gas limit value to switch block encoding --- op-chain-ops/cmd/celo-migrate/state.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/op-chain-ops/cmd/celo-migrate/state.go b/op-chain-ops/cmd/celo-migrate/state.go index 53396f237a7f..6902723432c3 100644 --- a/op-chain-ops/cmd/celo-migrate/state.go +++ b/op-chain-ops/cmd/celo-migrate/state.go @@ -125,6 +125,9 @@ func applyStateMigrationChanges(config *genesis.DeployConfig, genesis *core.Gene } // If gas limit was zero at the transition point use a default of 30M. + // Note that in op-geth we use gasLimit==0 to indicate a pre-gingerbread + // block and adjust encoding appropriately, so we must make sure that + // gasLimit is non-zero, bacause L2 blocks are all post gingerbread. gasLimit := header.GasLimit if gasLimit == 0 { gasLimit = 30e6