Commit 8d9287bc authored by Adrian Sutton's avatar Adrian Sutton Committed by GitHub

op-challenger: Deduplicate executor (#10785)

* challenger: Create generic vm executor and use for asterisc

* challenger: Move executor utils to vm package

* challenger: Switch cannon to use common vm executor.

* challenger: Use vm.Config to encapsulate Cannon config items.

* challenger: Rename to include Path

* challenger: Use vm.Config to encapsulate Asterisc config items.

* challenger: Remove unused interface

* challenger: Reduce amount of config passed to cannon/asterisc trace providers

* challenger: Remove Config from names

* challenger: Remove replaced cannon metrics
parent 48263376
......@@ -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) {
......
......@@ -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
}
}
......
......@@ -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())
......
......@@ -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,
......
......@@ -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
}
......
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
}
......@@ -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
......
......@@ -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
......
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++
}
......@@ -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
......
......@@ -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
......
......@@ -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,
......
......@@ -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,
......
package cannon
package vm
import (
"context"
......@@ -10,89 +10,88 @@ import (
"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 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 CannonMetricer
l1 string
l1Beacon string
l2 string
inputs utils.LocalGameInputs
cannon string
server string
network string
rollupConfig string
l2Genesis string
metrics Metricer
absolutePreState string
snapshotFreq uint
infoFreq uint
selectSnapshot utils.SnapshotSelect
cmdExecutor utils.CmdExecutor
inputs utils.LocalGameInputs
selectSnapshot SnapshotSelect
cmdExecutor CmdExecutor
}
func NewExecutor(logger log.Logger, m CannonMetricer, cfg *config.Config, prestate string, inputs utils.LocalGameInputs) *Executor {
func NewExecutor(logger log.Logger, m Metricer, cfg Config, prestate string, inputs utils.LocalGameInputs) *Executor {
return &Executor{
cfg: cfg,
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,
selectSnapshot: FindStartingSnapshot,
cmdExecutor: RunCmd,
}
}
// GenerateProof executes cannon to generate a proof at the specified trace index.
// 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.generateProof(ctx, dir, i, i)
return e.DoGenerateProof(ctx, dir, i, i)
}
// generateProof executes cannon from the specified starting trace index until the end trace index.
// 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) generateProof(ctx context.Context, dir string, begin uint64, end uint64, extraCannonArgs ...string) error {
snapshotDir := filepath.Join(dir, utils.SnapsDir)
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 := utils.PreimageDir(dir)
lastGeneratedState := filepath.Join(dir, utils.FinalState)
dataDir := PreimageDir(dir)
lastGeneratedState := filepath.Join(dir, FinalState)
args := []string{
"run",
"--input", start,
"--output", lastGeneratedState,
"--meta", "",
"--info-at", "%" + strconv.FormatUint(uint64(e.infoFreq), 10),
"--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.snapshotFreq), 10),
"--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, extraCannonArgs...)
args = append(args, extraVmArgs...)
args = append(args,
"--",
e.server, "--server",
"--l1", e.l1,
"--l1.beacon", e.l1Beacon,
"--l2", e.l2,
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(),
......@@ -100,14 +99,14 @@ func (e *Executor) generateProof(ctx context.Context, dir string, begin uint64,
"--l2.claim", e.inputs.L2Claim.Hex(),
"--l2.blocknumber", e.inputs.L2BlockNumber.Text(10),
)
if e.network != "" {
args = append(args, "--network", e.network)
if e.cfg.Network != "" {
args = append(args, "--network", e.cfg.Network)
}
if e.rollupConfig != "" {
args = append(args, "--rollup.config", e.rollupConfig)
if e.cfg.RollupConfigPath != "" {
args = append(args, "--rollup.config", e.cfg.RollupConfigPath)
}
if e.l2Genesis != "" {
args = append(args, "--l2.genesis", e.l2Genesis)
if e.cfg.L2GenesisPath != "" {
args = append(args, "--l2.genesis", e.cfg.L2GenesisPath)
}
if err := os.MkdirAll(snapshotDir, 0755); err != nil {
......@@ -119,9 +118,9 @@ func (e *Executor) generateProof(ctx context.Context, dir string, begin uint64,
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, ", "))
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.cannon, args...)
e.metrics.RecordCannonExecutionTime(time.Since(execStart).Seconds())
err = e.cmdExecutor(ctx, e.logger.New("proof", end), e.cfg.VmBin, args...)
e.metrics.RecordVmExecutionTime(e.cfg.VmType, time.Since(execStart))
return err
}
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++
}
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
......
......@@ -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) {
......
......@@ -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) {}
......
......@@ -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 != "" {
......
......@@ -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)
}
......
......@@ -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)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment