Commit 94056b99 authored by Adrian Sutton's avatar Adrian Sutton Committed by GitHub

op-challenger, op-program: Require specific opt-in to use the custom config...

op-challenger, op-program: Require specific opt-in to use the custom config chain ID indicator (#13217)

* op-challenger: Support --cannon-l2-chain-id to pass chain ID through to op-program.

* op-program: Default to assuming configs are available in client.

Provide a --l2.custom flag to set the chain ID to the custom chain indicator so the client will load configs via the preimage oracle.
Since loading configs via the preimage oracle doesn't work on-chain, this is a safer and simpler default now that op-program can be built
with custom configs embedded.

* op-challenger: Switch to custom L2 flag instead of specifying chain ID

* op-challenger: Fix boolean option in op-program execution.

* op-e2e: Set custom L2 flag in precompiles test
parent a88f6398
......@@ -765,7 +765,7 @@ func TestCannonRequiredArgs(t *testing.T) {
t.Run(fmt.Sprintf("TestMustNotSpecifyCannonNetworkAndRollup-%v", traceType), func(t *testing.T) {
verifyArgsInvalid(
t,
"flag cannon-network can not be used with cannon-rollup-config and cannon-l2-genesis",
"flag cannon-network can not be used with cannon-rollup-config, cannon-l2-genesis or cannon-l2-custom",
addRequiredArgsExcept(traceType, "--cannon-network",
"--cannon-network", cannonNetwork, "--cannon-rollup-config=rollup.json"))
})
......@@ -777,9 +777,10 @@ func TestCannonRequiredArgs(t *testing.T) {
args["--network"] = cannonNetwork
args["--cannon-rollup-config"] = "rollup.json"
args["--cannon-l2-genesis"] = "gensis.json"
args["--cannon-l2-custom"] = "true"
verifyArgsInvalid(
t,
"flag network can not be used with cannon-rollup-config and cannon-l2-genesis",
"flag network can not be used with cannon-rollup-config, cannon-l2-genesis or cannon-l2-custom",
toArgList(args))
})
......@@ -813,6 +814,14 @@ func TestCannonRequiredArgs(t *testing.T) {
})
})
t.Run(fmt.Sprintf("TestSetCannonL2ChainId-%v", traceType), func(t *testing.T) {
cfg := configForArgs(t, addRequiredArgsExcept(traceType, "--cannon-network",
"--cannon-rollup-config=rollup.json",
"--cannon-l2-genesis=genesis.json",
"--cannon-l2-custom"))
require.True(t, cfg.Cannon.L2Custom)
})
t.Run(fmt.Sprintf("TestCannonRollupConfig-%v", traceType), func(t *testing.T) {
t.Run("NotRequiredForAlphabetTrace", func(t *testing.T) {
configForArgs(t, addRequiredArgsExcept(types.TraceTypeAlphabet, "--cannon-rollup-config"))
......
......@@ -115,6 +115,14 @@ var (
Usage: fmt.Sprintf("Deprecated: Use %v instead", flags.NetworkFlagName),
EnvVars: prefixEnvVars("CANNON_NETWORK"),
}
CannonL2CustomFlag = &cli.BoolFlag{
Name: "cannon-l2-custom",
Usage: "Notify the op-program host that the L2 chain uses custom config to be loaded via the preimage oracle. " +
"WARNING: This is incompatible with on-chain testing and must only be used for testing purposes.",
EnvVars: prefixEnvVars("CANNON_L2_CUSTOM"),
Value: false,
Hidden: true,
}
CannonRollupConfigFlag = &cli.StringFlag{
Name: "cannon-rollup-config",
Usage: "Rollup chain parameters (cannon trace type only)",
......@@ -249,6 +257,7 @@ var optionalFlags = []cli.Flag{
AdditionalBondClaimants,
GameAllowlistFlag,
CannonNetworkFlag,
CannonL2CustomFlag,
CannonRollupConfigFlag,
CannonL2GenesisFlag,
CannonBinFlag,
......@@ -296,14 +305,17 @@ func CheckCannonFlags(ctx *cli.Context) error {
CannonNetworkFlag.Name, flags.NetworkFlagName, CannonRollupConfigFlag.Name, CannonL2GenesisFlag.Name)
}
if ctx.IsSet(flags.NetworkFlagName) &&
(ctx.IsSet(CannonRollupConfigFlag.Name) || ctx.IsSet(CannonL2GenesisFlag.Name)) {
return fmt.Errorf("flag %v can not be used with %v and %v",
flags.NetworkFlagName, CannonRollupConfigFlag.Name, CannonL2GenesisFlag.Name)
(ctx.IsSet(CannonRollupConfigFlag.Name) || ctx.IsSet(CannonL2GenesisFlag.Name) || ctx.Bool(CannonL2CustomFlag.Name)) {
return fmt.Errorf("flag %v can not be used with %v, %v or %v",
flags.NetworkFlagName, CannonRollupConfigFlag.Name, CannonL2GenesisFlag.Name, CannonL2CustomFlag.Name)
}
if ctx.IsSet(CannonNetworkFlag.Name) &&
(ctx.IsSet(CannonRollupConfigFlag.Name) || ctx.IsSet(CannonL2GenesisFlag.Name)) {
return fmt.Errorf("flag %v can not be used with %v and %v",
CannonNetworkFlag.Name, CannonRollupConfigFlag.Name, CannonL2GenesisFlag.Name)
(ctx.IsSet(CannonRollupConfigFlag.Name) || ctx.IsSet(CannonL2GenesisFlag.Name) || ctx.Bool(CannonL2CustomFlag.Name)) {
return fmt.Errorf("flag %v can not be used with %v, %v or %v",
CannonNetworkFlag.Name, CannonRollupConfigFlag.Name, CannonL2GenesisFlag.Name, CannonL2CustomFlag.Name)
}
if ctx.Bool(CannonL2CustomFlag.Name) && !(ctx.IsSet(CannonRollupConfigFlag.Name) && ctx.IsSet(CannonL2GenesisFlag.Name)) {
return fmt.Errorf("flag %v and %v must be set when %v is true", CannonRollupConfigFlag.Name, CannonL2GenesisFlag.Name, CannonL2CustomFlag.Name)
}
if !ctx.IsSet(CannonBinFlag.Name) {
return fmt.Errorf("flag %s is required", CannonBinFlag.Name)
......@@ -563,6 +575,7 @@ func NewConfigFromCLI(ctx *cli.Context, logger log.Logger) (*config.Config, erro
VmBin: ctx.String(CannonBinFlag.Name),
Server: ctx.String(CannonServerFlag.Name),
Network: cannonNetwork,
L2Custom: ctx.Bool(CannonL2CustomFlag.Name),
RollupConfigPath: ctx.String(CannonRollupConfigFlag.Name),
L2GenesisPath: ctx.String(CannonL2GenesisFlag.Name),
SnapshotFreq: ctx.Uint(CannonSnapshotFreqFlag.Name),
......
......@@ -41,6 +41,7 @@ type Config struct {
L2 string
Server string // Path to the executable that provides the pre-image oracle server
Network string
L2Custom bool
RollupConfigPath string
L2GenesisPath string
}
......
......@@ -54,5 +54,8 @@ func (s *OpProgramServerExecutor) OracleCommand(cfg Config, dataDir string, inpu
logLevel = "CRIT"
}
args = append(args, "--log.level", logLevel)
if cfg.L2Custom {
args = append(args, "--l2.custom")
}
return args, nil
}
......@@ -19,6 +19,12 @@ func TestOpProgramFillHostCommand(t *testing.T) {
toPairs := func(args []string) map[string]string {
pairs := make(map[string]string, len(args)/2)
for i := 0; i < len(args); i += 2 {
// l2.custom is a boolean flag so can't accept a value after a space
if args[i] == "--l2.custom" {
pairs[args[i]] = "true"
i--
continue
}
pairs[args[i]] = args[i+1]
}
return pairs
......@@ -72,6 +78,13 @@ func TestOpProgramFillHostCommand(t *testing.T) {
require.Equal(t, "op-test", pairs["--network"])
})
t.Run("WithL2ChainID", func(t *testing.T) {
pairs := oracleCommand(t, log.LvlInfo, func(c *Config) {
c.L2Custom = true
})
require.Equal(t, "true", pairs["--l2.custom"])
})
t.Run("WithRollupConfigPath", func(t *testing.T) {
pairs := oracleCommand(t, log.LvlInfo, func(c *Config) {
c.RollupConfigPath = "rollup.config.json"
......
......@@ -190,6 +190,7 @@ func NewChallengerConfig(t *testing.T, sys EndpointProvider, l2NodeName string,
l1Endpoint := sys.NodeEndpoint("l1").RPC()
l1Beacon := sys.L1BeaconEndpoint().RestHTTP()
cfg := config.NewConfig(common.Address{}, l1Endpoint, l1Beacon, sys.RollupEndpoint(l2NodeName).RPC(), sys.NodeEndpoint(l2NodeName).RPC(), t.TempDir())
cfg.Cannon.L2Custom = true
// The devnet can't set the absolute prestate output root because the contracts are deployed in L1 genesis
// before the L2 genesis is known.
cfg.AllowInvalidPrestate = true
......
......@@ -269,6 +269,7 @@ func runCannon(t *testing.T, ctx context.Context, sys *e2esys.System, inputs uti
dir := t.TempDir()
proofsDir := filepath.Join(dir, "cannon-proofs")
cfg := config.NewConfig(common.Address{}, l1Endpoint, l1Beacon, rollupEndpoint, l2Endpoint, dir)
cfg.Cannon.L2Custom = true
cannonOpts(&cfg)
logger := testlog.Logger(t, log.LevelInfo).New("role", "cannon")
......
......@@ -9,6 +9,7 @@ import (
"github.com/ethereum-optimism/optimism/op-node/chaincfg"
"github.com/ethereum-optimism/optimism/op-program/chainconfig"
"github.com/ethereum-optimism/optimism/op-program/client"
"github.com/ethereum-optimism/optimism/op-program/host/config"
"github.com/ethereum-optimism/optimism/op-program/host/types"
oplog "github.com/ethereum-optimism/optimism/op-service/log"
......@@ -160,6 +161,30 @@ func TestL2Genesis(t *testing.T) {
})
}
func TestL2ChainID(t *testing.T) {
t.Run("DefaultToNetworkChainID", func(t *testing.T) {
cfg := configForArgs(t, replaceRequiredArg("--network", "op-mainnet"))
require.Equal(t, uint64(10), cfg.L2ChainID)
})
t.Run("DefaultToGenesisChainID", func(t *testing.T) {
rollupCfgFile := writeValidRollupConfig(t)
genesisFile := writeValidGenesis(t)
cfg := configForArgs(t, addRequiredArgsExcept("--network", "--rollup.config", rollupCfgFile, "--l2.genesis", genesisFile))
require.Equal(t, l2GenesisConfig.ChainID.Uint64(), cfg.L2ChainID)
})
t.Run("OverrideToCustomIndicator", func(t *testing.T) {
rollupCfgFile := writeValidRollupConfig(t)
genesisFile := writeValidGenesis(t)
cfg := configForArgs(t, addRequiredArgsExcept("--network",
"--rollup.config", rollupCfgFile,
"--l2.genesis", genesisFile,
"--l2.custom"))
require.Equal(t, client.CustomChainIDIndicator, cfg.L2ChainID)
})
}
func TestL2Head(t *testing.T) {
t.Run("Required", func(t *testing.T) {
verifyArgsInvalid(t, "flag l2.head is required", addRequiredArgsExcept("--l2.head"))
......
......@@ -10,6 +10,7 @@ import (
"github.com/ethereum-optimism/optimism/op-node/chaincfg"
"github.com/ethereum-optimism/optimism/op-program/chainconfig"
"github.com/ethereum-optimism/optimism/op-program/client"
"github.com/ethereum-optimism/optimism/op-program/host/types"
"github.com/ethereum-optimism/optimism/op-node/rollup"
......@@ -37,7 +38,8 @@ var (
)
type Config struct {
Rollup *rollup.Config
L2ChainID uint64
Rollup *rollup.Config
// DataDir is the directory to read/write pre-image data from/to.
// If not set, an in-memory key-value store is used and fetching data must be enabled
DataDir string
......@@ -75,9 +77,6 @@ type Config struct {
// ServerMode indicates that the program should run in pre-image server mode and wait for requests.
// No client program is run.
ServerMode bool
// IsCustomChainConfig indicates that the program uses a custom chain configuration
IsCustomChainConfig bool
}
func (c *Config) Check() error {
......@@ -131,19 +130,23 @@ func NewConfig(
l2Claim common.Hash,
l2ClaimBlockNum uint64,
) *Config {
_, err := params.LoadOPStackChainConfig(l2Genesis.ChainID.Uint64())
isCustomConfig := err != nil
l2ChainID := l2Genesis.ChainID.Uint64()
_, err := params.LoadOPStackChainConfig(l2ChainID)
if err != nil {
// Unknown chain ID so assume it is custom
l2ChainID = client.CustomChainIDIndicator
}
return &Config{
Rollup: rollupCfg,
L2ChainConfig: l2Genesis,
L1Head: l1Head,
L2Head: l2Head,
L2OutputRoot: l2OutputRoot,
L2Claim: l2Claim,
L2ClaimBlockNumber: l2ClaimBlockNum,
L1RPCKind: sources.RPCKindStandard,
IsCustomChainConfig: isCustomConfig,
DataFormat: types.DataFormatDirectory,
L2ChainID: l2ChainID,
Rollup: rollupCfg,
L2ChainConfig: l2Genesis,
L1Head: l1Head,
L2Head: l2Head,
L2OutputRoot: l2OutputRoot,
L2Claim: l2Claim,
L2ClaimBlockNumber: l2ClaimBlockNum,
L1RPCKind: sources.RPCKindStandard,
DataFormat: types.DataFormatDirectory,
}
}
......@@ -177,7 +180,7 @@ func NewConfigFromCLI(log log.Logger, ctx *cli.Context) (*Config, error) {
var err error
var rollupCfg *rollup.Config
var l2ChainConfig *params.ChainConfig
var isCustomConfig bool
var l2ChainID uint64
networkName := ctx.String(flags.Network.Name)
if networkName != "" {
var chainID uint64
......@@ -197,6 +200,7 @@ func NewConfigFromCLI(log log.Logger, ctx *cli.Context) (*Config, error) {
if err != nil {
return nil, fmt.Errorf("failed to load rollup config for chain %d: %w", chainID, err)
}
l2ChainID = chainID
} else {
l2GenesisPath := ctx.String(flags.L2GenesisPath.Name)
l2ChainConfig, err = loadChainConfigFromGenesis(l2GenesisPath)
......@@ -210,7 +214,11 @@ func NewConfigFromCLI(log log.Logger, ctx *cli.Context) (*Config, error) {
return nil, fmt.Errorf("invalid rollup config: %w", err)
}
isCustomConfig = true
l2ChainID = l2ChainConfig.ChainID.Uint64()
if ctx.Bool(flags.L2Custom.Name) {
log.Warn("Using custom chain configuration via preimage oracle. This is not compatible with on-chain execution.")
l2ChainID = client.CustomChainIDIndicator
}
}
dbFormat := types.DataFormat(ctx.String(flags.DataFormat.Name))
......@@ -218,24 +226,24 @@ func NewConfigFromCLI(log log.Logger, ctx *cli.Context) (*Config, error) {
return nil, fmt.Errorf("invalid %w: %v", ErrInvalidDataFormat, dbFormat)
}
return &Config{
Rollup: rollupCfg,
DataDir: ctx.String(flags.DataDir.Name),
DataFormat: dbFormat,
L2URL: ctx.String(flags.L2NodeAddr.Name),
L2ExperimentalURL: ctx.String(flags.L2NodeExperimentalAddr.Name),
L2ChainConfig: l2ChainConfig,
L2Head: l2Head,
L2OutputRoot: l2OutputRoot,
L2Claim: l2Claim,
L2ClaimBlockNumber: l2ClaimBlockNum,
L1Head: l1Head,
L1URL: ctx.String(flags.L1NodeAddr.Name),
L1BeaconURL: ctx.String(flags.L1BeaconAddr.Name),
L1TrustRPC: ctx.Bool(flags.L1TrustRPC.Name),
L1RPCKind: sources.RPCProviderKind(ctx.String(flags.L1RPCProviderKind.Name)),
ExecCmd: ctx.String(flags.Exec.Name),
ServerMode: ctx.Bool(flags.Server.Name),
IsCustomChainConfig: isCustomConfig,
L2ChainID: l2ChainID,
Rollup: rollupCfg,
DataDir: ctx.String(flags.DataDir.Name),
DataFormat: dbFormat,
L2URL: ctx.String(flags.L2NodeAddr.Name),
L2ExperimentalURL: ctx.String(flags.L2NodeExperimentalAddr.Name),
L2ChainConfig: l2ChainConfig,
L2Head: l2Head,
L2OutputRoot: l2OutputRoot,
L2Claim: l2Claim,
L2ClaimBlockNumber: l2ClaimBlockNum,
L1Head: l1Head,
L1URL: ctx.String(flags.L1NodeAddr.Name),
L1BeaconURL: ctx.String(flags.L1BeaconAddr.Name),
L1TrustRPC: ctx.Bool(flags.L1TrustRPC.Name),
L1RPCKind: sources.RPCProviderKind(ctx.String(flags.L1RPCProviderKind.Name)),
ExecCmd: ctx.String(flags.Exec.Name),
ServerMode: ctx.Bool(flags.Server.Name),
}, nil
}
......
......@@ -8,6 +8,7 @@ import (
"github.com/ethereum-optimism/optimism/op-node/chaincfg"
"github.com/ethereum-optimism/optimism/op-node/rollup"
"github.com/ethereum-optimism/optimism/op-program/chainconfig"
"github.com/ethereum-optimism/optimism/op-program/client"
"github.com/ethereum-optimism/optimism/op-program/host/types"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/params"
......@@ -163,15 +164,15 @@ func TestRejectExecAndServerMode(t *testing.T) {
require.ErrorIs(t, err, ErrNoExecInServerMode)
}
func TestIsCustomChainConfig(t *testing.T) {
func TestCustomL2ChainID(t *testing.T) {
t.Run("nonCustom", func(t *testing.T) {
cfg := validConfig()
require.Equal(t, cfg.IsCustomChainConfig, false)
require.Equal(t, cfg.L2ChainID, validL2Genesis.ChainID.Uint64())
})
t.Run("custom", func(t *testing.T) {
customChainConfig := &params.ChainConfig{ChainID: big.NewInt(0x1212121212)}
cfg := NewConfig(validRollupConfig, customChainConfig, validL1Head, validL2Head, validL2OutputRoot, validL2Claim, validL2ClaimBlockNum)
require.Equal(t, cfg.IsCustomChainConfig, true)
require.Equal(t, cfg.L2ChainID, client.CustomChainIDIndicator)
})
}
......
......@@ -21,6 +21,14 @@ func prefixEnvVars(name string) []string {
}
var (
L2Custom = &cli.BoolFlag{
Name: "l2.custom",
Usage: "Override the L2 chain ID to the custom chain indicator for custom chain configuration not present in the client program. " +
"WARNING: This is not compatible with on-chain execution and must only be used for testing.",
EnvVars: prefixEnvVars("L2_CHAINID"),
Value: false,
Hidden: true,
}
RollupConfig = &cli.StringFlag{
Name: "rollup.config",
Usage: "Rollup chain parameters",
......@@ -131,6 +139,7 @@ var requiredFlags = []cli.Flag{
}
var programFlags = []cli.Flag{
L2Custom,
RollupConfig,
Network,
DataDir,
......@@ -167,6 +176,9 @@ func CheckRequired(ctx *cli.Context) error {
if ctx.String(L2GenesisPath.Name) != "" && network != "" {
return fmt.Errorf("cannot specify both %s and %s", L2GenesisPath.Name, Network.Name)
}
if ctx.Bool(L2Custom.Name) && rollupConfig == "" {
return fmt.Errorf("flag %s cannot be used with named networks", L2Custom.Name)
}
for _, flag := range requiredFlags {
if !ctx.IsSet(flag.Names()[0]) {
return fmt.Errorf("flag %s is required", flag.Names()[0])
......
......@@ -38,22 +38,14 @@ func (s *LocalPreimageSource) Get(key common.Hash) ([]byte, error) {
case l2ClaimBlockNumberKey:
return binary.BigEndian.AppendUint64(nil, s.config.L2ClaimBlockNumber), nil
case l2ChainIDKey:
// The CustomChainIDIndicator informs the client to rely on the L2ChainConfigKey to
// read the chain config. Otherwise, it'll attempt to read a non-existent hardcoded chain config
var chainID uint64
if s.config.IsCustomChainConfig {
chainID = client.CustomChainIDIndicator
} else {
chainID = s.config.L2ChainConfig.ChainID.Uint64()
}
return binary.BigEndian.AppendUint64(nil, chainID), nil
return binary.BigEndian.AppendUint64(nil, s.config.L2ChainID), nil
case l2ChainConfigKey:
if !s.config.IsCustomChainConfig {
if s.config.L2ChainID != client.CustomChainIDIndicator {
return nil, ErrNotFound
}
return json.Marshal(s.config.L2ChainConfig)
case rollupKey:
if !s.config.IsCustomChainConfig {
if s.config.L2ChainID != client.CustomChainIDIndicator {
return nil, ErrNotFound
}
return json.Marshal(s.config.Rollup)
......
......@@ -7,6 +7,7 @@ import (
"github.com/ethereum-optimism/optimism/op-node/chaincfg"
preimage "github.com/ethereum-optimism/optimism/op-preimage"
"github.com/ethereum-optimism/optimism/op-program/client"
"github.com/ethereum-optimism/optimism/op-program/host/config"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/params"
......@@ -15,6 +16,7 @@ import (
func TestLocalPreimageSource(t *testing.T) {
cfg := &config.Config{
L2ChainID: 86,
Rollup: chaincfg.OPSepolia(),
L1Head: common.HexToHash("0x1111"),
L2OutputRoot: common.HexToHash("0x2222"),
......@@ -32,7 +34,7 @@ func TestLocalPreimageSource(t *testing.T) {
{"L2OutputRoot", l2OutputRootKey, cfg.L2OutputRoot.Bytes()},
{"L2Claim", l2ClaimKey, cfg.L2Claim.Bytes()},
{"L2ClaimBlockNumber", l2ClaimBlockNumberKey, binary.BigEndian.AppendUint64(nil, cfg.L2ClaimBlockNumber)},
{"L2ChainID", l2ChainIDKey, binary.BigEndian.AppendUint64(nil, cfg.L2ChainConfig.ChainID.Uint64())},
{"L2ChainID", l2ChainIDKey, binary.BigEndian.AppendUint64(nil, 86)},
{"Rollup", rollupKey, nil}, // Only available for custom chain configs
{"ChainConfig", l2ChainConfigKey, nil}, // Only available for custom chain configs
{"Unknown", preimage.LocalIndexKey(1000).PreimageKey(), nil},
......@@ -52,13 +54,13 @@ func TestLocalPreimageSource(t *testing.T) {
func TestGetCustomChainConfigPreimages(t *testing.T) {
cfg := &config.Config{
Rollup: chaincfg.OPSepolia(),
IsCustomChainConfig: true,
L1Head: common.HexToHash("0x1111"),
L2OutputRoot: common.HexToHash("0x2222"),
L2Claim: common.HexToHash("0x3333"),
L2ClaimBlockNumber: 1234,
L2ChainConfig: params.SepoliaChainConfig,
Rollup: chaincfg.OPSepolia(),
L2ChainID: client.CustomChainIDIndicator,
L1Head: common.HexToHash("0x1111"),
L2OutputRoot: common.HexToHash("0x2222"),
L2Claim: common.HexToHash("0x3333"),
L2ClaimBlockNumber: 1234,
L2ChainConfig: params.SepoliaChainConfig,
}
source := NewLocalPreimageSource(cfg)
actualRollup, err := source.Get(rollupKey)
......
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