Commit cda9d843 authored by mergify[bot]'s avatar mergify[bot] Committed by GitHub

Merge branch 'develop' into k0621/sec-90-fd-tests

parents 5ce5bbdf 505751c1
...@@ -32,9 +32,8 @@ And as it executes each step, it can optionally produce the witness data for the ...@@ -32,9 +32,8 @@ And as it executes each step, it can optionally produce the witness data for the
The Cannon CLI is used to load a program into an initial state, The Cannon CLI is used to load a program into an initial state,
transition it N steps quickly without witness generation, and 1 step while producing a witness. transition it N steps quickly without witness generation, and 1 step while producing a witness.
`mipsevm` is backed by the Unicorn emulator, but instrumented for proof generation, `mipsevm` is instrumented for proof generation and handles delay-slots by isolating each individual instruction
and handles delay-slots by isolating each individual instruction and tracking `nextPC` and tracking `nextPC` to emulate the delayed `PC` changes after delay-slot execution.
to emulate the delayed `PC` changes after delay-slot execution.
## Witness Data ## Witness Data
......
...@@ -108,7 +108,7 @@ func TestEVM(t *testing.T) { ...@@ -108,7 +108,7 @@ func TestEVM(t *testing.T) {
// TODO: maybe more readable to decode the evmPost state, and do attribute-wise comparison. // TODO: maybe more readable to decode the evmPost state, and do attribute-wise comparison.
uniPost := us.state.EncodeWitness() uniPost := us.state.EncodeWitness()
require.Equal(t, hexutil.Bytes(uniPost).String(), hexutil.Bytes(evmPost).String(), require.Equal(t, hexutil.Bytes(uniPost).String(), hexutil.Bytes(evmPost).String(),
"unicorn produced different state than EVM") "mipsevm produced different state than EVM")
} }
require.Equal(t, uint32(endAddr), state.PC, "must reach end") require.Equal(t, uint32(endAddr), state.PC, "must reach end")
// inspect test result // inspect test result
...@@ -175,7 +175,7 @@ func TestHelloEVM(t *testing.T) { ...@@ -175,7 +175,7 @@ func TestHelloEVM(t *testing.T) {
// TODO: maybe more readable to decode the evmPost state, and do attribute-wise comparison. // TODO: maybe more readable to decode the evmPost state, and do attribute-wise comparison.
uniPost := us.state.EncodeWitness() uniPost := us.state.EncodeWitness()
require.Equal(t, hexutil.Bytes(uniPost).String(), hexutil.Bytes(evmPost).String(), require.Equal(t, hexutil.Bytes(uniPost).String(), hexutil.Bytes(evmPost).String(),
"unicorn produced different state than EVM") "mipsevm produced different state than EVM")
} }
end := time.Now() end := time.Now()
delta := end.Sub(start) delta := end.Sub(start)
......
...@@ -11,8 +11,7 @@ import ( ...@@ -11,8 +11,7 @@ import (
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
) )
// Note: 2**12 = 4 KiB, the minimum page-size in Unicorn for mmap // Note: 2**12 = 4 KiB, the min phys page size in the Go runtime.
// as well as the Go runtime min phys page size.
const ( const (
PageAddrSize = 12 PageAddrSize = 12
PageKeySize = 32 - PageAddrSize PageKeySize = 32 - PageAddrSize
......
...@@ -56,15 +56,6 @@ func (m *InstrumentedState) handleSyscall() error { ...@@ -56,15 +56,6 @@ func (m *InstrumentedState) handleSyscall() error {
v0 = a0 v0 = a0
//fmt.Printf("mmap hint 0x%x size 0x%x\n", v0, sz) //fmt.Printf("mmap hint 0x%x size 0x%x\n", v0, sz)
} }
// Go does this thing where it first gets memory with PROT_NONE,
// and then mmaps with a hint with prot=3 (PROT_READ|WRITE).
// We can ignore the NONE case, to avoid duplicate/overlapping mmap calls to unicorn.
//prot := a2
//if prot != 0 {
// if err := mu.MemMap(uint64(v0), uint64(sz)); err != nil {
// log.Fatalf("mmap fail: %v", err)
// }
//}
case 4045: // brk case 4045: // brk
v0 = 0x40000000 v0 = 0x40000000
case 4120: // clone (not supported) case 4120: // clone (not supported)
......
FROM golang:1.18.0-alpine3.15 as builder FROM golang:1.19.9-alpine3.16 as builder
COPY ./endpoint-monitor /app
WORKDIR /app
RUN apk --no-cache add make jq bash git alpine-sdk RUN apk --no-cache add make jq bash git alpine-sdk
COPY ./endpoint-monitor /app/endpoint-monitor
COPY ./op-service /app/op-service
COPY ./op-node /app/op-node
COPY ./go.mod /app/go.mod
COPY ./go.sum /app/go.sum
COPY ./.git /app/.git
WORKDIR /app/endpoint-monitor
RUN go mod download
RUN make build RUN make build
FROM alpine:3.15 FROM alpine:3.16
RUN apk --no-cache add ca-certificates RUN apk --no-cache add ca-certificates
RUN addgroup -S app && adduser -S app -G app RUN addgroup -S app && adduser -S app -G app
USER app USER app
WORKDIR /app WORKDIR /app
COPY --from=builder /app/bin/endpoint-monitor /app COPY --from=builder /app/endpoint-monitor/bin/endpoint-monitor /app
ENTRYPOINT ["/app/endpoint-monitor"] ENTRYPOINT ["/app/endpoint-monitor"]
...@@ -4,7 +4,7 @@ The endpoint-monitor runs websocket checks on edge-proxyd endpoints and downstre ...@@ -4,7 +4,7 @@ The endpoint-monitor runs websocket checks on edge-proxyd endpoints and downstre
## Setup ## Setup
Install go1.18 Install go1.19
```bash ```bash
make build make build
......
...@@ -6,7 +6,7 @@ import ( ...@@ -6,7 +6,7 @@ import (
oplog "github.com/ethereum-optimism/optimism/op-service/log" oplog "github.com/ethereum-optimism/optimism/op-service/log"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
endpointMonitor "github.com/ethereum-optimism/optimism/endpoint-monitor" endpointMonitor "github.com/ethereum-optimism/optimism/endpoint-monitor"
) )
......
...@@ -9,7 +9,7 @@ import ( ...@@ -9,7 +9,7 @@ import (
opservice "github.com/ethereum-optimism/optimism/op-service" opservice "github.com/ethereum-optimism/optimism/op-service"
oplog "github.com/ethereum-optimism/optimism/op-service/log" oplog "github.com/ethereum-optimism/optimism/op-service/log"
opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics" opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
) )
type ProviderConfig struct { type ProviderConfig struct {
...@@ -24,24 +24,27 @@ const ( ...@@ -24,24 +24,27 @@ const (
) )
func CLIFlags(envPrefix string) []cli.Flag { func CLIFlags(envPrefix string) []cli.Flag {
prefixEnvVars := func(name string) []string {
return opservice.PrefixEnvVar(envPrefix, name)
}
flags := []cli.Flag{ flags := []cli.Flag{
cli.StringSliceFlag{ &cli.StringSliceFlag{
Name: ProvidersFlagName, Name: ProvidersFlagName,
Usage: "List of providers", Usage: "List of providers",
Required: true, Required: true,
EnvVar: opservice.PrefixEnvVar(envPrefix, "PROVIDERS"), EnvVars: prefixEnvVars("PROVIDERS"),
}, },
cli.DurationFlag{ &cli.DurationFlag{
Name: CheckIntervalFlagName, Name: CheckIntervalFlagName,
Usage: "Check interval duration", Usage: "Check interval duration",
Value: 5 * time.Minute, Value: 5 * time.Minute,
EnvVar: opservice.PrefixEnvVar(envPrefix, "CHECK_INTERVAL"), EnvVars: prefixEnvVars("CHECK_INTERVAL"),
}, },
cli.DurationFlag{ &cli.DurationFlag{
Name: CheckDurationFlagName, Name: CheckDurationFlagName,
Usage: "Check duration", Usage: "Check duration",
Value: 4 * time.Minute, Value: 4 * time.Minute,
EnvVar: opservice.PrefixEnvVar(envPrefix, "CHECK_DURATION"), EnvVars: prefixEnvVars("CHECK_DURATION"),
}, },
} }
flags = append(flags, opmetrics.CLIFlags(envPrefix)...) flags = append(flags, opmetrics.CLIFlags(envPrefix)...)
...@@ -73,9 +76,9 @@ func (c Config) Check() error { ...@@ -73,9 +76,9 @@ func (c Config) Check() error {
func NewConfig(ctx *cli.Context) Config { func NewConfig(ctx *cli.Context) Config {
return Config{ return Config{
Providers: ctx.GlobalStringSlice(ProvidersFlagName), Providers: ctx.StringSlice(ProvidersFlagName),
CheckInterval: ctx.GlobalDuration(CheckIntervalFlagName), CheckInterval: ctx.Duration(CheckIntervalFlagName),
CheckDuration: ctx.GlobalDuration(CheckDurationFlagName), CheckDuration: ctx.Duration(CheckDurationFlagName),
LogConfig: oplog.ReadCLIConfig(ctx), LogConfig: oplog.ReadCLIConfig(ctx),
MetricsConfig: opmetrics.ReadCLIConfig(ctx), MetricsConfig: opmetrics.ReadCLIConfig(ctx),
} }
......
...@@ -11,10 +11,10 @@ import ( ...@@ -11,10 +11,10 @@ import (
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
"github.com/ethereum-optimism/optimism/l2geth/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum-optimism/optimism/l2geth/ethclient" "github.com/ethereum/go-ethereum/ethclient"
) )
var ( var (
......
module github.com/ethereum-optimism/optimism/endpoint-monitor
go 1.18
require (
github.com/ethereum-optimism/optimism/l2geth v0.0.0-20220923210602-7121648c1f26
github.com/ethereum-optimism/optimism/op-service v0.8.8
github.com/ethereum/go-ethereum v1.10.26
github.com/pkg/errors v0.9.1
github.com/prometheus/client_golang v1.13.0
github.com/urfave/cli v1.22.9
)
replace github.com/ethereum-optimism/optimism/l2geth v0.0.0-20220923210602-7121648c1f26 => github.com/ethereum-optimism/optimism-legacy/l2geth v0.0.0-20220923210602-7121648c1f26
require (
github.com/VictoriaMetrics/fastcache v1.9.0 // indirect
github.com/aristanetworks/goarista v0.0.0-20170210015632-ea17b1a17847 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/btcsuite/btcd v0.22.1 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/deckarep/golang-set v1.8.0 // indirect
github.com/elastic/gosigar v0.12.0 // indirect
github.com/go-stack/stack v1.8.1 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/common v0.37.0 // indirect
github.com/prometheus/procfs v0.8.0 // indirect
github.com/rs/cors v1.8.2 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570 // indirect
github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3 // indirect
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e // indirect
golang.org/x/sys v0.0.0-20220701225701-179beb0bd1a1 // indirect
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect
)
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -26,11 +26,11 @@ require ( ...@@ -26,11 +26,11 @@ require (
github.com/multiformats/go-multiaddr v0.8.0 github.com/multiformats/go-multiaddr v0.8.0
github.com/multiformats/go-multiaddr-dns v0.3.1 github.com/multiformats/go-multiaddr-dns v0.3.1
github.com/olekukonko/tablewriter v0.0.5 github.com/olekukonko/tablewriter v0.0.5
github.com/pkg/errors v0.9.1
github.com/pkg/profile v1.7.0 github.com/pkg/profile v1.7.0
github.com/prometheus/client_golang v1.14.0 github.com/prometheus/client_golang v1.14.0
github.com/stretchr/testify v1.8.1 github.com/stretchr/testify v1.8.1
github.com/urfave/cli v1.22.9 github.com/urfave/cli/v2 v2.25.7
github.com/urfave/cli/v2 v2.17.2-0.20221006022127-8f469abc00aa
golang.org/x/crypto v0.6.0 golang.org/x/crypto v0.6.0
golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb
golang.org/x/sync v0.1.0 golang.org/x/sync v0.1.0
...@@ -134,7 +134,6 @@ require ( ...@@ -134,7 +134,6 @@ require (
github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/opentracing/opentracing-go v1.2.0 // indirect
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect
github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7 // indirect github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_model v0.3.0 // indirect github.com/prometheus/client_model v0.3.0 // indirect
github.com/prometheus/common v0.39.0 // indirect github.com/prometheus/common v0.39.0 // indirect
......
...@@ -10,7 +10,7 @@ git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGy ...@@ -10,7 +10,7 @@ git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGy
github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 h1:cTp8I5+VIoKjsnZuH8vjyaysT/ses3EvZeaV/1UkF2M= github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 h1:cTp8I5+VIoKjsnZuH8vjyaysT/ses3EvZeaV/1UkF2M=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0= github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno= github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=
github.com/CloudyKit/jet/v3 v3.0.0/go.mod h1:HKQPgSJmdK8hdoAbKUUWajkHyHo4RaU5rMdUywE7VMo= github.com/CloudyKit/jet/v3 v3.0.0/go.mod h1:HKQPgSJmdK8hdoAbKUUWajkHyHo4RaU5rMdUywE7VMo=
github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8= github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8=
...@@ -681,10 +681,8 @@ github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljT ...@@ -681,10 +681,8 @@ github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljT
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/urfave/cli v1.22.9 h1:cv3/KhXGBGjEXLC4bH0sLuJ9BewaAbpk5oyMOveu4pw= github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs=
github.com/urfave/cli v1.22.9/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
github.com/urfave/cli/v2 v2.17.2-0.20221006022127-8f469abc00aa h1:5SqCsI/2Qya2bCzK15ozrqo2sZxkh0FHynJZOTVoV6Q=
github.com/urfave/cli/v2 v2.17.2-0.20221006022127-8f469abc00aa/go.mod h1:1CNUng3PtjQMtRzJO4FMXBQvkGtuYRxxiR9xMa7jMwI=
github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.6.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w= github.com/valyala/fasthttp v1.6.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w=
......
...@@ -6,7 +6,7 @@ import ( ...@@ -6,7 +6,7 @@ import (
_ "net/http/pprof" _ "net/http/pprof"
gethrpc "github.com/ethereum/go-ethereum/rpc" gethrpc "github.com/ethereum/go-ethereum/rpc"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
"github.com/ethereum-optimism/optimism/op-batcher/flags" "github.com/ethereum-optimism/optimism/op-batcher/flags"
"github.com/ethereum-optimism/optimism/op-batcher/metrics" "github.com/ethereum-optimism/optimism/op-batcher/metrics"
......
...@@ -5,7 +5,7 @@ import ( ...@@ -5,7 +5,7 @@ import (
"github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
"github.com/ethereum-optimism/optimism/op-batcher/compressor" "github.com/ethereum-optimism/optimism/op-batcher/compressor"
"github.com/ethereum-optimism/optimism/op-batcher/flags" "github.com/ethereum-optimism/optimism/op-batcher/flags"
...@@ -118,17 +118,17 @@ func (c CLIConfig) Check() error { ...@@ -118,17 +118,17 @@ func (c CLIConfig) Check() error {
func NewConfig(ctx *cli.Context) CLIConfig { func NewConfig(ctx *cli.Context) CLIConfig {
return CLIConfig{ return CLIConfig{
/* Required Flags */ /* Required Flags */
L1EthRpc: ctx.GlobalString(flags.L1EthRpcFlag.Name), L1EthRpc: ctx.String(flags.L1EthRpcFlag.Name),
L2EthRpc: ctx.GlobalString(flags.L2EthRpcFlag.Name), L2EthRpc: ctx.String(flags.L2EthRpcFlag.Name),
RollupRpc: ctx.GlobalString(flags.RollupRpcFlag.Name), RollupRpc: ctx.String(flags.RollupRpcFlag.Name),
SubSafetyMargin: ctx.GlobalUint64(flags.SubSafetyMarginFlag.Name), SubSafetyMargin: ctx.Uint64(flags.SubSafetyMarginFlag.Name),
PollInterval: ctx.GlobalDuration(flags.PollIntervalFlag.Name), PollInterval: ctx.Duration(flags.PollIntervalFlag.Name),
/* Optional Flags */ /* Optional Flags */
MaxPendingTransactions: ctx.GlobalUint64(flags.MaxPendingTransactionsFlag.Name), MaxPendingTransactions: ctx.Uint64(flags.MaxPendingTransactionsFlag.Name),
MaxChannelDuration: ctx.GlobalUint64(flags.MaxChannelDurationFlag.Name), MaxChannelDuration: ctx.Uint64(flags.MaxChannelDurationFlag.Name),
MaxL1TxSize: ctx.GlobalUint64(flags.MaxL1TxSizeBytesFlag.Name), MaxL1TxSize: ctx.Uint64(flags.MaxL1TxSizeBytesFlag.Name),
Stopped: ctx.GlobalBool(flags.StoppedFlag.Name), Stopped: ctx.Bool(flags.StoppedFlag.Name),
TxMgrConfig: txmgr.ReadCLIConfig(ctx), TxMgrConfig: txmgr.ReadCLIConfig(ctx),
RPCConfig: rpc.ReadCLIConfig(ctx), RPCConfig: rpc.ReadCLIConfig(ctx),
LogConfig: oplog.ReadCLIConfig(ctx), LogConfig: oplog.ReadCLIConfig(ctx),
......
...@@ -8,7 +8,7 @@ import ( ...@@ -8,7 +8,7 @@ import (
"github.com/ethereum-optimism/optimism/op-batcher/metrics" "github.com/ethereum-optimism/optimism/op-batcher/metrics"
"github.com/olekukonko/tablewriter" "github.com/olekukonko/tablewriter"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
) )
var Subcommands = cli.Commands{ var Subcommands = cli.Commands{
...@@ -16,7 +16,7 @@ var Subcommands = cli.Commands{ ...@@ -16,7 +16,7 @@ var Subcommands = cli.Commands{
Name: "metrics", Name: "metrics",
Usage: "Dumps a list of supported metrics to stdout", Usage: "Dumps a list of supported metrics to stdout",
Flags: []cli.Flag{ Flags: []cli.Flag{
cli.StringFlag{ &cli.StringFlag{
Name: "format", Name: "format",
Value: "markdown", Value: "markdown",
Usage: "Output format (json|markdown)", Usage: "Output format (json|markdown)",
......
...@@ -4,7 +4,7 @@ import ( ...@@ -4,7 +4,7 @@ import (
"fmt" "fmt"
"os" "os"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
"github.com/ethereum-optimism/optimism/op-batcher/batcher" "github.com/ethereum-optimism/optimism/op-batcher/batcher"
"github.com/ethereum-optimism/optimism/op-batcher/cmd/doc" "github.com/ethereum-optimism/optimism/op-batcher/cmd/doc"
...@@ -29,7 +29,7 @@ func main() { ...@@ -29,7 +29,7 @@ func main() {
app.Usage = "Batch Submitter Service" app.Usage = "Batch Submitter Service"
app.Description = "Service for generating and submitting L2 tx batches to L1" app.Description = "Service for generating and submitting L2 tx batches to L1"
app.Action = curryMain(Version) app.Action = curryMain(Version)
app.Commands = []cli.Command{ app.Commands = []*cli.Command{
{ {
Name: "doc", Name: "doc",
Subcommands: doc.Subcommands, Subcommands: doc.Subcommands,
......
...@@ -4,7 +4,7 @@ import ( ...@@ -4,7 +4,7 @@ import (
"strings" "strings"
opservice "github.com/ethereum-optimism/optimism/op-service" opservice "github.com/ethereum-optimism/optimism/op-service"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
) )
const ( const (
...@@ -16,29 +16,29 @@ const ( ...@@ -16,29 +16,29 @@ const (
func CLIFlags(envPrefix string) []cli.Flag { func CLIFlags(envPrefix string) []cli.Flag {
return []cli.Flag{ return []cli.Flag{
cli.Uint64Flag{ &cli.Uint64Flag{
Name: TargetL1TxSizeBytesFlagName, Name: TargetL1TxSizeBytesFlagName,
Usage: "The target size of a batch tx submitted to L1.", Usage: "The target size of a batch tx submitted to L1.",
Value: 100_000, Value: 100_000,
EnvVar: opservice.PrefixEnvVar(envPrefix, "TARGET_L1_TX_SIZE_BYTES"), EnvVars: opservice.PrefixEnvVar(envPrefix, "TARGET_L1_TX_SIZE_BYTES"),
}, },
cli.IntFlag{ &cli.IntFlag{
Name: TargetNumFramesFlagName, Name: TargetNumFramesFlagName,
Usage: "The target number of frames to create per channel", Usage: "The target number of frames to create per channel",
Value: 1, Value: 1,
EnvVar: opservice.PrefixEnvVar(envPrefix, "TARGET_NUM_FRAMES"), EnvVars: opservice.PrefixEnvVar(envPrefix, "TARGET_NUM_FRAMES"),
}, },
cli.Float64Flag{ &cli.Float64Flag{
Name: ApproxComprRatioFlagName, Name: ApproxComprRatioFlagName,
Usage: "The approximate compression ratio (<= 1.0)", Usage: "The approximate compression ratio (<= 1.0)",
Value: 0.4, Value: 0.4,
EnvVar: opservice.PrefixEnvVar(envPrefix, "APPROX_COMPR_RATIO"), EnvVars: opservice.PrefixEnvVar(envPrefix, "APPROX_COMPR_RATIO"),
}, },
cli.StringFlag{ &cli.StringFlag{
Name: KindFlagName, Name: KindFlagName,
Usage: "The type of compressor. Valid options: " + strings.Join(KindKeys, ", "), Usage: "The type of compressor. Valid options: " + strings.Join(KindKeys, ", "),
EnvVar: opservice.PrefixEnvVar(envPrefix, "COMPRESSOR"), EnvVars: opservice.PrefixEnvVar(envPrefix, "COMPRESSOR"),
Value: RatioKind, Value: RatioKind,
}, },
} }
} }
...@@ -70,9 +70,9 @@ func (c *CLIConfig) Config() Config { ...@@ -70,9 +70,9 @@ func (c *CLIConfig) Config() Config {
func ReadCLIConfig(ctx *cli.Context) CLIConfig { func ReadCLIConfig(ctx *cli.Context) CLIConfig {
return CLIConfig{ return CLIConfig{
Kind: ctx.GlobalString(KindFlagName), Kind: ctx.String(KindFlagName),
TargetL1TxSizeBytes: ctx.GlobalUint64(TargetL1TxSizeBytesFlagName), TargetL1TxSizeBytes: ctx.Uint64(TargetL1TxSizeBytesFlagName),
TargetNumFrames: ctx.GlobalInt(TargetNumFramesFlagName), TargetNumFrames: ctx.Int(TargetNumFramesFlagName),
ApproxComprRatio: ctx.GlobalFloat64(ApproxComprRatioFlagName), ApproxComprRatio: ctx.Float64(ApproxComprRatioFlagName),
} }
} }
...@@ -4,7 +4,7 @@ import ( ...@@ -4,7 +4,7 @@ import (
"fmt" "fmt"
"time" "time"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
"github.com/ethereum-optimism/optimism/op-batcher/compressor" "github.com/ethereum-optimism/optimism/op-batcher/compressor"
"github.com/ethereum-optimism/optimism/op-batcher/rpc" "github.com/ethereum-optimism/optimism/op-batcher/rpc"
...@@ -18,60 +18,64 @@ import ( ...@@ -18,60 +18,64 @@ import (
const EnvVarPrefix = "OP_BATCHER" const EnvVarPrefix = "OP_BATCHER"
func prefixEnvVars(name string) []string {
return opservice.PrefixEnvVar(EnvVarPrefix, name)
}
var ( var (
// Required flags // Required flags
L1EthRpcFlag = cli.StringFlag{ L1EthRpcFlag = &cli.StringFlag{
Name: "l1-eth-rpc", Name: "l1-eth-rpc",
Usage: "HTTP provider URL for L1", Usage: "HTTP provider URL for L1",
EnvVar: opservice.PrefixEnvVar(EnvVarPrefix, "L1_ETH_RPC"), EnvVars: prefixEnvVars("L1_ETH_RPC"),
} }
L2EthRpcFlag = cli.StringFlag{ L2EthRpcFlag = &cli.StringFlag{
Name: "l2-eth-rpc", Name: "l2-eth-rpc",
Usage: "HTTP provider URL for L2 execution engine", Usage: "HTTP provider URL for L2 execution engine",
EnvVar: opservice.PrefixEnvVar(EnvVarPrefix, "L2_ETH_RPC"), EnvVars: prefixEnvVars("L2_ETH_RPC"),
} }
RollupRpcFlag = cli.StringFlag{ RollupRpcFlag = &cli.StringFlag{
Name: "rollup-rpc", Name: "rollup-rpc",
Usage: "HTTP provider URL for Rollup node", Usage: "HTTP provider URL for Rollup node",
EnvVar: opservice.PrefixEnvVar(EnvVarPrefix, "ROLLUP_RPC"), EnvVars: prefixEnvVars("ROLLUP_RPC"),
} }
// Optional flags // Optional flags
SubSafetyMarginFlag = cli.Uint64Flag{ SubSafetyMarginFlag = &cli.Uint64Flag{
Name: "sub-safety-margin", Name: "sub-safety-margin",
Usage: "The batcher tx submission safety margin (in #L1-blocks) to subtract " + Usage: "The batcher tx submission safety margin (in #L1-blocks) to subtract " +
"from a channel's timeout and sequencing window, to guarantee safe inclusion " + "from a channel's timeout and sequencing window, to guarantee safe inclusion " +
"of a channel on L1.", "of a channel on L1.",
Value: 10, Value: 10,
EnvVar: opservice.PrefixEnvVar(EnvVarPrefix, "SUB_SAFETY_MARGIN"), EnvVars: prefixEnvVars("SUB_SAFETY_MARGIN"),
} }
PollIntervalFlag = cli.DurationFlag{ PollIntervalFlag = &cli.DurationFlag{
Name: "poll-interval", Name: "poll-interval",
Usage: "How frequently to poll L2 for new blocks", Usage: "How frequently to poll L2 for new blocks",
Value: 6 * time.Second, Value: 6 * time.Second,
EnvVar: opservice.PrefixEnvVar(EnvVarPrefix, "POLL_INTERVAL"), EnvVars: prefixEnvVars("POLL_INTERVAL"),
} }
MaxPendingTransactionsFlag = cli.Uint64Flag{ MaxPendingTransactionsFlag = &cli.Uint64Flag{
Name: "max-pending-tx", Name: "max-pending-tx",
Usage: "The maximum number of pending transactions. 0 for no limit.", Usage: "The maximum number of pending transactions. 0 for no limit.",
Value: 1, Value: 1,
EnvVar: opservice.PrefixEnvVar(EnvVarPrefix, "MAX_PENDING_TX"), EnvVars: prefixEnvVars("MAX_PENDING_TX"),
} }
MaxChannelDurationFlag = cli.Uint64Flag{ MaxChannelDurationFlag = &cli.Uint64Flag{
Name: "max-channel-duration", Name: "max-channel-duration",
Usage: "The maximum duration of L1-blocks to keep a channel open. 0 to disable.", Usage: "The maximum duration of L1-blocks to keep a channel open. 0 to disable.",
Value: 0, Value: 0,
EnvVar: opservice.PrefixEnvVar(EnvVarPrefix, "MAX_CHANNEL_DURATION"), EnvVars: prefixEnvVars("MAX_CHANNEL_DURATION"),
} }
MaxL1TxSizeBytesFlag = cli.Uint64Flag{ MaxL1TxSizeBytesFlag = &cli.Uint64Flag{
Name: "max-l1-tx-size-bytes", Name: "max-l1-tx-size-bytes",
Usage: "The maximum size of a batch tx submitted to L1.", Usage: "The maximum size of a batch tx submitted to L1.",
Value: 120_000, Value: 120_000,
EnvVar: opservice.PrefixEnvVar(EnvVarPrefix, "MAX_L1_TX_SIZE_BYTES"), EnvVars: prefixEnvVars("MAX_L1_TX_SIZE_BYTES"),
} }
StoppedFlag = cli.BoolFlag{ StoppedFlag = &cli.BoolFlag{
Name: "stopped", Name: "stopped",
Usage: "Initialize the batcher in a stopped state. The batcher can be started using the admin_startBatcher RPC", Usage: "Initialize the batcher in a stopped state. The batcher can be started using the admin_startBatcher RPC",
EnvVar: opservice.PrefixEnvVar(EnvVarPrefix, "STOPPED"), EnvVars: prefixEnvVars("STOPPED"),
} }
// Legacy Flags // Legacy Flags
SequencerHDPathFlag = txmgr.SequencerHDPathFlag SequencerHDPathFlag = txmgr.SequencerHDPathFlag
...@@ -110,8 +114,8 @@ var Flags []cli.Flag ...@@ -110,8 +114,8 @@ var Flags []cli.Flag
func CheckRequired(ctx *cli.Context) error { func CheckRequired(ctx *cli.Context) error {
for _, f := range requiredFlags { for _, f := range requiredFlags {
if !ctx.GlobalIsSet(f.GetName()) { if !ctx.IsSet(f.Names()[0]) {
return fmt.Errorf("flag %s is required", f.GetName()) return fmt.Errorf("flag %s is required", f.Names()[0])
} }
} }
return nil return nil
......
package rpc package rpc
import ( import (
"github.com/urfave/cli" "github.com/urfave/cli/v2"
opservice "github.com/ethereum-optimism/optimism/op-service" opservice "github.com/ethereum-optimism/optimism/op-service"
oprpc "github.com/ethereum-optimism/optimism/op-service/rpc" oprpc "github.com/ethereum-optimism/optimism/op-service/rpc"
...@@ -13,10 +13,10 @@ const ( ...@@ -13,10 +13,10 @@ const (
func CLIFlags(envPrefix string) []cli.Flag { func CLIFlags(envPrefix string) []cli.Flag {
return []cli.Flag{ return []cli.Flag{
cli.BoolFlag{ &cli.BoolFlag{
Name: EnableAdminFlagName, Name: EnableAdminFlagName,
Usage: "Enable the admin API (experimental)", Usage: "Enable the admin API (experimental)",
EnvVar: opservice.PrefixEnvVar(envPrefix, "RPC_ENABLE_ADMIN"), EnvVars: opservice.PrefixEnvVar(envPrefix, "RPC_ENABLE_ADMIN"),
}, },
} }
} }
...@@ -29,6 +29,6 @@ type CLIConfig struct { ...@@ -29,6 +29,6 @@ type CLIConfig struct {
func ReadCLIConfig(ctx *cli.Context) CLIConfig { func ReadCLIConfig(ctx *cli.Context) CLIConfig {
return CLIConfig{ return CLIConfig{
CLIConfig: oprpc.ReadCLIConfig(ctx), CLIConfig: oprpc.ReadCLIConfig(ctx),
EnableAdmin: ctx.GlobalBool(EnableAdminFlagName), EnableAdmin: ctx.Bool(EnableAdminFlagName),
} }
} }
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -16,7 +16,7 @@ import ( ...@@ -16,7 +16,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/libp2p/go-libp2p/core/peer" "github.com/libp2p/go-libp2p/core/peer"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
) )
type gossipNoop struct{} type gossipNoop struct{}
......
...@@ -6,7 +6,7 @@ import ( ...@@ -6,7 +6,7 @@ import (
"github.com/ethereum-optimism/optimism/op-bootnode/bootnode" "github.com/ethereum-optimism/optimism/op-bootnode/bootnode"
"github.com/ethereum-optimism/optimism/op-bootnode/flags" "github.com/ethereum-optimism/optimism/op-bootnode/flags"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
) )
func main() { func main() {
......
...@@ -8,21 +8,25 @@ import ( ...@@ -8,21 +8,25 @@ import (
"github.com/ethereum-optimism/optimism/op-node/flags" "github.com/ethereum-optimism/optimism/op-node/flags"
opservice "github.com/ethereum-optimism/optimism/op-service" opservice "github.com/ethereum-optimism/optimism/op-service"
oplog "github.com/ethereum-optimism/optimism/op-service/log" oplog "github.com/ethereum-optimism/optimism/op-service/log"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
) )
const envVarPrefix = "OP_BOOTNODE" const envVarPrefix = "OP_BOOTNODE"
func prefixEnvVars(name string) []string {
return opservice.PrefixEnvVar(envVarPrefix, name)
}
var ( var (
RollupConfig = cli.StringFlag{ RollupConfig = &cli.StringFlag{
Name: flags.RollupConfig.Name, Name: flags.RollupConfig.Name,
Usage: "Rollup chain parameters", Usage: "Rollup chain parameters",
EnvVar: opservice.PrefixEnvVar(envVarPrefix, "ROLLUP_CONFIG"), EnvVars: prefixEnvVars("ROLLUP_CONFIG"),
} }
Network = cli.StringFlag{ Network = &cli.StringFlag{
Name: flags.Network.Name, Name: flags.Network.Name,
Usage: fmt.Sprintf("Predefined network selection. Available networks: %s", strings.Join(chaincfg.AvailableNetworks(), ", ")), Usage: fmt.Sprintf("Predefined network selection. Available networks: %s", strings.Join(chaincfg.AvailableNetworks(), ", ")),
EnvVar: opservice.PrefixEnvVar(envVarPrefix, "NETWORK"), EnvVars: prefixEnvVars("NETWORK"),
} }
) )
......
...@@ -4,7 +4,7 @@ import ( ...@@ -4,7 +4,7 @@ import (
"os" "os"
log "github.com/ethereum/go-ethereum/log" log "github.com/ethereum/go-ethereum/log"
cli "github.com/urfave/cli" cli "github.com/urfave/cli/v2"
watch "github.com/ethereum-optimism/optimism/op-challenger/cmd/watch" watch "github.com/ethereum-optimism/optimism/op-challenger/cmd/watch"
config "github.com/ethereum-optimism/optimism/op-challenger/config" config "github.com/ethereum-optimism/optimism/op-challenger/config"
...@@ -70,7 +70,7 @@ func run(args []string, action ConfigAction) error { ...@@ -70,7 +70,7 @@ func run(args []string, action ConfigAction) error {
} }
return action(logger, VersionWithMeta, cfg) return action(logger, VersionWithMeta, cfg)
} }
app.Commands = []cli.Command{ app.Commands = []*cli.Command{
{ {
Name: "watch", Name: "watch",
Subcommands: watch.Subcommands, Subcommands: watch.Subcommands,
......
package watch package watch
import ( import (
"github.com/urfave/cli" "github.com/urfave/cli/v2"
"github.com/ethereum-optimism/optimism/op-challenger/config" "github.com/ethereum-optimism/optimism/op-challenger/config"
) )
......
...@@ -5,7 +5,7 @@ import ( ...@@ -5,7 +5,7 @@ import (
"time" "time"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
flags "github.com/ethereum-optimism/optimism/op-challenger/flags" flags "github.com/ethereum-optimism/optimism/op-challenger/flags"
...@@ -141,19 +141,19 @@ func NewConfigFromCLI(ctx *cli.Context) (*Config, error) { ...@@ -141,19 +141,19 @@ func NewConfigFromCLI(ctx *cli.Context) (*Config, error) {
if err := flags.CheckRequired(ctx); err != nil { if err := flags.CheckRequired(ctx); err != nil {
return nil, err return nil, err
} }
l1EthRpc := ctx.GlobalString(flags.L1EthRpcFlag.Name) l1EthRpc := ctx.String(flags.L1EthRpcFlag.Name)
if l1EthRpc == "" { if l1EthRpc == "" {
return nil, ErrMissingL1EthRPC return nil, ErrMissingL1EthRPC
} }
rollupRpc := ctx.GlobalString(flags.RollupRpcFlag.Name) rollupRpc := ctx.String(flags.RollupRpcFlag.Name)
if rollupRpc == "" { if rollupRpc == "" {
return nil, ErrMissingRollupRpc return nil, ErrMissingRollupRpc
} }
l2ooAddress, err := opservice.ParseAddress(ctx.GlobalString(flags.L2OOAddressFlag.Name)) l2ooAddress, err := opservice.ParseAddress(ctx.String(flags.L2OOAddressFlag.Name))
if err != nil { if err != nil {
return nil, ErrMissingL2OOAddress return nil, ErrMissingL2OOAddress
} }
dgfAddress, err := opservice.ParseAddress(ctx.GlobalString(flags.DGFAddressFlag.Name)) dgfAddress, err := opservice.ParseAddress(ctx.String(flags.DGFAddressFlag.Name))
if err != nil { if err != nil {
return nil, ErrMissingDGFAddress return nil, ErrMissingDGFAddress
} }
......
...@@ -4,7 +4,7 @@ import ( ...@@ -4,7 +4,7 @@ import (
"fmt" "fmt"
log "github.com/ethereum/go-ethereum/log" log "github.com/ethereum/go-ethereum/log"
cli "github.com/urfave/cli" cli "github.com/urfave/cli/v2"
oplog "github.com/ethereum-optimism/optimism/op-service/log" oplog "github.com/ethereum-optimism/optimism/op-service/log"
) )
......
...@@ -8,6 +8,38 @@ import ( ...@@ -8,6 +8,38 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
// TestAlphabetProvider_Get_ClaimsByTraceIndex tests the [fault.AlphabetProvider] Get function.
func TestAlphabetProvider_Get_ClaimsByTraceIndex(t *testing.T) {
// Create a new alphabet provider.
canonicalProvider := NewAlphabetProvider("abcdefgh", uint64(3))
// Build a list of traces.
traces := []struct {
traceIndex uint64
expectedHash common.Hash
}{
{
7,
common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000768"),
},
{
3,
common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000364"),
},
{
5,
common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000566"),
},
}
// Execute each trace and check the alphabet provider returns the expected hash.
for _, trace := range traces {
expectedHash, err := canonicalProvider.Get(trace.traceIndex)
require.NoError(t, err)
require.Equal(t, trace.expectedHash, expectedHash)
}
}
// FuzzIndexToBytes tests the IndexToBytes function. // FuzzIndexToBytes tests the IndexToBytes function.
func FuzzIndexToBytes(f *testing.F) { func FuzzIndexToBytes(f *testing.F) {
f.Fuzz(func(t *testing.T, index uint64) { f.Fuzz(func(t *testing.T, index uint64) {
......
package examples
import (
"github.com/ethereum-optimism/optimism/op-challenger/fault"
)
func PositionExampleOne() {
// Example 1
// abcdefgh
// abcdexyz
// go left to d, then right to f, then left to e
p := fault.Position{}
p.Print(3)
p = p.Attack()
p.Print(3)
p = p.Defend()
p.Print(3)
p = p.Attack()
p.Print(3)
}
func PositionExampleTwo() {
// Example 2
// abcdefgh
// abqrstuv
// go left r, then left to b, then right to q
p := fault.Position{}
p.Print(3)
p = p.Attack()
p.Print(3)
p = p.Attack()
p.Print(3)
p = p.Defend()
p.Print(3)
}
package examples
import (
"fmt"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum-optimism/optimism/op-challenger/fault"
)
// SolverExampleOne uses the [fault.Solver] with a [fault.AlphabetProvider]
// to print out fault game traces for the "abcdexyz" counter-state.
func SolverExampleOne() {
fmt.Println()
fmt.Println("Solver: Example 1")
fmt.Println()
// Construct the fault position.
canonical := "abcdefgh"
disputed := "abcdexyz"
maxDepth := 3
parent := fault.Claim{
Value: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000768"),
Position: fault.NewPosition(0, 0),
}
canonicalProvider := fault.NewAlphabetProvider(canonical, uint64(maxDepth))
disputedProvider := fault.NewAlphabetProvider(disputed, uint64(maxDepth))
// Create a solver with the canonical provider.
solver := fault.NewSolver(maxDepth, canonicalProvider)
// Print the initial state.
fmt.Println("Canonical state: ", canonical)
fmt.Println("Disputed state: ", disputed)
fmt.Println()
fmt.Println("Proceeding with the following moves:")
fmt.Println("go left to d, then right to f, then left to e")
fmt.Println()
// Get the claim from the disputed provider.
claim, err := disputedProvider.Get(3)
if err != nil {
fmt.Printf("error getting claim from disputed provider: %v", err)
}
firstDisputedClaim := fault.Claim{
Value: claim,
Position: fault.NewPosition(1, 0),
}
res, err := solver.NextMove(firstDisputedClaim, parent)
if err != nil {
fmt.Printf("error getting next move: %v", err)
}
fmt.Printf("Disputed claim: %s\n", claim)
fmt.Printf("Expected claim: %s\n", parent.Value)
fmt.Printf("Response: [Attack: %v, Value: %s]\n", res.Attack, res.Value)
fmt.Println()
// Get the next claim from the disputed provider.
claim, err = disputedProvider.Get(5)
if err != nil {
fmt.Printf("error getting claim from disputed provider: %v", err)
}
firstDisputedClaim = fault.Claim{
Value: claim,
Position: fault.NewPosition(2, 2),
}
res, err = solver.NextMove(firstDisputedClaim, parent)
if err != nil {
fmt.Printf("error getting next move: %v", err)
}
fmt.Printf("Disputed claim: %s\n", claim)
fmt.Printf("Expected claim: %s\n", parent.Value)
fmt.Printf("Response: [Attack: %v, Value: %s]\n", res.Attack, res.Value)
fmt.Println()
// This marks the end of the game!
if res.Attack {
fmt.Println("Game successfully completed!")
} else {
fmt.Println("Game failed!")
}
fmt.Println()
}
package main package main
import ( import (
"github.com/ethereum-optimism/optimism/op-challenger/fault" "github.com/ethereum-optimism/optimism/op-challenger/fault/cmd/examples"
) )
func main() { func main() {
// Example 1 examples.SolverExampleOne()
// abcdefgh
// abcdexyz
// go left to d, then right to f, then left to e
p := fault.Position{}
p.Print(3)
p.Attack()
p.Print(3)
p.Defend()
p.Print(3)
p.Attack()
p.Print(3)
// Trace Position is 0000 Trace Depth is: 0 Trace Index is: 8 // examples.PositionExampleOne()
// Trace Position is 0000 Trace Depth is: 1 Trace Index is: 4 // examples.PositionExampleTwo()
// Trace Position is 0010 Trace Depth is: 2 Trace Index is: 6
// Trace Position is 0100 Trace Depth is: 3 Trace Index is: 5
// Example 2
// abcdefgh
// abqrstuv
// go left r, then left to b, then right to q
p = fault.Position{}
p.Print(3)
p.Attack()
p.Print(3)
p.Attack()
p.Print(3)
p.Defend()
p.Print(3)
// Trace Position is 0000 Trace Depth is: 0 Trace Index is: 8
// Trace Position is 0000 Trace Depth is: 1 Trace Index is: 4
// Trace Position is 0000 Trace Depth is: 2 Trace Index is: 2
// Trace Position is 0010 Trace Depth is: 3 Trace Index is: 3
} }
package fault
// Game is an interface that represents the state of a dispute game.
type Game interface {
// Put adds a claim into the game state and returns its parent claim.
Put(claim Claim) (Claim, error)
// ClaimPairs returns a list of claim pairs.
ClaimPairs() []struct {
claim Claim
parent Claim
}
}
...@@ -3,36 +3,42 @@ package fault ...@@ -3,36 +3,42 @@ package fault
import "fmt" import "fmt"
// Position is a golang wrapper around the dispute game Position type. // Position is a golang wrapper around the dispute game Position type.
// Depth refers to how many bisection steps have occurred.
// IndexAtDepth refers to the path that the bisection has taken
// where 1 = goes right & 0 = goes left.
type Position struct { type Position struct {
Depth int depth int
IndexAtDepth int indexAtDepth int
} }
// TraceIndex calculates the what the index of the claim value func NewPosition(depth, indexAtDepth int) Position {
// would be inside the trace. return Position{depth, indexAtDepth}
func (p *Position) TraceIndex(maxDepth int) int { }
lo := 0
hi := 1 << maxDepth func NewPositionFromGIndex(x uint64) Position {
mid := hi depth := MSBIndex(x)
path := p.IndexAtDepth indexAtDepth := ^(1 << depth) & x
for i := p.Depth - 1; i >= 0; i-- { return NewPosition(depth, int(indexAtDepth))
mid = (lo + hi) / 2 }
mask := 1 << i
if path&mask == mask { func (p *Position) Depth() int {
lo = mid return p.depth
} else { }
hi = mid
} func (p *Position) IndexAtDepth() int {
} return p.indexAtDepth
return mid
} }
// TraceIndex calculates the what the index of the claim value would be inside the trace.
// It is equivalent to going right until the final depth has been reached.
func (p *Position) TraceIndex(maxDepth int) uint64 {
// When we go right, we do a shift left and set the bottom bit to be 1.
// To do this in a single step, do all the shifts at once & or in all 1s for the bottom bits.
rd := maxDepth - p.depth
return uint64(p.indexAtDepth<<rd | ((1 << rd) - 1))
}
// move goes to the left or right child.
func (p *Position) move(right bool) { func (p *Position) move(right bool) {
p.Depth++ p.depth++
p.IndexAtDepth = (p.IndexAtDepth << 1) | boolToInt(right) p.indexAtDepth = (p.indexAtDepth << 1) | boolToInt(right)
} }
func boolToInt(b bool) int { func boolToInt(b bool) int {
...@@ -43,21 +49,44 @@ func boolToInt(b bool) int { ...@@ -43,21 +49,44 @@ func boolToInt(b bool) int {
} }
} }
// parent moves up to the parent.
func (p *Position) parent() { func (p *Position) parent() {
p.Depth-- p.depth--
p.IndexAtDepth = p.IndexAtDepth >> 1 p.indexAtDepth = p.indexAtDepth >> 1
} }
func (p *Position) Attack() { // Attack creates a new position which is the attack position of this one.
p.move(false) func (p *Position) Attack() Position {
p2 := NewPosition(p.depth, p.indexAtDepth)
p2.move(false)
return p2
} }
func (p *Position) Defend() { // Defend creates a new position which is the defend position of this one.
p.parent() func (p *Position) Defend() Position {
p.move(true) p2 := NewPosition(p.depth, p.indexAtDepth)
p.move(false) p2.parent()
p2.move(true)
p2.move(false)
return p2
} }
func (p *Position) Print(maxDepth int) { func (p *Position) Print(maxDepth int) {
fmt.Printf("Trace Position is %04b\tTrace Depth is: %d\tTrace Index is: %d\n", p.IndexAtDepth, p.Depth, p.TraceIndex(maxDepth)) fmt.Printf("GIN: %4b\tTrace Position is %4b\tTrace Depth is: %d\tTrace Index is: %d\n", p.ToGIndex(), p.indexAtDepth, p.depth, p.TraceIndex(maxDepth))
}
func (p *Position) ToGIndex() uint64 {
return uint64(1<<p.depth | p.indexAtDepth)
}
// MSBIndex returns the index of the most significant bit
func MSBIndex(x uint64) int {
if x == 0 {
return 0
}
out := 0
for ; x != 0; out++ {
x = x >> 1
}
return out - 1
} }
package fault package fault
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestMSBIndex(t *testing.T) {
tests := []struct {
input uint64
expected int
}{
{0, 0},
{1, 0},
{2, 1},
{4, 2},
{8, 3},
{16, 4},
{255, 7},
{1024, 10},
{18446744073709551615, 63},
}
for _, test := range tests {
result := MSBIndex(test.input)
if result != test.expected {
t.Errorf("MSBIndex(%d) expected %d, but got %d", test.input, test.expected, result)
}
}
}
type testNodeInfo struct {
GIndex uint64
Depth int
IndexAtDepth int
TraceIndex uint64
}
var treeNodesMaxDepth4 = []testNodeInfo{
{GIndex: 1, Depth: 0, IndexAtDepth: 0, TraceIndex: 15},
{GIndex: 2, Depth: 1, IndexAtDepth: 0, TraceIndex: 7},
{GIndex: 3, Depth: 1, IndexAtDepth: 1, TraceIndex: 15},
{GIndex: 4, Depth: 2, IndexAtDepth: 0, TraceIndex: 3},
{GIndex: 5, Depth: 2, IndexAtDepth: 1, TraceIndex: 7},
{GIndex: 6, Depth: 2, IndexAtDepth: 2, TraceIndex: 11},
{GIndex: 7, Depth: 2, IndexAtDepth: 3, TraceIndex: 15},
{GIndex: 8, Depth: 3, IndexAtDepth: 0, TraceIndex: 1},
{GIndex: 9, Depth: 3, IndexAtDepth: 1, TraceIndex: 3},
{GIndex: 10, Depth: 3, IndexAtDepth: 2, TraceIndex: 5},
{GIndex: 11, Depth: 3, IndexAtDepth: 3, TraceIndex: 7},
{GIndex: 12, Depth: 3, IndexAtDepth: 4, TraceIndex: 9},
{GIndex: 13, Depth: 3, IndexAtDepth: 5, TraceIndex: 11},
{GIndex: 14, Depth: 3, IndexAtDepth: 6, TraceIndex: 13},
{GIndex: 15, Depth: 3, IndexAtDepth: 7, TraceIndex: 15},
{GIndex: 16, Depth: 4, IndexAtDepth: 0, TraceIndex: 0},
{GIndex: 17, Depth: 4, IndexAtDepth: 1, TraceIndex: 1},
{GIndex: 18, Depth: 4, IndexAtDepth: 2, TraceIndex: 2},
{GIndex: 19, Depth: 4, IndexAtDepth: 3, TraceIndex: 3},
{GIndex: 20, Depth: 4, IndexAtDepth: 4, TraceIndex: 4},
{GIndex: 21, Depth: 4, IndexAtDepth: 5, TraceIndex: 5},
{GIndex: 22, Depth: 4, IndexAtDepth: 6, TraceIndex: 6},
{GIndex: 23, Depth: 4, IndexAtDepth: 7, TraceIndex: 7},
{GIndex: 24, Depth: 4, IndexAtDepth: 8, TraceIndex: 8},
{GIndex: 25, Depth: 4, IndexAtDepth: 9, TraceIndex: 9},
{GIndex: 26, Depth: 4, IndexAtDepth: 10, TraceIndex: 10},
{GIndex: 27, Depth: 4, IndexAtDepth: 11, TraceIndex: 11},
{GIndex: 28, Depth: 4, IndexAtDepth: 12, TraceIndex: 12},
{GIndex: 29, Depth: 4, IndexAtDepth: 13, TraceIndex: 13},
{GIndex: 30, Depth: 4, IndexAtDepth: 14, TraceIndex: 14},
{GIndex: 31, Depth: 4, IndexAtDepth: 15, TraceIndex: 15},
}
// TestGINConversions does To & From the generalized index on the treeNodesMaxDepth4 data
func TestGINConversions(t *testing.T) {
for _, test := range treeNodesMaxDepth4 {
from := NewPositionFromGIndex(test.GIndex)
pos := NewPosition(test.Depth, test.IndexAtDepth)
require.Equal(t, pos, from)
to := pos.ToGIndex()
require.Equal(t, test.GIndex, to)
}
}
// TestTraceIndex creates the position & then tests the trace index function on the treeNodesMaxDepth4 data
func TestTraceIndex(t *testing.T) {
for _, test := range treeNodesMaxDepth4 {
pos := NewPosition(test.Depth, test.IndexAtDepth)
result := pos.TraceIndex(4)
require.Equal(t, test.TraceIndex, result)
}
}
package fault
import (
"errors"
"github.com/ethereum/go-ethereum/common"
)
// Solver uses a [TraceProvider] to determine the moves to make in a dispute game.
type Solver struct {
TraceProvider
gameDepth int
}
// NewSolver creates a new [Solver] using the provided [TraceProvider].
func NewSolver(gameDepth int, traceProvider TraceProvider) *Solver {
return &Solver{
traceProvider,
gameDepth,
}
}
// NextMove returns the next move to make given the current state of the game.
func (s *Solver) NextMove(claim Claim, parent Claim) (*Response, error) {
parentCorrect, err := s.agreeWithClaim(parent)
if err != nil {
return nil, err
}
claimCorrect, err := s.agreeWithClaim(claim)
if err != nil {
return nil, err
}
if parentCorrect && claimCorrect {
// We agree with the parent, but the claim is disagreeing with it.
// Since we agree with the claim, the difference must be to the right of the claim
return s.defend(claim)
} else if parentCorrect && !claimCorrect {
// We agree with the parent, but the claim disagrees with it.
// Since we disagree with the claim, the difference must be to the left of the claim
return s.attack(claim)
} else if !parentCorrect && claimCorrect {
// Do nothing, we disagree with the parent, but this claim has correctly countered it
return s.doNothing()
} else if !parentCorrect && !claimCorrect {
// We disagree with the parent so want to counter it (which the claim is doing)
// but we also disagree with the claim so there must be a difference to the left of claim
// Note that we will create the correct counter-claim for parent when it is evaluated, no need to do it here
return s.attack(claim)
}
// This should not be reached
return nil, errors.New("no next move")
}
func (s *Solver) doNothing() (*Response, error) {
return nil, nil
}
// attack returns a response that attacks the claim.
func (s *Solver) attack(claim Claim) (*Response, error) {
value, err := s.traceAtPosition(claim.Attack())
if err != nil {
return nil, err
}
return &Response{Attack: true, Value: value}, nil
}
// defend returns a response that defends the claim.
func (s *Solver) defend(claim Claim) (*Response, error) {
value, err := s.traceAtPosition(claim.Defend())
if err != nil {
return nil, err
}
return &Response{Attack: false, Value: value}, nil
}
// agreeWithClaim returns true if the [Claim] is correct according to the internal [TraceProvider].
func (s *Solver) agreeWithClaim(claim Claim) (bool, error) {
ourValue, err := s.traceAtPosition(claim.Position)
return ourValue == claim.Value, err
}
// traceAtPosition returns the [common.Hash] from internal [TraceProvider] at the given [Position].
func (s *Solver) traceAtPosition(p Position) (common.Hash, error) {
index := p.TraceIndex(s.gameDepth)
hash, err := s.Get(index)
return hash, err
}
package fault
import (
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/require"
)
// TestSolver_NextMove_Opponent tests the [Solver] NextMove function
// with an [fault.AlphabetProvider] as the [TraceProvider].
func TestSolver_NextMove_Opponent(t *testing.T) {
// Construct the solver.
maxDepth := 3
canonicalProvider := NewAlphabetProvider("abcdefgh", uint64(maxDepth))
solver := NewSolver(maxDepth, canonicalProvider)
// The following claims are created using the state: "abcdexyz".
// The responses are the responses we expect from the solver.
indices := []struct {
traceIndex int
claim Claim
parent Claim
response *Response
}{
{
3,
Claim{
Value: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000364"),
Position: NewPosition(1, 0),
},
Claim{
Value: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000768"),
Position: NewPosition(0, 0),
},
&Response{
Attack: false,
Value: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000566"),
},
},
{
5,
Claim{
Value: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000578"),
Position: NewPosition(2, 2),
},
Claim{
Value: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000768"),
Position: NewPosition(1, 1),
},
&Response{
Attack: true,
Value: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000465"),
},
},
}
for _, test := range indices {
res, err := solver.NextMove(test.claim, test.parent)
require.NoError(t, err)
require.Equal(t, test.response, res)
}
}
package fault package fault
import ( import (
"context"
"errors" "errors"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
...@@ -26,4 +27,11 @@ type Claim struct { ...@@ -26,4 +27,11 @@ type Claim struct {
type Response struct { type Response struct {
Attack bool // note: can we flip this to true == going right / defending?? Attack bool // note: can we flip this to true == going right / defending??
Value common.Hash Value common.Hash
Parent Claim
}
// Responder takes a response action & executes.
// For full op-challenger this means executing the transaction on chain.
type Responder interface {
Respond(ctx context.Context, response Response) error
} }
...@@ -3,7 +3,7 @@ package flags ...@@ -3,7 +3,7 @@ package flags
import ( import (
"fmt" "fmt"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
opservice "github.com/ethereum-optimism/optimism/op-service" opservice "github.com/ethereum-optimism/optimism/op-service"
oplog "github.com/ethereum-optimism/optimism/op-service/log" oplog "github.com/ethereum-optimism/optimism/op-service/log"
...@@ -15,27 +15,31 @@ import ( ...@@ -15,27 +15,31 @@ import (
const envVarPrefix = "OP_CHALLENGER" const envVarPrefix = "OP_CHALLENGER"
func prefixEnvVars(name string) []string {
return opservice.PrefixEnvVar(envVarPrefix, name)
}
var ( var (
// Required Flags // Required Flags
L1EthRpcFlag = cli.StringFlag{ L1EthRpcFlag = &cli.StringFlag{
Name: "l1-eth-rpc", Name: "l1-eth-rpc",
Usage: "HTTP provider URL for L1.", Usage: "HTTP provider URL for L1.",
EnvVar: opservice.PrefixEnvVar(envVarPrefix, "L1_ETH_RPC"), EnvVars: prefixEnvVars("L1_ETH_RPC"),
} }
RollupRpcFlag = cli.StringFlag{ RollupRpcFlag = &cli.StringFlag{
Name: "rollup-rpc", Name: "rollup-rpc",
Usage: "HTTP provider URL for the rollup node.", Usage: "HTTP provider URL for the rollup node.",
EnvVar: opservice.PrefixEnvVar(envVarPrefix, "ROLLUP_RPC"), EnvVars: prefixEnvVars("ROLLUP_RPC"),
} }
L2OOAddressFlag = cli.StringFlag{ L2OOAddressFlag = &cli.StringFlag{
Name: "l2oo-address", Name: "l2oo-address",
Usage: "Address of the L2OutputOracle contract.", Usage: "Address of the L2OutputOracle contract.",
EnvVar: opservice.PrefixEnvVar(envVarPrefix, "L2OO_ADDRESS"), EnvVars: prefixEnvVars("L2OO_ADDRESS"),
} }
DGFAddressFlag = cli.StringFlag{ DGFAddressFlag = &cli.StringFlag{
Name: "dgf-address", Name: "dgf-address",
Usage: "Address of the DisputeGameFactory contract.", Usage: "Address of the DisputeGameFactory contract.",
EnvVar: opservice.PrefixEnvVar(envVarPrefix, "DGF_ADDRESS"), EnvVars: prefixEnvVars("DGF_ADDRESS"),
} }
) )
...@@ -65,8 +69,8 @@ var Flags []cli.Flag ...@@ -65,8 +69,8 @@ var Flags []cli.Flag
func CheckRequired(ctx *cli.Context) error { func CheckRequired(ctx *cli.Context) error {
for _, f := range requiredFlags { for _, f := range requiredFlags {
if !ctx.GlobalIsSet(f.GetName()) { if !ctx.IsSet(f.Names()[0]) {
return fmt.Errorf("flag %s is required", f.GetName()) return fmt.Errorf("flag %s is required", f.Names()[0])
} }
} }
return nil return nil
......
...@@ -5,14 +5,14 @@ import ( ...@@ -5,14 +5,14 @@ import (
"strings" "strings"
"testing" "testing"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
) )
// TestUniqueFlags asserts that all flag names are unique, to avoid accidental conflicts between the many flags. // TestUniqueFlags asserts that all flag names are unique, to avoid accidental conflicts between the many flags.
func TestUniqueFlags(t *testing.T) { func TestUniqueFlags(t *testing.T) {
seenCLI := make(map[string]struct{}) seenCLI := make(map[string]struct{})
for _, flag := range Flags { for _, flag := range Flags {
name := flag.GetName() name := flag.Names()[0]
if _, ok := seenCLI[name]; ok { if _, ok := seenCLI[name]; ok {
t.Errorf("duplicate flag %s", name) t.Errorf("duplicate flag %s", name)
continue continue
...@@ -38,22 +38,22 @@ func TestCorrectEnvVarPrefix(t *testing.T) { ...@@ -38,22 +38,22 @@ func TestCorrectEnvVarPrefix(t *testing.T) {
for _, flag := range Flags { for _, flag := range Flags {
envVar := envVarForFlag(flag) envVar := envVarForFlag(flag)
if envVar == "" { if envVar == "" {
t.Errorf("Failed to find EnvVar for flag %v", flag.GetName()) t.Errorf("Failed to find EnvVar for flag %v", flag.Names()[0])
} }
if !strings.HasPrefix(envVar, "OP_CHALLENGER_") { if !strings.HasPrefix(envVar, "OP_CHALLENGER_") {
t.Errorf("Flag %v env var (%v) does not start with OP_CHALLENGER_", flag.GetName(), envVar) t.Errorf("Flag %v env var (%v) does not start with OP_CHALLENGER_", flag.Names()[0], envVar)
} }
if strings.Contains(envVar, "__") { if strings.Contains(envVar, "__") {
t.Errorf("Flag %v env var (%v) has duplicate underscores", flag.GetName(), envVar) t.Errorf("Flag %v env var (%v) has duplicate underscores", flag.Names()[0], envVar)
} }
} }
} }
func envVarForFlag(flag cli.Flag) string { func envVarForFlag(flag cli.Flag) string {
values := reflect.ValueOf(flag) values := reflect.ValueOf(flag)
envVarValue := values.FieldByName("EnvVar") envVarValue := values.Elem().FieldByName("EnvVars")
if envVarValue == (reflect.Value{}) { if envVarValue == (reflect.Value{}) || envVarValue.Len() == 0 {
return "" return ""
} }
return envVarValue.String() return envVarValue.Index(0).String()
} }
FROM golang:1.18.0-alpine3.15 as builder FROM golang:1.19.9-alpine3.16 as builder
RUN apk add --no-cache make gcc musl-dev linux-headers git jq bash RUN apk add --no-cache make gcc musl-dev linux-headers git jq bash
# build op-heartbeat with local monorepo go modules # build op-heartbeat with local monorepo go modules
COPY ./op-heartbeat /app/op-heartbeat COPY ./op-heartbeat /app/op-heartbeat
COPY ./op-node /app/op-node
COPY ./op-service /app/op-service
COPY ./go.mod /app/go.mod
COPY ./go.sum /app/go.sum
COPY ./.git /app/.git COPY ./.git /app/.git
COPY ./op-heartbeat/go.mod /app/go.mod
COPY ./op-heartbeat/go.sum /app/go.sum
WORKDIR /app/op-heartbeat WORKDIR /app/op-heartbeat
RUN make op-heartbeat RUN make op-heartbeat
FROM alpine:3.15 FROM alpine:3.16
COPY --from=builder /app/op-heartbeat/bin/op-heartbeat /usr/local/bin COPY --from=builder /app/op-heartbeat/bin/op-heartbeat /usr/local/bin
......
...@@ -8,7 +8,7 @@ import ( ...@@ -8,7 +8,7 @@ import (
"github.com/ethereum-optimism/optimism/op-heartbeat/flags" "github.com/ethereum-optimism/optimism/op-heartbeat/flags"
oplog "github.com/ethereum-optimism/optimism/op-service/log" oplog "github.com/ethereum-optimism/optimism/op-service/log"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
) )
var ( var (
......
...@@ -7,7 +7,7 @@ import ( ...@@ -7,7 +7,7 @@ import (
oplog "github.com/ethereum-optimism/optimism/op-service/log" oplog "github.com/ethereum-optimism/optimism/op-service/log"
opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics" opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics"
oppprof "github.com/ethereum-optimism/optimism/op-service/pprof" oppprof "github.com/ethereum-optimism/optimism/op-service/pprof"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
) )
type Config struct { type Config struct {
...@@ -42,8 +42,8 @@ func (c Config) Check() error { ...@@ -42,8 +42,8 @@ func (c Config) Check() error {
func NewConfig(ctx *cli.Context) Config { func NewConfig(ctx *cli.Context) Config {
return Config{ return Config{
HTTPAddr: ctx.GlobalString(flags.HTTPAddrFlag.Name), HTTPAddr: ctx.String(flags.HTTPAddrFlag.Name),
HTTPPort: ctx.GlobalInt(flags.HTTPPortFlag.Name), HTTPPort: ctx.Int(flags.HTTPPortFlag.Name),
Log: oplog.ReadCLIConfig(ctx), Log: oplog.ReadCLIConfig(ctx),
Metrics: opmetrics.ReadCLIConfig(ctx), Metrics: opmetrics.ReadCLIConfig(ctx),
Pprof: oppprof.ReadCLIConfig(ctx), Pprof: oppprof.ReadCLIConfig(ctx),
......
...@@ -4,28 +4,32 @@ import ( ...@@ -4,28 +4,32 @@ import (
opservice "github.com/ethereum-optimism/optimism/op-service" opservice "github.com/ethereum-optimism/optimism/op-service"
oplog "github.com/ethereum-optimism/optimism/op-service/log" oplog "github.com/ethereum-optimism/optimism/op-service/log"
opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics" opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
) )
const envPrefix = "OP_HEARTBEAT" const envPrefix = "OP_HEARTBEAT"
func prefixEnvVars(name string) []string {
return opservice.PrefixEnvVar(envPrefix, name)
}
const ( const (
HTTPAddrFlagName = "http.addr" HTTPAddrFlagName = "http.addr"
HTTPPortFlagName = "http.port" HTTPPortFlagName = "http.port"
) )
var ( var (
HTTPAddrFlag = cli.StringFlag{ HTTPAddrFlag = &cli.StringFlag{
Name: HTTPAddrFlagName, Name: HTTPAddrFlagName,
Usage: "Address the server should listen on", Usage: "Address the server should listen on",
Value: "0.0.0.0", Value: "0.0.0.0",
EnvVar: opservice.PrefixEnvVar(envPrefix, "HTTP_ADDR"), EnvVars: prefixEnvVars("HTTP_ADDR"),
} }
HTTPPortFlag = cli.IntFlag{ HTTPPortFlag = &cli.IntFlag{
Name: HTTPPortFlagName, Name: HTTPPortFlagName,
Usage: "Port the server should listen on", Usage: "Port the server should listen on",
Value: 8080, Value: 8080,
EnvVar: opservice.PrefixEnvVar(envPrefix, "HTTP_PORT"), EnvVars: prefixEnvVars("HTTP_PORT"),
} }
) )
......
module github.com/ethereum-optimism/optimism/op-heartbeat
go 1.18
require (
github.com/ethereum-optimism/optimism/op-node v0.10.13
github.com/ethereum-optimism/optimism/op-service v0.10.13
github.com/ethereum/go-ethereum v1.10.26
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d
github.com/prometheus/client_golang v1.14.0
github.com/stretchr/testify v1.8.1
github.com/urfave/cli v1.22.10
)
require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/btcsuite/btcd/btcec/v2 v2.2.0 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/deckarep/golang-set v1.8.0 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-stack/stack v1.8.1 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_model v0.3.0 // indirect
github.com/prometheus/common v0.37.0 // indirect
github.com/prometheus/procfs v0.8.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/shirou/gopsutil v3.21.11+incompatible // indirect
github.com/tklauser/go-sysconf v0.3.10 // indirect
github.com/tklauser/numcpus v0.5.0 // indirect
github.com/yusufpapurcu/wmi v1.2.2 // indirect
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect
golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43 // indirect
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 // indirect
google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
This diff is collapsed.
...@@ -14,7 +14,7 @@ import ( ...@@ -14,7 +14,7 @@ import (
"syscall" "syscall"
"time" "time"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
......
...@@ -12,48 +12,48 @@ import ( ...@@ -12,48 +12,48 @@ import (
"github.com/ethereum-optimism/optimism/op-node/rollup/derive" "github.com/ethereum-optimism/optimism/op-node/rollup/derive"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/ethclient"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
) )
func main() { func main() {
app := cli.NewApp() app := cli.NewApp()
app.Name = "batch-decoder" app.Name = "batch-decoder"
app.Usage = "Optimism Batch Decoding Utility" app.Usage = "Optimism Batch Decoding Utility"
app.Commands = []cli.Command{ app.Commands = []*cli.Command{
{ {
Name: "fetch", Name: "fetch",
Usage: "Fetches batches in the specified range", Usage: "Fetches batches in the specified range",
Flags: []cli.Flag{ Flags: []cli.Flag{
cli.IntFlag{ &cli.IntFlag{
Name: "start", Name: "start",
Required: true, Required: true,
Usage: "First block (inclusive) to fetch", Usage: "First block (inclusive) to fetch",
}, },
cli.IntFlag{ &cli.IntFlag{
Name: "end", Name: "end",
Required: true, Required: true,
Usage: "Last block (exclusive) to fetch", Usage: "Last block (exclusive) to fetch",
}, },
cli.StringFlag{ &cli.StringFlag{
Name: "inbox", Name: "inbox",
Required: true, Required: true,
Usage: "Batch Inbox Address", Usage: "Batch Inbox Address",
}, },
cli.StringFlag{ &cli.StringFlag{
Name: "sender", Name: "sender",
Required: true, Required: true,
Usage: "Batch Sender Address", Usage: "Batch Sender Address",
}, },
cli.StringFlag{ &cli.StringFlag{
Name: "out", Name: "out",
Value: "/tmp/batch_decoder/transactions_cache", Value: "/tmp/batch_decoder/transactions_cache",
Usage: "Cache directory for the found transactions", Usage: "Cache directory for the found transactions",
}, },
cli.StringFlag{ &cli.StringFlag{
Name: "l1", Name: "l1",
Required: true, Required: true,
Usage: "L1 RPC URL", Usage: "L1 RPC URL",
EnvVar: "L1_RPC", EnvVars: []string{"L1_RPC"},
}, },
}, },
Action: func(cliCtx *cli.Context) error { Action: func(cliCtx *cli.Context) error {
...@@ -88,17 +88,17 @@ func main() { ...@@ -88,17 +88,17 @@ func main() {
Name: "reassemble", Name: "reassemble",
Usage: "Reassembles channels from fetched batches", Usage: "Reassembles channels from fetched batches",
Flags: []cli.Flag{ Flags: []cli.Flag{
cli.StringFlag{ &cli.StringFlag{
Name: "inbox", Name: "inbox",
Value: "0xff00000000000000000000000000000000000420", Value: "0xff00000000000000000000000000000000000420",
Usage: "Batch Inbox Address", Usage: "Batch Inbox Address",
}, },
cli.StringFlag{ &cli.StringFlag{
Name: "in", Name: "in",
Value: "/tmp/batch_decoder/transactions_cache", Value: "/tmp/batch_decoder/transactions_cache",
Usage: "Cache directory for the found transactions", Usage: "Cache directory for the found transactions",
}, },
cli.StringFlag{ &cli.StringFlag{
Name: "out", Name: "out",
Value: "/tmp/batch_decoder/channel_cache", Value: "/tmp/batch_decoder/channel_cache",
Usage: "Cache directory for the found channels", Usage: "Cache directory for the found channels",
...@@ -118,17 +118,17 @@ func main() { ...@@ -118,17 +118,17 @@ func main() {
Name: "force-close", Name: "force-close",
Usage: "Create the tx data which will force close a channel", Usage: "Create the tx data which will force close a channel",
Flags: []cli.Flag{ Flags: []cli.Flag{
cli.StringFlag{ &cli.StringFlag{
Name: "id", Name: "id",
Required: true, Required: true,
Usage: "ID of the channel to close", Usage: "ID of the channel to close",
}, },
cli.StringFlag{ &cli.StringFlag{
Name: "inbox", Name: "inbox",
Value: "0x0000000000000000000000000000000000000000", Value: "0x0000000000000000000000000000000000000000",
Usage: "(Optional) Batch Inbox Address", Usage: "(Optional) Batch Inbox Address",
}, },
cli.StringFlag{ &cli.StringFlag{
Name: "in", Name: "in",
Value: "/tmp/batch_decoder/transactions_cache", Value: "/tmp/batch_decoder/transactions_cache",
Usage: "Cache directory for the found transactions", Usage: "Cache directory for the found transactions",
......
...@@ -8,7 +8,7 @@ import ( ...@@ -8,7 +8,7 @@ import (
"github.com/ethereum-optimism/optimism/op-node/metrics" "github.com/ethereum-optimism/optimism/op-node/metrics"
"github.com/olekukonko/tablewriter" "github.com/olekukonko/tablewriter"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
) )
var Subcommands = cli.Commands{ var Subcommands = cli.Commands{
...@@ -16,7 +16,7 @@ var Subcommands = cli.Commands{ ...@@ -16,7 +16,7 @@ var Subcommands = cli.Commands{
Name: "metrics", Name: "metrics",
Usage: "Dumps a list of supported metrics to stdout", Usage: "Dumps a list of supported metrics to stdout",
Flags: []cli.Flag{ Flags: []cli.Flag{
cli.StringFlag{ &cli.StringFlag{
Name: "format", Name: "format",
Value: "markdown", Value: "markdown",
Usage: "Output format (json|markdown)", Usage: "Output format (json|markdown)",
......
...@@ -8,7 +8,7 @@ import ( ...@@ -8,7 +8,7 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/ethclient"
...@@ -22,19 +22,19 @@ var Subcommands = cli.Commands{ ...@@ -22,19 +22,19 @@ var Subcommands = cli.Commands{
Name: "devnet", Name: "devnet",
Usage: "Initialize new L1 and L2 genesis files and rollup config suitable for a local devnet", Usage: "Initialize new L1 and L2 genesis files and rollup config suitable for a local devnet",
Flags: []cli.Flag{ Flags: []cli.Flag{
cli.StringFlag{ &cli.StringFlag{
Name: "deploy-config", Name: "deploy-config",
Usage: "Path to hardhat deploy config file", Usage: "Path to hardhat deploy config file",
}, },
cli.StringFlag{ &cli.StringFlag{
Name: "outfile.l1", Name: "outfile.l1",
Usage: "Path to L1 genesis output file", Usage: "Path to L1 genesis output file",
}, },
cli.StringFlag{ &cli.StringFlag{
Name: "outfile.l2", Name: "outfile.l2",
Usage: "Path to L2 genesis output file", Usage: "Path to L2 genesis output file",
}, },
cli.StringFlag{ &cli.StringFlag{
Name: "outfile.rollup", Name: "outfile.rollup",
Usage: "Path to rollup output file", Usage: "Path to rollup output file",
}, },
...@@ -85,23 +85,23 @@ var Subcommands = cli.Commands{ ...@@ -85,23 +85,23 @@ var Subcommands = cli.Commands{
Name: "l2", Name: "l2",
Usage: "Generates an L2 genesis file and rollup config suitable for a deployed network", Usage: "Generates an L2 genesis file and rollup config suitable for a deployed network",
Flags: []cli.Flag{ Flags: []cli.Flag{
cli.StringFlag{ &cli.StringFlag{
Name: "l1-rpc", Name: "l1-rpc",
Usage: "L1 RPC URL", Usage: "L1 RPC URL",
}, },
cli.StringFlag{ &cli.StringFlag{
Name: "deploy-config", Name: "deploy-config",
Usage: "Path to hardhat deploy config file", Usage: "Path to hardhat deploy config file",
}, },
cli.StringFlag{ &cli.StringFlag{
Name: "deployment-dir", Name: "deployment-dir",
Usage: "Path to deployment directory", Usage: "Path to deployment directory",
}, },
cli.StringFlag{ &cli.StringFlag{
Name: "outfile.l2", Name: "outfile.l2",
Usage: "Path to L2 genesis output file", Usage: "Path to L2 genesis output file",
}, },
cli.StringFlag{ &cli.StringFlag{
Name: "outfile.rollup", Name: "outfile.rollup",
Usage: "Path to rollup output file", Usage: "Path to rollup output file",
}, },
......
...@@ -9,7 +9,7 @@ import ( ...@@ -9,7 +9,7 @@ import (
"github.com/ethereum-optimism/optimism/op-node/chaincfg" "github.com/ethereum-optimism/optimism/op-node/chaincfg"
"github.com/ethereum-optimism/optimism/op-node/cmd/doc" "github.com/ethereum-optimism/optimism/op-node/cmd/doc"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
...@@ -59,7 +59,7 @@ func main() { ...@@ -59,7 +59,7 @@ func main() {
app.Usage = "Optimism Rollup Node" app.Usage = "Optimism Rollup Node"
app.Description = "The Optimism Rollup Node derives L2 block inputs from L1 data and drives an external L2 Execution Engine to build a L2 chain." app.Description = "The Optimism Rollup Node derives L2 block inputs from L1 data and drives an external L2 Execution Engine to build a L2 chain."
app.Action = RollupNodeMain app.Action = RollupNodeMain
app.Commands = []cli.Command{ app.Commands = []*cli.Command{
{ {
Name: "p2p", Name: "p2p",
Subcommands: p2p.Subcommands, Subcommands: p2p.Subcommands,
......
...@@ -10,7 +10,7 @@ import ( ...@@ -10,7 +10,7 @@ import (
"github.com/libp2p/go-libp2p/core/crypto" "github.com/libp2p/go-libp2p/core/crypto"
"github.com/libp2p/go-libp2p/core/peer" "github.com/libp2p/go-libp2p/core/peer"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
) )
func Priv2PeerID(r io.Reader) (string, error) { func Priv2PeerID(r io.Reader) (string, error) {
......
This diff is collapsed.
...@@ -4,7 +4,7 @@ import ( ...@@ -4,7 +4,7 @@ import (
"testing" "testing"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
) )
// TestOptionalFlagsDontSetRequired asserts that all flags deemed optional set // TestOptionalFlagsDontSetRequired asserts that all flags deemed optional set
...@@ -21,7 +21,7 @@ func TestOptionalFlagsDontSetRequired(t *testing.T) { ...@@ -21,7 +21,7 @@ func TestOptionalFlagsDontSetRequired(t *testing.T) {
func TestUniqueFlags(t *testing.T) { func TestUniqueFlags(t *testing.T) {
seenCLI := make(map[string]struct{}) seenCLI := make(map[string]struct{})
for _, flag := range Flags { for _, flag := range Flags {
name := flag.GetName() name := flag.Names()[0]
if _, ok := seenCLI[name]; ok { if _, ok := seenCLI[name]; ok {
t.Errorf("duplicate flag %s", name) t.Errorf("duplicate flag %s", name)
continue continue
......
This diff is collapsed.
package p2p
import (
"time"
"github.com/ethereum-optimism/optimism/op-node/rollup"
)
type ApplicationScoreParams struct {
ValidResponseCap float64
ValidResponseWeight float64
ValidResponseDecay float64
ErrorResponseCap float64
ErrorResponseWeight float64
ErrorResponseDecay float64
RejectedPayloadCap float64
RejectedPayloadWeight float64
RejectedPayloadDecay float64
DecayToZero float64
DecayInterval time.Duration
}
func LightApplicationScoreParams(cfg *rollup.Config) ApplicationScoreParams {
slot := time.Duration(cfg.BlockTime) * time.Second
if slot == 0 {
slot = 2 * time.Second
}
// We initialize an "epoch" as 6 blocks suggesting 6 blocks,
// each taking ~ 2 seconds, is 12 seconds
epoch := 6 * slot
tenEpochs := 10 * epoch
return ApplicationScoreParams{
// Max positive score from valid responses: 5
ValidResponseCap: 10,
ValidResponseWeight: 0.5,
ValidResponseDecay: ScoreDecay(tenEpochs, slot),
// Takes 20 error responses to reach the default ban threshold of -100
// But at most we track 10. These errors include not supporting p2p sync
// so we don't (yet) want to ban a peer based on this measure alone.
ErrorResponseCap: 10,
ErrorResponseWeight: -5,
ErrorResponseDecay: ScoreDecay(tenEpochs, slot),
// Takes 5 rejected payloads to reach the default ban threshold of -100
RejectedPayloadCap: 20,
RejectedPayloadWeight: -20,
RejectedPayloadDecay: ScoreDecay(tenEpochs, slot),
DecayToZero: DecayToZero,
DecayInterval: slot,
}
}
package p2p
import (
"context"
"sync"
"github.com/ethereum-optimism/optimism/op-node/p2p/store"
"github.com/ethereum-optimism/optimism/op-service/clock"
"github.com/ethereum/go-ethereum/log"
"github.com/libp2p/go-libp2p/core/peer"
)
type ScoreBook interface {
GetPeerScores(id peer.ID) (store.PeerScores, error)
SetScore(id peer.ID, diff store.ScoreDiff) (store.PeerScores, error)
}
type ApplicationScorer interface {
ApplicationScore(id peer.ID) float64
onValidResponse(id peer.ID)
onResponseError(id peer.ID)
onRejectedPayload(id peer.ID)
start()
stop()
}
type peerApplicationScorer struct {
ctx context.Context
cancelFunc context.CancelFunc
log log.Logger
clock clock.Clock
params *ApplicationScoreParams
scorebook ScoreBook
connectedPeers func() []peer.ID
done sync.WaitGroup
}
var _ ApplicationScorer = (*peerApplicationScorer)(nil)
func newPeerApplicationScorer(ctx context.Context, logger log.Logger, clock clock.Clock, params *ApplicationScoreParams, scorebook ScoreBook, connectedPeers func() []peer.ID) *peerApplicationScorer {
ctx, cancelFunc := context.WithCancel(ctx)
return &peerApplicationScorer{
ctx: ctx,
cancelFunc: cancelFunc,
log: logger,
clock: clock,
params: params,
scorebook: scorebook,
connectedPeers: connectedPeers,
}
}
func (s *peerApplicationScorer) ApplicationScore(id peer.ID) float64 {
scores, err := s.scorebook.GetPeerScores(id)
if err != nil {
s.log.Error("Failed to load peer scores", "peer", id, "err", err)
return 0
}
score := scores.ReqResp.ValidResponses * s.params.ValidResponseWeight
score += scores.ReqResp.ErrorResponses * s.params.ErrorResponseWeight
score += scores.ReqResp.RejectedPayloads * s.params.RejectedPayloadWeight
return score
}
func (s *peerApplicationScorer) onValidResponse(id peer.ID) {
_, err := s.scorebook.SetScore(id, store.IncrementValidResponses{Cap: s.params.ValidResponseCap})
if err != nil {
s.log.Error("Unable to update peer score", "peer", id, "err", err)
return
}
}
func (s *peerApplicationScorer) onResponseError(id peer.ID) {
_, err := s.scorebook.SetScore(id, store.IncrementErrorResponses{Cap: s.params.ErrorResponseCap})
if err != nil {
s.log.Error("Unable to update peer score", "peer", id, "err", err)
return
}
}
func (s *peerApplicationScorer) onRejectedPayload(id peer.ID) {
_, err := s.scorebook.SetScore(id, store.IncrementRejectedPayloads{Cap: s.params.RejectedPayloadCap})
if err != nil {
s.log.Error("Unable to update peer score", "peer", id, "err", err)
return
}
}
func (s *peerApplicationScorer) decayScores(id peer.ID) {
_, err := s.scorebook.SetScore(id, &store.DecayApplicationScores{
ValidResponseDecay: s.params.ValidResponseDecay,
ErrorResponseDecay: s.params.ErrorResponseDecay,
RejectedPayloadDecay: s.params.RejectedPayloadDecay,
DecayToZero: s.params.DecayToZero,
})
if err != nil {
s.log.Error("Unable to decay peer score", "peer", id, "err", err)
return
}
}
func (s *peerApplicationScorer) decayConnectedPeerScores() {
for _, id := range s.connectedPeers() {
s.decayScores(id)
}
}
func (s *peerApplicationScorer) start() {
s.done.Add(1)
go func() {
defer s.done.Done()
ticker := s.clock.NewTicker(s.params.DecayInterval)
for {
select {
case <-s.ctx.Done():
return
case <-ticker.Ch():
s.decayConnectedPeerScores()
}
}
}()
}
func (s *peerApplicationScorer) stop() {
s.cancelFunc()
s.done.Wait()
}
type NoopApplicationScorer struct{}
func (n *NoopApplicationScorer) ApplicationScore(_ peer.ID) float64 {
return 0
}
func (n *NoopApplicationScorer) onValidResponse(_ peer.ID) {
}
func (n *NoopApplicationScorer) onResponseError(_ peer.ID) {
}
func (n *NoopApplicationScorer) onRejectedPayload(_ peer.ID) {
}
func (n *NoopApplicationScorer) start() {
}
func (n *NoopApplicationScorer) stop() {
}
var _ ApplicationScorer = (*NoopApplicationScorer)(nil)
package p2p
import (
"context"
"errors"
"testing"
"time"
"github.com/ethereum-optimism/optimism/op-node/p2p/store"
"github.com/ethereum-optimism/optimism/op-node/testlog"
"github.com/ethereum-optimism/optimism/op-service/clock"
"github.com/ethereum/go-ethereum/log"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/stretchr/testify/require"
)
type stubScoreBookUpdate struct {
id peer.ID
diff store.ScoreDiff
}
type stubScoreBook struct {
err error
scores map[peer.ID]store.PeerScores
updates chan stubScoreBookUpdate
}
func (s *stubScoreBook) GetPeerScores(id peer.ID) (store.PeerScores, error) {
if s.err != nil {
return store.PeerScores{}, s.err
}
scores, ok := s.scores[id]
if !ok {
return store.PeerScores{}, nil
}
return scores, nil
}
func (s *stubScoreBook) SetScore(id peer.ID, diff store.ScoreDiff) (store.PeerScores, error) {
s.updates <- stubScoreBookUpdate{id, diff}
return s.GetPeerScores(id)
}
type appScoreTestData struct {
ctx context.Context
logger log.Logger
clock *clock.DeterministicClock
peers []peer.ID
scorebook *stubScoreBook
}
func (a *appScoreTestData) WaitForNextScoreBookUpdate(t *testing.T) stubScoreBookUpdate {
ctx, cancelFunc := context.WithTimeout(a.ctx, 30*time.Second)
defer cancelFunc()
select {
case update := <-a.scorebook.updates:
return update
case <-ctx.Done():
t.Fatal("Did not receive expected scorebook update")
return stubScoreBookUpdate{}
}
}
func setupPeerApplicationScorerTest(t *testing.T, params *ApplicationScoreParams) (*appScoreTestData, *peerApplicationScorer) {
data := &appScoreTestData{
ctx: context.Background(),
logger: testlog.Logger(t, log.LvlInfo),
clock: clock.NewDeterministicClock(time.UnixMilli(1000)),
peers: []peer.ID{},
scorebook: &stubScoreBook{
scores: make(map[peer.ID]store.PeerScores),
updates: make(chan stubScoreBookUpdate, 10),
},
}
appScorer := newPeerApplicationScorer(data.ctx, data.logger, data.clock, params, data.scorebook, func() []peer.ID {
return data.peers
})
return data, appScorer
}
func TestIncrementValidResponses(t *testing.T) {
data, appScorer := setupPeerApplicationScorerTest(t, &ApplicationScoreParams{
ValidResponseCap: 10,
})
appScorer.onValidResponse("aaa")
require.Len(t, data.scorebook.updates, 1)
update := <-data.scorebook.updates
require.Equal(t, stubScoreBookUpdate{peer.ID("aaa"), store.IncrementValidResponses{Cap: 10}}, update)
}
func TestIncrementErrorResponses(t *testing.T) {
data, appScorer := setupPeerApplicationScorerTest(t, &ApplicationScoreParams{
ErrorResponseCap: 10,
})
appScorer.onResponseError("aaa")
require.Len(t, data.scorebook.updates, 1)
update := <-data.scorebook.updates
require.Equal(t, stubScoreBookUpdate{peer.ID("aaa"), store.IncrementErrorResponses{Cap: 10}}, update)
}
func TestIncrementRejectedPayloads(t *testing.T) {
data, appScorer := setupPeerApplicationScorerTest(t, &ApplicationScoreParams{
RejectedPayloadCap: 10,
})
appScorer.onRejectedPayload("aaa")
require.Len(t, data.scorebook.updates, 1)
update := <-data.scorebook.updates
require.Equal(t, stubScoreBookUpdate{peer.ID("aaa"), store.IncrementRejectedPayloads{Cap: 10}}, update)
}
func TestApplicationScore(t *testing.T) {
data, appScorer := setupPeerApplicationScorerTest(t, &ApplicationScoreParams{
ValidResponseWeight: 0.8,
ErrorResponseWeight: 0.6,
RejectedPayloadWeight: 0.4,
})
peerScore := store.PeerScores{
ReqResp: store.ReqRespScores{
ValidResponses: 1,
ErrorResponses: 2,
RejectedPayloads: 3,
},
}
data.scorebook.scores["aaa"] = peerScore
score := appScorer.ApplicationScore("aaa")
require.Equal(t, 1*0.8+2*0.6+3*0.4, score)
}
func TestApplicationScoreZeroWhenScoreDoesNotLoad(t *testing.T) {
data, appScorer := setupPeerApplicationScorerTest(t, &ApplicationScoreParams{})
data.scorebook.err = errors.New("boom")
score := appScorer.ApplicationScore("aaa")
require.Zero(t, score)
}
func TestDecayScoresAfterDecayInterval(t *testing.T) {
params := &ApplicationScoreParams{
ValidResponseDecay: 0.8,
ErrorResponseDecay: 0.7,
RejectedPayloadDecay: 0.3,
DecayToZero: 0.1,
DecayInterval: 90 * time.Second,
}
data, appScorer := setupPeerApplicationScorerTest(t, params)
data.peers = []peer.ID{"aaa", "bbb"}
expectedDecay := &store.DecayApplicationScores{
ValidResponseDecay: 0.8,
ErrorResponseDecay: 0.7,
RejectedPayloadDecay: 0.3,
DecayToZero: 0.1,
}
appScorer.start()
defer appScorer.stop()
data.clock.WaitForNewPendingTaskWithTimeout(30 * time.Second)
data.clock.AdvanceTime(params.DecayInterval)
require.Equal(t, stubScoreBookUpdate{id: "aaa", diff: expectedDecay}, data.WaitForNextScoreBookUpdate(t))
require.Equal(t, stubScoreBookUpdate{id: "bbb", diff: expectedDecay}, data.WaitForNextScoreBookUpdate(t))
}
...@@ -20,7 +20,7 @@ import ( ...@@ -20,7 +20,7 @@ import (
"github.com/ethereum-optimism/optimism/op-node/flags" "github.com/ethereum-optimism/optimism/op-node/flags"
"github.com/ethereum-optimism/optimism/op-node/p2p" "github.com/ethereum-optimism/optimism/op-node/p2p"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
"github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/p2p/enode"
) )
...@@ -28,7 +28,7 @@ import ( ...@@ -28,7 +28,7 @@ import (
func NewConfig(ctx *cli.Context, rollupCfg *rollup.Config) (*p2p.Config, error) { func NewConfig(ctx *cli.Context, rollupCfg *rollup.Config) (*p2p.Config, error) {
conf := &p2p.Config{} conf := &p2p.Config{}
if ctx.GlobalBool(flags.DisableP2P.Name) { if ctx.Bool(flags.DisableP2P.Name) {
conf.DisableP2P = true conf.DisableP2P = true
return conf, nil return conf, nil
} }
...@@ -63,7 +63,7 @@ func NewConfig(ctx *cli.Context, rollupCfg *rollup.Config) (*p2p.Config, error) ...@@ -63,7 +63,7 @@ func NewConfig(ctx *cli.Context, rollupCfg *rollup.Config) (*p2p.Config, error)
return nil, fmt.Errorf("failed to load banning option: %w", err) return nil, fmt.Errorf("failed to load banning option: %w", err)
} }
conf.EnableReqRespSync = ctx.GlobalBool(flags.SyncReqRespFlag.Name) conf.EnableReqRespSync = ctx.Bool(flags.SyncReqRespFlag.Name)
return conf, nil return conf, nil
} }
...@@ -83,13 +83,13 @@ func validatePort(p uint) (uint16, error) { ...@@ -83,13 +83,13 @@ func validatePort(p uint) (uint16, error) {
// loadScoringParams loads the peer scoring options from the CLI context. // loadScoringParams loads the peer scoring options from the CLI context.
func loadScoringParams(conf *p2p.Config, ctx *cli.Context, rollupCfg *rollup.Config) error { func loadScoringParams(conf *p2p.Config, ctx *cli.Context, rollupCfg *rollup.Config) error {
scoringLevel := ctx.GlobalString(flags.Scoring.Name) scoringLevel := ctx.String(flags.Scoring.Name)
// Check old names for backwards compatibility // Check old names for backwards compatibility
if scoringLevel == "" { if scoringLevel == "" {
scoringLevel = ctx.GlobalString(flags.PeerScoring.Name) scoringLevel = ctx.String(flags.PeerScoring.Name)
} }
if scoringLevel == "" { if scoringLevel == "" {
scoringLevel = ctx.GlobalString(flags.TopicScoring.Name) scoringLevel = ctx.String(flags.TopicScoring.Name)
} }
if scoringLevel != "" { if scoringLevel != "" {
params, err := p2p.GetScoringParams(scoringLevel, rollupCfg) params, err := p2p.GetScoringParams(scoringLevel, rollupCfg)
...@@ -104,14 +104,14 @@ func loadScoringParams(conf *p2p.Config, ctx *cli.Context, rollupCfg *rollup.Con ...@@ -104,14 +104,14 @@ func loadScoringParams(conf *p2p.Config, ctx *cli.Context, rollupCfg *rollup.Con
// loadBanningOptions loads whether or not to ban peers from the CLI context. // loadBanningOptions loads whether or not to ban peers from the CLI context.
func loadBanningOptions(conf *p2p.Config, ctx *cli.Context) error { func loadBanningOptions(conf *p2p.Config, ctx *cli.Context) error {
conf.BanningEnabled = ctx.GlobalBool(flags.Banning.Name) conf.BanningEnabled = ctx.Bool(flags.Banning.Name)
conf.BanningThreshold = ctx.GlobalFloat64(flags.BanningThreshold.Name) conf.BanningThreshold = ctx.Float64(flags.BanningThreshold.Name)
conf.BanningDuration = ctx.GlobalDuration(flags.BanningDuration.Name) conf.BanningDuration = ctx.Duration(flags.BanningDuration.Name)
return nil return nil
} }
func loadListenOpts(conf *p2p.Config, ctx *cli.Context) error { func loadListenOpts(conf *p2p.Config, ctx *cli.Context) error {
listenIP := ctx.GlobalString(flags.ListenIP.Name) listenIP := ctx.String(flags.ListenIP.Name)
if listenIP != "" { // optional if listenIP != "" { // optional
conf.ListenIP = net.ParseIP(listenIP) conf.ListenIP = net.ParseIP(listenIP)
if conf.ListenIP == nil { if conf.ListenIP == nil {
...@@ -119,11 +119,11 @@ func loadListenOpts(conf *p2p.Config, ctx *cli.Context) error { ...@@ -119,11 +119,11 @@ func loadListenOpts(conf *p2p.Config, ctx *cli.Context) error {
} }
} }
var err error var err error
conf.ListenTCPPort, err = validatePort(ctx.GlobalUint(flags.ListenTCPPort.Name)) conf.ListenTCPPort, err = validatePort(ctx.Uint(flags.ListenTCPPort.Name))
if err != nil { if err != nil {
return fmt.Errorf("bad listen TCP port: %w", err) return fmt.Errorf("bad listen TCP port: %w", err)
} }
conf.ListenUDPPort, err = validatePort(ctx.GlobalUint(flags.ListenUDPPort.Name)) conf.ListenUDPPort, err = validatePort(ctx.Uint(flags.ListenUDPPort.Name))
if err != nil { if err != nil {
return fmt.Errorf("bad listen UDP port: %w", err) return fmt.Errorf("bad listen UDP port: %w", err)
} }
...@@ -131,20 +131,20 @@ func loadListenOpts(conf *p2p.Config, ctx *cli.Context) error { ...@@ -131,20 +131,20 @@ func loadListenOpts(conf *p2p.Config, ctx *cli.Context) error {
} }
func loadDiscoveryOpts(conf *p2p.Config, ctx *cli.Context) error { func loadDiscoveryOpts(conf *p2p.Config, ctx *cli.Context) error {
if ctx.GlobalBool(flags.NoDiscovery.Name) { if ctx.Bool(flags.NoDiscovery.Name) {
conf.NoDiscovery = true conf.NoDiscovery = true
} }
var err error var err error
conf.AdvertiseTCPPort, err = validatePort(ctx.GlobalUint(flags.AdvertiseTCPPort.Name)) conf.AdvertiseTCPPort, err = validatePort(ctx.Uint(flags.AdvertiseTCPPort.Name))
if err != nil { if err != nil {
return fmt.Errorf("bad advertised TCP port: %w", err) return fmt.Errorf("bad advertised TCP port: %w", err)
} }
conf.AdvertiseUDPPort, err = validatePort(ctx.GlobalUint(flags.AdvertiseUDPPort.Name)) conf.AdvertiseUDPPort, err = validatePort(ctx.Uint(flags.AdvertiseUDPPort.Name))
if err != nil { if err != nil {
return fmt.Errorf("bad advertised UDP port: %w", err) return fmt.Errorf("bad advertised UDP port: %w", err)
} }
adIP := ctx.GlobalString(flags.AdvertiseIP.Name) adIP := ctx.String(flags.AdvertiseIP.Name)
if adIP != "" { // optional if adIP != "" { // optional
ips, err := net.LookupIP(adIP) ips, err := net.LookupIP(adIP)
if err != nil { if err != nil {
...@@ -162,7 +162,7 @@ func loadDiscoveryOpts(conf *p2p.Config, ctx *cli.Context) error { ...@@ -162,7 +162,7 @@ func loadDiscoveryOpts(conf *p2p.Config, ctx *cli.Context) error {
} }
} }
dbPath := ctx.GlobalString(flags.DiscoveryPath.Name) dbPath := ctx.String(flags.DiscoveryPath.Name)
if dbPath == "" { if dbPath == "" {
dbPath = "opnode_discovery_db" dbPath = "opnode_discovery_db"
} }
...@@ -175,7 +175,7 @@ func loadDiscoveryOpts(conf *p2p.Config, ctx *cli.Context) error { ...@@ -175,7 +175,7 @@ func loadDiscoveryOpts(conf *p2p.Config, ctx *cli.Context) error {
} }
bootnodes := make([]*enode.Node, 0) bootnodes := make([]*enode.Node, 0)
records := strings.Split(ctx.GlobalString(flags.Bootnodes.Name), ",") records := strings.Split(ctx.String(flags.Bootnodes.Name), ",")
for i, recordB64 := range records { for i, recordB64 := range records {
recordB64 = strings.TrimSpace(recordB64) recordB64 = strings.TrimSpace(recordB64)
if recordB64 == "" { // ignore empty records if recordB64 == "" { // ignore empty records
...@@ -197,7 +197,7 @@ func loadDiscoveryOpts(conf *p2p.Config, ctx *cli.Context) error { ...@@ -197,7 +197,7 @@ func loadDiscoveryOpts(conf *p2p.Config, ctx *cli.Context) error {
} }
func loadLibp2pOpts(conf *p2p.Config, ctx *cli.Context) error { func loadLibp2pOpts(conf *p2p.Config, ctx *cli.Context) error {
addrs := strings.Split(ctx.GlobalString(flags.StaticPeers.Name), ",") addrs := strings.Split(ctx.String(flags.StaticPeers.Name), ",")
for i, addr := range addrs { for i, addr := range addrs {
addr = strings.TrimSpace(addr) addr = strings.TrimSpace(addr)
if addr == "" { if addr == "" {
...@@ -210,7 +210,7 @@ func loadLibp2pOpts(conf *p2p.Config, ctx *cli.Context) error { ...@@ -210,7 +210,7 @@ func loadLibp2pOpts(conf *p2p.Config, ctx *cli.Context) error {
conf.StaticPeers = append(conf.StaticPeers, a) conf.StaticPeers = append(conf.StaticPeers, a)
} }
for _, v := range strings.Split(ctx.GlobalString(flags.HostMux.Name), ",") { for _, v := range strings.Split(ctx.String(flags.HostMux.Name), ",") {
v = strings.ToLower(strings.TrimSpace(v)) v = strings.ToLower(strings.TrimSpace(v))
switch v { switch v {
case "yamux": case "yamux":
...@@ -222,7 +222,7 @@ func loadLibp2pOpts(conf *p2p.Config, ctx *cli.Context) error { ...@@ -222,7 +222,7 @@ func loadLibp2pOpts(conf *p2p.Config, ctx *cli.Context) error {
} }
} }
secArr := strings.Split(ctx.GlobalString(flags.HostSecurity.Name), ",") secArr := strings.Split(ctx.String(flags.HostSecurity.Name), ",")
for _, v := range secArr { for _, v := range secArr {
v = strings.ToLower(strings.TrimSpace(v)) v = strings.ToLower(strings.TrimSpace(v))
switch v { switch v {
...@@ -240,16 +240,16 @@ func loadLibp2pOpts(conf *p2p.Config, ctx *cli.Context) error { ...@@ -240,16 +240,16 @@ func loadLibp2pOpts(conf *p2p.Config, ctx *cli.Context) error {
} }
} }
conf.PeersLo = ctx.GlobalUint(flags.PeersLo.Name) conf.PeersLo = ctx.Uint(flags.PeersLo.Name)
conf.PeersHi = ctx.GlobalUint(flags.PeersHi.Name) conf.PeersHi = ctx.Uint(flags.PeersHi.Name)
conf.PeersGrace = ctx.GlobalDuration(flags.PeersGrace.Name) conf.PeersGrace = ctx.Duration(flags.PeersGrace.Name)
conf.NAT = ctx.GlobalBool(flags.NAT.Name) conf.NAT = ctx.Bool(flags.NAT.Name)
conf.UserAgent = ctx.GlobalString(flags.UserAgent.Name) conf.UserAgent = ctx.String(flags.UserAgent.Name)
conf.TimeoutNegotiation = ctx.GlobalDuration(flags.TimeoutNegotiation.Name) conf.TimeoutNegotiation = ctx.Duration(flags.TimeoutNegotiation.Name)
conf.TimeoutAccept = ctx.GlobalDuration(flags.TimeoutAccept.Name) conf.TimeoutAccept = ctx.Duration(flags.TimeoutAccept.Name)
conf.TimeoutDial = ctx.GlobalDuration(flags.TimeoutDial.Name) conf.TimeoutDial = ctx.Duration(flags.TimeoutDial.Name)
peerstorePath := ctx.GlobalString(flags.PeerstorePath.Name) peerstorePath := ctx.String(flags.PeerstorePath.Name)
if peerstorePath == "" { if peerstorePath == "" {
return errors.New("peerstore path must be specified, use 'memory' to explicitly not persist peer records") return errors.New("peerstore path must be specified, use 'memory' to explicitly not persist peer records")
} }
...@@ -270,11 +270,11 @@ func loadLibp2pOpts(conf *p2p.Config, ctx *cli.Context) error { ...@@ -270,11 +270,11 @@ func loadLibp2pOpts(conf *p2p.Config, ctx *cli.Context) error {
} }
func loadNetworkPrivKey(ctx *cli.Context) (*crypto.Secp256k1PrivateKey, error) { func loadNetworkPrivKey(ctx *cli.Context) (*crypto.Secp256k1PrivateKey, error) {
raw := ctx.GlobalString(flags.P2PPrivRaw.Name) raw := ctx.String(flags.P2PPrivRaw.Name)
if raw != "" { if raw != "" {
return parsePriv(raw) return parsePriv(raw)
} }
keyPath := ctx.GlobalString(flags.P2PPrivPath.Name) keyPath := ctx.String(flags.P2PPrivPath.Name)
if keyPath == "" { if keyPath == "" {
return nil, errors.New("no p2p private key path specified, cannot auto-generate key without path") return nil, errors.New("no p2p private key path specified, cannot auto-generate key without path")
} }
...@@ -324,10 +324,10 @@ func parsePriv(data string) (*crypto.Secp256k1PrivateKey, error) { ...@@ -324,10 +324,10 @@ func parsePriv(data string) (*crypto.Secp256k1PrivateKey, error) {
} }
func loadGossipOptions(conf *p2p.Config, ctx *cli.Context) error { func loadGossipOptions(conf *p2p.Config, ctx *cli.Context) error {
conf.MeshD = ctx.GlobalInt(flags.GossipMeshDFlag.Name) conf.MeshD = ctx.Int(flags.GossipMeshDFlag.Name)
conf.MeshDLo = ctx.GlobalInt(flags.GossipMeshDloFlag.Name) conf.MeshDLo = ctx.Int(flags.GossipMeshDloFlag.Name)
conf.MeshDHi = ctx.GlobalInt(flags.GossipMeshDhiFlag.Name) conf.MeshDHi = ctx.Int(flags.GossipMeshDhiFlag.Name)
conf.MeshDLazy = ctx.GlobalInt(flags.GossipMeshDlazyFlag.Name) conf.MeshDLazy = ctx.Int(flags.GossipMeshDlazyFlag.Name)
conf.FloodPublish = ctx.GlobalBool(flags.GossipFloodPublishFlag.Name) conf.FloodPublish = ctx.Bool(flags.GossipFloodPublishFlag.Name)
return nil return nil
} }
...@@ -4,7 +4,7 @@ import ( ...@@ -4,7 +4,7 @@ import (
"fmt" "fmt"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
"github.com/ethereum-optimism/optimism/op-node/flags" "github.com/ethereum-optimism/optimism/op-node/flags"
"github.com/ethereum-optimism/optimism/op-node/p2p" "github.com/ethereum-optimism/optimism/op-node/p2p"
...@@ -15,7 +15,7 @@ import ( ...@@ -15,7 +15,7 @@ import (
// LoadSignerSetup loads a configuration for a Signer to be set up later // LoadSignerSetup loads a configuration for a Signer to be set up later
func LoadSignerSetup(ctx *cli.Context) (p2p.SignerSetup, error) { func LoadSignerSetup(ctx *cli.Context) (p2p.SignerSetup, error) {
key := ctx.GlobalString(flags.SequencerP2PKeyFlag.Name) key := ctx.String(flags.SequencerP2PKeyFlag.Name)
if key != "" { if key != "" {
// Mnemonics are bad because they leak *all* keys when they leak. // Mnemonics are bad because they leak *all* keys when they leak.
// Unencrypted keys from file are bad because they are easy to leak (and we are not checking file permissions). // Unencrypted keys from file are bad because they are easy to leak (and we are not checking file permissions).
......
...@@ -53,7 +53,8 @@ type SetupP2P interface { ...@@ -53,7 +53,8 @@ type SetupP2P interface {
// ScoringParams defines the various types of peer scoring parameters. // ScoringParams defines the various types of peer scoring parameters.
type ScoringParams struct { type ScoringParams struct {
PeerScoring pubsub.PeerScoreParams PeerScoring pubsub.PeerScoreParams
ApplicationScoring ApplicationScoreParams
} }
// Config sets up a p2p host and discv5 service from configuration. // Config sets up a p2p host and discv5 service from configuration.
...@@ -137,11 +138,11 @@ func (conf *Config) Disabled() bool { ...@@ -137,11 +138,11 @@ func (conf *Config) Disabled() bool {
return conf.DisableP2P return conf.DisableP2P
} }
func (conf *Config) PeerScoringParams() *pubsub.PeerScoreParams { func (conf *Config) PeerScoringParams() *ScoringParams {
if conf.ScoringParams == nil { if conf.ScoringParams == nil {
return nil return nil
} }
return &conf.ScoringParams.PeerScoring return conf.ScoringParams
} }
func (conf *Config) BanPeers() bool { func (conf *Config) BanPeers() bool {
......
...@@ -51,7 +51,7 @@ var MessageDomainInvalidSnappy = [4]byte{0, 0, 0, 0} ...@@ -51,7 +51,7 @@ var MessageDomainInvalidSnappy = [4]byte{0, 0, 0, 0}
var MessageDomainValidSnappy = [4]byte{1, 0, 0, 0} var MessageDomainValidSnappy = [4]byte{1, 0, 0, 0}
type GossipSetupConfigurables interface { type GossipSetupConfigurables interface {
PeerScoringParams() *pubsub.PeerScoreParams PeerScoringParams() *ScoringParams
// ConfigureGossip creates configuration options to apply to the GossipSub setup // ConfigureGossip creates configuration options to apply to the GossipSub setup
ConfigureGossip(rollupCfg *rollup.Config) []pubsub.Option ConfigureGossip(rollupCfg *rollup.Config) []pubsub.Option
} }
......
...@@ -149,7 +149,7 @@ func (conf *Config) Host(log log.Logger, reporter metrics.Reporter, metrics Host ...@@ -149,7 +149,7 @@ func (conf *Config) Host(log log.Logger, reporter metrics.Reporter, metrics Host
var scoreRetention time.Duration var scoreRetention time.Duration
if peerScoreParams != nil { if peerScoreParams != nil {
// Use the same retention period as gossip will if available // Use the same retention period as gossip will if available
scoreRetention = peerScoreParams.RetainScore scoreRetention = peerScoreParams.PeerScoring.RetainScore
} else { } else {
// Disable score GC if peer scoring is disabled // Disable score GC if peer scoring is disabled
scoreRetention = 0 scoreRetention = 0
......
...@@ -37,6 +37,7 @@ type NodeP2P struct { ...@@ -37,6 +37,7 @@ type NodeP2P struct {
connMgr connmgr.ConnManager // p2p conn manager, to keep a reliable number of peers, may be nil even with p2p enabled connMgr connmgr.ConnManager // p2p conn manager, to keep a reliable number of peers, may be nil even with p2p enabled
peerMonitor *monitor.PeerMonitor // peer monitor to disconnect bad peers, may be nil even with p2p enabled peerMonitor *monitor.PeerMonitor // peer monitor to disconnect bad peers, may be nil even with p2p enabled
store store.ExtendedPeerstore // peerstore of host, with extra bindings for scoring and banning store store.ExtendedPeerstore // peerstore of host, with extra bindings for scoring and banning
appScorer ApplicationScorer
log log.Logger log log.Logger
// the below components are all optional, and may be nil. They require the host to not be nil. // the below components are all optional, and may be nil. They require the host to not be nil.
dv5Local *enode.LocalNode // p2p discovery identity dv5Local *enode.LocalNode // p2p discovery identity
...@@ -89,9 +90,21 @@ func (n *NodeP2P) init(resourcesCtx context.Context, rollupCfg *rollup.Config, l ...@@ -89,9 +90,21 @@ func (n *NodeP2P) init(resourcesCtx context.Context, rollupCfg *rollup.Config, l
n.gater = extra.ConnectionGater() n.gater = extra.ConnectionGater()
n.connMgr = extra.ConnectionManager() n.connMgr = extra.ConnectionManager()
} }
eps, ok := n.host.Peerstore().(store.ExtendedPeerstore)
if !ok {
return fmt.Errorf("cannot init without extended peerstore: %w", err)
}
n.store = eps
scoreParams := setup.PeerScoringParams()
if scoreParams != nil {
n.appScorer = newPeerApplicationScorer(resourcesCtx, log, clock.SystemClock, &scoreParams.ApplicationScoring, eps, n.host.Network().Peers)
} else {
n.appScorer = &NoopApplicationScorer{}
}
// Activate the P2P req-resp sync if enabled by feature-flag. // Activate the P2P req-resp sync if enabled by feature-flag.
if setup.ReqRespSyncEnabled() { if setup.ReqRespSyncEnabled() {
n.syncCl = NewSyncClient(log, rollupCfg, n.host.NewStream, gossipIn.OnUnsafeL2Payload, metrics) n.syncCl = NewSyncClient(log, rollupCfg, n.host.NewStream, gossipIn.OnUnsafeL2Payload, metrics, n.appScorer)
n.host.Network().Notify(&network.NotifyBundle{ n.host.Network().Notify(&network.NotifyBundle{
ConnectedF: func(nw network.Network, conn network.Conn) { ConnectedF: func(nw network.Network, conn network.Conn) {
n.syncCl.AddPeer(conn.RemotePeer()) n.syncCl.AddPeer(conn.RemotePeer())
...@@ -112,12 +125,7 @@ func (n *NodeP2P) init(resourcesCtx context.Context, rollupCfg *rollup.Config, l ...@@ -112,12 +125,7 @@ func (n *NodeP2P) init(resourcesCtx context.Context, rollupCfg *rollup.Config, l
n.host.SetStreamHandler(PayloadByNumberProtocolID(rollupCfg.L2ChainID), payloadByNumber) n.host.SetStreamHandler(PayloadByNumberProtocolID(rollupCfg.L2ChainID), payloadByNumber)
} }
} }
eps, ok := n.host.Peerstore().(store.ExtendedPeerstore) n.scorer = NewScorer(rollupCfg, eps, metrics, n.appScorer, log)
if !ok {
return fmt.Errorf("cannot init without extended peerstore: %w", err)
}
n.store = eps
n.scorer = NewScorer(rollupCfg, eps, metrics, log)
// notify of any new connections/streams/etc. // notify of any new connections/streams/etc.
n.host.Network().Notify(NewNetworkNotifier(log, metrics)) n.host.Network().Notify(NewNetworkNotifier(log, metrics))
// note: the IDDelta functionality was removed from libP2P, and no longer needs to be explicitly disabled. // note: the IDDelta functionality was removed from libP2P, and no longer needs to be explicitly disabled.
...@@ -150,6 +158,7 @@ func (n *NodeP2P) init(resourcesCtx context.Context, rollupCfg *rollup.Config, l ...@@ -150,6 +158,7 @@ func (n *NodeP2P) init(resourcesCtx context.Context, rollupCfg *rollup.Config, l
n.peerMonitor = monitor.NewPeerMonitor(resourcesCtx, log, clock.SystemClock, n, setup.BanThreshold(), setup.BanDuration()) n.peerMonitor = monitor.NewPeerMonitor(resourcesCtx, log, clock.SystemClock, n, setup.BanThreshold(), setup.BanDuration())
n.peerMonitor.Start() n.peerMonitor.Start()
} }
n.appScorer.start()
} }
return nil return nil
} }
...@@ -258,6 +267,9 @@ func (n *NodeP2P) Close() error { ...@@ -258,6 +267,9 @@ func (n *NodeP2P) Close() error {
} }
} }
} }
if n.appScorer != nil {
n.appScorer.stop()
}
return result.ErrorOrNil() return result.ErrorOrNil()
} }
......
...@@ -32,7 +32,7 @@ func ScoreDecay(duration time.Duration, slot time.Duration) float64 { ...@@ -32,7 +32,7 @@ func ScoreDecay(duration time.Duration, slot time.Duration) float64 {
// See [PeerScoreParams] for detailed documentation. // See [PeerScoreParams] for detailed documentation.
// //
// [PeerScoreParams]: https://pkg.go.dev/github.com/libp2p/go-libp2p-pubsub@v0.8.1#PeerScoreParams // [PeerScoreParams]: https://pkg.go.dev/github.com/libp2p/go-libp2p-pubsub@v0.8.1#PeerScoreParams
var LightPeerScoreParams = func(cfg *rollup.Config) pubsub.PeerScoreParams { func LightPeerScoreParams(cfg *rollup.Config) pubsub.PeerScoreParams {
slot := time.Duration(cfg.BlockTime) * time.Second slot := time.Duration(cfg.BlockTime) * time.Second
if slot == 0 { if slot == 0 {
slot = 2 * time.Second slot = 2 * time.Second
...@@ -91,7 +91,8 @@ func GetScoringParams(name string, cfg *rollup.Config) (*ScoringParams, error) { ...@@ -91,7 +91,8 @@ func GetScoringParams(name string, cfg *rollup.Config) (*ScoringParams, error) {
switch name { switch name {
case "light": case "light":
return &ScoringParams{ return &ScoringParams{
PeerScoring: LightPeerScoreParams(cfg), PeerScoring: LightPeerScoreParams(cfg),
ApplicationScoring: LightApplicationScoreParams(cfg),
}, nil }, nil
case "none": case "none":
return nil, nil return nil, nil
......
...@@ -81,6 +81,19 @@ func (testSuite *PeerParamsTestSuite) TestGetPeerScoreParams_Light() { ...@@ -81,6 +81,19 @@ func (testSuite *PeerParamsTestSuite) TestGetPeerScoreParams_Light() {
testSuite.Equal(peerParams.DecayInterval, slot) testSuite.Equal(peerParams.DecayInterval, slot)
testSuite.Equal(peerParams.DecayToZero, DecayToZero) testSuite.Equal(peerParams.DecayToZero, DecayToZero)
testSuite.Equal(peerParams.RetainScore, oneHundredEpochs) testSuite.Equal(peerParams.RetainScore, oneHundredEpochs)
appParams := scoringParams.ApplicationScoring
testSuite.Positive(appParams.ValidResponseCap)
testSuite.Positive(appParams.ValidResponseWeight)
testSuite.Positive(appParams.ValidResponseDecay)
testSuite.Positive(appParams.ErrorResponseCap)
testSuite.Negative(appParams.ErrorResponseWeight)
testSuite.Positive(appParams.ErrorResponseDecay)
testSuite.Positive(appParams.RejectedPayloadCap)
testSuite.Negative(appParams.RejectedPayloadWeight)
testSuite.Positive(appParams.RejectedPayloadDecay)
testSuite.Equal(DecayToZero, appParams.DecayToZero)
testSuite.Equal(slot, appParams.DecayInterval)
} }
// TestParamsZeroBlockTime validates peer score params use default slot for 0 block time. // TestParamsZeroBlockTime validates peer score params use default slot for 0 block time.
...@@ -91,4 +104,5 @@ func (testSuite *PeerParamsTestSuite) TestParamsZeroBlockTime() { ...@@ -91,4 +104,5 @@ func (testSuite *PeerParamsTestSuite) TestParamsZeroBlockTime() {
params, err := GetScoringParams("light", &cfg) params, err := GetScoringParams("light", &cfg)
testSuite.NoError(err) testSuite.NoError(err)
testSuite.Equal(params.PeerScoring.DecayInterval, slot) testSuite.Equal(params.PeerScoring.DecayInterval, slot)
testSuite.Equal(params.ApplicationScoring.DecayInterval, slot)
} }
...@@ -14,6 +14,7 @@ import ( ...@@ -14,6 +14,7 @@ import (
type scorer struct { type scorer struct {
peerStore Peerstore peerStore Peerstore
metricer ScoreMetrics metricer ScoreMetrics
appScorer ApplicationScorer
log log.Logger log log.Logger
cfg *rollup.Config cfg *rollup.Config
} }
...@@ -36,6 +37,7 @@ type Peerstore interface { ...@@ -36,6 +37,7 @@ type Peerstore interface {
// Scorer is a peer scorer that scores peers based on application-specific metrics. // Scorer is a peer scorer that scores peers based on application-specific metrics.
type Scorer interface { type Scorer interface {
SnapshotHook() pubsub.ExtendedPeerScoreInspectFn SnapshotHook() pubsub.ExtendedPeerScoreInspectFn
ApplicationScore(peer.ID) float64
} }
//go:generate mockery --name ScoreMetrics --output mocks/ //go:generate mockery --name ScoreMetrics --output mocks/
...@@ -44,10 +46,11 @@ type ScoreMetrics interface { ...@@ -44,10 +46,11 @@ type ScoreMetrics interface {
} }
// NewScorer returns a new peer scorer. // NewScorer returns a new peer scorer.
func NewScorer(cfg *rollup.Config, peerStore Peerstore, metricer ScoreMetrics, log log.Logger) Scorer { func NewScorer(cfg *rollup.Config, peerStore Peerstore, metricer ScoreMetrics, appScorer ApplicationScorer, log log.Logger) Scorer {
return &scorer{ return &scorer{
peerStore: peerStore, peerStore: peerStore,
metricer: metricer, metricer: metricer,
appScorer: appScorer,
log: log, log: log,
cfg: cfg, cfg: cfg,
} }
...@@ -84,3 +87,7 @@ func (s *scorer) SnapshotHook() pubsub.ExtendedPeerScoreInspectFn { ...@@ -84,3 +87,7 @@ func (s *scorer) SnapshotHook() pubsub.ExtendedPeerScoreInspectFn {
s.metricer.SetPeerScores(allScores) s.metricer.SetPeerScores(allScores)
} }
} }
func (s *scorer) ApplicationScore(id peer.ID) float64 {
return s.appScorer.ApplicationScore(id)
}
...@@ -44,6 +44,7 @@ func (testSuite *PeerScorerTestSuite) TestScorer_SnapshotHook() { ...@@ -44,6 +44,7 @@ func (testSuite *PeerScorerTestSuite) TestScorer_SnapshotHook() {
&rollup.Config{L2ChainID: big.NewInt(123)}, &rollup.Config{L2ChainID: big.NewInt(123)},
testSuite.mockStore, testSuite.mockStore,
testSuite.mockMetricer, testSuite.mockMetricer,
&p2p.NoopApplicationScorer{},
testSuite.logger, testSuite.logger,
) )
inspectFn := scorer.SnapshotHook() inspectFn := scorer.SnapshotHook()
......
...@@ -9,12 +9,15 @@ import ( ...@@ -9,12 +9,15 @@ import (
func ConfigurePeerScoring(gossipConf GossipSetupConfigurables, scorer Scorer, log log.Logger) []pubsub.Option { func ConfigurePeerScoring(gossipConf GossipSetupConfigurables, scorer Scorer, log log.Logger) []pubsub.Option {
// If we want to completely disable scoring config here, we can use the [peerScoringParams] // If we want to completely disable scoring config here, we can use the [peerScoringParams]
// to return early without returning any [pubsub.Option]. // to return early without returning any [pubsub.Option].
peerScoreParams := gossipConf.PeerScoringParams() scoreParams := gossipConf.PeerScoringParams()
peerScoreThresholds := NewPeerScoreThresholds()
opts := []pubsub.Option{} opts := []pubsub.Option{}
if peerScoreParams != nil { if scoreParams != nil {
peerScoreThresholds := NewPeerScoreThresholds()
// Create copy of params before modifying the AppSpecificScore
params := scoreParams.PeerScoring
params.AppSpecificScore = scorer.ApplicationScore
opts = []pubsub.Option{ opts = []pubsub.Option{
pubsub.WithPeerScore(peerScoreParams, &peerScoreThresholds), pubsub.WithPeerScore(&params, &peerScoreThresholds),
pubsub.WithPeerScoreInspect(scorer.SnapshotHook(), peerScoreInspectFrequency), pubsub.WithPeerScoreInspect(scorer.SnapshotHook(), peerScoreInspectFrequency),
} }
} else { } else {
......
...@@ -82,6 +82,18 @@ func getNetHosts(testSuite *PeerScoresTestSuite, ctx context.Context, n int) []h ...@@ -82,6 +82,18 @@ func getNetHosts(testSuite *PeerScoresTestSuite, ctx context.Context, n int) []h
return out return out
} }
type discriminatingAppScorer struct {
badPeer peer.ID
NoopApplicationScorer
}
func (d *discriminatingAppScorer) ApplicationScore(id peer.ID) float64 {
if id == d.badPeer {
return -1000
}
return 0
}
func newGossipSubs(testSuite *PeerScoresTestSuite, ctx context.Context, hosts []host.Host) []*pubsub.PubSub { func newGossipSubs(testSuite *PeerScoresTestSuite, ctx context.Context, hosts []host.Host) []*pubsub.PubSub {
var psubs []*pubsub.PubSub var psubs []*pubsub.PubSub
...@@ -100,17 +112,10 @@ func newGossipSubs(testSuite *PeerScoresTestSuite, ctx context.Context, hosts [] ...@@ -100,17 +112,10 @@ func newGossipSubs(testSuite *PeerScoresTestSuite, ctx context.Context, hosts []
scorer := NewScorer( scorer := NewScorer(
&rollup.Config{L2ChainID: big.NewInt(123)}, &rollup.Config{L2ChainID: big.NewInt(123)},
extPeerStore, testSuite.mockMetricer, logger) extPeerStore, testSuite.mockMetricer, &discriminatingAppScorer{badPeer: hosts[0].ID()}, logger)
opts = append(opts, ConfigurePeerScoring(&Config{ opts = append(opts, ConfigurePeerScoring(&Config{
ScoringParams: &ScoringParams{ ScoringParams: &ScoringParams{
PeerScoring: pubsub.PeerScoreParams{ PeerScoring: pubsub.PeerScoreParams{
AppSpecificScore: func(p peer.ID) float64 {
if p == hosts[0].ID() {
return -1000
} else {
return 0
}
},
AppSpecificWeight: 1, AppSpecificWeight: 1,
DecayInterval: time.Second, DecayInterval: time.Second,
DecayToZero: 0.01, DecayToZero: 0.01,
......
...@@ -69,7 +69,7 @@ func (p *Prepared) ConfigureGossip(rollupCfg *rollup.Config) []pubsub.Option { ...@@ -69,7 +69,7 @@ func (p *Prepared) ConfigureGossip(rollupCfg *rollup.Config) []pubsub.Option {
} }
} }
func (p *Prepared) PeerScoringParams() *pubsub.PeerScoreParams { func (p *Prepared) PeerScoringParams() *ScoringParams {
return nil return nil
} }
......
...@@ -111,6 +111,12 @@ type SyncClientMetrics interface { ...@@ -111,6 +111,12 @@ type SyncClientMetrics interface {
PayloadsQuarantineSize(n int) PayloadsQuarantineSize(n int)
} }
type SyncPeerScorer interface {
onValidResponse(id peer.ID)
onResponseError(id peer.ID)
onRejectedPayload(id peer.ID)
}
// SyncClient implements a reverse chain sync with a minimal interface: // SyncClient implements a reverse chain sync with a minimal interface:
// signal the desired range, and receive blocks within this range back. // signal the desired range, and receive blocks within this range back.
// Through parent-hash verification, received blocks are all ensured to be part of the canonical chain at one point, // Through parent-hash verification, received blocks are all ensured to be part of the canonical chain at one point,
...@@ -180,7 +186,8 @@ type SyncClient struct { ...@@ -180,7 +186,8 @@ type SyncClient struct {
cfg *rollup.Config cfg *rollup.Config
metrics SyncClientMetrics metrics SyncClientMetrics
appScorer SyncPeerScorer
newStreamFn newStreamFn newStreamFn newStreamFn
payloadByNumber protocol.ID payloadByNumber protocol.ID
...@@ -227,13 +234,14 @@ type SyncClient struct { ...@@ -227,13 +234,14 @@ type SyncClient struct {
closingPeers bool closingPeers bool
} }
func NewSyncClient(log log.Logger, cfg *rollup.Config, newStream newStreamFn, rcv receivePayloadFn, metrics SyncClientMetrics) *SyncClient { func NewSyncClient(log log.Logger, cfg *rollup.Config, newStream newStreamFn, rcv receivePayloadFn, metrics SyncClientMetrics, appScorer SyncPeerScorer) *SyncClient {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
c := &SyncClient{ c := &SyncClient{
log: log, log: log,
cfg: cfg, cfg: cfg,
metrics: metrics, metrics: metrics,
appScorer: appScorer,
newStreamFn: newStream, newStreamFn: newStream,
payloadByNumber: PayloadByNumberProtocolID(cfg.L2ChainID), payloadByNumber: PayloadByNumberProtocolID(cfg.L2ChainID),
peers: make(map[peer.ID]context.CancelFunc), peers: make(map[peer.ID]context.CancelFunc),
...@@ -268,11 +276,11 @@ func (s *SyncClient) Start() { ...@@ -268,11 +276,11 @@ func (s *SyncClient) Start() {
func (s *SyncClient) AddPeer(id peer.ID) { func (s *SyncClient) AddPeer(id peer.ID) {
s.peersLock.Lock() s.peersLock.Lock()
defer s.peersLock.Unlock() defer s.peersLock.Unlock()
if _, ok := s.peers[id]; ok { if s.closingPeers {
s.log.Warn("cannot register peer for sync duties, peer was already registered", "peer", id)
return return
} }
if s.closingPeers { if _, ok := s.peers[id]; ok {
s.log.Warn("cannot register peer for sync duties, peer was already registered", "peer", id)
return return
} }
s.wg.Add(1) s.wg.Add(1)
...@@ -424,7 +432,8 @@ func (s *SyncClient) onQuarantineEvict(key common.Hash, value syncResult) { ...@@ -424,7 +432,8 @@ func (s *SyncClient) onQuarantineEvict(key common.Hash, value syncResult) {
s.metrics.PayloadsQuarantineSize(s.quarantine.Len()) s.metrics.PayloadsQuarantineSize(s.quarantine.Len())
if !s.trusted.Contains(key) { if !s.trusted.Contains(key) {
s.log.Debug("evicting untrusted payload from quarantine", "id", value.payload.ID(), "peer", value.peer) s.log.Debug("evicting untrusted payload from quarantine", "id", value.payload.ID(), "peer", value.peer)
// TODO(CLI-3732): downscore peer for having provided us a bad block that never turned out to be canonical // Down-score peer for having provided us a bad block that never turned out to be canonical
s.appScorer.onRejectedPayload(value.peer)
} else { } else {
s.log.Debug("evicting trusted payload from quarantine", "id", value.payload.ID(), "peer", value.peer) s.log.Debug("evicting trusted payload from quarantine", "id", value.payload.ID(), "peer", value.peer)
} }
...@@ -492,9 +501,9 @@ func (s *SyncClient) peerLoop(ctx context.Context, id peer.ID) { ...@@ -492,9 +501,9 @@ func (s *SyncClient) peerLoop(ctx context.Context, id peer.ID) {
defer func() { defer func() {
s.peersLock.Lock() s.peersLock.Lock()
delete(s.peers, id) // clean up delete(s.peers, id) // clean up
s.log.Debug("stopped syncing loop of peer", "id", id)
s.wg.Done() s.wg.Done()
s.peersLock.Unlock() s.peersLock.Unlock()
s.log.Debug("stopped syncing loop of peer", "id", id)
}() }()
log := s.log.New("peer", id) log := s.log.New("peer", id)
...@@ -525,6 +534,7 @@ func (s *SyncClient) peerLoop(ctx context.Context, id peer.ID) { ...@@ -525,6 +534,7 @@ func (s *SyncClient) peerLoop(ctx context.Context, id peer.ID) {
// mark as complete if there's an error: we are not sending any result and can complete immediately. // mark as complete if there's an error: we are not sending any result and can complete immediately.
pr.complete.Store(true) pr.complete.Store(true)
log.Warn("failed p2p sync request", "num", pr.num, "err", err) log.Warn("failed p2p sync request", "num", pr.num, "err", err)
s.appScorer.onResponseError(id)
// If we hit an error, then count it as many requests. // If we hit an error, then count it as many requests.
// We'd like to avoid making more requests for a while, to back off. // We'd like to avoid making more requests for a while, to back off.
if err := rl.WaitN(ctx, clientErrRateCost); err != nil { if err := rl.WaitN(ctx, clientErrRateCost); err != nil {
...@@ -532,11 +542,9 @@ func (s *SyncClient) peerLoop(ctx context.Context, id peer.ID) { ...@@ -532,11 +542,9 @@ func (s *SyncClient) peerLoop(ctx context.Context, id peer.ID) {
} }
} else { } else {
log.Debug("completed p2p sync request", "num", pr.num) log.Debug("completed p2p sync request", "num", pr.num)
s.appScorer.onValidResponse(id)
} }
took := time.Since(start) took := time.Since(start)
// TODO(CLI-3732): update scores: depending on the speed of the result,
// increase the p2p-sync part of the peer score
// (don't allow the score to grow indefinitely only based on this factor though)
resultCode := byte(0) resultCode := byte(0)
if err != nil { if err != nil {
......
...@@ -137,7 +137,7 @@ func TestSinglePeerSync(t *testing.T) { ...@@ -137,7 +137,7 @@ func TestSinglePeerSync(t *testing.T) {
hostA.SetStreamHandler(PayloadByNumberProtocolID(cfg.L2ChainID), payloadByNumber) hostA.SetStreamHandler(PayloadByNumberProtocolID(cfg.L2ChainID), payloadByNumber)
// Setup host B as the client // Setup host B as the client
cl := NewSyncClient(log.New("role", "client"), cfg, hostB.NewStream, receivePayload, metrics.NoopMetrics) cl := NewSyncClient(log.New("role", "client"), cfg, hostB.NewStream, receivePayload, metrics.NoopMetrics, &NoopApplicationScorer{})
// Setup host B (client) to sync from its peer Host A (server) // Setup host B (client) to sync from its peer Host A (server)
cl.AddPeer(hostA.ID()) cl.AddPeer(hostA.ID())
...@@ -190,7 +190,7 @@ func TestMultiPeerSync(t *testing.T) { ...@@ -190,7 +190,7 @@ func TestMultiPeerSync(t *testing.T) {
payloadByNumber := MakeStreamHandler(ctx, log.New("serve", "payloads_by_number"), srv.HandleSyncRequest) payloadByNumber := MakeStreamHandler(ctx, log.New("serve", "payloads_by_number"), srv.HandleSyncRequest)
h.SetStreamHandler(PayloadByNumberProtocolID(cfg.L2ChainID), payloadByNumber) h.SetStreamHandler(PayloadByNumberProtocolID(cfg.L2ChainID), payloadByNumber)
cl := NewSyncClient(log.New("role", "client"), cfg, h.NewStream, receivePayload, metrics.NoopMetrics) cl := NewSyncClient(log.New("role", "client"), cfg, h.NewStream, receivePayload, metrics.NoopMetrics, &NoopApplicationScorer{})
return cl, received return cl, received
} }
......
...@@ -12,7 +12,7 @@ import ( ...@@ -12,7 +12,7 @@ import (
"github.com/ethereum-optimism/optimism/op-node/sources" "github.com/ethereum-optimism/optimism/op-node/sources"
oppprof "github.com/ethereum-optimism/optimism/op-service/pprof" oppprof "github.com/ethereum-optimism/optimism/op-service/pprof"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
...@@ -64,27 +64,27 @@ func NewConfig(ctx *cli.Context, log log.Logger) (*node.Config, error) { ...@@ -64,27 +64,27 @@ func NewConfig(ctx *cli.Context, log log.Logger) (*node.Config, error) {
Rollup: *rollupConfig, Rollup: *rollupConfig,
Driver: *driverConfig, Driver: *driverConfig,
RPC: node.RPCConfig{ RPC: node.RPCConfig{
ListenAddr: ctx.GlobalString(flags.RPCListenAddr.Name), ListenAddr: ctx.String(flags.RPCListenAddr.Name),
ListenPort: ctx.GlobalInt(flags.RPCListenPort.Name), ListenPort: ctx.Int(flags.RPCListenPort.Name),
EnableAdmin: ctx.GlobalBool(flags.RPCEnableAdmin.Name), EnableAdmin: ctx.Bool(flags.RPCEnableAdmin.Name),
}, },
Metrics: node.MetricsConfig{ Metrics: node.MetricsConfig{
Enabled: ctx.GlobalBool(flags.MetricsEnabledFlag.Name), Enabled: ctx.Bool(flags.MetricsEnabledFlag.Name),
ListenAddr: ctx.GlobalString(flags.MetricsAddrFlag.Name), ListenAddr: ctx.String(flags.MetricsAddrFlag.Name),
ListenPort: ctx.GlobalInt(flags.MetricsPortFlag.Name), ListenPort: ctx.Int(flags.MetricsPortFlag.Name),
}, },
Pprof: oppprof.CLIConfig{ Pprof: oppprof.CLIConfig{
Enabled: ctx.GlobalBool(flags.PprofEnabledFlag.Name), Enabled: ctx.Bool(flags.PprofEnabledFlag.Name),
ListenAddr: ctx.GlobalString(flags.PprofAddrFlag.Name), ListenAddr: ctx.String(flags.PprofAddrFlag.Name),
ListenPort: ctx.GlobalInt(flags.PprofPortFlag.Name), ListenPort: ctx.Int(flags.PprofPortFlag.Name),
}, },
P2P: p2pConfig, P2P: p2pConfig,
P2PSigner: p2pSignerSetup, P2PSigner: p2pSignerSetup,
L1EpochPollInterval: ctx.GlobalDuration(flags.L1EpochPollIntervalFlag.Name), L1EpochPollInterval: ctx.Duration(flags.L1EpochPollIntervalFlag.Name),
Heartbeat: node.HeartbeatConfig{ Heartbeat: node.HeartbeatConfig{
Enabled: ctx.GlobalBool(flags.HeartbeatEnabledFlag.Name), Enabled: ctx.Bool(flags.HeartbeatEnabledFlag.Name),
Moniker: ctx.GlobalString(flags.HeartbeatMonikerFlag.Name), Moniker: ctx.String(flags.HeartbeatMonikerFlag.Name),
URL: ctx.GlobalString(flags.HeartbeatURLFlag.Name), URL: ctx.String(flags.HeartbeatURLFlag.Name),
}, },
} }
if err := cfg.Check(); err != nil { if err := cfg.Check(); err != nil {
...@@ -95,18 +95,18 @@ func NewConfig(ctx *cli.Context, log log.Logger) (*node.Config, error) { ...@@ -95,18 +95,18 @@ func NewConfig(ctx *cli.Context, log log.Logger) (*node.Config, error) {
func NewL1EndpointConfig(ctx *cli.Context) *node.L1EndpointConfig { func NewL1EndpointConfig(ctx *cli.Context) *node.L1EndpointConfig {
return &node.L1EndpointConfig{ return &node.L1EndpointConfig{
L1NodeAddr: ctx.GlobalString(flags.L1NodeAddr.Name), L1NodeAddr: ctx.String(flags.L1NodeAddr.Name),
L1TrustRPC: ctx.GlobalBool(flags.L1TrustRPC.Name), L1TrustRPC: ctx.Bool(flags.L1TrustRPC.Name),
L1RPCKind: sources.RPCProviderKind(strings.ToLower(ctx.GlobalString(flags.L1RPCProviderKind.Name))), L1RPCKind: sources.RPCProviderKind(strings.ToLower(ctx.String(flags.L1RPCProviderKind.Name))),
RateLimit: ctx.GlobalFloat64(flags.L1RPCRateLimit.Name), RateLimit: ctx.Float64(flags.L1RPCRateLimit.Name),
BatchSize: ctx.GlobalInt(flags.L1RPCMaxBatchSize.Name), BatchSize: ctx.Int(flags.L1RPCMaxBatchSize.Name),
HttpPollInterval: ctx.Duration(flags.L1HTTPPollInterval.Name), HttpPollInterval: ctx.Duration(flags.L1HTTPPollInterval.Name),
} }
} }
func NewL2EndpointConfig(ctx *cli.Context, log log.Logger) (*node.L2EndpointConfig, error) { func NewL2EndpointConfig(ctx *cli.Context, log log.Logger) (*node.L2EndpointConfig, error) {
l2Addr := ctx.GlobalString(flags.L2EngineAddr.Name) l2Addr := ctx.String(flags.L2EngineAddr.Name)
fileName := ctx.GlobalString(flags.L2EngineJWTSecret.Name) fileName := ctx.String(flags.L2EngineJWTSecret.Name)
var secret [32]byte var secret [32]byte
fileName = strings.TrimSpace(fileName) fileName = strings.TrimSpace(fileName)
if fileName == "" { if fileName == "" {
...@@ -138,23 +138,23 @@ func NewL2EndpointConfig(ctx *cli.Context, log log.Logger) (*node.L2EndpointConf ...@@ -138,23 +138,23 @@ func NewL2EndpointConfig(ctx *cli.Context, log log.Logger) (*node.L2EndpointConf
// flag is set, otherwise nil. // flag is set, otherwise nil.
func NewL2SyncEndpointConfig(ctx *cli.Context) *node.L2SyncEndpointConfig { func NewL2SyncEndpointConfig(ctx *cli.Context) *node.L2SyncEndpointConfig {
return &node.L2SyncEndpointConfig{ return &node.L2SyncEndpointConfig{
L2NodeAddr: ctx.GlobalString(flags.BackupL2UnsafeSyncRPC.Name), L2NodeAddr: ctx.String(flags.BackupL2UnsafeSyncRPC.Name),
TrustRPC: ctx.GlobalBool(flags.BackupL2UnsafeSyncRPCTrustRPC.Name), TrustRPC: ctx.Bool(flags.BackupL2UnsafeSyncRPCTrustRPC.Name),
} }
} }
func NewDriverConfig(ctx *cli.Context) *driver.Config { func NewDriverConfig(ctx *cli.Context) *driver.Config {
return &driver.Config{ return &driver.Config{
VerifierConfDepth: ctx.GlobalUint64(flags.VerifierL1Confs.Name), VerifierConfDepth: ctx.Uint64(flags.VerifierL1Confs.Name),
SequencerConfDepth: ctx.GlobalUint64(flags.SequencerL1Confs.Name), SequencerConfDepth: ctx.Uint64(flags.SequencerL1Confs.Name),
SequencerEnabled: ctx.GlobalBool(flags.SequencerEnabledFlag.Name), SequencerEnabled: ctx.Bool(flags.SequencerEnabledFlag.Name),
SequencerStopped: ctx.GlobalBool(flags.SequencerStoppedFlag.Name), SequencerStopped: ctx.Bool(flags.SequencerStoppedFlag.Name),
SequencerMaxSafeLag: ctx.GlobalUint64(flags.SequencerMaxSafeLagFlag.Name), SequencerMaxSafeLag: ctx.Uint64(flags.SequencerMaxSafeLagFlag.Name),
} }
} }
func NewRollupConfig(ctx *cli.Context) (*rollup.Config, error) { func NewRollupConfig(ctx *cli.Context) (*rollup.Config, error) {
network := ctx.GlobalString(flags.Network.Name) network := ctx.String(flags.Network.Name)
if network != "" { if network != "" {
config, err := chaincfg.GetRollupConfig(network) config, err := chaincfg.GetRollupConfig(network)
if err != nil { if err != nil {
...@@ -164,7 +164,7 @@ func NewRollupConfig(ctx *cli.Context) (*rollup.Config, error) { ...@@ -164,7 +164,7 @@ func NewRollupConfig(ctx *cli.Context) (*rollup.Config, error) {
return &config, nil return &config, nil
} }
rollupConfigPath := ctx.GlobalString(flags.RollupConfig.Name) rollupConfigPath := ctx.String(flags.RollupConfig.Name)
file, err := os.Open(rollupConfigPath) file, err := os.Open(rollupConfigPath)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to read rollup config: %w", err) return nil, fmt.Errorf("failed to read rollup config: %w", err)
...@@ -179,7 +179,7 @@ func NewRollupConfig(ctx *cli.Context) (*rollup.Config, error) { ...@@ -179,7 +179,7 @@ func NewRollupConfig(ctx *cli.Context) (*rollup.Config, error) {
} }
func NewSnapshotLogger(ctx *cli.Context) (log.Logger, error) { func NewSnapshotLogger(ctx *cli.Context) (log.Logger, error) {
snapshotFile := ctx.GlobalString(flags.SnapshotLog.Name) snapshotFile := ctx.String(flags.SnapshotLog.Name)
handler := log.DiscardHandler() handler := log.DiscardHandler()
if snapshotFile != "" { if snapshotFile != "" {
var err error var err error
......
...@@ -10,7 +10,7 @@ import ( ...@@ -10,7 +10,7 @@ import (
"github.com/ethereum-optimism/optimism/op-program/host/version" "github.com/ethereum-optimism/optimism/op-program/host/version"
oplog "github.com/ethereum-optimism/optimism/op-service/log" oplog "github.com/ethereum-optimism/optimism/op-service/log"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
) )
var ( var (
......
...@@ -13,7 +13,7 @@ import ( ...@@ -13,7 +13,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
) )
var ( var (
...@@ -118,23 +118,23 @@ func NewConfigFromCLI(ctx *cli.Context) (*Config, error) { ...@@ -118,23 +118,23 @@ func NewConfigFromCLI(ctx *cli.Context) (*Config, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
l2Head := common.HexToHash(ctx.GlobalString(flags.L2Head.Name)) l2Head := common.HexToHash(ctx.String(flags.L2Head.Name))
if l2Head == (common.Hash{}) { if l2Head == (common.Hash{}) {
return nil, ErrInvalidL2Head return nil, ErrInvalidL2Head
} }
l2Claim := common.HexToHash(ctx.GlobalString(flags.L2Claim.Name)) l2Claim := common.HexToHash(ctx.String(flags.L2Claim.Name))
if l2Claim == (common.Hash{}) { if l2Claim == (common.Hash{}) {
return nil, ErrInvalidL2Claim return nil, ErrInvalidL2Claim
} }
l2ClaimBlockNum := ctx.GlobalUint64(flags.L2BlockNumber.Name) l2ClaimBlockNum := ctx.Uint64(flags.L2BlockNumber.Name)
l1Head := common.HexToHash(ctx.GlobalString(flags.L1Head.Name)) l1Head := common.HexToHash(ctx.String(flags.L1Head.Name))
if l1Head == (common.Hash{}) { if l1Head == (common.Hash{}) {
return nil, ErrInvalidL1Head return nil, ErrInvalidL1Head
} }
l2GenesisPath := ctx.GlobalString(flags.L2GenesisPath.Name) l2GenesisPath := ctx.String(flags.L2GenesisPath.Name)
var l2ChainConfig *params.ChainConfig var l2ChainConfig *params.ChainConfig
if l2GenesisPath == "" { if l2GenesisPath == "" {
networkName := ctx.GlobalString(flags.Network.Name) networkName := ctx.String(flags.Network.Name)
l2ChainConfig = L2ChainConfigsByName[networkName] l2ChainConfig = L2ChainConfigsByName[networkName]
if l2ChainConfig == nil { if l2ChainConfig == nil {
return nil, fmt.Errorf("flag %s is required for network %s", flags.L2GenesisPath.Name, networkName) return nil, fmt.Errorf("flag %s is required for network %s", flags.L2GenesisPath.Name, networkName)
...@@ -147,18 +147,18 @@ func NewConfigFromCLI(ctx *cli.Context) (*Config, error) { ...@@ -147,18 +147,18 @@ func NewConfigFromCLI(ctx *cli.Context) (*Config, error) {
} }
return &Config{ return &Config{
Rollup: rollupCfg, Rollup: rollupCfg,
DataDir: ctx.GlobalString(flags.DataDir.Name), DataDir: ctx.String(flags.DataDir.Name),
L2URL: ctx.GlobalString(flags.L2NodeAddr.Name), L2URL: ctx.String(flags.L2NodeAddr.Name),
L2ChainConfig: l2ChainConfig, L2ChainConfig: l2ChainConfig,
L2Head: l2Head, L2Head: l2Head,
L2Claim: l2Claim, L2Claim: l2Claim,
L2ClaimBlockNumber: l2ClaimBlockNum, L2ClaimBlockNumber: l2ClaimBlockNum,
L1Head: l1Head, L1Head: l1Head,
L1URL: ctx.GlobalString(flags.L1NodeAddr.Name), L1URL: ctx.String(flags.L1NodeAddr.Name),
L1TrustRPC: ctx.GlobalBool(flags.L1TrustRPC.Name), L1TrustRPC: ctx.Bool(flags.L1TrustRPC.Name),
L1RPCKind: sources.RPCProviderKind(ctx.GlobalString(flags.L1RPCProviderKind.Name)), L1RPCKind: sources.RPCProviderKind(ctx.String(flags.L1RPCProviderKind.Name)),
ExecCmd: ctx.GlobalString(flags.Exec.Name), ExecCmd: ctx.String(flags.Exec.Name),
ServerMode: ctx.GlobalBool(flags.Server.Name), ServerMode: ctx.Bool(flags.Server.Name),
}, nil }, nil
} }
......
...@@ -4,7 +4,7 @@ import ( ...@@ -4,7 +4,7 @@ import (
"fmt" "fmt"
"strings" "strings"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
"github.com/ethereum-optimism/optimism/op-node/chaincfg" "github.com/ethereum-optimism/optimism/op-node/chaincfg"
"github.com/ethereum-optimism/optimism/op-node/sources" "github.com/ethereum-optimism/optimism/op-node/sources"
...@@ -15,81 +15,85 @@ import ( ...@@ -15,81 +15,85 @@ import (
const EnvVarPrefix = "OP_PROGRAM" const EnvVarPrefix = "OP_PROGRAM"
func prefixEnvVars(name string) []string {
return service.PrefixEnvVar(EnvVarPrefix, name)
}
var ( var (
RollupConfig = cli.StringFlag{ RollupConfig = &cli.StringFlag{
Name: "rollup.config", Name: "rollup.config",
Usage: "Rollup chain parameters", Usage: "Rollup chain parameters",
EnvVar: service.PrefixEnvVar(EnvVarPrefix, "ROLLUP_CONFIG"), EnvVars: prefixEnvVars("ROLLUP_CONFIG"),
} }
Network = cli.StringFlag{ Network = &cli.StringFlag{
Name: "network", Name: "network",
Usage: fmt.Sprintf("Predefined network selection. Available networks: %s", strings.Join(chaincfg.AvailableNetworks(), ", ")), Usage: fmt.Sprintf("Predefined network selection. Available networks: %s", strings.Join(chaincfg.AvailableNetworks(), ", ")),
EnvVar: service.PrefixEnvVar(EnvVarPrefix, "NETWORK"), EnvVars: prefixEnvVars("NETWORK"),
} }
DataDir = cli.StringFlag{ DataDir = &cli.StringFlag{
Name: "datadir", Name: "datadir",
Usage: "Directory to use for preimage data storage. Default uses in-memory storage", Usage: "Directory to use for preimage data storage. Default uses in-memory storage",
EnvVar: service.PrefixEnvVar(EnvVarPrefix, "DATADIR"), EnvVars: prefixEnvVars("DATADIR"),
} }
L2NodeAddr = cli.StringFlag{ L2NodeAddr = &cli.StringFlag{
Name: "l2", Name: "l2",
Usage: "Address of L2 JSON-RPC endpoint to use (eth and debug namespace required)", Usage: "Address of L2 JSON-RPC endpoint to use (eth and debug namespace required)",
EnvVar: service.PrefixEnvVar(EnvVarPrefix, "L2_RPC"), EnvVars: prefixEnvVars("L2_RPC"),
} }
L1Head = cli.StringFlag{ L1Head = &cli.StringFlag{
Name: "l1.head", Name: "l1.head",
Usage: "Hash of the L1 head block. Derivation stops after this block is processed.", Usage: "Hash of the L1 head block. Derivation stops after this block is processed.",
EnvVar: service.PrefixEnvVar(EnvVarPrefix, "L1_HEAD"), EnvVars: prefixEnvVars("L1_HEAD"),
} }
L2Head = cli.StringFlag{ L2Head = &cli.StringFlag{
Name: "l2.head", Name: "l2.head",
Usage: "Hash of the agreed L2 block to start derivation from", Usage: "Hash of the agreed L2 block to start derivation from",
EnvVar: service.PrefixEnvVar(EnvVarPrefix, "L2_HEAD"), EnvVars: prefixEnvVars("L2_HEAD"),
} }
L2Claim = cli.StringFlag{ L2Claim = &cli.StringFlag{
Name: "l2.claim", Name: "l2.claim",
Usage: "Claimed L2 output root to validate", Usage: "Claimed L2 output root to validate",
EnvVar: service.PrefixEnvVar(EnvVarPrefix, "L2_CLAIM"), EnvVars: prefixEnvVars("L2_CLAIM"),
} }
L2BlockNumber = cli.Uint64Flag{ L2BlockNumber = &cli.Uint64Flag{
Name: "l2.blocknumber", Name: "l2.blocknumber",
Usage: "Number of the L2 block that the claim is from", Usage: "Number of the L2 block that the claim is from",
EnvVar: service.PrefixEnvVar(EnvVarPrefix, "L2_BLOCK_NUM"), EnvVars: prefixEnvVars("L2_BLOCK_NUM"),
} }
L2GenesisPath = cli.StringFlag{ L2GenesisPath = &cli.StringFlag{
Name: "l2.genesis", Name: "l2.genesis",
Usage: "Path to the op-geth genesis file", Usage: "Path to the op-geth genesis file",
EnvVar: service.PrefixEnvVar(EnvVarPrefix, "L2_GENESIS"), EnvVars: prefixEnvVars("L2_GENESIS"),
} }
L1NodeAddr = cli.StringFlag{ L1NodeAddr = &cli.StringFlag{
Name: "l1", Name: "l1",
Usage: "Address of L1 JSON-RPC endpoint to use (eth namespace required)", Usage: "Address of L1 JSON-RPC endpoint to use (eth namespace required)",
EnvVar: service.PrefixEnvVar(EnvVarPrefix, "L1_RPC"), EnvVars: prefixEnvVars("L1_RPC"),
} }
L1TrustRPC = cli.BoolFlag{ L1TrustRPC = &cli.BoolFlag{
Name: "l1.trustrpc", Name: "l1.trustrpc",
Usage: "Trust the L1 RPC, sync faster at risk of malicious/buggy RPC providing bad or inconsistent L1 data", 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"), EnvVars: prefixEnvVars("L1_TRUST_RPC"),
} }
L1RPCProviderKind = cli.GenericFlag{ L1RPCProviderKind = &cli.GenericFlag{
Name: "l1.rpckind", Name: "l1.rpckind",
Usage: "The kind of RPC provider, used to inform optimal transactions receipts fetching, and thus reduce costs. Valid options: " + Usage: "The kind of RPC provider, used to inform optimal transactions receipts fetching, and thus reduce costs. Valid options: " +
openum.EnumString(sources.RPCProviderKinds), openum.EnumString(sources.RPCProviderKinds),
EnvVar: service.PrefixEnvVar(EnvVarPrefix, "L1_RPC_KIND"), EnvVars: prefixEnvVars("L1_RPC_KIND"),
Value: func() *sources.RPCProviderKind { Value: func() *sources.RPCProviderKind {
out := sources.RPCKindBasic out := sources.RPCKindBasic
return &out return &out
}(), }(),
} }
Exec = cli.StringFlag{ Exec = &cli.StringFlag{
Name: "exec", Name: "exec",
Usage: "Run the specified client program as a separate process detached from the host. Default is to run the client program in the host process.", Usage: "Run the specified client program as a separate process detached from the host. Default is to run the client program in the host process.",
EnvVar: service.PrefixEnvVar(EnvVarPrefix, "EXEC"), EnvVars: prefixEnvVars("EXEC"),
} }
Server = cli.BoolFlag{ Server = &cli.BoolFlag{
Name: "server", Name: "server",
Usage: "Run in pre-image server mode without executing any client program.", Usage: "Run in pre-image server mode without executing any client program.",
EnvVar: service.PrefixEnvVar(EnvVarPrefix, "SERVER"), EnvVars: prefixEnvVars("SERVER"),
} }
) )
...@@ -122,20 +126,20 @@ func init() { ...@@ -122,20 +126,20 @@ func init() {
} }
func CheckRequired(ctx *cli.Context) error { func CheckRequired(ctx *cli.Context) error {
rollupConfig := ctx.GlobalString(RollupConfig.Name) rollupConfig := ctx.String(RollupConfig.Name)
network := ctx.GlobalString(Network.Name) network := ctx.String(Network.Name)
if rollupConfig == "" && network == "" { if rollupConfig == "" && network == "" {
return fmt.Errorf("flag %s or %s is required", RollupConfig.Name, Network.Name) return fmt.Errorf("flag %s or %s is required", RollupConfig.Name, Network.Name)
} }
if rollupConfig != "" && network != "" { if rollupConfig != "" && network != "" {
return fmt.Errorf("cannot specify both %s and %s", RollupConfig.Name, Network.Name) return fmt.Errorf("cannot specify both %s and %s", RollupConfig.Name, Network.Name)
} }
if network == "" && ctx.GlobalString(L2GenesisPath.Name) == "" { if network == "" && ctx.String(L2GenesisPath.Name) == "" {
return fmt.Errorf("flag %s is required for custom networks", L2GenesisPath.Name) return fmt.Errorf("flag %s is required for custom networks", L2GenesisPath.Name)
} }
for _, flag := range requiredFlags { for _, flag := range requiredFlags {
if !ctx.IsSet(flag.GetName()) { if !ctx.IsSet(flag.Names()[0]) {
return fmt.Errorf("flag %s is required", flag.GetName()) return fmt.Errorf("flag %s is required", flag.Names()[0])
} }
} }
return nil return nil
......
...@@ -5,14 +5,14 @@ import ( ...@@ -5,14 +5,14 @@ import (
"strings" "strings"
"testing" "testing"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
) )
// TestUniqueFlags asserts that all flag names are unique, to avoid accidental conflicts between the many flags. // TestUniqueFlags asserts that all flag names are unique, to avoid accidental conflicts between the many flags.
func TestUniqueFlags(t *testing.T) { func TestUniqueFlags(t *testing.T) {
seenCLI := make(map[string]struct{}) seenCLI := make(map[string]struct{})
for _, flag := range Flags { for _, flag := range Flags {
name := flag.GetName() name := flag.Names()[0]
if _, ok := seenCLI[name]; ok { if _, ok := seenCLI[name]; ok {
t.Errorf("duplicate flag %s", name) t.Errorf("duplicate flag %s", name)
continue continue
...@@ -38,22 +38,22 @@ func TestCorrectEnvVarPrefix(t *testing.T) { ...@@ -38,22 +38,22 @@ func TestCorrectEnvVarPrefix(t *testing.T) {
for _, flag := range Flags { for _, flag := range Flags {
envVar := envVarForFlag(flag) envVar := envVarForFlag(flag)
if envVar == "" { if envVar == "" {
t.Errorf("Failed to find EnvVar for flag %v", flag.GetName()) t.Errorf("Failed to find EnvVar for flag %v", flag.Names()[0])
} }
if !strings.HasPrefix(envVar, "OP_PROGRAM_") { if !strings.HasPrefix(envVar, "OP_PROGRAM_") {
t.Errorf("Flag %v env var (%v) does not start with OP_PROGRAM_", flag.GetName(), envVar) t.Errorf("Flag %v env var (%v) does not start with OP_PROGRAM_", flag.Names()[0], envVar)
} }
if strings.Contains(envVar, "__") { if strings.Contains(envVar, "__") {
t.Errorf("Flag %v env var (%v) has duplicate underscores", flag.GetName(), envVar) t.Errorf("Flag %v env var (%v) has duplicate underscores", flag.Names()[0], envVar)
} }
} }
} }
func envVarForFlag(flag cli.Flag) string { func envVarForFlag(flag cli.Flag) string {
values := reflect.ValueOf(flag) values := reflect.ValueOf(flag)
envVarValue := values.FieldByName("EnvVar") envVarValue := values.Elem().FieldByName("EnvVars")
if envVarValue == (reflect.Value{}) { if envVarValue == (reflect.Value{}) || envVarValue.Len() == 0 {
return "" return ""
} }
return envVarValue.String() return envVarValue.Index(0).String()
} }
...@@ -6,7 +6,6 @@ import ( ...@@ -6,7 +6,6 @@ import (
"fmt" "fmt"
"io" "io"
"io/fs" "io/fs"
"math"
"os" "os"
"os/exec" "os/exec"
...@@ -22,13 +21,10 @@ import ( ...@@ -22,13 +21,10 @@ import (
"github.com/ethereum-optimism/optimism/op-program/host/prefetcher" "github.com/ethereum-optimism/optimism/op-program/host/prefetcher"
oppio "github.com/ethereum-optimism/optimism/op-program/io" oppio "github.com/ethereum-optimism/optimism/op-program/io"
opservice "github.com/ethereum-optimism/optimism/op-service" opservice "github.com/ethereum-optimism/optimism/op-service"
opclient "github.com/ethereum-optimism/optimism/op-service/client"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
) )
const maxRPCRetries = math.MaxInt
type L2Source struct { type L2Source struct {
*sources.L2Client *sources.L2Client
*sources.DebugClient *sources.DebugClient
...@@ -192,41 +188,31 @@ func PreimageServer(ctx context.Context, logger log.Logger, cfg *config.Config, ...@@ -192,41 +188,31 @@ func PreimageServer(ctx context.Context, logger log.Logger, cfg *config.Config,
func makePrefetcher(ctx context.Context, logger log.Logger, kv kvstore.KV, cfg *config.Config) (*prefetcher.Prefetcher, error) { func makePrefetcher(ctx context.Context, logger log.Logger, kv kvstore.KV, cfg *config.Config) (*prefetcher.Prefetcher, error) {
logger.Info("Connecting to L1 node", "l1", cfg.L1URL) logger.Info("Connecting to L1 node", "l1", cfg.L1URL)
l1RPC, err := createRetryingRPC(ctx, logger, cfg.L1URL) l1RPC, err := client.NewRPC(ctx, logger, cfg.L1URL)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to setup L1 RPC: %w", err) return nil, fmt.Errorf("failed to setup L1 RPC: %w", err)
} }
logger.Info("Connecting to L2 node", "l2", cfg.L2URL) logger.Info("Connecting to L2 node", "l2", cfg.L2URL)
l2RPC, err := createRetryingRPC(ctx, logger, cfg.L2URL) l2RPC, err := client.NewRPC(ctx, logger, cfg.L2URL)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to setup L2 RPC: %w", err) return nil, fmt.Errorf("failed to setup L2 RPC: %w", err)
} }
l1ClCfg := sources.L1ClientDefaultConfig(cfg.Rollup, cfg.L1TrustRPC, cfg.L1RPCKind) l1ClCfg := sources.L1ClientDefaultConfig(cfg.Rollup, cfg.L1TrustRPC, cfg.L1RPCKind)
l2ClCfg := sources.L2ClientDefaultConfig(cfg.Rollup, true)
l1Cl, err := sources.NewL1Client(l1RPC, logger, nil, l1ClCfg) l1Cl, err := sources.NewL1Client(l1RPC, logger, nil, l1ClCfg)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to create L1 client: %w", err) return nil, fmt.Errorf("failed to create L1 client: %w", err)
} }
l2ClCfg := sources.L2ClientDefaultConfig(cfg.Rollup, true)
l2Cl, err := sources.NewL2Client(l2RPC, logger, nil, l2ClCfg) l2Cl, err := sources.NewL2Client(l2RPC, logger, nil, l2ClCfg)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to create L2 client: %w", err) return nil, fmt.Errorf("failed to create L2 client: %w", err)
} }
l2DebugCl := &L2Source{L2Client: l2Cl, DebugClient: sources.NewDebugClient(l2RPC.CallContext)} l2DebugCl := &L2Source{L2Client: l2Cl, DebugClient: sources.NewDebugClient(l2RPC.CallContext)}
return prefetcher.NewPrefetcher(logger, l1Cl, l2DebugCl, kv), nil return prefetcher.NewPrefetcher(logger, l1Cl, l2DebugCl, kv), nil
} }
func createRetryingRPC(ctx context.Context, logger log.Logger, url string) (client.RPC, error) {
rpc, err := client.NewRPC(ctx, logger, url)
if err != nil {
return nil, err
}
return opclient.NewRetryingClient(logger, rpc, maxRPCRetries), nil
}
func routeHints(logger log.Logger, hHostRW io.ReadWriter, hinter preimage.HintHandler) chan error { func routeHints(logger log.Logger, hHostRW io.ReadWriter, hinter preimage.HintHandler) chan error {
chErr := make(chan error) chErr := make(chan error)
hintReader := preimage.NewHintReader(hHostRW) hintReader := preimage.NewHintReader(hHostRW)
......
...@@ -42,8 +42,8 @@ type Prefetcher struct { ...@@ -42,8 +42,8 @@ type Prefetcher struct {
func NewPrefetcher(logger log.Logger, l1Fetcher L1Source, l2Fetcher L2Source, kvStore kvstore.KV) *Prefetcher { func NewPrefetcher(logger log.Logger, l1Fetcher L1Source, l2Fetcher L2Source, kvStore kvstore.KV) *Prefetcher {
return &Prefetcher{ return &Prefetcher{
logger: logger, logger: logger,
l1Fetcher: l1Fetcher, l1Fetcher: NewRetryingL1Source(logger, l1Fetcher),
l2Fetcher: l2Fetcher, l2Fetcher: NewRetryingL2Source(logger, l2Fetcher),
kvStore: kvStore, kvStore: kvStore,
} }
} }
......
package prefetcher
import (
"context"
"math"
"github.com/ethereum-optimism/optimism/op-node/eth"
"github.com/ethereum-optimism/optimism/op-service/backoff"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
)
const maxAttempts = math.MaxInt // Succeed or die trying
type RetryingL1Source struct {
logger log.Logger
source L1Source
strategy backoff.Strategy
}
func NewRetryingL1Source(logger log.Logger, source L1Source) *RetryingL1Source {
return &RetryingL1Source{
logger: logger,
source: source,
strategy: backoff.Exponential(),
}
}
func (s *RetryingL1Source) InfoByHash(ctx context.Context, blockHash common.Hash) (eth.BlockInfo, error) {
var info eth.BlockInfo
err := backoff.DoCtx(ctx, maxAttempts, s.strategy, func() error {
res, err := s.source.InfoByHash(ctx, blockHash)
if err != nil {
s.logger.Warn("Failed to retrieve info", "hash", blockHash, "err", err)
return err
}
info = res
return nil
})
return info, err
}
func (s *RetryingL1Source) InfoAndTxsByHash(ctx context.Context, blockHash common.Hash) (eth.BlockInfo, types.Transactions, error) {
var info eth.BlockInfo
var txs types.Transactions
err := backoff.DoCtx(ctx, maxAttempts, s.strategy, func() error {
i, t, err := s.source.InfoAndTxsByHash(ctx, blockHash)
if err != nil {
s.logger.Warn("Failed to retrieve l1 info and txs", "hash", blockHash, "err", err)
return err
}
info = i
txs = t
return nil
})
return info, txs, err
}
func (s *RetryingL1Source) FetchReceipts(ctx context.Context, blockHash common.Hash) (eth.BlockInfo, types.Receipts, error) {
var info eth.BlockInfo
var rcpts types.Receipts
err := backoff.DoCtx(ctx, maxAttempts, s.strategy, func() error {
i, r, err := s.source.FetchReceipts(ctx, blockHash)
if err != nil {
s.logger.Warn("Failed to fetch receipts", "hash", blockHash, "err", err)
return err
}
info = i
rcpts = r
return nil
})
return info, rcpts, err
}
var _ L1Source = (*RetryingL1Source)(nil)
type RetryingL2Source struct {
logger log.Logger
source L2Source
strategy backoff.Strategy
}
func (s *RetryingL2Source) InfoAndTxsByHash(ctx context.Context, blockHash common.Hash) (eth.BlockInfo, types.Transactions, error) {
var info eth.BlockInfo
var txs types.Transactions
err := backoff.DoCtx(ctx, maxAttempts, s.strategy, func() error {
i, t, err := s.source.InfoAndTxsByHash(ctx, blockHash)
if err != nil {
s.logger.Warn("Failed to retrieve l2 info and txs", "hash", blockHash, "err", err)
return err
}
info = i
txs = t
return nil
})
return info, txs, err
}
func (s *RetryingL2Source) NodeByHash(ctx context.Context, hash common.Hash) ([]byte, error) {
var node []byte
err := backoff.DoCtx(ctx, maxAttempts, s.strategy, func() error {
n, err := s.source.NodeByHash(ctx, hash)
if err != nil {
s.logger.Warn("Failed to retrieve node", "hash", hash, "err", err)
return err
}
node = n
return nil
})
return node, err
}
func (s *RetryingL2Source) CodeByHash(ctx context.Context, hash common.Hash) ([]byte, error) {
var code []byte
err := backoff.DoCtx(ctx, maxAttempts, s.strategy, func() error {
c, err := s.source.CodeByHash(ctx, hash)
if err != nil {
s.logger.Warn("Failed to retrieve code", "hash", hash, "err", err)
return err
}
code = c
return nil
})
return code, err
}
func NewRetryingL2Source(logger log.Logger, source L2Source) *RetryingL2Source {
return &RetryingL2Source{
logger: logger,
source: source,
strategy: backoff.Exponential(),
}
}
var _ L2Source = (*RetryingL2Source)(nil)
package prefetcher
import (
"context"
"errors"
"testing"
"github.com/ethereum-optimism/optimism/op-node/eth"
"github.com/ethereum-optimism/optimism/op-node/testlog"
"github.com/ethereum-optimism/optimism/op-node/testutils"
"github.com/ethereum-optimism/optimism/op-service/backoff"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
)
func TestRetryingL1Source(t *testing.T) {
ctx := context.Background()
hash := common.Hash{0xab}
info := &testutils.MockBlockInfo{InfoHash: hash}
// The mock really doesn't like returning nil for a eth.BlockInfo so return a value we expect to be ignored instead
wrongInfo := &testutils.MockBlockInfo{InfoHash: common.Hash{0x99}}
txs := types.Transactions{
&types.Transaction{},
}
rcpts := types.Receipts{
&types.Receipt{},
}
t.Run("InfoByHash Success", func(t *testing.T) {
source, mock := createL1Source(t)
defer mock.AssertExpectations(t)
mock.ExpectInfoByHash(hash, info, nil)
result, err := source.InfoByHash(ctx, hash)
require.NoError(t, err)
require.Equal(t, info, result)
})
t.Run("InfoByHash Error", func(t *testing.T) {
source, mock := createL1Source(t)
defer mock.AssertExpectations(t)
expectedErr := errors.New("boom")
mock.ExpectInfoByHash(hash, wrongInfo, expectedErr)
mock.ExpectInfoByHash(hash, info, nil)
result, err := source.InfoByHash(ctx, hash)
require.NoError(t, err)
require.Equal(t, info, result)
})
t.Run("InfoAndTxsByHash Success", func(t *testing.T) {
source, mock := createL1Source(t)
defer mock.AssertExpectations(t)
mock.ExpectInfoAndTxsByHash(hash, info, txs, nil)
actualInfo, actualTxs, err := source.InfoAndTxsByHash(ctx, hash)
require.NoError(t, err)
require.Equal(t, info, actualInfo)
require.Equal(t, txs, actualTxs)
})
t.Run("InfoAndTxsByHash Error", func(t *testing.T) {
source, mock := createL1Source(t)
defer mock.AssertExpectations(t)
expectedErr := errors.New("boom")
mock.ExpectInfoAndTxsByHash(hash, wrongInfo, nil, expectedErr)
mock.ExpectInfoAndTxsByHash(hash, info, txs, nil)
actualInfo, actualTxs, err := source.InfoAndTxsByHash(ctx, hash)
require.NoError(t, err)
require.Equal(t, info, actualInfo)
require.Equal(t, txs, actualTxs)
})
t.Run("FetchReceipts Success", func(t *testing.T) {
source, mock := createL1Source(t)
defer mock.AssertExpectations(t)
mock.ExpectFetchReceipts(hash, info, rcpts, nil)
actualInfo, actualRcpts, err := source.FetchReceipts(ctx, hash)
require.NoError(t, err)
require.Equal(t, info, actualInfo)
require.Equal(t, rcpts, actualRcpts)
})
t.Run("FetchReceipts Error", func(t *testing.T) {
source, mock := createL1Source(t)
defer mock.AssertExpectations(t)
expectedErr := errors.New("boom")
mock.ExpectFetchReceipts(hash, wrongInfo, nil, expectedErr)
mock.ExpectFetchReceipts(hash, info, rcpts, nil)
actualInfo, actualRcpts, err := source.FetchReceipts(ctx, hash)
require.NoError(t, err)
require.Equal(t, info, actualInfo)
require.Equal(t, rcpts, actualRcpts)
})
}
func createL1Source(t *testing.T) (*RetryingL1Source, *testutils.MockL1Source) {
logger := testlog.Logger(t, log.LvlDebug)
mock := &testutils.MockL1Source{}
source := NewRetryingL1Source(logger, mock)
// Avoid sleeping in tests by using a fixed backoff strategy with no delay
source.strategy = backoff.Fixed(0)
return source, mock
}
func TestRetryingL2Source(t *testing.T) {
ctx := context.Background()
hash := common.Hash{0xab}
info := &testutils.MockBlockInfo{InfoHash: hash}
// The mock really doesn't like returning nil for a eth.BlockInfo so return a value we expect to be ignored instead
wrongInfo := &testutils.MockBlockInfo{InfoHash: common.Hash{0x99}}
txs := types.Transactions{
&types.Transaction{},
}
data := []byte{1, 2, 3, 4, 5}
t.Run("InfoAndTxsByHash Success", func(t *testing.T) {
source, mock := createL2Source(t)
defer mock.AssertExpectations(t)
mock.ExpectInfoAndTxsByHash(hash, info, txs, nil)
actualInfo, actualTxs, err := source.InfoAndTxsByHash(ctx, hash)
require.NoError(t, err)
require.Equal(t, info, actualInfo)
require.Equal(t, txs, actualTxs)
})
t.Run("InfoAndTxsByHash Error", func(t *testing.T) {
source, mock := createL2Source(t)
defer mock.AssertExpectations(t)
expectedErr := errors.New("boom")
mock.ExpectInfoAndTxsByHash(hash, wrongInfo, nil, expectedErr)
mock.ExpectInfoAndTxsByHash(hash, info, txs, nil)
actualInfo, actualTxs, err := source.InfoAndTxsByHash(ctx, hash)
require.NoError(t, err)
require.Equal(t, info, actualInfo)
require.Equal(t, txs, actualTxs)
})
t.Run("NodeByHash Success", func(t *testing.T) {
source, mock := createL2Source(t)
defer mock.AssertExpectations(t)
mock.ExpectNodeByHash(hash, data, nil)
actual, err := source.NodeByHash(ctx, hash)
require.NoError(t, err)
require.Equal(t, data, actual)
})
t.Run("NodeByHash Error", func(t *testing.T) {
source, mock := createL2Source(t)
defer mock.AssertExpectations(t)
expectedErr := errors.New("boom")
mock.ExpectNodeByHash(hash, nil, expectedErr)
mock.ExpectNodeByHash(hash, data, nil)
actual, err := source.NodeByHash(ctx, hash)
require.NoError(t, err)
require.Equal(t, data, actual)
})
t.Run("CodeByHash Success", func(t *testing.T) {
source, mock := createL2Source(t)
defer mock.AssertExpectations(t)
mock.ExpectCodeByHash(hash, data, nil)
actual, err := source.CodeByHash(ctx, hash)
require.NoError(t, err)
require.Equal(t, data, actual)
})
t.Run("CodeByHash Error", func(t *testing.T) {
source, mock := createL2Source(t)
defer mock.AssertExpectations(t)
expectedErr := errors.New("boom")
mock.ExpectCodeByHash(hash, nil, expectedErr)
mock.ExpectCodeByHash(hash, data, nil)
actual, err := source.CodeByHash(ctx, hash)
require.NoError(t, err)
require.Equal(t, data, actual)
})
}
func createL2Source(t *testing.T) (*RetryingL2Source, *MockL2Source) {
logger := testlog.Logger(t, log.LvlDebug)
mock := &MockL2Source{}
source := NewRetryingL2Source(logger, mock)
// Avoid sleeping in tests by using a fixed backoff strategy with no delay
source.strategy = backoff.Fixed(0)
return source, mock
}
type MockL2Source struct {
mock.Mock
}
func (m *MockL2Source) InfoAndTxsByHash(ctx context.Context, blockHash common.Hash) (eth.BlockInfo, types.Transactions, error) {
out := m.Mock.MethodCalled("InfoAndTxsByHash", blockHash)
return out[0].(eth.BlockInfo), out[1].(types.Transactions), *out[2].(*error)
}
func (m *MockL2Source) NodeByHash(ctx context.Context, hash common.Hash) ([]byte, error) {
out := m.Mock.MethodCalled("NodeByHash", hash)
return out[0].([]byte), *out[1].(*error)
}
func (m *MockL2Source) CodeByHash(ctx context.Context, hash common.Hash) ([]byte, error) {
out := m.Mock.MethodCalled("CodeByHash", hash)
return out[0].([]byte), *out[1].(*error)
}
func (m *MockL2Source) ExpectInfoAndTxsByHash(blockHash common.Hash, info eth.BlockInfo, txs types.Transactions, err error) {
m.Mock.On("InfoAndTxsByHash", blockHash).Once().Return(info, txs, &err)
}
func (m *MockL2Source) ExpectNodeByHash(hash common.Hash, node []byte, err error) {
m.Mock.On("NodeByHash", hash).Once().Return(node, &err)
}
func (m *MockL2Source) ExpectCodeByHash(hash common.Hash, code []byte, err error) {
m.Mock.On("CodeByHash", hash).Once().Return(code, &err)
}
var _ L2Source = (*MockL2Source)(nil)
...@@ -8,7 +8,7 @@ import ( ...@@ -8,7 +8,7 @@ import (
"github.com/ethereum-optimism/optimism/op-proposer/metrics" "github.com/ethereum-optimism/optimism/op-proposer/metrics"
"github.com/olekukonko/tablewriter" "github.com/olekukonko/tablewriter"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
) )
var Subcommands = cli.Commands{ var Subcommands = cli.Commands{
...@@ -16,7 +16,7 @@ var Subcommands = cli.Commands{ ...@@ -16,7 +16,7 @@ var Subcommands = cli.Commands{
Name: "metrics", Name: "metrics",
Usage: "Dumps a list of supported metrics to stdout", Usage: "Dumps a list of supported metrics to stdout",
Flags: []cli.Flag{ Flags: []cli.Flag{
cli.StringFlag{ &cli.StringFlag{
Name: "format", Name: "format",
Value: "markdown", Value: "markdown",
Usage: "Output format (json|markdown)", Usage: "Output format (json|markdown)",
......
...@@ -4,7 +4,7 @@ import ( ...@@ -4,7 +4,7 @@ import (
"fmt" "fmt"
"os" "os"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
"github.com/ethereum-optimism/optimism/op-proposer/cmd/doc" "github.com/ethereum-optimism/optimism/op-proposer/cmd/doc"
"github.com/ethereum-optimism/optimism/op-proposer/flags" "github.com/ethereum-optimism/optimism/op-proposer/flags"
...@@ -29,7 +29,7 @@ func main() { ...@@ -29,7 +29,7 @@ func main() {
app.Usage = "L2Output Submitter" app.Usage = "L2Output Submitter"
app.Description = "Service for generating and submitting L2 Output checkpoints to the L2OutputOracle contract" app.Description = "Service for generating and submitting L2 Output checkpoints to the L2OutputOracle contract"
app.Action = curryMain(Version) app.Action = curryMain(Version)
app.Commands = []cli.Command{ app.Commands = []*cli.Command{
{ {
Name: "doc", Name: "doc",
Subcommands: doc.Subcommands, Subcommands: doc.Subcommands,
......
...@@ -4,7 +4,7 @@ import ( ...@@ -4,7 +4,7 @@ import (
"fmt" "fmt"
"time" "time"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
opservice "github.com/ethereum-optimism/optimism/op-service" opservice "github.com/ethereum-optimism/optimism/op-service"
oplog "github.com/ethereum-optimism/optimism/op-service/log" oplog "github.com/ethereum-optimism/optimism/op-service/log"
...@@ -16,35 +16,39 @@ import ( ...@@ -16,35 +16,39 @@ import (
const EnvVarPrefix = "OP_PROPOSER" const EnvVarPrefix = "OP_PROPOSER"
func prefixEnvVars(name string) []string {
return opservice.PrefixEnvVar(EnvVarPrefix, name)
}
var ( var (
// Required Flags // Required Flags
L1EthRpcFlag = cli.StringFlag{ L1EthRpcFlag = &cli.StringFlag{
Name: "l1-eth-rpc", Name: "l1-eth-rpc",
Usage: "HTTP provider URL for L1", Usage: "HTTP provider URL for L1",
EnvVar: opservice.PrefixEnvVar(EnvVarPrefix, "L1_ETH_RPC"), EnvVars: prefixEnvVars("L1_ETH_RPC"),
} }
RollupRpcFlag = cli.StringFlag{ RollupRpcFlag = &cli.StringFlag{
Name: "rollup-rpc", Name: "rollup-rpc",
Usage: "HTTP provider URL for the rollup node", Usage: "HTTP provider URL for the rollup node",
EnvVar: opservice.PrefixEnvVar(EnvVarPrefix, "ROLLUP_RPC"), EnvVars: prefixEnvVars("ROLLUP_RPC"),
} }
L2OOAddressFlag = cli.StringFlag{ L2OOAddressFlag = &cli.StringFlag{
Name: "l2oo-address", Name: "l2oo-address",
Usage: "Address of the L2OutputOracle contract", Usage: "Address of the L2OutputOracle contract",
EnvVar: opservice.PrefixEnvVar(EnvVarPrefix, "L2OO_ADDRESS"), EnvVars: prefixEnvVars("L2OO_ADDRESS"),
} }
// Optional flags // Optional flags
PollIntervalFlag = cli.DurationFlag{ PollIntervalFlag = &cli.DurationFlag{
Name: "poll-interval", Name: "poll-interval",
Usage: "How frequently to poll L2 for new blocks", Usage: "How frequently to poll L2 for new blocks",
Value: 6 * time.Second, Value: 6 * time.Second,
EnvVar: opservice.PrefixEnvVar(EnvVarPrefix, "POLL_INTERVAL"), EnvVars: prefixEnvVars("POLL_INTERVAL"),
} }
AllowNonFinalizedFlag = cli.BoolFlag{ AllowNonFinalizedFlag = &cli.BoolFlag{
Name: "allow-non-finalized", Name: "allow-non-finalized",
Usage: "Allow the proposer to submit proposals for L2 blocks derived from non-finalized L1 blocks.", Usage: "Allow the proposer to submit proposals for L2 blocks derived from non-finalized L1 blocks.",
EnvVar: opservice.PrefixEnvVar(EnvVarPrefix, "ALLOW_NON_FINALIZED"), EnvVars: prefixEnvVars("ALLOW_NON_FINALIZED"),
} }
// Legacy Flags // Legacy Flags
L2OutputHDPathFlag = txmgr.L2OutputHDPathFlag L2OutputHDPathFlag = txmgr.L2OutputHDPathFlag
...@@ -77,8 +81,8 @@ var Flags []cli.Flag ...@@ -77,8 +81,8 @@ var Flags []cli.Flag
func CheckRequired(ctx *cli.Context) error { func CheckRequired(ctx *cli.Context) error {
for _, f := range requiredFlags { for _, f := range requiredFlags {
if !ctx.GlobalIsSet(f.GetName()) { if !ctx.IsSet(f.Names()[0]) {
return fmt.Errorf("flag %s is required", f.GetName()) return fmt.Errorf("flag %s is required", f.Names()[0])
} }
} }
return nil return nil
......
...@@ -5,7 +5,7 @@ import ( ...@@ -5,7 +5,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/ethclient"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
"github.com/ethereum-optimism/optimism/op-node/sources" "github.com/ethereum-optimism/optimism/op-node/sources"
"github.com/ethereum-optimism/optimism/op-proposer/flags" "github.com/ethereum-optimism/optimism/op-proposer/flags"
...@@ -86,13 +86,13 @@ func (c CLIConfig) Check() error { ...@@ -86,13 +86,13 @@ func (c CLIConfig) Check() error {
func NewConfig(ctx *cli.Context) CLIConfig { func NewConfig(ctx *cli.Context) CLIConfig {
return CLIConfig{ return CLIConfig{
// Required Flags // Required Flags
L1EthRpc: ctx.GlobalString(flags.L1EthRpcFlag.Name), L1EthRpc: ctx.String(flags.L1EthRpcFlag.Name),
RollupRpc: ctx.GlobalString(flags.RollupRpcFlag.Name), RollupRpc: ctx.String(flags.RollupRpcFlag.Name),
L2OOAddress: ctx.GlobalString(flags.L2OOAddressFlag.Name), L2OOAddress: ctx.String(flags.L2OOAddressFlag.Name),
PollInterval: ctx.GlobalDuration(flags.PollIntervalFlag.Name), PollInterval: ctx.Duration(flags.PollIntervalFlag.Name),
TxMgrConfig: txmgr.ReadCLIConfig(ctx), TxMgrConfig: txmgr.ReadCLIConfig(ctx),
// Optional Flags // Optional Flags
AllowNonFinalized: ctx.GlobalBool(flags.AllowNonFinalizedFlag.Name), AllowNonFinalized: ctx.Bool(flags.AllowNonFinalizedFlag.Name),
RPCConfig: oprpc.ReadCLIConfig(ctx), RPCConfig: oprpc.ReadCLIConfig(ctx),
LogConfig: oplog.ReadCLIConfig(ctx), LogConfig: oplog.ReadCLIConfig(ctx),
MetricsConfig: opmetrics.ReadCLIConfig(ctx), MetricsConfig: opmetrics.ReadCLIConfig(ctx),
......
...@@ -14,7 +14,7 @@ import ( ...@@ -14,7 +14,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
"github.com/ethereum-optimism/optimism/op-bindings/bindings" "github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-node/eth" "github.com/ethereum-optimism/optimism/op-node/eth"
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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