Commit d8616f54 authored by Adrian Sutton's avatar Adrian Sutton

op-program: Pass a ChainConfig to the Config struct rather than a file path to load from

parent a01d02c3
......@@ -9,6 +9,7 @@ import (
"github.com/ethereum-optimism/optimism/op-node/sources"
"github.com/ethereum-optimism/optimism/op-program/host/config"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/log"
"github.com/stretchr/testify/require"
)
......@@ -17,16 +18,19 @@ import (
var l1HeadValue = common.HexToHash("0x111111").Hex()
var l2HeadValue = common.HexToHash("0x222222").Hex()
var l2ClaimValue = common.HexToHash("0x333333").Hex()
var l2Genesis = core.DefaultGoerliGenesisBlock()
var l2GenesisConfig = l2Genesis.Config
func TestLogLevel(t *testing.T) {
t.Parallel()
t.Run("RejectInvalid", func(t *testing.T) {
verifyArgsInvalid(t, "unknown level: foo", addRequiredArgs("--log.level=foo"))
verifyArgsInvalid(t, "unknown level: foo", addRequiredArgs(t, "--log.level=foo"))
})
for _, lvl := range []string{"trace", "debug", "info", "error", "crit"} {
lvl := lvl
t.Run("AcceptValid_"+lvl, func(t *testing.T) {
logger, _, err := runWithArgs(addRequiredArgs("--log.level", lvl))
logger, _, err := runWithArgs(addRequiredArgs(t, "--log.level", lvl))
require.NoError(t, err)
require.NotNil(t, logger)
})
......@@ -34,10 +38,11 @@ func TestLogLevel(t *testing.T) {
}
func TestDefaultCLIOptionsMatchDefaultConfig(t *testing.T) {
cfg := configForArgs(t, addRequiredArgs())
t.Parallel()
cfg := configForArgs(t, addRequiredArgs(t))
defaultCfg := config.NewConfig(
&chaincfg.Goerli,
"genesis.json",
l2GenesisConfig,
common.HexToHash(l1HeadValue),
common.HexToHash(l2HeadValue),
common.HexToHash(l2ClaimValue))
......@@ -45,16 +50,17 @@ func TestDefaultCLIOptionsMatchDefaultConfig(t *testing.T) {
}
func TestNetwork(t *testing.T) {
t.Parallel()
t.Run("Unknown", func(t *testing.T) {
verifyArgsInvalid(t, "invalid network bar", replaceRequiredArg("--network", "bar"))
verifyArgsInvalid(t, "invalid network bar", replaceRequiredArg(t, "--network", "bar"))
})
t.Run("Required", func(t *testing.T) {
verifyArgsInvalid(t, "flag rollup.config or network is required", addRequiredArgsExcept("--network"))
verifyArgsInvalid(t, "flag rollup.config or network is required", addRequiredArgsExcept(t, "--network"))
})
t.Run("DisallowNetworkAndRollupConfig", func(t *testing.T) {
verifyArgsInvalid(t, "cannot specify both rollup.config and network", addRequiredArgs("--rollup.config=foo"))
verifyArgsInvalid(t, "cannot specify both rollup.config and network", addRequiredArgs(t, "--rollup.config=foo"))
})
t.Run("RollupConfig", func(t *testing.T) {
......@@ -65,7 +71,7 @@ func TestNetwork(t *testing.T) {
err = os.WriteFile(configFile, configJson, os.ModePerm)
require.NoError(t, err)
cfg := configForArgs(t, addRequiredArgsExcept("--network", "--rollup.config", configFile))
cfg := configForArgs(t, addRequiredArgsExcept(t, "--network", "--rollup.config", configFile))
require.Equal(t, chaincfg.Goerli, *cfg.Rollup)
})
......@@ -73,121 +79,130 @@ func TestNetwork(t *testing.T) {
name := name
expected := cfg
t.Run("Network_"+name, func(t *testing.T) {
cfg := configForArgs(t, replaceRequiredArg("--network", name))
cfg := configForArgs(t, replaceRequiredArg(t, "--network", name))
require.Equal(t, expected, *cfg.Rollup)
})
}
}
func TestDataDir(t *testing.T) {
t.Parallel()
expected := "/tmp/mainTestDataDir"
cfg := configForArgs(t, addRequiredArgs("--datadir", expected))
cfg := configForArgs(t, addRequiredArgs(t, "--datadir", expected))
require.Equal(t, expected, cfg.DataDir)
}
func TestL2(t *testing.T) {
t.Parallel()
expected := "https://example.com:8545"
cfg := configForArgs(t, addRequiredArgs("--l2", expected))
cfg := configForArgs(t, addRequiredArgs(t, "--l2", expected))
require.Equal(t, expected, cfg.L2URL)
}
func TestL2Genesis(t *testing.T) {
t.Parallel()
t.Run("Required", func(t *testing.T) {
verifyArgsInvalid(t, "flag l2.genesis is required", addRequiredArgsExcept("--l2.genesis"))
verifyArgsInvalid(t, "flag l2.genesis is required", addRequiredArgsExcept(t, "--l2.genesis"))
})
t.Run("Valid", func(t *testing.T) {
cfg := configForArgs(t, replaceRequiredArg("--l2.genesis", "/tmp/genesis.json"))
require.Equal(t, "/tmp/genesis.json", cfg.L2GenesisPath)
cfg := configForArgs(t, replaceRequiredArg(t, "--l2.genesis", writeValidGenesis(t)))
require.Equal(t, l2GenesisConfig, cfg.L2ChainConfig)
})
}
func TestL2Head(t *testing.T) {
t.Parallel()
t.Run("Required", func(t *testing.T) {
verifyArgsInvalid(t, "flag l2.head is required", addRequiredArgsExcept("--l2.head"))
verifyArgsInvalid(t, "flag l2.head is required", addRequiredArgsExcept(t, "--l2.head"))
})
t.Run("Valid", func(t *testing.T) {
cfg := configForArgs(t, replaceRequiredArg("--l2.head", l2HeadValue))
cfg := configForArgs(t, replaceRequiredArg(t, "--l2.head", l2HeadValue))
require.Equal(t, common.HexToHash(l2HeadValue), cfg.L2Head)
})
t.Run("Invalid", func(t *testing.T) {
verifyArgsInvalid(t, config.ErrInvalidL2Head.Error(), replaceRequiredArg("--l2.head", "something"))
verifyArgsInvalid(t, config.ErrInvalidL2Head.Error(), replaceRequiredArg(t, "--l2.head", "something"))
})
}
func TestL1Head(t *testing.T) {
t.Parallel()
t.Run("Required", func(t *testing.T) {
verifyArgsInvalid(t, "flag l1.head is required", addRequiredArgsExcept("--l1.head"))
verifyArgsInvalid(t, "flag l1.head is required", addRequiredArgsExcept(t, "--l1.head"))
})
t.Run("Valid", func(t *testing.T) {
cfg := configForArgs(t, replaceRequiredArg("--l1.head", l1HeadValue))
cfg := configForArgs(t, replaceRequiredArg(t, "--l1.head", l1HeadValue))
require.Equal(t, common.HexToHash(l1HeadValue), cfg.L1Head)
})
t.Run("Invalid", func(t *testing.T) {
verifyArgsInvalid(t, config.ErrInvalidL1Head.Error(), replaceRequiredArg("--l1.head", "something"))
verifyArgsInvalid(t, config.ErrInvalidL1Head.Error(), replaceRequiredArg(t, "--l1.head", "something"))
})
}
func TestL1(t *testing.T) {
t.Parallel()
expected := "https://example.com:8545"
cfg := configForArgs(t, addRequiredArgs("--l1", expected))
cfg := configForArgs(t, addRequiredArgs(t, "--l1", expected))
require.Equal(t, expected, cfg.L1URL)
}
func TestL1TrustRPC(t *testing.T) {
t.Parallel()
t.Run("DefaultFalse", func(t *testing.T) {
cfg := configForArgs(t, addRequiredArgs())
cfg := configForArgs(t, addRequiredArgs(t))
require.False(t, cfg.L1TrustRPC)
})
t.Run("Enabled", func(t *testing.T) {
cfg := configForArgs(t, addRequiredArgs("--l1.trustrpc"))
cfg := configForArgs(t, addRequiredArgs(t, "--l1.trustrpc"))
require.True(t, cfg.L1TrustRPC)
})
t.Run("EnabledWithArg", func(t *testing.T) {
cfg := configForArgs(t, addRequiredArgs("--l1.trustrpc=true"))
cfg := configForArgs(t, addRequiredArgs(t, "--l1.trustrpc=true"))
require.True(t, cfg.L1TrustRPC)
})
t.Run("Disabled", func(t *testing.T) {
cfg := configForArgs(t, addRequiredArgs("--l1.trustrpc=false"))
cfg := configForArgs(t, addRequiredArgs(t, "--l1.trustrpc=false"))
require.False(t, cfg.L1TrustRPC)
})
}
func TestL1RPCKind(t *testing.T) {
t.Parallel()
t.Run("DefaultBasic", func(t *testing.T) {
cfg := configForArgs(t, addRequiredArgs())
cfg := configForArgs(t, addRequiredArgs(t))
require.Equal(t, sources.RPCKindBasic, cfg.L1RPCKind)
})
for _, kind := range sources.RPCProviderKinds {
t.Run(kind.String(), func(t *testing.T) {
cfg := configForArgs(t, addRequiredArgs("--l1.rpckind", kind.String()))
cfg := configForArgs(t, addRequiredArgs(t, "--l1.rpckind", kind.String()))
require.Equal(t, kind, cfg.L1RPCKind)
})
}
t.Run("RequireLowercase", func(t *testing.T) {
verifyArgsInvalid(t, "rpc kind", addRequiredArgs("--l1.rpckind", "AlChemY"))
verifyArgsInvalid(t, "rpc kind", addRequiredArgs(t, "--l1.rpckind", "AlChemY"))
})
t.Run("UnknownKind", func(t *testing.T) {
verifyArgsInvalid(t, "\"foo\"", addRequiredArgs("--l1.rpckind", "foo"))
verifyArgsInvalid(t, "\"foo\"", addRequiredArgs(t, "--l1.rpckind", "foo"))
})
}
func TestL2Claim(t *testing.T) {
t.Parallel()
t.Run("Required", func(t *testing.T) {
verifyArgsInvalid(t, "flag l2.claim is required", addRequiredArgsExcept("--l2.claim"))
verifyArgsInvalid(t, "flag l2.claim is required", addRequiredArgsExcept(t, "--l2.claim"))
})
t.Run("Valid", func(t *testing.T) {
cfg := configForArgs(t, replaceRequiredArg("--l2.claim", l2ClaimValue))
cfg := configForArgs(t, replaceRequiredArg(t, "--l2.claim", l2ClaimValue))
require.EqualValues(t, common.HexToHash(l2ClaimValue), cfg.L2Claim)
})
t.Run("Invalid", func(t *testing.T) {
verifyArgsInvalid(t, config.ErrInvalidL2Claim.Error(), replaceRequiredArg("--l2.claim", "something"))
verifyArgsInvalid(t, config.ErrInvalidL2Claim.Error(), replaceRequiredArg(t, "--l2.claim", "something"))
})
}
......@@ -214,36 +229,46 @@ func runWithArgs(cliArgs []string) (log.Logger, *config.Config, error) {
return logger, cfg, err
}
func addRequiredArgs(args ...string) []string {
req := requiredArgs()
func addRequiredArgs(t *testing.T, args ...string) []string {
req := requiredArgs(t)
combined := toArgList(req)
return append(combined, args...)
}
func addRequiredArgsExcept(name string, optionalArgs ...string) []string {
req := requiredArgs()
func addRequiredArgsExcept(t *testing.T, name string, optionalArgs ...string) []string {
req := requiredArgs(t)
delete(req, name)
return append(toArgList(req), optionalArgs...)
}
func replaceRequiredArg(name string, value string) []string {
req := requiredArgs()
func replaceRequiredArg(t *testing.T, name string, value string) []string {
req := requiredArgs(t)
req[name] = value
return toArgList(req)
}
// requiredArgs returns map of argument names to values which are the minimal arguments required
// to create a valid Config
func requiredArgs() map[string]string {
func requiredArgs(t *testing.T) map[string]string {
genesisFile := writeValidGenesis(t)
return map[string]string{
"--network": "goerli",
"--l1.head": l1HeadValue,
"--l2.head": l2HeadValue,
"--l2.claim": l2ClaimValue,
"--l2.genesis": "genesis.json",
"--l2.genesis": genesisFile,
}
}
func writeValidGenesis(t *testing.T) string {
dir := t.TempDir()
j, err := json.Marshal(l2Genesis)
require.NoError(t, err)
genesisFile := dir + "/genesis.json"
require.NoError(t, os.WriteFile(genesisFile, j, 0666))
return genesisFile
}
func toArgList(req map[string]string) []string {
var combined []string
for name, value := range req {
......
package config
import (
"encoding/json"
"errors"
"fmt"
"os"
opnode "github.com/ethereum-optimism/optimism/op-node"
"github.com/ethereum-optimism/optimism/op-node/rollup"
"github.com/ethereum-optimism/optimism/op-node/sources"
"github.com/ethereum-optimism/optimism/op-program/host/flags"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/params"
"github.com/urfave/cli"
)
......@@ -22,16 +27,24 @@ var (
)
type Config struct {
Rollup *rollup.Config
DataDir string
L2URL string
L2GenesisPath string
L1Head common.Hash
L2Head common.Hash
L2Claim common.Hash
L1URL string
L1TrustRPC bool
L1RPCKind sources.RPCProviderKind
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
// L1Head is the block has of the L1 chain head block
L1Head common.Hash
L1URL string
L1TrustRPC bool
L1RPCKind sources.RPCProviderKind
// L2Head is the agreed L2 block to start derivation from
L2Head common.Hash
L2URL string
// L2Claim is the claimed L2 output root to verify
L2Claim common.Hash
// L2ChainConfig is the op-geth chain config for the L2 execution engine
L2ChainConfig *params.ChainConfig
}
func (c *Config) Check() error {
......@@ -50,7 +63,7 @@ func (c *Config) Check() error {
if c.L2Claim == (common.Hash{}) {
return ErrInvalidL2Claim
}
if c.L2GenesisPath == "" {
if c.L2ChainConfig == nil {
return ErrMissingL2Genesis
}
if (c.L1URL != "") != (c.L2URL != "") {
......@@ -67,10 +80,10 @@ func (c *Config) FetchingEnabled() bool {
}
// NewConfig creates a Config with all optional values set to the CLI default value
func NewConfig(rollupCfg *rollup.Config, l2GenesisPath string, l1Head common.Hash, l2Head common.Hash, l2Claim common.Hash) *Config {
func NewConfig(rollupCfg *rollup.Config, l2Genesis *params.ChainConfig, l1Head common.Hash, l2Head common.Hash, l2Claim common.Hash) *Config {
return &Config{
Rollup: rollupCfg,
L2GenesisPath: l2GenesisPath,
L2ChainConfig: l2Genesis,
L1Head: l1Head,
L2Head: l2Head,
L2Claim: l2Claim,
......@@ -98,11 +111,16 @@ func NewConfigFromCLI(ctx *cli.Context) (*Config, error) {
if l1Head == (common.Hash{}) {
return nil, ErrInvalidL1Head
}
l2GenesisPath := ctx.GlobalString(flags.L2GenesisPath.Name)
l2ChainConfig, err := loadChainConfigFromGenesis(l2GenesisPath)
if err != nil {
return nil, fmt.Errorf("invalid genesis: %w", err)
}
return &Config{
Rollup: rollupCfg,
DataDir: ctx.GlobalString(flags.DataDir.Name),
L2URL: ctx.GlobalString(flags.L2NodeAddr.Name),
L2GenesisPath: ctx.GlobalString(flags.L2GenesisPath.Name),
L2ChainConfig: l2ChainConfig,
L2Head: l2Head,
L2Claim: l2Claim,
L1Head: l1Head,
......@@ -111,3 +129,16 @@ func NewConfigFromCLI(ctx *cli.Context) (*Config, error) {
L1RPCKind: sources.RPCProviderKind(ctx.GlobalString(flags.L1RPCProviderKind.Name)),
}, nil
}
func loadChainConfigFromGenesis(path string) (*params.ChainConfig, error) {
data, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("read l2 genesis file: %w", err)
}
var genesis core.Genesis
err = json.Unmarshal(data, &genesis)
if err != nil {
return nil, fmt.Errorf("parse l2 genesis file: %w", err)
}
return genesis.Config, nil
}
......@@ -6,11 +6,12 @@ import (
"github.com/ethereum-optimism/optimism/op-node/chaincfg"
"github.com/ethereum-optimism/optimism/op-node/rollup"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/params"
"github.com/stretchr/testify/require"
)
var validRollupConfig = &chaincfg.Goerli
var validL2GenesisPath = "genesis.json"
var validL2Genesis = params.GoerliChainConfig
var validL1Head = common.Hash{0xaa}
var validL2Head = common.Hash{0xbb}
var validL2Claim = common.Hash{0xcc}
......@@ -60,7 +61,7 @@ func TestL2ClaimRequired(t *testing.T) {
func TestL2GenesisRequired(t *testing.T) {
config := validConfig()
config.L2GenesisPath = ""
config.L2ChainConfig = nil
err := config.Check()
require.ErrorIs(t, err, ErrMissingL2Genesis)
}
......@@ -132,7 +133,7 @@ func TestRequireDataDirInNonFetchingMode(t *testing.T) {
}
func validConfig() *Config {
cfg := NewConfig(validRollupConfig, validL2GenesisPath, validL1Head, validL2Head, validL2Claim)
cfg := NewConfig(validRollupConfig, validL2Genesis, validL1Head, validL2Head, validL2Claim)
cfg.DataDir = "/tmp/configTest"
return cfg
}
......@@ -2,25 +2,17 @@ package l2
import (
"context"
"encoding/json"
"fmt"
"os"
cll2 "github.com/ethereum-optimism/optimism/op-program/client/l2"
"github.com/ethereum-optimism/optimism/op-program/host/config"
"github.com/ethereum-optimism/optimism/op-program/preimage"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
)
func NewEngine(logger log.Logger, pre preimage.Oracle, hint preimage.Hinter, cfg *config.Config) (*cll2.OracleEngine, error) {
oracle := cll2.NewCachingOracle(cll2.NewPreimageOracle(pre, hint))
genesis, err := loadL2Genesis(cfg)
if err != nil {
return nil, err
}
engineBackend, err := cll2.NewOracleBackedL2Chain(logger, oracle, genesis, cfg.L2Head)
engineBackend, err := cll2.NewOracleBackedL2Chain(logger, oracle, cfg.L2ChainConfig, cfg.L2Head)
if err != nil {
return nil, fmt.Errorf("create l2 chain: %w", err)
}
......@@ -34,16 +26,3 @@ func NewFetchingOracle(ctx context.Context, logger log.Logger, cfg *config.Confi
}
return oracle, nil
}
func loadL2Genesis(cfg *config.Config) (*params.ChainConfig, error) {
data, err := os.ReadFile(cfg.L2GenesisPath)
if err != nil {
return nil, fmt.Errorf("read l2 genesis file: %w", err)
}
var genesis core.Genesis
err = json.Unmarshal(data, &genesis)
if err != nil {
return nil, fmt.Errorf("parse l2 genesis file: %w", err)
}
return genesis.Config, nil
}
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