diff --git a/op-challenger/cmd/main_test.go b/op-challenger/cmd/main_test.go index 7e77f19408e8b..e6234b063b340 100644 --- a/op-challenger/cmd/main_test.go +++ b/op-challenger/cmd/main_test.go @@ -277,7 +277,7 @@ func TestAsteriscRequiredArgs(t *testing.T) { t.Run("Valid", func(t *testing.T) { cfg := configForArgs(t, addRequiredArgsExcept(traceType, "--asterisc-bin", "--asterisc-bin=./asterisc")) - require.Equal(t, "./asterisc", cfg.AsteriscBin) + require.Equal(t, "./asterisc", cfg.Asterisc.VmBin) }) }) @@ -292,7 +292,7 @@ func TestAsteriscRequiredArgs(t *testing.T) { t.Run("Valid", func(t *testing.T) { cfg := configForArgs(t, addRequiredArgsExcept(traceType, "--asterisc-server", "--asterisc-server=./op-program")) - require.Equal(t, "./op-program", cfg.AsteriscServer) + require.Equal(t, "./op-program", cfg.Asterisc.Server) }) }) @@ -349,12 +349,12 @@ func TestAsteriscRequiredArgs(t *testing.T) { t.Run(fmt.Sprintf("TestAsteriscSnapshotFreq-%v", traceType), func(t *testing.T) { t.Run("UsesDefault", func(t *testing.T) { cfg := configForArgs(t, addRequiredArgs(traceType)) - require.Equal(t, config.DefaultAsteriscSnapshotFreq, cfg.AsteriscSnapshotFreq) + require.Equal(t, config.DefaultAsteriscSnapshotFreq, cfg.Asterisc.SnapshotFreq) }) t.Run("Valid", func(t *testing.T) { cfg := configForArgs(t, addRequiredArgs(traceType, "--asterisc-snapshot-freq=1234")) - require.Equal(t, uint(1234), cfg.AsteriscSnapshotFreq) + require.Equal(t, uint(1234), cfg.Asterisc.SnapshotFreq) }) t.Run("Invalid", func(t *testing.T) { @@ -366,12 +366,12 @@ func TestAsteriscRequiredArgs(t *testing.T) { t.Run(fmt.Sprintf("TestAsteriscInfoFreq-%v", traceType), func(t *testing.T) { t.Run("UsesDefault", func(t *testing.T) { cfg := configForArgs(t, addRequiredArgs(traceType)) - require.Equal(t, config.DefaultAsteriscInfoFreq, cfg.AsteriscInfoFreq) + require.Equal(t, config.DefaultAsteriscInfoFreq, cfg.Asterisc.InfoFreq) }) t.Run("Valid", func(t *testing.T) { cfg := configForArgs(t, addRequiredArgs(traceType, "--asterisc-info-freq=1234")) - require.Equal(t, uint(1234), cfg.AsteriscInfoFreq) + require.Equal(t, uint(1234), cfg.Asterisc.InfoFreq) }) t.Run("Invalid", func(t *testing.T) { @@ -432,7 +432,7 @@ func TestAsteriscRequiredArgs(t *testing.T) { delete(args, "--game-factory-address") args["--network"] = "op-sepolia" cfg := configForArgs(t, toArgList(args)) - require.Equal(t, "op-sepolia", cfg.AsteriscNetwork) + require.Equal(t, "op-sepolia", cfg.Asterisc.Network) }) t.Run("MustNotSpecifyNetworkAndAsteriscNetwork", func(t *testing.T) { @@ -442,7 +442,7 @@ func TestAsteriscRequiredArgs(t *testing.T) { t.Run("Valid", func(t *testing.T) { cfg := configForArgs(t, addRequiredArgsExcept(traceType, "--asterisc-network", "--asterisc-network", testNetwork)) - require.Equal(t, testNetwork, cfg.AsteriscNetwork) + require.Equal(t, testNetwork, cfg.Asterisc.Network) }) }) @@ -453,7 +453,7 @@ func TestAsteriscRequiredArgs(t *testing.T) { t.Run("Valid", func(t *testing.T) { cfg := configForArgs(t, addRequiredArgsExcept(traceType, "--asterisc-network", "--asterisc-rollup-config=rollup.json", "--asterisc-l2-genesis=genesis.json")) - require.Equal(t, "rollup.json", cfg.AsteriscRollupConfigPath) + require.Equal(t, "rollup.json", cfg.Asterisc.RollupConfigPath) }) }) @@ -464,7 +464,7 @@ func TestAsteriscRequiredArgs(t *testing.T) { t.Run("Valid", func(t *testing.T) { cfg := configForArgs(t, addRequiredArgsExcept(traceType, "--asterisc-network", "--asterisc-rollup-config=rollup.json", "--asterisc-l2-genesis=genesis.json")) - require.Equal(t, "genesis.json", cfg.AsteriscL2GenesisPath) + require.Equal(t, "genesis.json", cfg.Asterisc.L2GenesisPath) }) }) } @@ -502,7 +502,7 @@ func TestCannonRequiredArgs(t *testing.T) { t.Run("Valid", func(t *testing.T) { cfg := configForArgs(t, addRequiredArgsExcept(traceType, "--cannon-bin", "--cannon-bin=./cannon")) - require.Equal(t, "./cannon", cfg.CannonBin) + require.Equal(t, "./cannon", cfg.Cannon.VmBin) }) }) @@ -517,7 +517,7 @@ func TestCannonRequiredArgs(t *testing.T) { t.Run("Valid", func(t *testing.T) { cfg := configForArgs(t, addRequiredArgsExcept(traceType, "--cannon-server", "--cannon-server=./op-program")) - require.Equal(t, "./op-program", cfg.CannonServer) + require.Equal(t, "./op-program", cfg.Cannon.Server) }) }) @@ -570,12 +570,12 @@ func TestCannonRequiredArgs(t *testing.T) { t.Run(fmt.Sprintf("TestCannonSnapshotFreq-%v", traceType), func(t *testing.T) { t.Run("UsesDefault", func(t *testing.T) { cfg := configForArgs(t, addRequiredArgs(traceType)) - require.Equal(t, config.DefaultCannonSnapshotFreq, cfg.CannonSnapshotFreq) + require.Equal(t, config.DefaultCannonSnapshotFreq, cfg.Cannon.SnapshotFreq) }) t.Run("Valid", func(t *testing.T) { cfg := configForArgs(t, addRequiredArgs(traceType, "--cannon-snapshot-freq=1234")) - require.Equal(t, uint(1234), cfg.CannonSnapshotFreq) + require.Equal(t, uint(1234), cfg.Cannon.SnapshotFreq) }) t.Run("Invalid", func(t *testing.T) { @@ -587,12 +587,12 @@ func TestCannonRequiredArgs(t *testing.T) { t.Run(fmt.Sprintf("TestCannonInfoFreq-%v", traceType), func(t *testing.T) { t.Run("UsesDefault", func(t *testing.T) { cfg := configForArgs(t, addRequiredArgs(traceType)) - require.Equal(t, config.DefaultCannonInfoFreq, cfg.CannonInfoFreq) + require.Equal(t, config.DefaultCannonInfoFreq, cfg.Cannon.InfoFreq) }) t.Run("Valid", func(t *testing.T) { cfg := configForArgs(t, addRequiredArgs(traceType, "--cannon-info-freq=1234")) - require.Equal(t, uint(1234), cfg.CannonInfoFreq) + require.Equal(t, uint(1234), cfg.Cannon.InfoFreq) }) t.Run("Invalid", func(t *testing.T) { @@ -653,7 +653,7 @@ func TestCannonRequiredArgs(t *testing.T) { delete(args, "--game-factory-address") args["--network"] = "op-sepolia" cfg := configForArgs(t, toArgList(args)) - require.Equal(t, "op-sepolia", cfg.CannonNetwork) + require.Equal(t, "op-sepolia", cfg.Cannon.Network) }) t.Run("MustNotSpecifyNetworkAndCannonNetwork", func(t *testing.T) { @@ -663,7 +663,7 @@ func TestCannonRequiredArgs(t *testing.T) { t.Run("Valid", func(t *testing.T) { cfg := configForArgs(t, addRequiredArgsExcept(traceType, "--cannon-network", "--cannon-network", testNetwork)) - require.Equal(t, testNetwork, cfg.CannonNetwork) + require.Equal(t, testNetwork, cfg.Cannon.Network) }) }) @@ -674,7 +674,7 @@ func TestCannonRequiredArgs(t *testing.T) { t.Run("Valid", func(t *testing.T) { cfg := configForArgs(t, addRequiredArgsExcept(traceType, "--cannon-network", "--cannon-rollup-config=rollup.json", "--cannon-l2-genesis=genesis.json")) - require.Equal(t, "rollup.json", cfg.CannonRollupConfigPath) + require.Equal(t, "rollup.json", cfg.Cannon.RollupConfigPath) }) }) @@ -685,7 +685,7 @@ func TestCannonRequiredArgs(t *testing.T) { t.Run("Valid", func(t *testing.T) { cfg := configForArgs(t, addRequiredArgsExcept(traceType, "--cannon-network", "--cannon-rollup-config=rollup.json", "--cannon-l2-genesis=genesis.json")) - require.Equal(t, "genesis.json", cfg.CannonL2GenesisPath) + require.Equal(t, "genesis.json", cfg.Cannon.L2GenesisPath) }) }) } @@ -729,7 +729,7 @@ func TestGameWindow(t *testing.T) { t.Run("Valid", func(t *testing.T) { cfg := configForArgs(t, addRequiredArgs(config.TraceTypeAlphabet, "--game-window=1m")) - require.Equal(t, time.Duration(time.Minute), cfg.GameWindow) + require.Equal(t, time.Minute, cfg.GameWindow) }) t.Run("ParsesDefault", func(t *testing.T) { diff --git a/op-challenger/config/config.go b/op-challenger/config/config.go index 9586eb54906b2..3c6f8c846aff9 100644 --- a/op-challenger/config/config.go +++ b/op-challenger/config/config.go @@ -8,12 +8,12 @@ import ( "slices" "time" - "github.com/ethereum/go-ethereum/common" - + "github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/vm" "github.com/ethereum-optimism/optimism/op-node/chaincfg" opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics" "github.com/ethereum-optimism/optimism/op-service/oppprof" "github.com/ethereum-optimism/optimism/op-service/txmgr" + "github.com/ethereum/go-ethereum/common" ) var ( @@ -129,26 +129,14 @@ type Config struct { L2Rpc string // L2 RPC Url // Specific to the cannon trace provider - CannonBin string // Path to the cannon executable to run when generating trace data - CannonServer string // Path to the op-program executable that provides the pre-image oracle server + Cannon vm.Config CannonAbsolutePreState string // File to load the absolute pre-state for Cannon traces from CannonAbsolutePreStateBaseURL *url.URL // Base URL to retrieve absolute pre-states for Cannon traces from - CannonNetwork string - CannonRollupConfigPath string - CannonL2GenesisPath string - CannonSnapshotFreq uint // Frequency of snapshots to create when executing cannon (in VM instructions) - CannonInfoFreq uint // Frequency of cannon progress log messages (in VM instructions) // Specific to the asterisc trace provider - AsteriscBin string // Path to the asterisc executable to run when generating trace data - AsteriscServer string // Path to the op-program executable that provides the pre-image oracle server + Asterisc vm.Config AsteriscAbsolutePreState string // File to load the absolute pre-state for Asterisc traces from AsteriscAbsolutePreStateBaseURL *url.URL // Base URL to retrieve absolute pre-states for Asterisc traces from - AsteriscNetwork string - AsteriscRollupConfigPath string - AsteriscL2GenesisPath string - AsteriscSnapshotFreq uint // Frequency of snapshots to create when executing asterisc (in VM instructions) - AsteriscInfoFreq uint // Frequency of asterisc progress log messages (in VM instructions) MaxPendingTx uint64 // Maximum number of pending transactions (0 == no limit) @@ -185,11 +173,23 @@ func NewConfig( Datadir: datadir, - CannonSnapshotFreq: DefaultCannonSnapshotFreq, - CannonInfoFreq: DefaultCannonInfoFreq, - AsteriscSnapshotFreq: DefaultAsteriscSnapshotFreq, - AsteriscInfoFreq: DefaultAsteriscInfoFreq, - GameWindow: DefaultGameWindow, + Cannon: vm.Config{ + VmType: TraceTypeCannon.String(), + L1: l1EthRpc, + L1Beacon: l1BeaconApi, + L2: l2EthRpc, + SnapshotFreq: DefaultCannonSnapshotFreq, + InfoFreq: DefaultCannonInfoFreq, + }, + Asterisc: vm.Config{ + VmType: TraceTypeAsterisc.String(), + L1: l1EthRpc, + L1Beacon: l1BeaconApi, + L2: l2EthRpc, + SnapshotFreq: DefaultAsteriscSnapshotFreq, + InfoFreq: DefaultAsteriscInfoFreq, + }, + GameWindow: DefaultGameWindow, } } @@ -223,28 +223,28 @@ func (c Config) Check() error { return ErrMaxConcurrencyZero } if c.TraceTypeEnabled(TraceTypeCannon) || c.TraceTypeEnabled(TraceTypePermissioned) { - if c.CannonBin == "" { + if c.Cannon.VmBin == "" { return ErrMissingCannonBin } - if c.CannonServer == "" { + if c.Cannon.Server == "" { return ErrMissingCannonServer } - if c.CannonNetwork == "" { - if c.CannonRollupConfigPath == "" { + if c.Cannon.Network == "" { + if c.Cannon.RollupConfigPath == "" { return ErrMissingCannonRollupConfig } - if c.CannonL2GenesisPath == "" { + if c.Cannon.L2GenesisPath == "" { return ErrMissingCannonL2Genesis } } else { - if c.CannonRollupConfigPath != "" { + if c.Cannon.RollupConfigPath != "" { return ErrCannonNetworkAndRollupConfig } - if c.CannonL2GenesisPath != "" { + if c.Cannon.L2GenesisPath != "" { return ErrCannonNetworkAndL2Genesis } - if ch := chaincfg.ChainByName(c.CannonNetwork); ch == nil { - return fmt.Errorf("%w: %v", ErrCannonNetworkUnknown, c.CannonNetwork) + if ch := chaincfg.ChainByName(c.Cannon.Network); ch == nil { + return fmt.Errorf("%w: %v", ErrCannonNetworkUnknown, c.Cannon.Network) } } if c.CannonAbsolutePreState == "" && c.CannonAbsolutePreStateBaseURL == nil { @@ -253,36 +253,36 @@ func (c Config) Check() error { if c.CannonAbsolutePreState != "" && c.CannonAbsolutePreStateBaseURL != nil { return ErrCannonAbsolutePreStateAndBaseURL } - if c.CannonSnapshotFreq == 0 { + if c.Cannon.SnapshotFreq == 0 { return ErrMissingCannonSnapshotFreq } - if c.CannonInfoFreq == 0 { + if c.Cannon.InfoFreq == 0 { return ErrMissingCannonInfoFreq } } if c.TraceTypeEnabled(TraceTypeAsterisc) { - if c.AsteriscBin == "" { + if c.Asterisc.VmBin == "" { return ErrMissingAsteriscBin } - if c.AsteriscServer == "" { + if c.Asterisc.Server == "" { return ErrMissingAsteriscServer } - if c.AsteriscNetwork == "" { - if c.AsteriscRollupConfigPath == "" { + if c.Asterisc.Network == "" { + if c.Asterisc.RollupConfigPath == "" { return ErrMissingAsteriscRollupConfig } - if c.AsteriscL2GenesisPath == "" { + if c.Asterisc.L2GenesisPath == "" { return ErrMissingAsteriscL2Genesis } } else { - if c.AsteriscRollupConfigPath != "" { + if c.Asterisc.RollupConfigPath != "" { return ErrAsteriscNetworkAndRollupConfig } - if c.AsteriscL2GenesisPath != "" { + if c.Asterisc.L2GenesisPath != "" { return ErrAsteriscNetworkAndL2Genesis } - if ch := chaincfg.ChainByName(c.AsteriscNetwork); ch == nil { - return fmt.Errorf("%w: %v", ErrAsteriscNetworkUnknown, c.AsteriscNetwork) + if ch := chaincfg.ChainByName(c.Asterisc.Network); ch == nil { + return fmt.Errorf("%w: %v", ErrAsteriscNetworkUnknown, c.Asterisc.Network) } } if c.AsteriscAbsolutePreState == "" && c.AsteriscAbsolutePreStateBaseURL == nil { @@ -291,10 +291,10 @@ func (c Config) Check() error { if c.AsteriscAbsolutePreState != "" && c.AsteriscAbsolutePreStateBaseURL != nil { return ErrAsteriscAbsolutePreStateAndBaseURL } - if c.AsteriscSnapshotFreq == 0 { + if c.Asterisc.SnapshotFreq == 0 { return ErrMissingAsteriscSnapshotFreq } - if c.AsteriscInfoFreq == 0 { + if c.Asterisc.InfoFreq == 0 { return ErrMissingAsteriscInfoFreq } } diff --git a/op-challenger/config/config_test.go b/op-challenger/config/config_test.go index 297bc60b97dc3..6cfae373277ce 100644 --- a/op-challenger/config/config_test.go +++ b/op-challenger/config/config_test.go @@ -36,17 +36,17 @@ var cannonTraceTypes = []TraceType{TraceTypeCannon, TraceTypePermissioned} var asteriscTraceTypes = []TraceType{TraceTypeAsterisc} func applyValidConfigForCannon(cfg *Config) { - cfg.CannonBin = validCannonBin - cfg.CannonServer = validCannonOpProgramBin + cfg.Cannon.VmBin = validCannonBin + cfg.Cannon.Server = validCannonOpProgramBin cfg.CannonAbsolutePreStateBaseURL = validCannonAbsolutPreStateBaseURL - cfg.CannonNetwork = validCannonNetwork + cfg.Cannon.Network = validCannonNetwork } func applyValidConfigForAsterisc(cfg *Config) { - cfg.AsteriscBin = validAsteriscBin - cfg.AsteriscServer = validAsteriscOpProgramBin + cfg.Asterisc.VmBin = validAsteriscBin + cfg.Asterisc.Server = validAsteriscOpProgramBin cfg.AsteriscAbsolutePreStateBaseURL = validAsteriscAbsolutPreStateBaseURL - cfg.AsteriscNetwork = validAsteriscNetwork + cfg.Asterisc.Network = validAsteriscNetwork } func validConfig(traceType TraceType) Config { @@ -115,13 +115,13 @@ func TestCannonRequiredArgs(t *testing.T) { t.Run(fmt.Sprintf("TestCannonBinRequired-%v", traceType), func(t *testing.T) { config := validConfig(traceType) - config.CannonBin = "" + config.Cannon.VmBin = "" require.ErrorIs(t, config.Check(), ErrMissingCannonBin) }) t.Run(fmt.Sprintf("TestCannonServerRequired-%v", traceType), func(t *testing.T) { config := validConfig(traceType) - config.CannonServer = "" + config.Cannon.Server = "" require.ErrorIs(t, config.Check(), ErrMissingCannonServer) }) @@ -162,7 +162,7 @@ func TestCannonRequiredArgs(t *testing.T) { t.Run(fmt.Sprintf("TestCannonSnapshotFreq-%v", traceType), func(t *testing.T) { t.Run("MustNotBeZero", func(t *testing.T) { cfg := validConfig(traceType) - cfg.CannonSnapshotFreq = 0 + cfg.Cannon.SnapshotFreq = 0 require.ErrorIs(t, cfg.Check(), ErrMissingCannonSnapshotFreq) }) }) @@ -170,46 +170,46 @@ func TestCannonRequiredArgs(t *testing.T) { t.Run(fmt.Sprintf("TestCannonInfoFreq-%v", traceType), func(t *testing.T) { t.Run("MustNotBeZero", func(t *testing.T) { cfg := validConfig(traceType) - cfg.CannonInfoFreq = 0 + cfg.Cannon.InfoFreq = 0 require.ErrorIs(t, cfg.Check(), ErrMissingCannonInfoFreq) }) }) t.Run(fmt.Sprintf("TestCannonNetworkOrRollupConfigRequired-%v", traceType), func(t *testing.T) { cfg := validConfig(traceType) - cfg.CannonNetwork = "" - cfg.CannonRollupConfigPath = "" - cfg.CannonL2GenesisPath = "genesis.json" + cfg.Cannon.Network = "" + cfg.Cannon.RollupConfigPath = "" + cfg.Cannon.L2GenesisPath = "genesis.json" require.ErrorIs(t, cfg.Check(), ErrMissingCannonRollupConfig) }) t.Run(fmt.Sprintf("TestCannonNetworkOrL2GenesisRequired-%v", traceType), func(t *testing.T) { cfg := validConfig(traceType) - cfg.CannonNetwork = "" - cfg.CannonRollupConfigPath = "foo.json" - cfg.CannonL2GenesisPath = "" + cfg.Cannon.Network = "" + cfg.Cannon.RollupConfigPath = "foo.json" + cfg.Cannon.L2GenesisPath = "" require.ErrorIs(t, cfg.Check(), ErrMissingCannonL2Genesis) }) t.Run(fmt.Sprintf("TestMustNotSpecifyNetworkAndRollup-%v", traceType), func(t *testing.T) { cfg := validConfig(traceType) - cfg.CannonNetwork = validCannonNetwork - cfg.CannonRollupConfigPath = "foo.json" - cfg.CannonL2GenesisPath = "" + cfg.Cannon.Network = validCannonNetwork + cfg.Cannon.RollupConfigPath = "foo.json" + cfg.Cannon.L2GenesisPath = "" require.ErrorIs(t, cfg.Check(), ErrCannonNetworkAndRollupConfig) }) t.Run(fmt.Sprintf("TestMustNotSpecifyNetworkAndL2Genesis-%v", traceType), func(t *testing.T) { cfg := validConfig(traceType) - cfg.CannonNetwork = validCannonNetwork - cfg.CannonRollupConfigPath = "" - cfg.CannonL2GenesisPath = "foo.json" + cfg.Cannon.Network = validCannonNetwork + cfg.Cannon.RollupConfigPath = "" + cfg.Cannon.L2GenesisPath = "foo.json" require.ErrorIs(t, cfg.Check(), ErrCannonNetworkAndL2Genesis) }) t.Run(fmt.Sprintf("TestNetworkMustBeValid-%v", traceType), func(t *testing.T) { cfg := validConfig(traceType) - cfg.CannonNetwork = "unknown" + cfg.Cannon.Network = "unknown" require.ErrorIs(t, cfg.Check(), ErrCannonNetworkUnknown) }) } @@ -221,13 +221,13 @@ func TestAsteriscRequiredArgs(t *testing.T) { t.Run(fmt.Sprintf("TestAsteriscBinRequired-%v", traceType), func(t *testing.T) { config := validConfig(traceType) - config.AsteriscBin = "" + config.Asterisc.VmBin = "" require.ErrorIs(t, config.Check(), ErrMissingAsteriscBin) }) t.Run(fmt.Sprintf("TestAsteriscServerRequired-%v", traceType), func(t *testing.T) { config := validConfig(traceType) - config.AsteriscServer = "" + config.Asterisc.Server = "" require.ErrorIs(t, config.Check(), ErrMissingAsteriscServer) }) @@ -268,7 +268,7 @@ func TestAsteriscRequiredArgs(t *testing.T) { t.Run(fmt.Sprintf("TestAsteriscSnapshotFreq-%v", traceType), func(t *testing.T) { t.Run("MustNotBeZero", func(t *testing.T) { cfg := validConfig(traceType) - cfg.AsteriscSnapshotFreq = 0 + cfg.Asterisc.SnapshotFreq = 0 require.ErrorIs(t, cfg.Check(), ErrMissingAsteriscSnapshotFreq) }) }) @@ -276,46 +276,46 @@ func TestAsteriscRequiredArgs(t *testing.T) { t.Run(fmt.Sprintf("TestAsteriscInfoFreq-%v", traceType), func(t *testing.T) { t.Run("MustNotBeZero", func(t *testing.T) { cfg := validConfig(traceType) - cfg.AsteriscInfoFreq = 0 + cfg.Asterisc.InfoFreq = 0 require.ErrorIs(t, cfg.Check(), ErrMissingAsteriscInfoFreq) }) }) t.Run(fmt.Sprintf("TestAsteriscNetworkOrRollupConfigRequired-%v", traceType), func(t *testing.T) { cfg := validConfig(traceType) - cfg.AsteriscNetwork = "" - cfg.AsteriscRollupConfigPath = "" - cfg.AsteriscL2GenesisPath = "genesis.json" + cfg.Asterisc.Network = "" + cfg.Asterisc.RollupConfigPath = "" + cfg.Asterisc.L2GenesisPath = "genesis.json" require.ErrorIs(t, cfg.Check(), ErrMissingAsteriscRollupConfig) }) t.Run(fmt.Sprintf("TestAsteriscNetworkOrL2GenesisRequired-%v", traceType), func(t *testing.T) { cfg := validConfig(traceType) - cfg.AsteriscNetwork = "" - cfg.AsteriscRollupConfigPath = "foo.json" - cfg.AsteriscL2GenesisPath = "" + cfg.Asterisc.Network = "" + cfg.Asterisc.RollupConfigPath = "foo.json" + cfg.Asterisc.L2GenesisPath = "" require.ErrorIs(t, cfg.Check(), ErrMissingAsteriscL2Genesis) }) t.Run(fmt.Sprintf("TestMustNotSpecifyNetworkAndRollup-%v", traceType), func(t *testing.T) { cfg := validConfig(traceType) - cfg.AsteriscNetwork = validAsteriscNetwork - cfg.AsteriscRollupConfigPath = "foo.json" - cfg.AsteriscL2GenesisPath = "" + cfg.Asterisc.Network = validAsteriscNetwork + cfg.Asterisc.RollupConfigPath = "foo.json" + cfg.Asterisc.L2GenesisPath = "" require.ErrorIs(t, cfg.Check(), ErrAsteriscNetworkAndRollupConfig) }) t.Run(fmt.Sprintf("TestMustNotSpecifyNetworkAndL2Genesis-%v", traceType), func(t *testing.T) { cfg := validConfig(traceType) - cfg.AsteriscNetwork = validAsteriscNetwork - cfg.AsteriscRollupConfigPath = "" - cfg.AsteriscL2GenesisPath = "foo.json" + cfg.Asterisc.Network = validAsteriscNetwork + cfg.Asterisc.RollupConfigPath = "" + cfg.Asterisc.L2GenesisPath = "foo.json" require.ErrorIs(t, cfg.Check(), ErrAsteriscNetworkAndL2Genesis) }) t.Run(fmt.Sprintf("TestNetworkMustBeValid-%v", traceType), func(t *testing.T) { cfg := validConfig(traceType) - cfg.AsteriscNetwork = "unknown" + cfg.Asterisc.Network = "unknown" require.ErrorIs(t, cfg.Check(), ErrAsteriscNetworkUnknown) }) } @@ -404,9 +404,9 @@ func TestRequireConfigForMultipleTraceTypesForCannonAndAsterisc(t *testing.T) { require.NoError(t, cfg.Check()) // Require cannon specific args - cfg.CannonBin = "" + cfg.Cannon.VmBin = "" require.ErrorIs(t, cfg.Check(), ErrMissingCannonBin) - cfg.CannonBin = validCannonBin + cfg.Cannon.VmBin = validCannonBin // Require asterisc specific args cfg.AsteriscAbsolutePreState = "" @@ -415,9 +415,9 @@ func TestRequireConfigForMultipleTraceTypesForCannonAndAsterisc(t *testing.T) { cfg.AsteriscAbsolutePreState = validAsteriscAbsolutPreState // Require cannon specific args - cfg.AsteriscServer = "" + cfg.Asterisc.Server = "" require.ErrorIs(t, cfg.Check(), ErrMissingAsteriscServer) - cfg.AsteriscServer = validAsteriscOpProgramBin + cfg.Asterisc.Server = validAsteriscOpProgramBin // Check final config is valid require.NoError(t, cfg.Check()) diff --git a/op-challenger/flags/flags.go b/op-challenger/flags/flags.go index 5c2c6ea199599..726501ec65ec9 100644 --- a/op-challenger/flags/flags.go +++ b/op-challenger/flags/flags.go @@ -7,6 +7,7 @@ import ( "slices" "strings" + "github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/vm" "github.com/ethereum-optimism/optimism/op-service/flags" "github.com/ethereum-optimism/superchain-registry/superchain" "github.com/ethereum/go-ethereum/common" @@ -496,39 +497,53 @@ func NewConfigFromCLI(ctx *cli.Context, logger log.Logger) (*config.Config, erro if ctx.IsSet(flags.NetworkFlagName) { asteriscNetwork = ctx.String(flags.NetworkFlagName) } + l1EthRpc := ctx.String(L1EthRpcFlag.Name) + l1Beacon := ctx.String(L1BeaconFlag.Name) return &config.Config{ // Required Flags - L1EthRpc: ctx.String(L1EthRpcFlag.Name), - L1Beacon: ctx.String(L1BeaconFlag.Name), - TraceTypes: traceTypes, - GameFactoryAddress: gameFactoryAddress, - GameAllowlist: allowedGames, - GameWindow: ctx.Duration(GameWindowFlag.Name), - MaxConcurrency: maxConcurrency, - L2Rpc: l2Rpc, - MaxPendingTx: ctx.Uint64(MaxPendingTransactionsFlag.Name), - PollInterval: ctx.Duration(HTTPPollInterval.Name), - AdditionalBondClaimants: claimants, - RollupRpc: ctx.String(RollupRpcFlag.Name), - CannonNetwork: cannonNetwork, - CannonRollupConfigPath: ctx.String(CannonRollupConfigFlag.Name), - CannonL2GenesisPath: ctx.String(CannonL2GenesisFlag.Name), - CannonBin: ctx.String(CannonBinFlag.Name), - CannonServer: ctx.String(CannonServerFlag.Name), - CannonAbsolutePreState: ctx.String(CannonPreStateFlag.Name), - CannonAbsolutePreStateBaseURL: cannonPrestatesURL, - Datadir: ctx.String(DatadirFlag.Name), - CannonSnapshotFreq: ctx.Uint(CannonSnapshotFreqFlag.Name), - CannonInfoFreq: ctx.Uint(CannonInfoFreqFlag.Name), - AsteriscNetwork: asteriscNetwork, - AsteriscRollupConfigPath: ctx.String(AsteriscRollupConfigFlag.Name), - AsteriscL2GenesisPath: ctx.String(AsteriscL2GenesisFlag.Name), - AsteriscBin: ctx.String(AsteriscBinFlag.Name), - AsteriscServer: ctx.String(AsteriscServerFlag.Name), + L1EthRpc: l1EthRpc, + L1Beacon: l1Beacon, + TraceTypes: traceTypes, + GameFactoryAddress: gameFactoryAddress, + GameAllowlist: allowedGames, + GameWindow: ctx.Duration(GameWindowFlag.Name), + MaxConcurrency: maxConcurrency, + L2Rpc: l2Rpc, + MaxPendingTx: ctx.Uint64(MaxPendingTransactionsFlag.Name), + PollInterval: ctx.Duration(HTTPPollInterval.Name), + AdditionalBondClaimants: claimants, + RollupRpc: ctx.String(RollupRpcFlag.Name), + Cannon: vm.Config{ + VmType: config.TraceTypeCannon.String(), + L1: l1EthRpc, + L1Beacon: l1Beacon, + L2: l2Rpc, + VmBin: ctx.String(CannonBinFlag.Name), + Server: ctx.String(CannonServerFlag.Name), + Network: cannonNetwork, + RollupConfigPath: ctx.String(CannonRollupConfigFlag.Name), + L2GenesisPath: ctx.String(CannonL2GenesisFlag.Name), + SnapshotFreq: ctx.Uint(CannonSnapshotFreqFlag.Name), + InfoFreq: ctx.Uint(CannonInfoFreqFlag.Name), + }, + CannonAbsolutePreState: ctx.String(CannonPreStateFlag.Name), + CannonAbsolutePreStateBaseURL: cannonPrestatesURL, + Datadir: ctx.String(DatadirFlag.Name), + Asterisc: vm.Config{ + VmType: config.TraceTypeAsterisc.String(), + L1: l1EthRpc, + L1Beacon: l1Beacon, + L2: l2Rpc, + VmBin: ctx.String(AsteriscBinFlag.Name), + Server: ctx.String(AsteriscServerFlag.Name), + Network: asteriscNetwork, + RollupConfigPath: ctx.String(AsteriscRollupConfigFlag.Name), + L2GenesisPath: ctx.String(AsteriscL2GenesisFlag.Name), + SnapshotFreq: ctx.Uint(AsteriscSnapshotFreqFlag.Name), + InfoFreq: ctx.Uint(AsteriscInfoFreqFlag.Name), + }, AsteriscAbsolutePreState: ctx.String(AsteriscPreStateFlag.Name), AsteriscAbsolutePreStateBaseURL: asteriscPreStatesURL, - AsteriscSnapshotFreq: ctx.Uint(AsteriscSnapshotFreqFlag.Name), - AsteriscInfoFreq: ctx.Uint(AsteriscInfoFreqFlag.Name), TxMgrConfig: txMgrConfig, MetricsConfig: metricsConfig, PprofConfig: pprofConfig, diff --git a/op-challenger/game/fault/register.go b/op-challenger/game/fault/register.go index cb23266a2b123..7478dd4b23fed 100644 --- a/op-challenger/game/fault/register.go +++ b/op-challenger/game/fault/register.go @@ -254,7 +254,7 @@ func registerAsterisc( if err != nil { return nil, fmt.Errorf("failed to get asterisc prestate: %w", err) } - accessor, err := outputs.NewOutputAsteriscTraceAccessor(logger, m, cfg, l2Client, prestateProvider, asteriscPrestate, rollupClient, dir, l1HeadID, splitDepth, prestateBlock, poststateBlock) + accessor, err := outputs.NewOutputAsteriscTraceAccessor(logger, m, cfg.Asterisc, l2Client, prestateProvider, asteriscPrestate, rollupClient, dir, l1HeadID, splitDepth, prestateBlock, poststateBlock) if err != nil { return nil, err } @@ -349,7 +349,7 @@ func registerCannon( if err != nil { return nil, fmt.Errorf("failed to get cannon prestate: %w", err) } - accessor, err := outputs.NewOutputCannonTraceAccessor(logger, m, cfg, l2Client, prestateProvider, cannonPrestate, rollupClient, dir, l1HeadID, splitDepth, prestateBlock, poststateBlock) + accessor, err := outputs.NewOutputCannonTraceAccessor(logger, m, cfg.Cannon, l2Client, prestateProvider, cannonPrestate, rollupClient, dir, l1HeadID, splitDepth, prestateBlock, poststateBlock) if err != nil { return nil, err } diff --git a/op-challenger/game/fault/trace/asterisc/executor.go b/op-challenger/game/fault/trace/asterisc/executor.go deleted file mode 100644 index b84d5e444568a..0000000000000 --- a/op-challenger/game/fault/trace/asterisc/executor.go +++ /dev/null @@ -1,127 +0,0 @@ -package asterisc - -import ( - "context" - "fmt" - "math" - "os" - "path/filepath" - "strconv" - "strings" - "time" - - "github.com/ethereum-optimism/optimism/op-challenger/config" - "github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/utils" - "github.com/ethereum/go-ethereum/log" -) - -type Executor struct { - logger log.Logger - metrics AsteriscMetricer - l1 string - l1Beacon string - l2 string - inputs utils.LocalGameInputs - asterisc string - server string - network string - rollupConfig string - l2Genesis string - absolutePreState string - snapshotFreq uint - infoFreq uint - selectSnapshot utils.SnapshotSelect - cmdExecutor utils.CmdExecutor -} - -func NewExecutor(logger log.Logger, m AsteriscMetricer, cfg *config.Config, prestate string, inputs utils.LocalGameInputs) *Executor { - return &Executor{ - logger: logger, - metrics: m, - l1: cfg.L1EthRpc, - l1Beacon: cfg.L1Beacon, - l2: cfg.L2Rpc, - inputs: inputs, - asterisc: cfg.AsteriscBin, - server: cfg.AsteriscServer, - network: cfg.AsteriscNetwork, - rollupConfig: cfg.AsteriscRollupConfigPath, - l2Genesis: cfg.AsteriscL2GenesisPath, - absolutePreState: prestate, - snapshotFreq: cfg.AsteriscSnapshotFreq, - infoFreq: cfg.AsteriscInfoFreq, - selectSnapshot: utils.FindStartingSnapshot, - cmdExecutor: utils.RunCmd, - } -} - -// GenerateProof executes asterisc to generate a proof at the specified trace index. -// The proof is stored at the specified directory. -func (e *Executor) GenerateProof(ctx context.Context, dir string, i uint64) error { - return e.generateProof(ctx, dir, i, i) -} - -// generateProof executes asterisc from the specified starting trace index until the end trace index. -// The proof is stored at the specified directory. -func (e *Executor) generateProof(ctx context.Context, dir string, begin uint64, end uint64, extraAsteriscArgs ...string) error { - snapshotDir := filepath.Join(dir, utils.SnapsDir) - start, err := e.selectSnapshot(e.logger, snapshotDir, e.absolutePreState, begin) - if err != nil { - return fmt.Errorf("find starting snapshot: %w", err) - } - proofDir := filepath.Join(dir, proofsDir) - dataDir := utils.PreimageDir(dir) - lastGeneratedState := filepath.Join(dir, utils.FinalState) - args := []string{ - "run", - "--input", start, - "--output", lastGeneratedState, - "--meta", "", - "--info-at", "%" + strconv.FormatUint(uint64(e.infoFreq), 10), - "--proof-at", "=" + strconv.FormatUint(end, 10), - "--proof-fmt", filepath.Join(proofDir, "%d.json.gz"), - "--snapshot-at", "%" + strconv.FormatUint(uint64(e.snapshotFreq), 10), - "--snapshot-fmt", filepath.Join(snapshotDir, "%d.json.gz"), - } - if end < math.MaxUint64 { - args = append(args, "--stop-at", "="+strconv.FormatUint(end+1, 10)) - } - args = append(args, extraAsteriscArgs...) - args = append(args, - "--", - e.server, "--server", - "--l1", e.l1, - "--l1.beacon", e.l1Beacon, - "--l2", e.l2, - "--datadir", dataDir, - "--l1.head", e.inputs.L1Head.Hex(), - "--l2.head", e.inputs.L2Head.Hex(), - "--l2.outputroot", e.inputs.L2OutputRoot.Hex(), - "--l2.claim", e.inputs.L2Claim.Hex(), - "--l2.blocknumber", e.inputs.L2BlockNumber.Text(10), - ) - if e.network != "" { - args = append(args, "--network", e.network) - } - if e.rollupConfig != "" { - args = append(args, "--rollup.config", e.rollupConfig) - } - if e.l2Genesis != "" { - args = append(args, "--l2.genesis", e.l2Genesis) - } - - if err := os.MkdirAll(snapshotDir, 0755); err != nil { - return fmt.Errorf("could not create snapshot directory %v: %w", snapshotDir, err) - } - if err := os.MkdirAll(dataDir, 0755); err != nil { - return fmt.Errorf("could not create preimage cache directory %v: %w", dataDir, err) - } - if err := os.MkdirAll(proofDir, 0755); err != nil { - return fmt.Errorf("could not create proofs directory %v: %w", proofDir, err) - } - e.logger.Info("Generating trace", "proof", end, "cmd", e.asterisc, "args", strings.Join(args, ", ")) - execStart := time.Now() - err = e.cmdExecutor(ctx, e.logger.New("proof", end), e.asterisc, args...) - e.metrics.RecordAsteriscExecutionTime(time.Since(execStart).Seconds()) - return err -} diff --git a/op-challenger/game/fault/trace/asterisc/provider.go b/op-challenger/game/fault/trace/asterisc/provider.go index cbf7b6241ae2d..5c481777b6ce1 100644 --- a/op-challenger/game/fault/trace/asterisc/provider.go +++ b/op-challenger/game/fault/trace/asterisc/provider.go @@ -12,6 +12,7 @@ import ( "github.com/ethereum-optimism/optimism/op-challenger/config" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/utils" + "github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/vm" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/types" "github.com/ethereum-optimism/optimism/op-program/host/kvstore" "github.com/ethereum-optimism/optimism/op-service/ioutil" @@ -19,15 +20,6 @@ import ( "github.com/ethereum/go-ethereum/log" ) -const ( - proofsDir = "proofs" - diskStateCache = "state.json.gz" -) - -type AsteriscMetricer interface { - RecordAsteriscExecutionTime(t float64) -} - type AsteriscTraceProvider struct { logger log.Logger dir string @@ -43,14 +35,14 @@ type AsteriscTraceProvider struct { lastStep uint64 } -func NewTraceProvider(logger log.Logger, m AsteriscMetricer, cfg *config.Config, prestateProvider types.PrestateProvider, asteriscPrestate string, localInputs utils.LocalGameInputs, dir string, gameDepth types.Depth) *AsteriscTraceProvider { +func NewTraceProvider(logger log.Logger, m vm.Metricer, cfg vm.Config, prestateProvider types.PrestateProvider, asteriscPrestate string, localInputs utils.LocalGameInputs, dir string, gameDepth types.Depth) *AsteriscTraceProvider { return &AsteriscTraceProvider{ logger: logger, dir: dir, prestate: asteriscPrestate, - generator: NewExecutor(logger, m, cfg, asteriscPrestate, localInputs), + generator: vm.NewExecutor(logger, m, cfg, asteriscPrestate, localInputs), gameDepth: gameDepth, - preimageLoader: utils.NewPreimageLoader(kvstore.NewDiskKV(utils.PreimageDir(dir)).Get), + preimageLoader: utils.NewPreimageLoader(kvstore.NewDiskKV(vm.PreimageDir(dir)).Get), PrestateProvider: prestateProvider, } } @@ -116,7 +108,7 @@ func (p *AsteriscTraceProvider) loadProof(ctx context.Context, i uint64) (*utils if p.lastStep != 0 && i > p.lastStep { i = p.lastStep } - path := filepath.Join(p.dir, proofsDir, fmt.Sprintf("%d.json.gz", i)) + path := filepath.Join(p.dir, utils.ProofsDir, fmt.Sprintf("%d.json.gz", i)) file, err := ioutil.OpenDecompressed(path) if errors.Is(err, os.ErrNotExist) { if err := p.generator.GenerateProof(ctx, p.dir, i); err != nil { @@ -167,7 +159,7 @@ func (p *AsteriscTraceProvider) loadProof(ctx context.Context, i uint64) (*utils } func (c *AsteriscTraceProvider) finalState() (*VMState, error) { - state, err := parseState(filepath.Join(c.dir, utils.FinalState)) + state, err := parseState(filepath.Join(c.dir, vm.FinalState)) if err != nil { return nil, fmt.Errorf("cannot read final state: %w", err) } @@ -180,21 +172,21 @@ type AsteriscTraceProviderForTest struct { *AsteriscTraceProvider } -func NewTraceProviderForTest(logger log.Logger, m AsteriscMetricer, cfg *config.Config, localInputs utils.LocalGameInputs, dir string, gameDepth types.Depth) *AsteriscTraceProviderForTest { +func NewTraceProviderForTest(logger log.Logger, m vm.Metricer, cfg *config.Config, localInputs utils.LocalGameInputs, dir string, gameDepth types.Depth) *AsteriscTraceProviderForTest { p := &AsteriscTraceProvider{ logger: logger, dir: dir, prestate: cfg.AsteriscAbsolutePreState, - generator: NewExecutor(logger, m, cfg, cfg.AsteriscAbsolutePreState, localInputs), + generator: vm.NewExecutor(logger, m, cfg.Asterisc, cfg.AsteriscAbsolutePreState, localInputs), gameDepth: gameDepth, - preimageLoader: utils.NewPreimageLoader(kvstore.NewDiskKV(utils.PreimageDir(dir)).Get), + preimageLoader: utils.NewPreimageLoader(kvstore.NewDiskKV(vm.PreimageDir(dir)).Get), } return &AsteriscTraceProviderForTest{p} } func (p *AsteriscTraceProviderForTest) FindStep(ctx context.Context, start uint64, preimage utils.PreimageOpt) (uint64, error) { // Run asterisc to find the step that meets the preimage conditions - if err := p.generator.(*Executor).generateProof(ctx, p.dir, start, math.MaxUint64, preimage()...); err != nil { + if err := p.generator.(*vm.Executor).DoGenerateProof(ctx, p.dir, start, math.MaxUint64, preimage()...); err != nil { return 0, fmt.Errorf("generate asterisc trace (until preimage read): %w", err) } // Load the step from the state asterisc finished with diff --git a/op-challenger/game/fault/trace/asterisc/provider_test.go b/op-challenger/game/fault/trace/asterisc/provider_test.go index 950a5ad70375a..939a27decc304 100644 --- a/op-challenger/game/fault/trace/asterisc/provider_test.go +++ b/op-challenger/game/fault/trace/asterisc/provider_test.go @@ -12,6 +12,7 @@ import ( "testing" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/utils" + "github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/vm" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/types" "github.com/ethereum-optimism/optimism/op-service/ioutil" "github.com/ethereum-optimism/optimism/op-service/testlog" @@ -205,12 +206,12 @@ func setupTestData(t *testing.T) (string, string) { entries, err := testData.ReadDir(srcDir) require.NoError(t, err) dataDir := t.TempDir() - require.NoError(t, os.Mkdir(filepath.Join(dataDir, proofsDir), 0o777)) + require.NoError(t, os.Mkdir(filepath.Join(dataDir, utils.ProofsDir), 0o777)) for _, entry := range entries { path := filepath.Join(srcDir, entry.Name()) file, err := testData.ReadFile(path) require.NoErrorf(t, err, "reading %v", path) - proofFile := filepath.Join(dataDir, proofsDir, entry.Name()+".gz") + proofFile := filepath.Join(dataDir, utils.ProofsDir, entry.Name()+".gz") err = ioutil.WriteCompressedBytes(proofFile, file, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0o644) require.NoErrorf(t, err, "writing %v", path) } @@ -241,7 +242,7 @@ func (e *stubGenerator) GenerateProof(ctx context.Context, dir string, i uint64) var err error if e.finalState != nil && e.finalState.Step <= i { // Requesting a trace index past the end of the trace - proofFile = filepath.Join(dir, utils.FinalState) + proofFile = filepath.Join(dir, vm.FinalState) data, err = json.Marshal(e.finalState) if err != nil { return err @@ -249,7 +250,7 @@ func (e *stubGenerator) GenerateProof(ctx context.Context, dir string, i uint64) return ioutil.WriteCompressedBytes(proofFile, data, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0o644) } if e.proof != nil { - proofFile = filepath.Join(dir, proofsDir, fmt.Sprintf("%d.json.gz", i)) + proofFile = filepath.Join(dir, utils.ProofsDir, fmt.Sprintf("%d.json.gz", i)) data, err = json.Marshal(e.proof) if err != nil { return err diff --git a/op-challenger/game/fault/trace/cannon/executor.go b/op-challenger/game/fault/trace/cannon/executor.go deleted file mode 100644 index 688fd87d7fa2c..0000000000000 --- a/op-challenger/game/fault/trace/cannon/executor.go +++ /dev/null @@ -1,127 +0,0 @@ -package cannon - -import ( - "context" - "fmt" - "math" - "os" - "path/filepath" - "strconv" - "strings" - "time" - - "github.com/ethereum-optimism/optimism/op-challenger/config" - "github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/utils" - "github.com/ethereum/go-ethereum/log" -) - -type Executor struct { - logger log.Logger - metrics CannonMetricer - l1 string - l1Beacon string - l2 string - inputs utils.LocalGameInputs - cannon string - server string - network string - rollupConfig string - l2Genesis string - absolutePreState string - snapshotFreq uint - infoFreq uint - selectSnapshot utils.SnapshotSelect - cmdExecutor utils.CmdExecutor -} - -func NewExecutor(logger log.Logger, m CannonMetricer, cfg *config.Config, prestate string, inputs utils.LocalGameInputs) *Executor { - return &Executor{ - logger: logger, - metrics: m, - l1: cfg.L1EthRpc, - l1Beacon: cfg.L1Beacon, - l2: cfg.L2Rpc, - inputs: inputs, - cannon: cfg.CannonBin, - server: cfg.CannonServer, - network: cfg.CannonNetwork, - rollupConfig: cfg.CannonRollupConfigPath, - l2Genesis: cfg.CannonL2GenesisPath, - absolutePreState: prestate, - snapshotFreq: cfg.CannonSnapshotFreq, - infoFreq: cfg.CannonInfoFreq, - selectSnapshot: utils.FindStartingSnapshot, - cmdExecutor: utils.RunCmd, - } -} - -// GenerateProof executes cannon to generate a proof at the specified trace index. -// The proof is stored at the specified directory. -func (e *Executor) GenerateProof(ctx context.Context, dir string, i uint64) error { - return e.generateProof(ctx, dir, i, i) -} - -// generateProof executes cannon from the specified starting trace index until the end trace index. -// The proof is stored at the specified directory. -func (e *Executor) generateProof(ctx context.Context, dir string, begin uint64, end uint64, extraCannonArgs ...string) error { - snapshotDir := filepath.Join(dir, utils.SnapsDir) - start, err := e.selectSnapshot(e.logger, snapshotDir, e.absolutePreState, begin) - if err != nil { - return fmt.Errorf("find starting snapshot: %w", err) - } - proofDir := filepath.Join(dir, utils.ProofsDir) - dataDir := utils.PreimageDir(dir) - lastGeneratedState := filepath.Join(dir, utils.FinalState) - args := []string{ - "run", - "--input", start, - "--output", lastGeneratedState, - "--meta", "", - "--info-at", "%" + strconv.FormatUint(uint64(e.infoFreq), 10), - "--proof-at", "=" + strconv.FormatUint(end, 10), - "--proof-fmt", filepath.Join(proofDir, "%d.json.gz"), - "--snapshot-at", "%" + strconv.FormatUint(uint64(e.snapshotFreq), 10), - "--snapshot-fmt", filepath.Join(snapshotDir, "%d.json.gz"), - } - if end < math.MaxUint64 { - args = append(args, "--stop-at", "="+strconv.FormatUint(end+1, 10)) - } - args = append(args, extraCannonArgs...) - args = append(args, - "--", - e.server, "--server", - "--l1", e.l1, - "--l1.beacon", e.l1Beacon, - "--l2", e.l2, - "--datadir", dataDir, - "--l1.head", e.inputs.L1Head.Hex(), - "--l2.head", e.inputs.L2Head.Hex(), - "--l2.outputroot", e.inputs.L2OutputRoot.Hex(), - "--l2.claim", e.inputs.L2Claim.Hex(), - "--l2.blocknumber", e.inputs.L2BlockNumber.Text(10), - ) - if e.network != "" { - args = append(args, "--network", e.network) - } - if e.rollupConfig != "" { - args = append(args, "--rollup.config", e.rollupConfig) - } - if e.l2Genesis != "" { - args = append(args, "--l2.genesis", e.l2Genesis) - } - - if err := os.MkdirAll(snapshotDir, 0755); err != nil { - return fmt.Errorf("could not create snapshot directory %v: %w", snapshotDir, err) - } - if err := os.MkdirAll(dataDir, 0755); err != nil { - return fmt.Errorf("could not create preimage cache directory %v: %w", dataDir, err) - } - if err := os.MkdirAll(proofDir, 0755); err != nil { - return fmt.Errorf("could not create proofs directory %v: %w", proofDir, err) - } - e.logger.Info("Generating trace", "proof", end, "cmd", e.cannon, "args", strings.Join(args, ", ")) - execStart := time.Now() - err = e.cmdExecutor(ctx, e.logger.New("proof", end), e.cannon, args...) - e.metrics.RecordCannonExecutionTime(time.Since(execStart).Seconds()) - return err -} diff --git a/op-challenger/game/fault/trace/cannon/executor_test.go b/op-challenger/game/fault/trace/cannon/executor_test.go deleted file mode 100644 index 4f20c426e7780..0000000000000 --- a/op-challenger/game/fault/trace/cannon/executor_test.go +++ /dev/null @@ -1,227 +0,0 @@ -package cannon - -import ( - "context" - "fmt" - "math" - "math/big" - "os" - "path/filepath" - "testing" - "time" - - "github.com/ethereum-optimism/optimism/op-challenger/config" - "github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/utils" - "github.com/ethereum-optimism/optimism/op-challenger/metrics" - "github.com/ethereum-optimism/optimism/op-service/testlog" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/log" - "github.com/stretchr/testify/require" -) - -const execTestCannonPrestate = "/foo/pre.json" - -func TestGenerateProof(t *testing.T) { - input := "starting.json" - tempDir := t.TempDir() - dir := filepath.Join(tempDir, "gameDir") - cfg := config.NewConfig(common.Address{0xbb}, "http://localhost:8888", "http://localhost:9000", "http://localhost:9096", "http://localhost:9095", tempDir, config.TraceTypeCannon) - cfg.L2Rpc = "http://localhost:9999" - prestate := "pre.json" - cfg.CannonBin = "./bin/cannon" - cfg.CannonServer = "./bin/op-program" - cfg.CannonSnapshotFreq = 500 - cfg.CannonInfoFreq = 900 - - inputs := utils.LocalGameInputs{ - L1Head: common.Hash{0x11}, - L2Head: common.Hash{0x22}, - L2OutputRoot: common.Hash{0x33}, - L2Claim: common.Hash{0x44}, - L2BlockNumber: big.NewInt(3333), - } - captureExec := func(t *testing.T, cfg config.Config, proofAt uint64) (string, string, map[string]string) { - m := &cannonDurationMetrics{} - executor := NewExecutor(testlog.Logger(t, log.LevelInfo), m, &cfg, prestate, inputs) - executor.selectSnapshot = func(logger log.Logger, dir string, absolutePreState string, i uint64) (string, error) { - return input, nil - } - var binary string - var subcommand string - args := make(map[string]string) - executor.cmdExecutor = func(ctx context.Context, l log.Logger, b string, a ...string) error { - binary = b - subcommand = a[0] - for i := 1; i < len(a); { - if a[i] == "--" { - // Skip over the divider between cannon and server program - i += 1 - continue - } - args[a[i]] = a[i+1] - i += 2 - } - return nil - } - err := executor.GenerateProof(context.Background(), dir, proofAt) - require.NoError(t, err) - require.Equal(t, 1, m.executionTimeRecordCount, "Should record cannon execution time") - return binary, subcommand, args - } - - t.Run("Network", func(t *testing.T) { - cfg.CannonNetwork = "mainnet" - cfg.CannonRollupConfigPath = "" - cfg.CannonL2GenesisPath = "" - binary, subcommand, args := captureExec(t, cfg, 150_000_000) - require.DirExists(t, filepath.Join(dir, utils.PreimagesDir)) - require.DirExists(t, filepath.Join(dir, utils.ProofsDir)) - require.DirExists(t, filepath.Join(dir, utils.SnapsDir)) - require.Equal(t, cfg.CannonBin, binary) - require.Equal(t, "run", subcommand) - require.Equal(t, input, args["--input"]) - require.Contains(t, args, "--meta") - require.Equal(t, "", args["--meta"]) - require.Equal(t, filepath.Join(dir, utils.FinalState), args["--output"]) - require.Equal(t, "=150000000", args["--proof-at"]) - require.Equal(t, "=150000001", args["--stop-at"]) - require.Equal(t, "%500", args["--snapshot-at"]) - require.Equal(t, "%900", args["--info-at"]) - // Slight quirk of how we pair off args - // The server binary winds up as the key and the first arg --server as the value which has no value - // Then everything else pairs off correctly again - require.Equal(t, "--server", args[cfg.CannonServer]) - require.Equal(t, cfg.L1EthRpc, args["--l1"]) - require.Equal(t, cfg.L1Beacon, args["--l1.beacon"]) - require.Equal(t, cfg.L2Rpc, args["--l2"]) - require.Equal(t, filepath.Join(dir, utils.PreimagesDir), args["--datadir"]) - require.Equal(t, filepath.Join(dir, utils.ProofsDir, "%d.json.gz"), args["--proof-fmt"]) - require.Equal(t, filepath.Join(dir, utils.SnapsDir, "%d.json.gz"), args["--snapshot-fmt"]) - require.Equal(t, cfg.CannonNetwork, args["--network"]) - require.NotContains(t, args, "--rollup.config") - require.NotContains(t, args, "--l2.genesis") - - // Local game inputs - require.Equal(t, inputs.L1Head.Hex(), args["--l1.head"]) - require.Equal(t, inputs.L2Head.Hex(), args["--l2.head"]) - require.Equal(t, inputs.L2OutputRoot.Hex(), args["--l2.outputroot"]) - require.Equal(t, inputs.L2Claim.Hex(), args["--l2.claim"]) - require.Equal(t, "3333", args["--l2.blocknumber"]) - }) - - t.Run("RollupAndGenesis", func(t *testing.T) { - cfg.CannonNetwork = "" - cfg.CannonRollupConfigPath = "rollup.json" - cfg.CannonL2GenesisPath = "genesis.json" - _, _, args := captureExec(t, cfg, 150_000_000) - require.NotContains(t, args, "--network") - require.Equal(t, cfg.CannonRollupConfigPath, args["--rollup.config"]) - require.Equal(t, cfg.CannonL2GenesisPath, args["--l2.genesis"]) - }) - - t.Run("NoStopAtWhenProofIsMaxUInt", func(t *testing.T) { - cfg.CannonNetwork = "mainnet" - cfg.CannonRollupConfigPath = "rollup.json" - cfg.CannonL2GenesisPath = "genesis.json" - _, _, args := captureExec(t, cfg, math.MaxUint64) - // stop-at would need to be one more than the proof step which would overflow back to 0 - // so expect that it will be omitted. We'll ultimately want cannon to execute until the program exits. - require.NotContains(t, args, "--stop-at") - }) -} - -func TestRunCmdLogsOutput(t *testing.T) { - bin := "/bin/echo" - if _, err := os.Stat(bin); err != nil { - t.Skip(bin, " not available", err) - } - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - logger, logs := testlog.CaptureLogger(t, log.LevelInfo) - err := utils.RunCmd(ctx, logger, bin, "Hello World") - require.NoError(t, err) - levelFilter := testlog.NewLevelFilter(log.LevelInfo) - msgFilter := testlog.NewMessageFilter("Hello World") - require.NotNil(t, logs.FindLog(levelFilter, msgFilter)) -} - -func TestFindStartingSnapshot(t *testing.T) { - logger := testlog.Logger(t, log.LevelInfo) - - withSnapshots := func(t *testing.T, files ...string) string { - dir := t.TempDir() - for _, file := range files { - require.NoError(t, os.WriteFile(fmt.Sprintf("%v/%v", dir, file), nil, 0o644)) - } - return dir - } - - t.Run("UsePrestateWhenSnapshotsDirDoesNotExist", func(t *testing.T) { - dir := t.TempDir() - snapshot, err := utils.FindStartingSnapshot(logger, filepath.Join(dir, "doesNotExist"), execTestCannonPrestate, 1200) - require.NoError(t, err) - require.Equal(t, execTestCannonPrestate, snapshot) - }) - - t.Run("UsePrestateWhenSnapshotsDirEmpty", func(t *testing.T) { - dir := withSnapshots(t) - snapshot, err := utils.FindStartingSnapshot(logger, dir, execTestCannonPrestate, 1200) - require.NoError(t, err) - require.Equal(t, execTestCannonPrestate, snapshot) - }) - - t.Run("UsePrestateWhenNoSnapshotBeforeTraceIndex", func(t *testing.T) { - dir := withSnapshots(t, "100.json", "200.json") - snapshot, err := utils.FindStartingSnapshot(logger, dir, execTestCannonPrestate, 99) - require.NoError(t, err) - require.Equal(t, execTestCannonPrestate, snapshot) - - snapshot, err = utils.FindStartingSnapshot(logger, dir, execTestCannonPrestate, 100) - require.NoError(t, err) - require.Equal(t, execTestCannonPrestate, snapshot) - }) - - t.Run("UseClosestAvailableSnapshot", func(t *testing.T) { - dir := withSnapshots(t, "100.json.gz", "123.json.gz", "250.json.gz") - - snapshot, err := utils.FindStartingSnapshot(logger, dir, execTestCannonPrestate, 101) - require.NoError(t, err) - require.Equal(t, filepath.Join(dir, "100.json.gz"), snapshot) - - snapshot, err = utils.FindStartingSnapshot(logger, dir, execTestCannonPrestate, 123) - require.NoError(t, err) - require.Equal(t, filepath.Join(dir, "100.json.gz"), snapshot) - - snapshot, err = utils.FindStartingSnapshot(logger, dir, execTestCannonPrestate, 124) - require.NoError(t, err) - require.Equal(t, filepath.Join(dir, "123.json.gz"), snapshot) - - snapshot, err = utils.FindStartingSnapshot(logger, dir, execTestCannonPrestate, 256) - require.NoError(t, err) - require.Equal(t, filepath.Join(dir, "250.json.gz"), snapshot) - }) - - t.Run("IgnoreDirectories", func(t *testing.T) { - dir := withSnapshots(t, "100.json.gz") - require.NoError(t, os.Mkdir(filepath.Join(dir, "120.json.gz"), 0o777)) - snapshot, err := utils.FindStartingSnapshot(logger, dir, execTestCannonPrestate, 150) - require.NoError(t, err) - require.Equal(t, filepath.Join(dir, "100.json.gz"), snapshot) - }) - - t.Run("IgnoreUnexpectedFiles", func(t *testing.T) { - dir := withSnapshots(t, ".file", "100.json.gz", "foo", "bar.json.gz") - snapshot, err := utils.FindStartingSnapshot(logger, dir, execTestCannonPrestate, 150) - require.NoError(t, err) - require.Equal(t, filepath.Join(dir, "100.json.gz"), snapshot) - }) -} - -type cannonDurationMetrics struct { - metrics.NoopMetricsImpl - executionTimeRecordCount int -} - -func (c *cannonDurationMetrics) RecordCannonExecutionTime(_ float64) { - c.executionTimeRecordCount++ -} diff --git a/op-challenger/game/fault/trace/cannon/provider.go b/op-challenger/game/fault/trace/cannon/provider.go index 704d88e2e6a41..c565b9b39b75d 100644 --- a/op-challenger/game/fault/trace/cannon/provider.go +++ b/op-challenger/game/fault/trace/cannon/provider.go @@ -12,6 +12,7 @@ import ( "github.com/ethereum-optimism/optimism/op-challenger/config" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/utils" + "github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/vm" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/types" "github.com/ethereum-optimism/optimism/op-program/host/kvstore" "github.com/ethereum-optimism/optimism/op-service/ioutil" @@ -22,10 +23,6 @@ import ( "github.com/ethereum-optimism/optimism/cannon/mipsevm" ) -type CannonMetricer interface { - RecordCannonExecutionTime(t float64) -} - type CannonTraceProvider struct { logger log.Logger dir string @@ -41,14 +38,14 @@ type CannonTraceProvider struct { lastStep uint64 } -func NewTraceProvider(logger log.Logger, m CannonMetricer, cfg *config.Config, prestateProvider types.PrestateProvider, prestate string, localInputs utils.LocalGameInputs, dir string, gameDepth types.Depth) *CannonTraceProvider { +func NewTraceProvider(logger log.Logger, m vm.Metricer, cfg vm.Config, prestateProvider types.PrestateProvider, prestate string, localInputs utils.LocalGameInputs, dir string, gameDepth types.Depth) *CannonTraceProvider { return &CannonTraceProvider{ logger: logger, dir: dir, prestate: prestate, - generator: NewExecutor(logger, m, cfg, prestate, localInputs), + generator: vm.NewExecutor(logger, m, cfg, prestate, localInputs), gameDepth: gameDepth, - preimageLoader: utils.NewPreimageLoader(kvstore.NewDiskKV(utils.PreimageDir(dir)).Get), + preimageLoader: utils.NewPreimageLoader(kvstore.NewDiskKV(vm.PreimageDir(dir)).Get), PrestateProvider: prestateProvider, } } @@ -170,7 +167,7 @@ func (p *CannonTraceProvider) loadProof(ctx context.Context, i uint64) (*utils.P } func (c *CannonTraceProvider) finalState() (*mipsevm.State, error) { - state, err := parseState(filepath.Join(c.dir, utils.FinalState)) + state, err := parseState(filepath.Join(c.dir, vm.FinalState)) if err != nil { return nil, fmt.Errorf("cannot read final state: %w", err) } @@ -183,21 +180,21 @@ type CannonTraceProviderForTest struct { *CannonTraceProvider } -func NewTraceProviderForTest(logger log.Logger, m CannonMetricer, cfg *config.Config, localInputs utils.LocalGameInputs, dir string, gameDepth types.Depth) *CannonTraceProviderForTest { +func NewTraceProviderForTest(logger log.Logger, m vm.Metricer, cfg *config.Config, localInputs utils.LocalGameInputs, dir string, gameDepth types.Depth) *CannonTraceProviderForTest { p := &CannonTraceProvider{ logger: logger, dir: dir, prestate: cfg.CannonAbsolutePreState, - generator: NewExecutor(logger, m, cfg, cfg.CannonAbsolutePreState, localInputs), + generator: vm.NewExecutor(logger, m, cfg.Cannon, cfg.CannonAbsolutePreState, localInputs), gameDepth: gameDepth, - preimageLoader: utils.NewPreimageLoader(kvstore.NewDiskKV(utils.PreimageDir(dir)).Get), + preimageLoader: utils.NewPreimageLoader(kvstore.NewDiskKV(vm.PreimageDir(dir)).Get), } return &CannonTraceProviderForTest{p} } func (p *CannonTraceProviderForTest) FindStep(ctx context.Context, start uint64, preimage utils.PreimageOpt) (uint64, error) { // Run cannon to find the step that meets the preimage conditions - if err := p.generator.(*Executor).generateProof(ctx, p.dir, start, math.MaxUint64, preimage()...); err != nil { + if err := p.generator.(*vm.Executor).DoGenerateProof(ctx, p.dir, start, math.MaxUint64, preimage()...); err != nil { return 0, fmt.Errorf("generate cannon trace (until preimage read): %w", err) } // Load the step from the state cannon finished with diff --git a/op-challenger/game/fault/trace/cannon/provider_test.go b/op-challenger/game/fault/trace/cannon/provider_test.go index 388bd151991bc..94277ed88399b 100644 --- a/op-challenger/game/fault/trace/cannon/provider_test.go +++ b/op-challenger/game/fault/trace/cannon/provider_test.go @@ -13,6 +13,7 @@ import ( "github.com/ethereum-optimism/optimism/cannon/mipsevm" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/utils" + "github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/vm" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/types" "github.com/ethereum-optimism/optimism/op-service/ioutil" "github.com/ethereum-optimism/optimism/op-service/testlog" @@ -257,7 +258,7 @@ func (e *stubGenerator) GenerateProof(ctx context.Context, dir string, i uint64) var err error if e.finalState != nil && e.finalState.Step <= i { // Requesting a trace index past the end of the trace - proofFile = filepath.Join(dir, utils.FinalState) + proofFile = filepath.Join(dir, vm.FinalState) data, err = json.Marshal(e.finalState) if err != nil { return err diff --git a/op-challenger/game/fault/trace/outputs/output_asterisc.go b/op-challenger/game/fault/trace/outputs/output_asterisc.go index a6bbf39e7288f..ac129dbb26c82 100644 --- a/op-challenger/game/fault/trace/outputs/output_asterisc.go +++ b/op-challenger/game/fault/trace/outputs/output_asterisc.go @@ -5,12 +5,12 @@ import ( "fmt" "path/filepath" - "github.com/ethereum-optimism/optimism/op-challenger/config" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/contracts" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/asterisc" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/split" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/utils" + "github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/vm" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/types" "github.com/ethereum-optimism/optimism/op-challenger/metrics" "github.com/ethereum-optimism/optimism/op-service/eth" @@ -21,7 +21,7 @@ import ( func NewOutputAsteriscTraceAccessor( logger log.Logger, m metrics.Metricer, - cfg *config.Config, + cfg vm.Config, l2Client utils.L2HeaderSource, prestateProvider types.PrestateProvider, asteriscPrestate string, diff --git a/op-challenger/game/fault/trace/outputs/output_cannon.go b/op-challenger/game/fault/trace/outputs/output_cannon.go index 7a2b3d36a975a..ecc710380bfb8 100644 --- a/op-challenger/game/fault/trace/outputs/output_cannon.go +++ b/op-challenger/game/fault/trace/outputs/output_cannon.go @@ -5,12 +5,12 @@ import ( "fmt" "path/filepath" - "github.com/ethereum-optimism/optimism/op-challenger/config" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/contracts" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/cannon" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/split" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/utils" + "github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/vm" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/types" "github.com/ethereum-optimism/optimism/op-challenger/metrics" "github.com/ethereum-optimism/optimism/op-service/eth" @@ -21,7 +21,7 @@ import ( func NewOutputCannonTraceAccessor( logger log.Logger, m metrics.Metricer, - cfg *config.Config, + cfg vm.Config, l2Client utils.L2HeaderSource, prestateProvider types.PrestateProvider, cannonPrestate string, diff --git a/op-challenger/game/fault/trace/vm/executor.go b/op-challenger/game/fault/trace/vm/executor.go new file mode 100644 index 0000000000000..564ef6ac8d201 --- /dev/null +++ b/op-challenger/game/fault/trace/vm/executor.go @@ -0,0 +1,126 @@ +package vm + +import ( + "context" + "fmt" + "math" + "os" + "path/filepath" + "strconv" + "strings" + "time" + + "github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/utils" + "github.com/ethereum/go-ethereum/log" +) + +type Metricer interface { + RecordVmExecutionTime(vmType string, t time.Duration) +} + +type Config struct { + VmType string + L1 string + L1Beacon string + L2 string + VmBin string // Path to the vm executable to run when generating trace data + Server string // Path to the executable that provides the pre-image oracle server + Network string + RollupConfigPath string + L2GenesisPath string + SnapshotFreq uint // Frequency of snapshots to create when executing (in VM instructions) + InfoFreq uint // Frequency of progress log messages (in VM instructions) +} + +type Executor struct { + cfg Config + logger log.Logger + metrics Metricer + absolutePreState string + inputs utils.LocalGameInputs + selectSnapshot SnapshotSelect + cmdExecutor CmdExecutor +} + +func NewExecutor(logger log.Logger, m Metricer, cfg Config, prestate string, inputs utils.LocalGameInputs) *Executor { + return &Executor{ + cfg: cfg, + logger: logger, + metrics: m, + inputs: inputs, + absolutePreState: prestate, + selectSnapshot: FindStartingSnapshot, + cmdExecutor: RunCmd, + } +} + +// GenerateProof executes vm to generate a proof at the specified trace index. +// The proof is stored at the specified directory. +func (e *Executor) GenerateProof(ctx context.Context, dir string, i uint64) error { + return e.DoGenerateProof(ctx, dir, i, i) +} + +// DoGenerateProof executes vm from the specified starting trace index until the end trace index. +// The proof is stored at the specified directory. +func (e *Executor) DoGenerateProof(ctx context.Context, dir string, begin uint64, end uint64, extraVmArgs ...string) error { + snapshotDir := filepath.Join(dir, SnapsDir) + start, err := e.selectSnapshot(e.logger, snapshotDir, e.absolutePreState, begin) + if err != nil { + return fmt.Errorf("find starting snapshot: %w", err) + } + proofDir := filepath.Join(dir, utils.ProofsDir) + dataDir := PreimageDir(dir) + lastGeneratedState := filepath.Join(dir, FinalState) + args := []string{ + "run", + "--input", start, + "--output", lastGeneratedState, + "--meta", "", + "--info-at", "%" + strconv.FormatUint(uint64(e.cfg.InfoFreq), 10), + "--proof-at", "=" + strconv.FormatUint(end, 10), + "--proof-fmt", filepath.Join(proofDir, "%d.json.gz"), + "--snapshot-at", "%" + strconv.FormatUint(uint64(e.cfg.SnapshotFreq), 10), + "--snapshot-fmt", filepath.Join(snapshotDir, "%d.json.gz"), + } + if end < math.MaxUint64 { + args = append(args, "--stop-at", "="+strconv.FormatUint(end+1, 10)) + } + args = append(args, extraVmArgs...) + args = append(args, + "--", + e.cfg.Server, "--server", + "--l1", e.cfg.L1, + "--l1.beacon", e.cfg.L1Beacon, + "--l2", e.cfg.L2, + "--datadir", dataDir, + "--l1.head", e.inputs.L1Head.Hex(), + "--l2.head", e.inputs.L2Head.Hex(), + "--l2.outputroot", e.inputs.L2OutputRoot.Hex(), + "--l2.claim", e.inputs.L2Claim.Hex(), + "--l2.blocknumber", e.inputs.L2BlockNumber.Text(10), + ) + if e.cfg.Network != "" { + args = append(args, "--network", e.cfg.Network) + } + if e.cfg.RollupConfigPath != "" { + args = append(args, "--rollup.config", e.cfg.RollupConfigPath) + } + if e.cfg.L2GenesisPath != "" { + args = append(args, "--l2.genesis", e.cfg.L2GenesisPath) + } + + if err := os.MkdirAll(snapshotDir, 0755); err != nil { + return fmt.Errorf("could not create snapshot directory %v: %w", snapshotDir, err) + } + if err := os.MkdirAll(dataDir, 0755); err != nil { + return fmt.Errorf("could not create preimage cache directory %v: %w", dataDir, err) + } + if err := os.MkdirAll(proofDir, 0755); err != nil { + return fmt.Errorf("could not create proofs directory %v: %w", proofDir, err) + } + e.logger.Info("Generating trace", "proof", end, "cmd", e.cfg.VmBin, "args", strings.Join(args, ", ")) + execStart := time.Now() + err = e.cmdExecutor(ctx, e.logger.New("proof", end), e.cfg.VmBin, args...) + e.metrics.RecordVmExecutionTime(e.cfg.VmType, time.Since(execStart)) + return err +} diff --git a/op-challenger/game/fault/trace/asterisc/executor_test.go b/op-challenger/game/fault/trace/vm/executor_test.go similarity index 60% rename from op-challenger/game/fault/trace/asterisc/executor_test.go rename to op-challenger/game/fault/trace/vm/executor_test.go index 7ce44f3043751..00078bd2078e9 100644 --- a/op-challenger/game/fault/trace/asterisc/executor_test.go +++ b/op-challenger/game/fault/trace/vm/executor_test.go @@ -1,4 +1,4 @@ -package asterisc +package vm import ( "context" @@ -6,8 +6,8 @@ import ( "math/big" "path/filepath" "testing" + "time" - "github.com/ethereum-optimism/optimism/op-challenger/config" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/utils" "github.com/ethereum-optimism/optimism/op-challenger/metrics" "github.com/ethereum-optimism/optimism/op-service/testlog" @@ -20,13 +20,18 @@ func TestGenerateProof(t *testing.T) { input := "starting.json" tempDir := t.TempDir() dir := filepath.Join(tempDir, "gameDir") - cfg := config.NewConfig(common.Address{0xbb}, "http://localhost:8888", "http://localhost:9000", "http://localhost:9096", "http://localhost:9095", tempDir, config.TraceTypeAsterisc) - cfg.L2Rpc = "http://localhost:9999" + cfg := Config{ + VmType: "test", + L1: "http://localhost:8888", + L1Beacon: "http://localhost:9000", + L2: "http://localhost:9999", + VmBin: "./bin/testvm", + Server: "./bin/testserver", + Network: "op-test", + SnapshotFreq: 500, + InfoFreq: 900, + } prestate := "pre.json" - cfg.AsteriscBin = "./bin/asterisc" - cfg.AsteriscServer = "./bin/op-program" - cfg.AsteriscSnapshotFreq = 500 - cfg.AsteriscInfoFreq = 900 inputs := utils.LocalGameInputs{ L1Head: common.Hash{0x11}, @@ -35,9 +40,9 @@ func TestGenerateProof(t *testing.T) { L2Claim: common.Hash{0x44}, L2BlockNumber: big.NewInt(3333), } - captureExec := func(t *testing.T, cfg config.Config, proofAt uint64) (string, string, map[string]string) { - m := &asteriscDurationMetrics{} - executor := NewExecutor(testlog.Logger(t, log.LevelInfo), m, &cfg, prestate, inputs) + captureExec := func(t *testing.T, cfg Config, proofAt uint64) (string, string, map[string]string) { + m := &stubVmMetrics{} + executor := NewExecutor(testlog.Logger(t, log.LevelInfo), m, cfg, prestate, inputs) executor.selectSnapshot = func(logger log.Logger, dir string, absolutePreState string, i uint64) (string, error) { return input, nil } @@ -49,7 +54,7 @@ func TestGenerateProof(t *testing.T) { subcommand = a[0] for i := 1; i < len(a); { if a[i] == "--" { - // Skip over the divider between asterisc and server program + // Skip over the divider between vm and server program i += 1 continue } @@ -60,24 +65,24 @@ func TestGenerateProof(t *testing.T) { } err := executor.GenerateProof(context.Background(), dir, proofAt) require.NoError(t, err) - require.Equal(t, 1, m.executionTimeRecordCount, "Should record asterisc execution time") + require.Equal(t, 1, m.executionTimeRecordCount, "Should record vm execution time") return binary, subcommand, args } t.Run("Network", func(t *testing.T) { - cfg.AsteriscNetwork = "mainnet" - cfg.AsteriscRollupConfigPath = "" - cfg.AsteriscL2GenesisPath = "" + cfg.Network = "mainnet" + cfg.RollupConfigPath = "" + cfg.L2GenesisPath = "" binary, subcommand, args := captureExec(t, cfg, 150_000_000) - require.DirExists(t, filepath.Join(dir, utils.PreimagesDir)) - require.DirExists(t, filepath.Join(dir, proofsDir)) - require.DirExists(t, filepath.Join(dir, utils.SnapsDir)) - require.Equal(t, cfg.AsteriscBin, binary) + require.DirExists(t, filepath.Join(dir, PreimagesDir)) + require.DirExists(t, filepath.Join(dir, utils.ProofsDir)) + require.DirExists(t, filepath.Join(dir, SnapsDir)) + require.Equal(t, cfg.VmBin, binary) require.Equal(t, "run", subcommand) require.Equal(t, input, args["--input"]) require.Contains(t, args, "--meta") require.Equal(t, "", args["--meta"]) - require.Equal(t, filepath.Join(dir, utils.FinalState), args["--output"]) + require.Equal(t, filepath.Join(dir, FinalState), args["--output"]) require.Equal(t, "=150000000", args["--proof-at"]) require.Equal(t, "=150000001", args["--stop-at"]) require.Equal(t, "%500", args["--snapshot-at"]) @@ -85,14 +90,14 @@ func TestGenerateProof(t *testing.T) { // Slight quirk of how we pair off args // The server binary winds up as the key and the first arg --server as the value which has no value // Then everything else pairs off correctly again - require.Equal(t, "--server", args[cfg.AsteriscServer]) - require.Equal(t, cfg.L1EthRpc, args["--l1"]) + require.Equal(t, "--server", args[cfg.Server]) + require.Equal(t, cfg.L1, args["--l1"]) require.Equal(t, cfg.L1Beacon, args["--l1.beacon"]) - require.Equal(t, cfg.L2Rpc, args["--l2"]) - require.Equal(t, filepath.Join(dir, utils.PreimagesDir), args["--datadir"]) - require.Equal(t, filepath.Join(dir, proofsDir, "%d.json.gz"), args["--proof-fmt"]) - require.Equal(t, filepath.Join(dir, utils.SnapsDir, "%d.json.gz"), args["--snapshot-fmt"]) - require.Equal(t, cfg.AsteriscNetwork, args["--network"]) + require.Equal(t, cfg.L2, args["--l2"]) + require.Equal(t, filepath.Join(dir, PreimagesDir), args["--datadir"]) + require.Equal(t, filepath.Join(dir, utils.ProofsDir, "%d.json.gz"), args["--proof-fmt"]) + require.Equal(t, filepath.Join(dir, SnapsDir, "%d.json.gz"), args["--snapshot-fmt"]) + require.Equal(t, cfg.Network, args["--network"]) require.NotContains(t, args, "--rollup.config") require.NotContains(t, args, "--l2.genesis") @@ -105,19 +110,19 @@ func TestGenerateProof(t *testing.T) { }) t.Run("RollupAndGenesis", func(t *testing.T) { - cfg.AsteriscNetwork = "" - cfg.AsteriscRollupConfigPath = "rollup.json" - cfg.AsteriscL2GenesisPath = "genesis.json" + cfg.Network = "" + cfg.RollupConfigPath = "rollup.json" + cfg.L2GenesisPath = "genesis.json" _, _, args := captureExec(t, cfg, 150_000_000) require.NotContains(t, args, "--network") - require.Equal(t, cfg.AsteriscRollupConfigPath, args["--rollup.config"]) - require.Equal(t, cfg.AsteriscL2GenesisPath, args["--l2.genesis"]) + require.Equal(t, cfg.RollupConfigPath, args["--rollup.config"]) + require.Equal(t, cfg.L2GenesisPath, args["--l2.genesis"]) }) t.Run("NoStopAtWhenProofIsMaxUInt", func(t *testing.T) { - cfg.AsteriscNetwork = "mainnet" - cfg.AsteriscRollupConfigPath = "rollup.json" - cfg.AsteriscL2GenesisPath = "genesis.json" + cfg.Network = "mainnet" + cfg.RollupConfigPath = "rollup.json" + cfg.L2GenesisPath = "genesis.json" _, _, args := captureExec(t, cfg, math.MaxUint64) // stop-at would need to be one more than the proof step which would overflow back to 0 // so expect that it will be omitted. We'll ultimately want asterisc to execute until the program exits. @@ -125,11 +130,11 @@ func TestGenerateProof(t *testing.T) { }) } -type asteriscDurationMetrics struct { +type stubVmMetrics struct { metrics.NoopMetricsImpl executionTimeRecordCount int } -func (c *asteriscDurationMetrics) RecordAsteriscExecutionTime(_ float64) { +func (c *stubVmMetrics) RecordVmExecutionTime(_ string, _ time.Duration) { c.executionTimeRecordCount++ } diff --git a/op-challenger/game/fault/trace/utils/executor.go b/op-challenger/game/fault/trace/vm/prestates.go similarity index 93% rename from op-challenger/game/fault/trace/utils/executor.go rename to op-challenger/game/fault/trace/vm/prestates.go index f3c5feac8311a..c51dda7de1e5b 100644 --- a/op-challenger/game/fault/trace/utils/executor.go +++ b/op-challenger/game/fault/trace/vm/prestates.go @@ -1,4 +1,4 @@ -package utils +package vm import ( "context" @@ -10,7 +10,7 @@ import ( "regexp" "strconv" - oplog "github.com/ethereum-optimism/optimism/op-service/log" + log2 "github.com/ethereum-optimism/optimism/op-service/log" "github.com/ethereum/go-ethereum/log" ) @@ -31,10 +31,10 @@ func PreimageDir(dir string) string { func RunCmd(ctx context.Context, l log.Logger, binary string, args ...string) error { cmd := exec.CommandContext(ctx, binary, args...) - stdOut := oplog.NewWriter(l, log.LevelInfo) + stdOut := log2.NewWriter(l, log.LevelInfo) defer stdOut.Close() // Keep stdErr at info level because FPVM uses stderr for progress messages - stdErr := oplog.NewWriter(l, log.LevelInfo) + stdErr := log2.NewWriter(l, log.LevelInfo) defer stdErr.Close() cmd.Stdout = stdOut cmd.Stderr = stdErr diff --git a/op-challenger/metrics/metrics.go b/op-challenger/metrics/metrics.go index 9253f26cc6314..4f186f4c01e91 100644 --- a/op-challenger/metrics/metrics.go +++ b/op-challenger/metrics/metrics.go @@ -2,6 +2,7 @@ package metrics import ( "io" + "time" "github.com/ethereum-optimism/optimism/op-service/httputil" "github.com/ethereum-optimism/optimism/op-service/sources/caching" @@ -37,8 +38,7 @@ type Metricer interface { RecordGameStep() RecordGameMove() RecordGameL2Challenge() - RecordCannonExecutionTime(t float64) - RecordAsteriscExecutionTime(t float64) + RecordVmExecutionTime(vmType string, t time.Duration) RecordClaimResolutionTime(t float64) RecordGameActTime(t float64) @@ -88,10 +88,9 @@ type Metrics struct { steps prometheus.Counter l2Challenges prometheus.Counter - claimResolutionTime prometheus.Histogram - gameActTime prometheus.Histogram - cannonExecutionTime prometheus.Histogram - asteriscExecutionTime prometheus.Histogram + claimResolutionTime prometheus.Histogram + gameActTime prometheus.Histogram + vmExecutionTime *prometheus.HistogramVec trackedGames prometheus.GaugeVec inflightGames prometheus.Gauge @@ -152,14 +151,6 @@ func NewMetrics() *Metrics { Name: "l2_challenges", Help: "Number of L2 challenges made by the challenge agent", }), - cannonExecutionTime: factory.NewHistogram(prometheus.HistogramOpts{ - Namespace: Namespace, - Name: "cannon_execution_time", - Help: "Time (in seconds) to execute cannon", - Buckets: append( - []float64{1.0, 10.0}, - prometheus.ExponentialBuckets(30.0, 2.0, 14)...), - }), claimResolutionTime: factory.NewHistogram(prometheus.HistogramOpts{ Namespace: Namespace, Name: "claim_resolution_time", @@ -174,14 +165,14 @@ func NewMetrics() *Metrics { []float64{1.0, 2.0, 5.0, 10.0}, prometheus.ExponentialBuckets(30.0, 2.0, 14)...), }), - asteriscExecutionTime: factory.NewHistogram(prometheus.HistogramOpts{ + vmExecutionTime: factory.NewHistogramVec(prometheus.HistogramOpts{ Namespace: Namespace, Name: "asterisc_execution_time", Help: "Time (in seconds) to execute asterisc", Buckets: append( []float64{1.0, 10.0}, prometheus.ExponentialBuckets(30.0, 2.0, 14)...), - }), + }, []string{"vm"}), bondClaimFailures: factory.NewCounter(prometheus.CounterOpts{ Namespace: Namespace, Name: "claim_failures", @@ -278,12 +269,8 @@ func (m *Metrics) RecordBondClaimed(amount uint64) { m.bondsClaimed.Add(float64(amount)) } -func (m *Metrics) RecordCannonExecutionTime(t float64) { - m.cannonExecutionTime.Observe(t) -} - -func (m *Metrics) RecordAsteriscExecutionTime(t float64) { - m.asteriscExecutionTime.Observe(t) +func (m *Metrics) RecordVmExecutionTime(vmType string, dur time.Duration) { + m.vmExecutionTime.WithLabelValues(vmType).Observe(dur.Seconds()) } func (m *Metrics) RecordClaimResolutionTime(t float64) { diff --git a/op-challenger/metrics/noop.go b/op-challenger/metrics/noop.go index c238f03fcb733..fc0f6d077803b 100644 --- a/op-challenger/metrics/noop.go +++ b/op-challenger/metrics/noop.go @@ -2,6 +2,7 @@ package metrics import ( "io" + "time" contractMetrics "github.com/ethereum-optimism/optimism/op-challenger/game/fault/contracts/metrics" "github.com/ethereum/go-ethereum/common" @@ -37,10 +38,9 @@ func (*NoopMetricsImpl) RecordPreimageChallengeFailed() {} func (*NoopMetricsImpl) RecordBondClaimFailed() {} func (*NoopMetricsImpl) RecordBondClaimed(uint64) {} -func (*NoopMetricsImpl) RecordCannonExecutionTime(t float64) {} -func (*NoopMetricsImpl) RecordAsteriscExecutionTime(t float64) {} -func (*NoopMetricsImpl) RecordClaimResolutionTime(t float64) {} -func (*NoopMetricsImpl) RecordGameActTime(t float64) {} +func (*NoopMetricsImpl) RecordVmExecutionTime(_ string, _ time.Duration) {} +func (*NoopMetricsImpl) RecordClaimResolutionTime(t float64) {} +func (*NoopMetricsImpl) RecordGameActTime(t float64) {} func (*NoopMetricsImpl) RecordGamesStatus(inProgress, defenderWon, challengerWon int) {} diff --git a/op-e2e/e2eutils/challenger/helper.go b/op-e2e/e2eutils/challenger/helper.go index 95b963666d633..5431efbd3992b 100644 --- a/op-e2e/e2eutils/challenger/helper.go +++ b/op-e2e/e2eutils/challenger/helper.go @@ -102,22 +102,22 @@ func FindMonorepoRoot(t *testing.T) string { func applyCannonConfig(c *config.Config, t *testing.T, rollupCfg *rollup.Config, l2Genesis *core.Genesis) { require := require.New(t) root := FindMonorepoRoot(t) - c.CannonBin = root + "cannon/bin/cannon" - c.CannonServer = root + "op-program/bin/op-program" + c.Cannon.VmBin = root + "cannon/bin/cannon" + c.Cannon.Server = root + "op-program/bin/op-program" c.CannonAbsolutePreState = root + "op-program/bin/prestate.json" - c.CannonSnapshotFreq = 10_000_000 + c.Cannon.SnapshotFreq = 10_000_000 genesisBytes, err := json.Marshal(l2Genesis) require.NoError(err, "marshall l2 genesis config") genesisFile := filepath.Join(c.Datadir, "l2-genesis.json") require.NoError(os.WriteFile(genesisFile, genesisBytes, 0o644)) - c.CannonL2GenesisPath = genesisFile + c.Cannon.L2GenesisPath = genesisFile rollupBytes, err := json.Marshal(rollupCfg) require.NoError(err, "marshall rollup config") rollupFile := filepath.Join(c.Datadir, "rollup.json") require.NoError(os.WriteFile(rollupFile, rollupBytes, 0o644)) - c.CannonRollupConfigPath = rollupFile + c.Cannon.RollupConfigPath = rollupFile } func WithCannon(t *testing.T, rollupCfg *rollup.Config, l2Genesis *core.Genesis) Option { @@ -177,12 +177,12 @@ func NewChallengerConfig(t *testing.T, sys EndpointProvider, l2NodeName string, require.NotEmpty(t, cfg.TxMgrConfig.PrivateKey, "Missing private key for TxMgrConfig") require.NoError(t, cfg.Check(), "op-challenger config should be valid") - if cfg.CannonBin != "" { - _, err := os.Stat(cfg.CannonBin) + if cfg.Cannon.VmBin != "" { + _, err := os.Stat(cfg.Cannon.VmBin) require.NoError(t, err, "cannon should be built. Make sure you've run make cannon-prestate") } - if cfg.CannonServer != "" { - _, err := os.Stat(cfg.CannonServer) + if cfg.Cannon.Server != "" { + _, err := os.Stat(cfg.Cannon.Server) require.NoError(t, err, "op-program should be built. Make sure you've run make cannon-prestate") } if cfg.CannonAbsolutePreState != "" { diff --git a/op-e2e/e2eutils/disputegame/output_cannon_helper.go b/op-e2e/e2eutils/disputegame/output_cannon_helper.go index 6b6e6ff4ff760..e2f72c4915d96 100644 --- a/op-e2e/e2eutils/disputegame/output_cannon_helper.go +++ b/op-e2e/e2eutils/disputegame/output_cannon_helper.go @@ -62,7 +62,7 @@ func (g *OutputCannonGameHelper) CreateHonestActor(ctx context.Context, l2Node s prestateProvider := outputs.NewPrestateProvider(rollupClient, prestateBlock) l1Head := g.GetL1Head(ctx) accessor, err := outputs.NewOutputCannonTraceAccessor( - logger, metrics.NoopMetrics, cfg, l2Client, prestateProvider, cfg.CannonAbsolutePreState, rollupClient, dir, l1Head, splitDepth, prestateBlock, poststateBlock) + logger, metrics.NoopMetrics, cfg.Cannon, l2Client, prestateProvider, cfg.CannonAbsolutePreState, rollupClient, dir, l1Head, splitDepth, prestateBlock, poststateBlock) g.Require.NoError(err, "Failed to create output cannon trace accessor") return NewOutputHonestHelper(g.T, g.Require, &g.OutputGameHelper, g.Game, accessor) } diff --git a/op-e2e/faultproofs/precompile_test.go b/op-e2e/faultproofs/precompile_test.go index 28807edee6dde..eae3334438427 100644 --- a/op-e2e/faultproofs/precompile_test.go +++ b/op-e2e/faultproofs/precompile_test.go @@ -10,8 +10,8 @@ import ( "github.com/ethereum-optimism/optimism/cannon/mipsevm" "github.com/ethereum-optimism/optimism/op-challenger/config" - "github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/cannon" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/utils" + "github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/vm" "github.com/ethereum-optimism/optimism/op-challenger/metrics" op_e2e "github.com/ethereum-optimism/optimism/op-e2e" "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/challenger" @@ -147,7 +147,7 @@ func runCannon(t *testing.T, ctx context.Context, sys *op_e2e.System, inputs uti cannonOpts(&cfg) logger := testlog.Logger(t, log.LevelInfo).New("role", "cannon") - executor := cannon.NewExecutor(logger, metrics.NoopMetrics, &cfg, cfg.CannonAbsolutePreState, inputs) + executor := vm.NewExecutor(logger, metrics.NoopMetrics, cfg.Cannon, cfg.CannonAbsolutePreState, inputs) t.Log("Running cannon") err := executor.GenerateProof(ctx, proofsDir, math.MaxUint)