diff --git a/cmd/conf/init.go b/cmd/conf/init.go index 4bea00f9f2..a3b5504077 100644 --- a/cmd/conf/init.go +++ b/cmd/conf/init.go @@ -30,9 +30,11 @@ type InitConfig struct { PruneBloomSize uint64 `koanf:"prune-bloom-size"` PruneThreads int `koanf:"prune-threads"` PruneTrieCleanCache int `koanf:"prune-trie-clean-cache"` - ResetToMessage int64 `koanf:"reset-to-message"` RecreateMissingStateFrom uint64 `koanf:"recreate-missing-state-from"` RebuildLocalWasm bool `koanf:"rebuild-local-wasm"` + ReorgToBatch int64 `koanf:"reorg-to-batch"` + ReorgToMessageBatch int64 `koanf:"reorg-to-message-batch"` + ReorgToBlockBatch int64 `koanf:"reorg-to-block-batch"` } var InitConfigDefault = InitConfig{ @@ -54,9 +56,11 @@ var InitConfigDefault = InitConfig{ PruneBloomSize: 2048, PruneThreads: runtime.NumCPU(), PruneTrieCleanCache: gethexec.DefaultCachingConfig.TrieCleanCache, - ResetToMessage: -1, RecreateMissingStateFrom: 0, // 0 = disabled RebuildLocalWasm: true, + ReorgToBatch: -1, + ReorgToMessageBatch: -1, + ReorgToBlockBatch: -1, } func InitConfigAddOptions(prefix string, f *pflag.FlagSet) { @@ -78,9 +82,11 @@ func InitConfigAddOptions(prefix string, f *pflag.FlagSet) { f.Uint64(prefix+".prune-bloom-size", InitConfigDefault.PruneBloomSize, "the amount of memory in megabytes to use for the pruning bloom filter (higher values prune better)") f.Int(prefix+".prune-threads", InitConfigDefault.PruneThreads, "the number of threads to use when pruning") f.Int(prefix+".prune-trie-clean-cache", InitConfigDefault.PruneTrieCleanCache, "amount of memory in megabytes to cache unchanged state trie nodes with when traversing state database during pruning") - f.Int64(prefix+".reset-to-message", InitConfigDefault.ResetToMessage, "forces a reset to an old message height. Also set max-reorg-resequence-depth=0 to force re-reading messages") f.Uint64(prefix+".recreate-missing-state-from", InitConfigDefault.RecreateMissingStateFrom, "block number to start recreating missing states from (0 = disabled)") f.Bool(prefix+".rebuild-local-wasm", InitConfigDefault.RebuildLocalWasm, "rebuild local wasm database on boot if needed (otherwise-will be done lazily)") + f.Int64(prefix+".reorg-to-batch", InitConfigDefault.ReorgToBatch, "rolls back the blockchain to a specified batch number") + f.Int64(prefix+".reorg-to-message-batch", InitConfigDefault.ReorgToMessageBatch, "rolls back the blockchain to the first batch at or before a given message index") + f.Int64(prefix+".reorg-to-block-batch", InitConfigDefault.ReorgToBlockBatch, "rolls back the blockchain to the first batch at or before a given block number") } func (c *InitConfig) Validate() error { @@ -96,9 +102,22 @@ func (c *InitConfig) Validate() error { if c.PruneTrieCleanCache < 0 { return fmt.Errorf("invalid trie clean cache size: %d, has to be greater or equal 0", c.PruneTrieCleanCache) } + numReorgOptionsSpecified := 0 + for _, reorgOption := range []int64{c.ReorgToBatch, c.ReorgToMessageBatch, c.ReorgToBlockBatch} { + if reorgOption >= 0 { + numReorgOptionsSpecified++ + if numReorgOptionsSpecified > 1 { + return fmt.Errorf("at most one init reorg option can be specified") + } + } + } return nil } +func (c *InitConfig) IsReorgRequested() bool { + return c.ReorgToBatch >= 0 || c.ReorgToBlockBatch >= 0 || c.ReorgToMessageBatch >= 0 +} + var ( acceptedSnapshotKinds = []string{"archive", "pruned", "genesis"} acceptedSnapshotKindsStr = "(accepted values: \"" + strings.Join(acceptedSnapshotKinds, "\" | \"") + "\")" diff --git a/cmd/nitro/nitro.go b/cmd/nitro/nitro.go index 9861bb673e..2c7d07cf3b 100644 --- a/cmd/nitro/nitro.go +++ b/cmd/nitro/nitro.go @@ -41,6 +41,7 @@ import ( "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/metrics/exp" "github.com/ethereum/go-ethereum/node" + "github.com/ethereum/go-ethereum/params" "github.com/offchainlabs/nitro/arbnode" "github.com/offchainlabs/nitro/arbnode/resourcemanager" @@ -513,7 +514,7 @@ func mainImpl() int { } } - if nodeConfig.Init.ThenQuit && nodeConfig.Init.ResetToMessage < 0 { + if nodeConfig.Init.ThenQuit && !nodeConfig.Init.IsReorgRequested() { return 0 } @@ -669,29 +670,34 @@ func mainImpl() int { sigint := make(chan os.Signal, 1) signal.Notify(sigint, os.Interrupt, syscall.SIGTERM) - exitCode := 0 - - if err == nil && nodeConfig.Init.ResetToMessage > 0 { - err = currentNode.TxStreamer.ReorgTo(arbutil.MessageIndex(nodeConfig.Init.ResetToMessage)) + if err == nil && nodeConfig.Init.IsReorgRequested() { + err = initReorg(nodeConfig.Init, chainInfo.ChainConfig, currentNode.InboxTracker) if err != nil { - fatalErrChan <- fmt.Errorf("error reseting message: %w", err) - exitCode = 1 - } - if nodeConfig.Init.ThenQuit { - return exitCode + fatalErrChan <- fmt.Errorf("error reorging per init config: %w", err) + } else if nodeConfig.Init.ThenQuit { + return 0 } } + err = nil select { - case err := <-fatalErrChan: + case err = <-fatalErrChan: + case <-sigint: + // If there was both a sigint and a fatal error, we want to log the fatal error + select { + case err = <-fatalErrChan: + default: + log.Info("shutting down because of sigint") + } + } + + if err != nil { log.Error("shutting down due to fatal error", "err", err) defer log.Error("shut down due to fatal error", "err", err) - exitCode = 1 - case <-sigint: - log.Info("shutting down because of sigint") + return 1 } - return exitCode + return 0 } type NodeConfig struct { @@ -1003,6 +1009,39 @@ func applyChainParameters(ctx context.Context, k *koanf.Koanf, chainId uint64, c return nil } +func initReorg(initConfig conf.InitConfig, chainConfig *params.ChainConfig, inboxTracker *arbnode.InboxTracker) error { + var batchCount uint64 + if initConfig.ReorgToBatch >= 0 { + batchCount = uint64(initConfig.ReorgToBatch) + 1 + } else { + var messageIndex arbutil.MessageIndex + if initConfig.ReorgToMessageBatch >= 0 { + messageIndex = arbutil.MessageIndex(initConfig.ReorgToMessageBatch) + } else if initConfig.ReorgToBlockBatch > 0 { + genesis := chainConfig.ArbitrumChainParams.GenesisBlockNum + blockNum := uint64(initConfig.ReorgToBlockBatch) + if blockNum < genesis { + return fmt.Errorf("ReorgToBlockBatch %d before genesis %d", blockNum, genesis) + } + messageIndex = arbutil.MessageIndex(blockNum - genesis) + } else { + log.Warn("Tried to do init reorg, but no init reorg options specified") + return nil + } + // Reorg out the batch containing the next message + var missing bool + var err error + batchCount, missing, err = inboxTracker.FindInboxBatchContainingMessage(messageIndex + 1) + if err != nil { + return err + } + if missing { + return fmt.Errorf("cannot reorg to unknown message index %v", messageIndex) + } + } + return inboxTracker.ReorgBatchesTo(batchCount) +} + type NodeConfigFetcher struct { *genericconf.LiveConfig[*NodeConfig] } diff --git a/execution/gethexec/node.go b/execution/gethexec/node.go index 93f643b8a1..6624188cbd 100644 --- a/execution/gethexec/node.go +++ b/execution/gethexec/node.go @@ -26,18 +26,6 @@ import ( flag "github.com/spf13/pflag" ) -type DangerousConfig struct { - ReorgToBlock int64 `koanf:"reorg-to-block"` -} - -var DefaultDangerousConfig = DangerousConfig{ - ReorgToBlock: -1, -} - -func DangerousConfigAddOptions(prefix string, f *flag.FlagSet) { - f.Int64(prefix+".reorg-to-block", DefaultDangerousConfig.ReorgToBlock, "DANGEROUS! forces a reorg to an old block height. To be used for testing only. -1 to disable") -} - type Config struct { ParentChainReader headerreader.Config `koanf:"parent-chain-reader" reload:"hot"` Sequencer SequencerConfig `koanf:"sequencer" reload:"hot"` @@ -49,7 +37,6 @@ type Config struct { Caching CachingConfig `koanf:"caching"` RPC arbitrum.Config `koanf:"rpc"` TxLookupLimit uint64 `koanf:"tx-lookup-limit"` - Dangerous DangerousConfig `koanf:"dangerous"` EnablePrefetchBlock bool `koanf:"enable-prefetch-block"` SyncMonitor SyncMonitorConfig `koanf:"sync-monitor"` @@ -89,7 +76,6 @@ func ConfigAddOptions(prefix string, f *flag.FlagSet) { CachingConfigAddOptions(prefix+".caching", f) SyncMonitorConfigAddOptions(prefix+".sync-monitor", f) f.Uint64(prefix+".tx-lookup-limit", ConfigDefault.TxLookupLimit, "retain the ability to lookup transactions by hash for the past N blocks (0 = all blocks)") - DangerousConfigAddOptions(prefix+".dangerous", f) f.Bool(prefix+".enable-prefetch-block", ConfigDefault.EnablePrefetchBlock, "enable prefetching of blocks") } @@ -103,7 +89,6 @@ var ConfigDefault = Config{ TxPreChecker: DefaultTxPreCheckerConfig, TxLookupLimit: 126_230_400, // 1 year at 4 blocks per second Caching: DefaultCachingConfig, - Dangerous: DefaultDangerousConfig, Forwarder: DefaultNodeForwarderConfig, EnablePrefetchBlock: true, }