Commit 8ca043fc authored by Adrian Sutton's avatar Adrian Sutton Committed by protolambda

op-program: Integrate existing L1Client implementation to fetch L1 data

parent 7a6b8ff8
......@@ -9,6 +9,7 @@ import (
"github.com/ethereum-optimism/optimism/op-node/chaincfg"
"github.com/ethereum-optimism/optimism/op-program/config"
"github.com/ethereum-optimism/optimism/op-program/flags"
"github.com/ethereum-optimism/optimism/op-program/l1"
"github.com/ethereum-optimism/optimism/op-program/l2"
"github.com/ethereum-optimism/optimism/op-program/version"
oplog "github.com/ethereum-optimism/optimism/op-service/log"
......@@ -94,8 +95,14 @@ func FaultProofProgram(logger log.Logger, cfg *config.Config) error {
}
ctx := context.Background()
logger.Info("Connecting to L1 node", "l1", cfg.L1URL)
_, err := l1.NewFetchingL1(ctx, logger, cfg)
if err != nil {
return fmt.Errorf("connect l1 oracle: %w", err)
}
logger.Info("Connecting to L2 node", "l2", cfg.L2URL)
_, err := l2.NewFetchingEngine(ctx, logger, cfg)
_, err = l2.NewFetchingEngine(ctx, logger, cfg)
if err != nil {
return fmt.Errorf("connect l2 oracle: %w", err)
}
......
......@@ -6,6 +6,7 @@ import (
"testing"
"github.com/ethereum-optimism/optimism/op-node/chaincfg"
"github.com/ethereum-optimism/optimism/op-node/sources"
"github.com/ethereum-optimism/optimism/op-program/config"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
......@@ -101,6 +102,50 @@ func TestL2Head(t *testing.T) {
})
}
func TestL1(t *testing.T) {
expected := "https://example.com:8545"
cfg := configForArgs(t, addRequiredArgs("--l1", expected))
require.Equal(t, expected, cfg.L1URL)
}
func TestL1TrustRPC(t *testing.T) {
t.Run("DefaultFalse", func(t *testing.T) {
cfg := configForArgs(t, addRequiredArgs())
require.False(t, cfg.L1TrustRPC)
})
t.Run("Enabled", func(t *testing.T) {
cfg := configForArgs(t, addRequiredArgs("--l1.trustrpc"))
require.True(t, cfg.L1TrustRPC)
})
t.Run("EnabledWithArg", func(t *testing.T) {
cfg := configForArgs(t, addRequiredArgs("--l1.trustrpc=true"))
require.True(t, cfg.L1TrustRPC)
})
t.Run("Disabled", func(t *testing.T) {
cfg := configForArgs(t, addRequiredArgs("--l1.trustrpc=false"))
require.False(t, cfg.L1TrustRPC)
})
}
func TestL1RPCKind(t *testing.T) {
t.Run("DefaultBasic", func(t *testing.T) {
cfg := configForArgs(t, addRequiredArgs())
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()))
require.Equal(t, kind, cfg.L1RPCKind)
})
}
t.Run("RequireLowercase", func(t *testing.T) {
verifyArgsInvalid(t, "rpc kind", addRequiredArgs("--l1.rpckind", "AlChemY"))
})
t.Run("UnknownKind", func(t *testing.T) {
verifyArgsInvalid(t, "\"foo\"", addRequiredArgs("--l1.rpckind", "foo"))
})
}
// Offline support will be added later, but for now it just bails out with an error
func TestOfflineModeNotSupported(t *testing.T) {
logger := log.New()
......
......@@ -5,6 +5,7 @@ import (
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/flags"
"github.com/ethereum/go-ethereum/common"
"github.com/urfave/cli"
......@@ -14,6 +15,7 @@ var (
ErrMissingRollupConfig = errors.New("missing rollup config")
ErrMissingL2Genesis = errors.New("missing l2 genesis")
ErrInvalidL2Head = errors.New("invalid l2 head")
ErrL1AndL2Inconsistent = errors.New("l1 and l2 options must be specified together or both omitted")
)
type Config struct {
......@@ -21,6 +23,9 @@ type Config struct {
L2URL string
L2GenesisPath string
L2Head common.Hash
L1URL string
L1TrustRPC bool
L1RPCKind sources.RPCProviderKind
}
func (c *Config) Check() error {
......@@ -36,11 +41,14 @@ func (c *Config) Check() error {
if c.L2Head == (common.Hash{}) {
return ErrInvalidL2Head
}
if (c.L1URL != "") != (c.L2URL != "") {
return ErrL1AndL2Inconsistent
}
return nil
}
func (c *Config) FetchingEnabled() bool {
return c.L2URL != ""
return c.L1URL != "" && c.L2URL != ""
}
// NewConfig creates a Config with all optional values set to the CLI default value
......@@ -49,6 +57,7 @@ func NewConfig(rollupCfg *rollup.Config, l2GenesisPath string, l2Head common.Has
Rollup: rollupCfg,
L2GenesisPath: l2GenesisPath,
L2Head: l2Head,
L1RPCKind: sources.RPCKindBasic,
}
}
......@@ -69,5 +78,8 @@ func NewConfigFromCLI(ctx *cli.Context) (*Config, error) {
L2URL: ctx.GlobalString(flags.L2NodeAddr.Name),
L2GenesisPath: ctx.GlobalString(flags.L2GenesisPath.Name),
L2Head: l2Head,
L1URL: ctx.GlobalString(flags.L1NodeAddr.Name),
L1TrustRPC: ctx.GlobalBool(flags.L1TrustRPC.Name),
L1RPCKind: sources.RPCProviderKind(ctx.GlobalString(flags.L1RPCProviderKind.Name)),
}, nil
}
......@@ -54,6 +54,29 @@ func TestL2Head(t *testing.T) {
})
}
func TestFetchingArgConsistency(t *testing.T) {
t.Run("RequireL2WhenL1Set", func(t *testing.T) {
cfg := NewConfig(&chaincfg.Beta1, validL2GenesisPath, validL2Head)
cfg.L1URL = "https://example.com:1234"
require.ErrorIs(t, cfg.Check(), ErrL1AndL2Inconsistent)
})
t.Run("RequireL1WhenL2Set", func(t *testing.T) {
cfg := NewConfig(&chaincfg.Beta1, validL2GenesisPath, validL2Head)
cfg.L2URL = "https://example.com:1234"
require.ErrorIs(t, cfg.Check(), ErrL1AndL2Inconsistent)
})
t.Run("AllowNeitherSet", func(t *testing.T) {
cfg := NewConfig(&chaincfg.Beta1, validL2GenesisPath, validL2Head)
require.NoError(t, cfg.Check())
})
t.Run("AllowBothSet", func(t *testing.T) {
cfg := NewConfig(&chaincfg.Beta1, validL2GenesisPath, validL2Head)
cfg.L1URL = "https://example.com:1234"
cfg.L2URL = "https://example.com:4678"
require.NoError(t, cfg.Check())
})
}
func TestFetchingEnabled(t *testing.T) {
t.Run("FetchingNotEnabledWhenNoFetcherUrlsSpecified", func(t *testing.T) {
cfg := NewConfig(&chaincfg.Beta1, validL2GenesisPath, validL2Head)
......@@ -63,6 +86,25 @@ func TestFetchingEnabled(t *testing.T) {
t.Run("FetchingEnabledWhenFetcherUrlsSpecified", func(t *testing.T) {
cfg := NewConfig(&chaincfg.Beta1, validL2GenesisPath, validL2Head)
cfg.L2URL = "https://example.com:1234"
require.False(t, cfg.FetchingEnabled(), "Should not enable fetching when node URL not supplied")
})
t.Run("FetchingNotEnabledWhenNoL1UrlSpecified", func(t *testing.T) {
cfg := NewConfig(&chaincfg.Beta1, validL2GenesisPath, validL2Head)
cfg.L2URL = "https://example.com:1234"
require.False(t, cfg.FetchingEnabled(), "Should not enable L1 fetching when L1 node URL not supplied")
})
t.Run("FetchingNotEnabledWhenNoL2UrlSpecified", func(t *testing.T) {
cfg := NewConfig(&chaincfg.Beta1, validL2GenesisPath, validL2Head)
cfg.L1URL = "https://example.com:1234"
require.False(t, cfg.FetchingEnabled(), "Should not enable L2 fetching when L2 node URL not supplied")
})
t.Run("FetchingEnabledWhenBothFetcherUrlsSpecified", func(t *testing.T) {
cfg := NewConfig(&chaincfg.Beta1, validL2GenesisPath, validL2Head)
cfg.L1URL = "https://example.com:1234"
cfg.L2URL = "https://example.com:5678"
require.True(t, cfg.FetchingEnabled(), "Should enable fetching when node URL supplied")
})
}
......@@ -4,10 +4,13 @@ import (
"fmt"
"strings"
"github.com/urfave/cli"
"github.com/ethereum-optimism/optimism/op-node/chaincfg"
nodeflags "github.com/ethereum-optimism/optimism/op-node/flags"
"github.com/ethereum-optimism/optimism/op-node/sources"
service "github.com/ethereum-optimism/optimism/op-service"
oplog "github.com/ethereum-optimism/optimism/op-service/log"
"github.com/urfave/cli"
)
const envVarPrefix = "OP_PROGRAM"
......@@ -38,6 +41,26 @@ var (
Usage: "Hash of the agreed L2 block to start derivation from",
EnvVar: service.PrefixEnvVar(envVarPrefix, "L2_HEAD"),
}
L1NodeAddr = cli.StringFlag{
Name: "l1",
Usage: "Address of L1 JSON-RPC endpoint to use (eth namespace required)",
EnvVar: service.PrefixEnvVar(envVarPrefix, "L1_RPC"),
}
L1TrustRPC = cli.BoolFlag{
Name: "l1.trustrpc",
Usage: "Trust the L1 RPC, sync faster at risk of malicious/buggy RPC providing bad or inconsistent L1 data",
EnvVar: service.PrefixEnvVar(envVarPrefix, "L1_TRUST_RPC"),
}
L1RPCProviderKind = cli.GenericFlag{
Name: "l1.rpckind",
Usage: "The kind of RPC provider, used to inform optimal transactions receipts fetching, and thus reduce costs. Valid options: " +
nodeflags.EnumString[sources.RPCProviderKind](sources.RPCProviderKinds),
EnvVar: service.PrefixEnvVar(envVarPrefix, "L1_RPC_KIND"),
Value: func() *sources.RPCProviderKind {
out := sources.RPCKindBasic
return &out
}(),
}
)
// Flags contains the list of configuration options available to the binary.
......@@ -49,6 +72,9 @@ var programFlags = []cli.Flag{
L2NodeAddr,
L2GenesisPath,
L2Head,
L1NodeAddr,
L1TrustRPC,
L1RPCProviderKind,
}
func init() {
......
......@@ -4,6 +4,8 @@ import (
"reflect"
"strings"
"testing"
"github.com/urfave/cli"
)
// TestUniqueFlags asserts that all flag names are unique, to avoid accidental conflicts between the many flags.
......@@ -19,15 +21,25 @@ func TestUniqueFlags(t *testing.T) {
}
}
// TestUniqueEnvVars asserts that all flag env vars are unique, to avoid accidental conflicts between the many flags.
func TestUniqueEnvVars(t *testing.T) {
seenCLI := make(map[string]struct{})
for _, flag := range Flags {
envVar := envVarForFlag(flag)
if _, ok := seenCLI[envVar]; envVar != "" && ok {
t.Errorf("duplicate flag env var %s", envVar)
continue
}
seenCLI[envVar] = struct{}{}
}
}
func TestCorrectEnvVarPrefix(t *testing.T) {
for _, flag := range Flags {
values := reflect.ValueOf(flag)
envVarValue := values.FieldByName("EnvVar")
if envVarValue == (reflect.Value{}) {
envVar := envVarForFlag(flag)
if envVar == "" {
t.Errorf("Failed to find EnvVar for flag %v", flag.GetName())
continue
}
envVar := envVarValue.String()
if envVar[:len("OP_PROGRAM_")] != "OP_PROGRAM_" {
t.Errorf("Flag %v env var (%v) does not start with OP_PROGRAM_", flag.GetName(), envVar)
}
......@@ -36,3 +48,12 @@ func TestCorrectEnvVarPrefix(t *testing.T) {
}
}
}
func envVarForFlag(flag cli.Flag) string {
values := reflect.ValueOf(flag)
envVarValue := values.FieldByName("EnvVar")
if envVarValue == (reflect.Value{}) {
return ""
}
return envVarValue.String()
}
package l1
import (
"context"
"github.com/ethereum-optimism/optimism/op-node/client"
"github.com/ethereum-optimism/optimism/op-node/rollup/derive"
"github.com/ethereum-optimism/optimism/op-node/sources"
"github.com/ethereum-optimism/optimism/op-program/config"
"github.com/ethereum/go-ethereum/log"
)
func NewFetchingL1(ctx context.Context, logger log.Logger, cfg *config.Config) (derive.L1Fetcher, error) {
rpc, err := client.NewRPC(ctx, logger, cfg.L1URL)
if err != nil {
return nil, err
}
return sources.NewL1Client(rpc, logger, nil, sources.L1ClientDefaultConfig(cfg.Rollup, cfg.L1TrustRPC, cfg.L1RPCKind))
}
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