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

Merge branch 'develop' into aj/op-service-lint

parents a71d5853 4741dee0
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
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
FROM alpine:3.15
FROM alpine:3.16
RUN apk --no-cache add ca-certificates
RUN addgroup -S app && adduser -S app -G app
USER 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"]
......@@ -4,7 +4,7 @@ The endpoint-monitor runs websocket checks on edge-proxyd endpoints and downstre
## Setup
Install go1.18
Install go1.19
```bash
make build
......
......@@ -6,7 +6,7 @@ import (
oplog "github.com/ethereum-optimism/optimism/op-service/log"
"github.com/ethereum/go-ethereum/log"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
endpointMonitor "github.com/ethereum-optimism/optimism/endpoint-monitor"
)
......
......@@ -9,7 +9,7 @@ import (
opservice "github.com/ethereum-optimism/optimism/op-service"
oplog "github.com/ethereum-optimism/optimism/op-service/log"
opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
type ProviderConfig struct {
......@@ -24,24 +24,27 @@ const (
)
func CLIFlags(envPrefix string) []cli.Flag {
prefixEnvVars := func(name string) []string {
return opservice.PrefixEnvVar(envPrefix, name)
}
flags := []cli.Flag{
cli.StringSliceFlag{
&cli.StringSliceFlag{
Name: ProvidersFlagName,
Usage: "List of providers",
Required: true,
EnvVar: opservice.PrefixEnvVar(envPrefix, "PROVIDERS"),
EnvVars: prefixEnvVars("PROVIDERS"),
},
cli.DurationFlag{
&cli.DurationFlag{
Name: CheckIntervalFlagName,
Usage: "Check interval duration",
Value: 5 * time.Minute,
EnvVar: opservice.PrefixEnvVar(envPrefix, "CHECK_INTERVAL"),
EnvVars: prefixEnvVars("CHECK_INTERVAL"),
},
cli.DurationFlag{
&cli.DurationFlag{
Name: CheckDurationFlagName,
Usage: "Check duration",
Value: 4 * time.Minute,
EnvVar: opservice.PrefixEnvVar(envPrefix, "CHECK_DURATION"),
EnvVars: prefixEnvVars("CHECK_DURATION"),
},
}
flags = append(flags, opmetrics.CLIFlags(envPrefix)...)
......@@ -73,9 +76,9 @@ func (c Config) Check() error {
func NewConfig(ctx *cli.Context) Config {
return Config{
Providers: ctx.GlobalStringSlice(ProvidersFlagName),
CheckInterval: ctx.GlobalDuration(CheckIntervalFlagName),
CheckDuration: ctx.GlobalDuration(CheckDurationFlagName),
Providers: ctx.StringSlice(ProvidersFlagName),
CheckInterval: ctx.Duration(CheckIntervalFlagName),
CheckDuration: ctx.Duration(CheckDurationFlagName),
LogConfig: oplog.ReadCLIConfig(ctx),
MetricsConfig: opmetrics.ReadCLIConfig(ctx),
}
......
......@@ -11,10 +11,10 @@ import (
"github.com/ethereum/go-ethereum/log"
"github.com/pkg/errors"
"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-optimism/optimism/l2geth/ethclient"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethclient"
)
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 (
github.com/multiformats/go-multiaddr v0.8.0
github.com/multiformats/go-multiaddr-dns v0.3.1
github.com/olekukonko/tablewriter v0.0.5
github.com/pkg/errors v0.9.1
github.com/pkg/profile v1.7.0
github.com/prometheus/client_golang v1.14.0
github.com/stretchr/testify v1.8.1
github.com/urfave/cli v1.22.9
github.com/urfave/cli/v2 v2.17.2-0.20221006022127-8f469abc00aa
github.com/urfave/cli/v2 v2.25.7
golang.org/x/crypto v0.6.0
golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb
golang.org/x/sync v0.1.0
......@@ -134,7 +134,6 @@ require (
github.com/opentracing/opentracing-go v1.2.0 // indirect
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // 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/prometheus/client_model v0.3.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
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/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/jet/v3 v3.0.0/go.mod h1:HKQPgSJmdK8hdoAbKUUWajkHyHo4RaU5rMdUywE7VMo=
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
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/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/urfave/cli v1.22.9 h1:cv3/KhXGBGjEXLC4bH0sLuJ9BewaAbpk5oyMOveu4pw=
github.com/urfave/cli v1.22.9/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
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/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs=
github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
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/fasthttp v1.6.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w=
......
......@@ -6,7 +6,7 @@ import (
_ "net/http/pprof"
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/metrics"
......
......@@ -5,7 +5,7 @@ import (
"github.com/ethereum/go-ethereum/ethclient"
"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/flags"
......@@ -118,17 +118,17 @@ func (c CLIConfig) Check() error {
func NewConfig(ctx *cli.Context) CLIConfig {
return CLIConfig{
/* Required Flags */
L1EthRpc: ctx.GlobalString(flags.L1EthRpcFlag.Name),
L2EthRpc: ctx.GlobalString(flags.L2EthRpcFlag.Name),
RollupRpc: ctx.GlobalString(flags.RollupRpcFlag.Name),
SubSafetyMargin: ctx.GlobalUint64(flags.SubSafetyMarginFlag.Name),
PollInterval: ctx.GlobalDuration(flags.PollIntervalFlag.Name),
L1EthRpc: ctx.String(flags.L1EthRpcFlag.Name),
L2EthRpc: ctx.String(flags.L2EthRpcFlag.Name),
RollupRpc: ctx.String(flags.RollupRpcFlag.Name),
SubSafetyMargin: ctx.Uint64(flags.SubSafetyMarginFlag.Name),
PollInterval: ctx.Duration(flags.PollIntervalFlag.Name),
/* Optional Flags */
MaxPendingTransactions: ctx.GlobalUint64(flags.MaxPendingTransactionsFlag.Name),
MaxChannelDuration: ctx.GlobalUint64(flags.MaxChannelDurationFlag.Name),
MaxL1TxSize: ctx.GlobalUint64(flags.MaxL1TxSizeBytesFlag.Name),
Stopped: ctx.GlobalBool(flags.StoppedFlag.Name),
MaxPendingTransactions: ctx.Uint64(flags.MaxPendingTransactionsFlag.Name),
MaxChannelDuration: ctx.Uint64(flags.MaxChannelDurationFlag.Name),
MaxL1TxSize: ctx.Uint64(flags.MaxL1TxSizeBytesFlag.Name),
Stopped: ctx.Bool(flags.StoppedFlag.Name),
TxMgrConfig: txmgr.ReadCLIConfig(ctx),
RPCConfig: rpc.ReadCLIConfig(ctx),
LogConfig: oplog.ReadCLIConfig(ctx),
......
......@@ -8,7 +8,7 @@ import (
"github.com/ethereum-optimism/optimism/op-batcher/metrics"
"github.com/olekukonko/tablewriter"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
var Subcommands = cli.Commands{
......@@ -16,7 +16,7 @@ var Subcommands = cli.Commands{
Name: "metrics",
Usage: "Dumps a list of supported metrics to stdout",
Flags: []cli.Flag{
cli.StringFlag{
&cli.StringFlag{
Name: "format",
Value: "markdown",
Usage: "Output format (json|markdown)",
......
......@@ -4,7 +4,7 @@ import (
"fmt"
"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/cmd/doc"
......@@ -29,7 +29,7 @@ func main() {
app.Usage = "Batch Submitter Service"
app.Description = "Service for generating and submitting L2 tx batches to L1"
app.Action = curryMain(Version)
app.Commands = []cli.Command{
app.Commands = []*cli.Command{
{
Name: "doc",
Subcommands: doc.Subcommands,
......
......@@ -4,7 +4,7 @@ import (
"strings"
opservice "github.com/ethereum-optimism/optimism/op-service"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
const (
......@@ -16,28 +16,28 @@ const (
func CLIFlags(envPrefix string) []cli.Flag {
return []cli.Flag{
cli.Uint64Flag{
&cli.Uint64Flag{
Name: TargetL1TxSizeBytesFlagName,
Usage: "The target size of a batch tx submitted to L1.",
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,
Usage: "The target number of frames to create per channel",
Value: 1,
EnvVar: opservice.PrefixEnvVar(envPrefix, "TARGET_NUM_FRAMES"),
EnvVars: opservice.PrefixEnvVar(envPrefix, "TARGET_NUM_FRAMES"),
},
cli.Float64Flag{
&cli.Float64Flag{
Name: ApproxComprRatioFlagName,
Usage: "The approximate compression ratio (<= 1.0)",
Value: 0.4,
EnvVar: opservice.PrefixEnvVar(envPrefix, "APPROX_COMPR_RATIO"),
EnvVars: opservice.PrefixEnvVar(envPrefix, "APPROX_COMPR_RATIO"),
},
cli.StringFlag{
&cli.StringFlag{
Name: KindFlagName,
Usage: "The type of compressor. Valid options: " + strings.Join(KindKeys, ", "),
EnvVar: opservice.PrefixEnvVar(envPrefix, "COMPRESSOR"),
EnvVars: opservice.PrefixEnvVar(envPrefix, "COMPRESSOR"),
Value: RatioKind,
},
}
......@@ -70,9 +70,9 @@ func (c *CLIConfig) Config() Config {
func ReadCLIConfig(ctx *cli.Context) CLIConfig {
return CLIConfig{
Kind: ctx.GlobalString(KindFlagName),
TargetL1TxSizeBytes: ctx.GlobalUint64(TargetL1TxSizeBytesFlagName),
TargetNumFrames: ctx.GlobalInt(TargetNumFramesFlagName),
ApproxComprRatio: ctx.GlobalFloat64(ApproxComprRatioFlagName),
Kind: ctx.String(KindFlagName),
TargetL1TxSizeBytes: ctx.Uint64(TargetL1TxSizeBytesFlagName),
TargetNumFrames: ctx.Int(TargetNumFramesFlagName),
ApproxComprRatio: ctx.Float64(ApproxComprRatioFlagName),
}
}
......@@ -4,7 +4,7 @@ import (
"fmt"
"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/rpc"
......@@ -18,60 +18,64 @@ import (
const EnvVarPrefix = "OP_BATCHER"
func prefixEnvVars(name string) []string {
return opservice.PrefixEnvVar(EnvVarPrefix, name)
}
var (
// Required flags
L1EthRpcFlag = cli.StringFlag{
L1EthRpcFlag = &cli.StringFlag{
Name: "l1-eth-rpc",
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",
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",
Usage: "HTTP provider URL for Rollup node",
EnvVar: opservice.PrefixEnvVar(EnvVarPrefix, "ROLLUP_RPC"),
EnvVars: prefixEnvVars("ROLLUP_RPC"),
}
// Optional flags
SubSafetyMarginFlag = cli.Uint64Flag{
SubSafetyMarginFlag = &cli.Uint64Flag{
Name: "sub-safety-margin",
Usage: "The batcher tx submission safety margin (in #L1-blocks) to subtract " +
"from a channel's timeout and sequencing window, to guarantee safe inclusion " +
"of a channel on L1.",
Value: 10,
EnvVar: opservice.PrefixEnvVar(EnvVarPrefix, "SUB_SAFETY_MARGIN"),
EnvVars: prefixEnvVars("SUB_SAFETY_MARGIN"),
}
PollIntervalFlag = cli.DurationFlag{
PollIntervalFlag = &cli.DurationFlag{
Name: "poll-interval",
Usage: "How frequently to poll L2 for new blocks",
Value: 6 * time.Second,
EnvVar: opservice.PrefixEnvVar(EnvVarPrefix, "POLL_INTERVAL"),
EnvVars: prefixEnvVars("POLL_INTERVAL"),
}
MaxPendingTransactionsFlag = cli.Uint64Flag{
MaxPendingTransactionsFlag = &cli.Uint64Flag{
Name: "max-pending-tx",
Usage: "The maximum number of pending transactions. 0 for no limit.",
Value: 1,
EnvVar: opservice.PrefixEnvVar(EnvVarPrefix, "MAX_PENDING_TX"),
EnvVars: prefixEnvVars("MAX_PENDING_TX"),
}
MaxChannelDurationFlag = cli.Uint64Flag{
MaxChannelDurationFlag = &cli.Uint64Flag{
Name: "max-channel-duration",
Usage: "The maximum duration of L1-blocks to keep a channel open. 0 to disable.",
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",
Usage: "The maximum size of a batch tx submitted to L1.",
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",
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
SequencerHDPathFlag = txmgr.SequencerHDPathFlag
......@@ -110,8 +114,8 @@ var Flags []cli.Flag
func CheckRequired(ctx *cli.Context) error {
for _, f := range requiredFlags {
if !ctx.GlobalIsSet(f.GetName()) {
return fmt.Errorf("flag %s is required", f.GetName())
if !ctx.IsSet(f.Names()[0]) {
return fmt.Errorf("flag %s is required", f.Names()[0])
}
}
return nil
......
package rpc
import (
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
opservice "github.com/ethereum-optimism/optimism/op-service"
oprpc "github.com/ethereum-optimism/optimism/op-service/rpc"
......@@ -13,10 +13,10 @@ const (
func CLIFlags(envPrefix string) []cli.Flag {
return []cli.Flag{
cli.BoolFlag{
&cli.BoolFlag{
Name: EnableAdminFlagName,
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 {
func ReadCLIConfig(ctx *cli.Context) CLIConfig {
return CLIConfig{
CLIConfig: oprpc.ReadCLIConfig(ctx),
EnableAdmin: ctx.GlobalBool(EnableAdminFlagName),
EnableAdmin: ctx.Bool(EnableAdminFlagName),
}
}
......@@ -16,7 +16,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
type gossipNoop struct{}
......
......@@ -6,7 +6,7 @@ import (
"github.com/ethereum-optimism/optimism/op-bootnode/bootnode"
"github.com/ethereum-optimism/optimism/op-bootnode/flags"
"github.com/ethereum/go-ethereum/log"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
func main() {
......
......@@ -8,21 +8,25 @@ import (
"github.com/ethereum-optimism/optimism/op-node/flags"
opservice "github.com/ethereum-optimism/optimism/op-service"
oplog "github.com/ethereum-optimism/optimism/op-service/log"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
const envVarPrefix = "OP_BOOTNODE"
func prefixEnvVars(name string) []string {
return opservice.PrefixEnvVar(envVarPrefix, name)
}
var (
RollupConfig = cli.StringFlag{
RollupConfig = &cli.StringFlag{
Name: flags.RollupConfig.Name,
Usage: "Rollup chain parameters",
EnvVar: opservice.PrefixEnvVar(envVarPrefix, "ROLLUP_CONFIG"),
EnvVars: prefixEnvVars("ROLLUP_CONFIG"),
}
Network = cli.StringFlag{
Network = &cli.StringFlag{
Name: flags.Network.Name,
Usage: fmt.Sprintf("Predefined network selection. Available networks: %s", strings.Join(chaincfg.AvailableNetworks(), ", ")),
EnvVar: opservice.PrefixEnvVar(envVarPrefix, "NETWORK"),
EnvVars: prefixEnvVars("NETWORK"),
}
)
......
......@@ -244,6 +244,11 @@ func BuildL1DeveloperGenesis(config *DeployConfig) (*core.Genesis, error) {
return nil, err
}
if config.FundDevAccounts {
FundDevAccounts(stateDB)
SetPrecompileBalances(stateDB)
}
for _, dep := range deployments {
st, err := stateDB.StorageTrie(dep.Address)
if err != nil {
......
......@@ -4,7 +4,7 @@ import (
"os"
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"
config "github.com/ethereum-optimism/optimism/op-challenger/config"
......@@ -70,7 +70,7 @@ func run(args []string, action ConfigAction) error {
}
return action(logger, VersionWithMeta, cfg)
}
app.Commands = []cli.Command{
app.Commands = []*cli.Command{
{
Name: "watch",
Subcommands: watch.Subcommands,
......
package watch
import (
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
"github.com/ethereum-optimism/optimism/op-challenger/config"
)
......
......@@ -5,7 +5,7 @@ import (
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
flags "github.com/ethereum-optimism/optimism/op-challenger/flags"
......@@ -141,19 +141,19 @@ func NewConfigFromCLI(ctx *cli.Context) (*Config, error) {
if err := flags.CheckRequired(ctx); err != nil {
return nil, err
}
l1EthRpc := ctx.GlobalString(flags.L1EthRpcFlag.Name)
l1EthRpc := ctx.String(flags.L1EthRpcFlag.Name)
if l1EthRpc == "" {
return nil, ErrMissingL1EthRPC
}
rollupRpc := ctx.GlobalString(flags.RollupRpcFlag.Name)
rollupRpc := ctx.String(flags.RollupRpcFlag.Name)
if rollupRpc == "" {
return nil, ErrMissingRollupRpc
}
l2ooAddress, err := opservice.ParseAddress(ctx.GlobalString(flags.L2OOAddressFlag.Name))
l2ooAddress, err := opservice.ParseAddress(ctx.String(flags.L2OOAddressFlag.Name))
if err != nil {
return nil, ErrMissingL2OOAddress
}
dgfAddress, err := opservice.ParseAddress(ctx.GlobalString(flags.DGFAddressFlag.Name))
dgfAddress, err := opservice.ParseAddress(ctx.String(flags.DGFAddressFlag.Name))
if err != nil {
return nil, ErrMissingDGFAddress
}
......
......@@ -4,7 +4,7 @@ import (
"fmt"
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"
)
......
......@@ -18,10 +18,10 @@ func main() {
p.Attack()
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 0010 Trace Depth is: 2 Trace Index is: 6
// Trace Position is 0100 Trace Depth is: 3 Trace Index is: 5
// GIN: 1 Trace Position is 0 Trace Depth is: 0 Trace Index is: 8
// GIN: 10 Trace Position is 0 Trace Depth is: 1 Trace Index is: 4
// GIN: 110 Trace Position is 10 Trace Depth is: 2 Trace Index is: 6
// GIN: 1100 Trace Position is 100 Trace Depth is: 3 Trace Index is: 5
// Example 2
// abcdefgh
......
......@@ -3,36 +3,42 @@ package fault
import "fmt"
// 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 {
Depth int
IndexAtDepth int
depth int
indexAtDepth int
}
// TraceIndex calculates the what the index of the claim value
// would be inside the trace.
func NewPosition(depth, indexAtDepth int) Position {
return Position{depth, indexAtDepth}
}
func NewPositionFromGIndex(x uint64) Position {
depth := MSBIndex(x)
indexAtDepth := ^(1 << depth) & x
return NewPosition(depth, int(indexAtDepth))
}
func (p *Position) Depth() int {
return p.depth
}
func (p *Position) IndexAtDepth() int {
return p.indexAtDepth
}
// 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) int {
lo := 0
hi := 1 << maxDepth
mid := hi
path := p.IndexAtDepth
for i := p.Depth - 1; i >= 0; i-- {
mid = (lo + hi) / 2
mask := 1 << i
if path&mask == mask {
lo = mid
} else {
hi = mid
}
}
return mid
// 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 p.indexAtDepth<<rd | ((1 << rd) - 1)
}
// move goes to the left or right child.
func (p *Position) move(right bool) {
p.Depth++
p.IndexAtDepth = (p.IndexAtDepth << 1) | boolToInt(right)
p.depth++
p.indexAtDepth = (p.indexAtDepth << 1) | boolToInt(right)
}
func boolToInt(b bool) int {
......@@ -43,15 +49,18 @@ func boolToInt(b bool) int {
}
}
// parent moves up to the parent.
func (p *Position) parent() {
p.Depth--
p.IndexAtDepth = p.IndexAtDepth >> 1
p.depth--
p.indexAtDepth = p.indexAtDepth >> 1
}
// Attack moves this position to a position to the left which disagrees with this position.
func (p *Position) Attack() {
p.move(false)
}
// Defend moves this position to the right which agrees with this position. Note:
func (p *Position) Defend() {
p.parent()
p.move(true)
......@@ -59,5 +68,21 @@ func (p *Position) Defend() {
}
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
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 int
}
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)
}
}
......@@ -3,7 +3,7 @@ package flags
import (
"fmt"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
opservice "github.com/ethereum-optimism/optimism/op-service"
oplog "github.com/ethereum-optimism/optimism/op-service/log"
......@@ -15,27 +15,31 @@ import (
const envVarPrefix = "OP_CHALLENGER"
func prefixEnvVars(name string) []string {
return opservice.PrefixEnvVar(envVarPrefix, name)
}
var (
// Required Flags
L1EthRpcFlag = cli.StringFlag{
L1EthRpcFlag = &cli.StringFlag{
Name: "l1-eth-rpc",
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",
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",
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",
Usage: "Address of the DisputeGameFactory contract.",
EnvVar: opservice.PrefixEnvVar(envVarPrefix, "DGF_ADDRESS"),
EnvVars: prefixEnvVars("DGF_ADDRESS"),
}
)
......@@ -65,8 +69,8 @@ var Flags []cli.Flag
func CheckRequired(ctx *cli.Context) error {
for _, f := range requiredFlags {
if !ctx.GlobalIsSet(f.GetName()) {
return fmt.Errorf("flag %s is required", f.GetName())
if !ctx.IsSet(f.Names()[0]) {
return fmt.Errorf("flag %s is required", f.Names()[0])
}
}
return nil
......
......@@ -5,14 +5,14 @@ import (
"strings"
"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.
func TestUniqueFlags(t *testing.T) {
seenCLI := make(map[string]struct{})
for _, flag := range Flags {
name := flag.GetName()
name := flag.Names()[0]
if _, ok := seenCLI[name]; ok {
t.Errorf("duplicate flag %s", name)
continue
......@@ -38,22 +38,22 @@ func TestCorrectEnvVarPrefix(t *testing.T) {
for _, flag := range Flags {
envVar := envVarForFlag(flag)
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_") {
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, "__") {
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 {
values := reflect.ValueOf(flag)
envVarValue := values.FieldByName("EnvVar")
if envVarValue == (reflect.Value{}) {
envVarValue := values.Elem().FieldByName("EnvVars")
if envVarValue == (reflect.Value{}) || envVarValue.Len() == 0 {
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
# build op-heartbeat with local monorepo go modules
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 ./op-heartbeat/go.mod /app/go.mod
COPY ./op-heartbeat/go.sum /app/go.sum
WORKDIR /app/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
......
......@@ -8,7 +8,7 @@ import (
"github.com/ethereum-optimism/optimism/op-heartbeat/flags"
oplog "github.com/ethereum-optimism/optimism/op-service/log"
"github.com/ethereum/go-ethereum/log"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
var (
......
......@@ -7,7 +7,7 @@ import (
oplog "github.com/ethereum-optimism/optimism/op-service/log"
opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics"
oppprof "github.com/ethereum-optimism/optimism/op-service/pprof"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
type Config struct {
......@@ -42,8 +42,8 @@ func (c Config) Check() error {
func NewConfig(ctx *cli.Context) Config {
return Config{
HTTPAddr: ctx.GlobalString(flags.HTTPAddrFlag.Name),
HTTPPort: ctx.GlobalInt(flags.HTTPPortFlag.Name),
HTTPAddr: ctx.String(flags.HTTPAddrFlag.Name),
HTTPPort: ctx.Int(flags.HTTPPortFlag.Name),
Log: oplog.ReadCLIConfig(ctx),
Metrics: opmetrics.ReadCLIConfig(ctx),
Pprof: oppprof.ReadCLIConfig(ctx),
......
......@@ -4,28 +4,32 @@ import (
opservice "github.com/ethereum-optimism/optimism/op-service"
oplog "github.com/ethereum-optimism/optimism/op-service/log"
opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
const envPrefix = "OP_HEARTBEAT"
func prefixEnvVars(name string) []string {
return opservice.PrefixEnvVar(envPrefix, name)
}
const (
HTTPAddrFlagName = "http.addr"
HTTPPortFlagName = "http.port"
)
var (
HTTPAddrFlag = cli.StringFlag{
HTTPAddrFlag = &cli.StringFlag{
Name: HTTPAddrFlagName,
Usage: "Address the server should listen on",
Value: "0.0.0.0",
EnvVar: opservice.PrefixEnvVar(envPrefix, "HTTP_ADDR"),
EnvVars: prefixEnvVars("HTTP_ADDR"),
}
HTTPPortFlag = cli.IntFlag{
HTTPPortFlag = &cli.IntFlag{
Name: HTTPPortFlagName,
Usage: "Port the server should listen on",
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 (
"syscall"
"time"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
"github.com/ethereum/go-ethereum/log"
......
......@@ -12,48 +12,48 @@ import (
"github.com/ethereum-optimism/optimism/op-node/rollup/derive"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
func main() {
app := cli.NewApp()
app.Name = "batch-decoder"
app.Usage = "Optimism Batch Decoding Utility"
app.Commands = []cli.Command{
app.Commands = []*cli.Command{
{
Name: "fetch",
Usage: "Fetches batches in the specified range",
Flags: []cli.Flag{
cli.IntFlag{
&cli.IntFlag{
Name: "start",
Required: true,
Usage: "First block (inclusive) to fetch",
},
cli.IntFlag{
&cli.IntFlag{
Name: "end",
Required: true,
Usage: "Last block (exclusive) to fetch",
},
cli.StringFlag{
&cli.StringFlag{
Name: "inbox",
Required: true,
Usage: "Batch Inbox Address",
},
cli.StringFlag{
&cli.StringFlag{
Name: "sender",
Required: true,
Usage: "Batch Sender Address",
},
cli.StringFlag{
&cli.StringFlag{
Name: "out",
Value: "/tmp/batch_decoder/transactions_cache",
Usage: "Cache directory for the found transactions",
},
cli.StringFlag{
&cli.StringFlag{
Name: "l1",
Required: true,
Usage: "L1 RPC URL",
EnvVar: "L1_RPC",
EnvVars: []string{"L1_RPC"},
},
},
Action: func(cliCtx *cli.Context) error {
......@@ -88,17 +88,17 @@ func main() {
Name: "reassemble",
Usage: "Reassembles channels from fetched batches",
Flags: []cli.Flag{
cli.StringFlag{
&cli.StringFlag{
Name: "inbox",
Value: "0xff00000000000000000000000000000000000420",
Usage: "Batch Inbox Address",
},
cli.StringFlag{
&cli.StringFlag{
Name: "in",
Value: "/tmp/batch_decoder/transactions_cache",
Usage: "Cache directory for the found transactions",
},
cli.StringFlag{
&cli.StringFlag{
Name: "out",
Value: "/tmp/batch_decoder/channel_cache",
Usage: "Cache directory for the found channels",
......@@ -118,17 +118,17 @@ func main() {
Name: "force-close",
Usage: "Create the tx data which will force close a channel",
Flags: []cli.Flag{
cli.StringFlag{
&cli.StringFlag{
Name: "id",
Required: true,
Usage: "ID of the channel to close",
},
cli.StringFlag{
&cli.StringFlag{
Name: "inbox",
Value: "0x0000000000000000000000000000000000000000",
Usage: "(Optional) Batch Inbox Address",
},
cli.StringFlag{
&cli.StringFlag{
Name: "in",
Value: "/tmp/batch_decoder/transactions_cache",
Usage: "Cache directory for the found transactions",
......
......@@ -8,7 +8,7 @@ import (
"github.com/ethereum-optimism/optimism/op-node/metrics"
"github.com/olekukonko/tablewriter"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
var Subcommands = cli.Commands{
......@@ -16,7 +16,7 @@ var Subcommands = cli.Commands{
Name: "metrics",
Usage: "Dumps a list of supported metrics to stdout",
Flags: []cli.Flag{
cli.StringFlag{
&cli.StringFlag{
Name: "format",
Value: "markdown",
Usage: "Output format (json|markdown)",
......
......@@ -8,7 +8,7 @@ import (
"os"
"path/filepath"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethclient"
......@@ -22,19 +22,19 @@ var Subcommands = cli.Commands{
Name: "devnet",
Usage: "Initialize new L1 and L2 genesis files and rollup config suitable for a local devnet",
Flags: []cli.Flag{
cli.StringFlag{
&cli.StringFlag{
Name: "deploy-config",
Usage: "Path to hardhat deploy config file",
},
cli.StringFlag{
&cli.StringFlag{
Name: "outfile.l1",
Usage: "Path to L1 genesis output file",
},
cli.StringFlag{
&cli.StringFlag{
Name: "outfile.l2",
Usage: "Path to L2 genesis output file",
},
cli.StringFlag{
&cli.StringFlag{
Name: "outfile.rollup",
Usage: "Path to rollup output file",
},
......@@ -85,23 +85,23 @@ var Subcommands = cli.Commands{
Name: "l2",
Usage: "Generates an L2 genesis file and rollup config suitable for a deployed network",
Flags: []cli.Flag{
cli.StringFlag{
&cli.StringFlag{
Name: "l1-rpc",
Usage: "L1 RPC URL",
},
cli.StringFlag{
&cli.StringFlag{
Name: "deploy-config",
Usage: "Path to hardhat deploy config file",
},
cli.StringFlag{
&cli.StringFlag{
Name: "deployment-dir",
Usage: "Path to deployment directory",
},
cli.StringFlag{
&cli.StringFlag{
Name: "outfile.l2",
Usage: "Path to L2 genesis output file",
},
cli.StringFlag{
&cli.StringFlag{
Name: "outfile.rollup",
Usage: "Path to rollup output file",
},
......
......@@ -9,7 +9,7 @@ import (
"github.com/ethereum-optimism/optimism/op-node/chaincfg"
"github.com/ethereum-optimism/optimism/op-node/cmd/doc"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
"github.com/ethereum/go-ethereum/log"
......@@ -59,7 +59,7 @@ func main() {
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.Action = RollupNodeMain
app.Commands = []cli.Command{
app.Commands = []*cli.Command{
{
Name: "p2p",
Subcommands: p2p.Subcommands,
......
......@@ -10,7 +10,7 @@ import (
"github.com/libp2p/go-libp2p/core/crypto"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
func Priv2PeerID(r io.Reader) (string, error) {
......
This diff is collapsed.
......@@ -4,7 +4,7 @@ import (
"testing"
"github.com/stretchr/testify/require"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
// TestOptionalFlagsDontSetRequired asserts that all flags deemed optional set
......@@ -21,7 +21,7 @@ func TestOptionalFlagsDontSetRequired(t *testing.T) {
func TestUniqueFlags(t *testing.T) {
seenCLI := make(map[string]struct{})
for _, flag := range Flags {
name := flag.GetName()
name := flag.Names()[0]
if _, ok := seenCLI[name]; ok {
t.Errorf("duplicate flag %s", name)
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 (
"github.com/ethereum-optimism/optimism/op-node/flags"
"github.com/ethereum-optimism/optimism/op-node/p2p"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
"github.com/ethereum/go-ethereum/p2p/enode"
)
......@@ -28,7 +28,7 @@ import (
func NewConfig(ctx *cli.Context, rollupCfg *rollup.Config) (*p2p.Config, error) {
conf := &p2p.Config{}
if ctx.GlobalBool(flags.DisableP2P.Name) {
if ctx.Bool(flags.DisableP2P.Name) {
conf.DisableP2P = true
return conf, nil
}
......@@ -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)
}
conf.EnableReqRespSync = ctx.GlobalBool(flags.SyncReqRespFlag.Name)
conf.EnableReqRespSync = ctx.Bool(flags.SyncReqRespFlag.Name)
return conf, nil
}
......@@ -83,13 +83,13 @@ func validatePort(p uint) (uint16, error) {
// loadScoringParams loads the peer scoring options from the CLI context.
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
if scoringLevel == "" {
scoringLevel = ctx.GlobalString(flags.PeerScoring.Name)
scoringLevel = ctx.String(flags.PeerScoring.Name)
}
if scoringLevel == "" {
scoringLevel = ctx.GlobalString(flags.TopicScoring.Name)
scoringLevel = ctx.String(flags.TopicScoring.Name)
}
if scoringLevel != "" {
params, err := p2p.GetScoringParams(scoringLevel, rollupCfg)
......@@ -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.
func loadBanningOptions(conf *p2p.Config, ctx *cli.Context) error {
conf.BanningEnabled = ctx.GlobalBool(flags.Banning.Name)
conf.BanningThreshold = ctx.GlobalFloat64(flags.BanningThreshold.Name)
conf.BanningDuration = ctx.GlobalDuration(flags.BanningDuration.Name)
conf.BanningEnabled = ctx.Bool(flags.Banning.Name)
conf.BanningThreshold = ctx.Float64(flags.BanningThreshold.Name)
conf.BanningDuration = ctx.Duration(flags.BanningDuration.Name)
return nil
}
func loadListenOpts(conf *p2p.Config, ctx *cli.Context) error {
listenIP := ctx.GlobalString(flags.ListenIP.Name)
listenIP := ctx.String(flags.ListenIP.Name)
if listenIP != "" { // optional
conf.ListenIP = net.ParseIP(listenIP)
if conf.ListenIP == nil {
......@@ -119,11 +119,11 @@ func loadListenOpts(conf *p2p.Config, ctx *cli.Context) 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 {
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 {
return fmt.Errorf("bad listen UDP port: %w", err)
}
......@@ -131,20 +131,20 @@ func loadListenOpts(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
}
var err error
conf.AdvertiseTCPPort, err = validatePort(ctx.GlobalUint(flags.AdvertiseTCPPort.Name))
conf.AdvertiseTCPPort, err = validatePort(ctx.Uint(flags.AdvertiseTCPPort.Name))
if err != nil {
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 {
return fmt.Errorf("bad advertised UDP port: %w", err)
}
adIP := ctx.GlobalString(flags.AdvertiseIP.Name)
adIP := ctx.String(flags.AdvertiseIP.Name)
if adIP != "" { // optional
ips, err := net.LookupIP(adIP)
if err != nil {
......@@ -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 == "" {
dbPath = "opnode_discovery_db"
}
......@@ -175,7 +175,7 @@ func loadDiscoveryOpts(conf *p2p.Config, ctx *cli.Context) error {
}
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 {
recordB64 = strings.TrimSpace(recordB64)
if recordB64 == "" { // ignore empty records
......@@ -197,7 +197,7 @@ func loadDiscoveryOpts(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 {
addr = strings.TrimSpace(addr)
if addr == "" {
......@@ -210,7 +210,7 @@ func loadLibp2pOpts(conf *p2p.Config, ctx *cli.Context) error {
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))
switch v {
case "yamux":
......@@ -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 {
v = strings.ToLower(strings.TrimSpace(v))
switch v {
......@@ -240,16 +240,16 @@ func loadLibp2pOpts(conf *p2p.Config, ctx *cli.Context) error {
}
}
conf.PeersLo = ctx.GlobalUint(flags.PeersLo.Name)
conf.PeersHi = ctx.GlobalUint(flags.PeersHi.Name)
conf.PeersGrace = ctx.GlobalDuration(flags.PeersGrace.Name)
conf.NAT = ctx.GlobalBool(flags.NAT.Name)
conf.UserAgent = ctx.GlobalString(flags.UserAgent.Name)
conf.TimeoutNegotiation = ctx.GlobalDuration(flags.TimeoutNegotiation.Name)
conf.TimeoutAccept = ctx.GlobalDuration(flags.TimeoutAccept.Name)
conf.TimeoutDial = ctx.GlobalDuration(flags.TimeoutDial.Name)
conf.PeersLo = ctx.Uint(flags.PeersLo.Name)
conf.PeersHi = ctx.Uint(flags.PeersHi.Name)
conf.PeersGrace = ctx.Duration(flags.PeersGrace.Name)
conf.NAT = ctx.Bool(flags.NAT.Name)
conf.UserAgent = ctx.String(flags.UserAgent.Name)
conf.TimeoutNegotiation = ctx.Duration(flags.TimeoutNegotiation.Name)
conf.TimeoutAccept = ctx.Duration(flags.TimeoutAccept.Name)
conf.TimeoutDial = ctx.Duration(flags.TimeoutDial.Name)
peerstorePath := ctx.GlobalString(flags.PeerstorePath.Name)
peerstorePath := ctx.String(flags.PeerstorePath.Name)
if peerstorePath == "" {
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 {
}
func loadNetworkPrivKey(ctx *cli.Context) (*crypto.Secp256k1PrivateKey, error) {
raw := ctx.GlobalString(flags.P2PPrivRaw.Name)
raw := ctx.String(flags.P2PPrivRaw.Name)
if raw != "" {
return parsePriv(raw)
}
keyPath := ctx.GlobalString(flags.P2PPrivPath.Name)
keyPath := ctx.String(flags.P2PPrivPath.Name)
if keyPath == "" {
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) {
}
func loadGossipOptions(conf *p2p.Config, ctx *cli.Context) error {
conf.MeshD = ctx.GlobalInt(flags.GossipMeshDFlag.Name)
conf.MeshDLo = ctx.GlobalInt(flags.GossipMeshDloFlag.Name)
conf.MeshDHi = ctx.GlobalInt(flags.GossipMeshDhiFlag.Name)
conf.MeshDLazy = ctx.GlobalInt(flags.GossipMeshDlazyFlag.Name)
conf.FloodPublish = ctx.GlobalBool(flags.GossipFloodPublishFlag.Name)
conf.MeshD = ctx.Int(flags.GossipMeshDFlag.Name)
conf.MeshDLo = ctx.Int(flags.GossipMeshDloFlag.Name)
conf.MeshDHi = ctx.Int(flags.GossipMeshDhiFlag.Name)
conf.MeshDLazy = ctx.Int(flags.GossipMeshDlazyFlag.Name)
conf.FloodPublish = ctx.Bool(flags.GossipFloodPublishFlag.Name)
return nil
}
......@@ -4,7 +4,7 @@ import (
"fmt"
"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/p2p"
......@@ -15,7 +15,7 @@ import (
// LoadSignerSetup loads a configuration for a Signer to be set up later
func LoadSignerSetup(ctx *cli.Context) (p2p.SignerSetup, error) {
key := ctx.GlobalString(flags.SequencerP2PKeyFlag.Name)
key := ctx.String(flags.SequencerP2PKeyFlag.Name)
if key != "" {
// 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).
......
......@@ -54,6 +54,7 @@ type SetupP2P interface {
// ScoringParams defines the various types of peer scoring parameters.
type ScoringParams struct {
PeerScoring pubsub.PeerScoreParams
ApplicationScoring ApplicationScoreParams
}
// Config sets up a p2p host and discv5 service from configuration.
......@@ -137,11 +138,11 @@ func (conf *Config) Disabled() bool {
return conf.DisableP2P
}
func (conf *Config) PeerScoringParams() *pubsub.PeerScoreParams {
func (conf *Config) PeerScoringParams() *ScoringParams {
if conf.ScoringParams == nil {
return nil
}
return &conf.ScoringParams.PeerScoring
return conf.ScoringParams
}
func (conf *Config) BanPeers() bool {
......
......@@ -51,7 +51,7 @@ var MessageDomainInvalidSnappy = [4]byte{0, 0, 0, 0}
var MessageDomainValidSnappy = [4]byte{1, 0, 0, 0}
type GossipSetupConfigurables interface {
PeerScoringParams() *pubsub.PeerScoreParams
PeerScoringParams() *ScoringParams
// ConfigureGossip creates configuration options to apply to the GossipSub setup
ConfigureGossip(rollupCfg *rollup.Config) []pubsub.Option
}
......
......@@ -149,7 +149,7 @@ func (conf *Config) Host(log log.Logger, reporter metrics.Reporter, metrics Host
var scoreRetention time.Duration
if peerScoreParams != nil {
// Use the same retention period as gossip will if available
scoreRetention = peerScoreParams.RetainScore
scoreRetention = peerScoreParams.PeerScoring.RetainScore
} else {
// Disable score GC if peer scoring is disabled
scoreRetention = 0
......
......@@ -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
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
appScorer ApplicationScorer
log log.Logger
// the below components are all optional, and may be nil. They require the host to not be nil.
dv5Local *enode.LocalNode // p2p discovery identity
......@@ -89,9 +90,21 @@ func (n *NodeP2P) init(resourcesCtx context.Context, rollupCfg *rollup.Config, l
n.gater = extra.ConnectionGater()
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.
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{
ConnectedF: func(nw network.Network, conn network.Conn) {
n.syncCl.AddPeer(conn.RemotePeer())
......@@ -112,12 +125,7 @@ func (n *NodeP2P) init(resourcesCtx context.Context, rollupCfg *rollup.Config, l
n.host.SetStreamHandler(PayloadByNumberProtocolID(rollupCfg.L2ChainID), payloadByNumber)
}
}
eps, ok := n.host.Peerstore().(store.ExtendedPeerstore)
if !ok {
return fmt.Errorf("cannot init without extended peerstore: %w", err)
}
n.store = eps
n.scorer = NewScorer(rollupCfg, eps, metrics, log)
n.scorer = NewScorer(rollupCfg, eps, metrics, n.appScorer, log)
// notify of any new connections/streams/etc.
n.host.Network().Notify(NewNetworkNotifier(log, metrics))
// 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
n.peerMonitor = monitor.NewPeerMonitor(resourcesCtx, log, clock.SystemClock, n, setup.BanThreshold(), setup.BanDuration())
n.peerMonitor.Start()
}
n.appScorer.start()
}
return nil
}
......@@ -258,6 +267,9 @@ func (n *NodeP2P) Close() error {
}
}
}
if n.appScorer != nil {
n.appScorer.stop()
}
return result.ErrorOrNil()
}
......
......@@ -32,7 +32,7 @@ func ScoreDecay(duration time.Duration, slot time.Duration) float64 {
// See [PeerScoreParams] for detailed documentation.
//
// [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
if slot == 0 {
slot = 2 * time.Second
......@@ -92,6 +92,7 @@ func GetScoringParams(name string, cfg *rollup.Config) (*ScoringParams, error) {
case "light":
return &ScoringParams{
PeerScoring: LightPeerScoreParams(cfg),
ApplicationScoring: LightApplicationScoreParams(cfg),
}, nil
case "none":
return nil, nil
......
......@@ -81,6 +81,19 @@ func (testSuite *PeerParamsTestSuite) TestGetPeerScoreParams_Light() {
testSuite.Equal(peerParams.DecayInterval, slot)
testSuite.Equal(peerParams.DecayToZero, DecayToZero)
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.
......@@ -91,4 +104,5 @@ func (testSuite *PeerParamsTestSuite) TestParamsZeroBlockTime() {
params, err := GetScoringParams("light", &cfg)
testSuite.NoError(err)
testSuite.Equal(params.PeerScoring.DecayInterval, slot)
testSuite.Equal(params.ApplicationScoring.DecayInterval, slot)
}
......@@ -14,6 +14,7 @@ import (
type scorer struct {
peerStore Peerstore
metricer ScoreMetrics
appScorer ApplicationScorer
log log.Logger
cfg *rollup.Config
}
......@@ -36,6 +37,7 @@ type Peerstore interface {
// Scorer is a peer scorer that scores peers based on application-specific metrics.
type Scorer interface {
SnapshotHook() pubsub.ExtendedPeerScoreInspectFn
ApplicationScore(peer.ID) float64
}
//go:generate mockery --name ScoreMetrics --output mocks/
......@@ -44,10 +46,11 @@ type ScoreMetrics interface {
}
// 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{
peerStore: peerStore,
metricer: metricer,
appScorer: appScorer,
log: log,
cfg: cfg,
}
......@@ -84,3 +87,7 @@ func (s *scorer) SnapshotHook() pubsub.ExtendedPeerScoreInspectFn {
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() {
&rollup.Config{L2ChainID: big.NewInt(123)},
testSuite.mockStore,
testSuite.mockMetricer,
&p2p.NoopApplicationScorer{},
testSuite.logger,
)
inspectFn := scorer.SnapshotHook()
......
......@@ -9,12 +9,15 @@ import (
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]
// to return early without returning any [pubsub.Option].
peerScoreParams := gossipConf.PeerScoringParams()
peerScoreThresholds := NewPeerScoreThresholds()
scoreParams := gossipConf.PeerScoringParams()
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{
pubsub.WithPeerScore(peerScoreParams, &peerScoreThresholds),
pubsub.WithPeerScore(&params, &peerScoreThresholds),
pubsub.WithPeerScoreInspect(scorer.SnapshotHook(), peerScoreInspectFrequency),
}
} else {
......
......@@ -82,6 +82,18 @@ func getNetHosts(testSuite *PeerScoresTestSuite, ctx context.Context, n int) []h
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 {
var psubs []*pubsub.PubSub
......@@ -100,17 +112,10 @@ func newGossipSubs(testSuite *PeerScoresTestSuite, ctx context.Context, hosts []
scorer := NewScorer(
&rollup.Config{L2ChainID: big.NewInt(123)},
extPeerStore, testSuite.mockMetricer, logger)
extPeerStore, testSuite.mockMetricer, &discriminatingAppScorer{badPeer: hosts[0].ID()}, logger)
opts = append(opts, ConfigurePeerScoring(&Config{
ScoringParams: &ScoringParams{
PeerScoring: pubsub.PeerScoreParams{
AppSpecificScore: func(p peer.ID) float64 {
if p == hosts[0].ID() {
return -1000
} else {
return 0
}
},
AppSpecificWeight: 1,
DecayInterval: time.Second,
DecayToZero: 0.01,
......
......@@ -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
}
......
......@@ -111,6 +111,12 @@ type SyncClientMetrics interface {
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:
// 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,
......@@ -181,6 +187,7 @@ type SyncClient struct {
cfg *rollup.Config
metrics SyncClientMetrics
appScorer SyncPeerScorer
newStreamFn newStreamFn
payloadByNumber protocol.ID
......@@ -227,13 +234,14 @@ type SyncClient struct {
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())
c := &SyncClient{
log: log,
cfg: cfg,
metrics: metrics,
appScorer: appScorer,
newStreamFn: newStream,
payloadByNumber: PayloadByNumberProtocolID(cfg.L2ChainID),
peers: make(map[peer.ID]context.CancelFunc),
......@@ -268,11 +276,11 @@ func (s *SyncClient) Start() {
func (s *SyncClient) AddPeer(id peer.ID) {
s.peersLock.Lock()
defer s.peersLock.Unlock()
if _, ok := s.peers[id]; ok {
s.log.Warn("cannot register peer for sync duties, peer was already registered", "peer", id)
if s.closingPeers {
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
}
s.wg.Add(1)
......@@ -424,7 +432,8 @@ func (s *SyncClient) onQuarantineEvict(key common.Hash, value syncResult) {
s.metrics.PayloadsQuarantineSize(s.quarantine.Len())
if !s.trusted.Contains(key) {
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 {
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) {
defer func() {
s.peersLock.Lock()
delete(s.peers, id) // clean up
s.log.Debug("stopped syncing loop of peer", "id", id)
s.wg.Done()
s.peersLock.Unlock()
s.log.Debug("stopped syncing loop of peer", "id", id)
}()
log := s.log.New("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.
pr.complete.Store(true)
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.
// We'd like to avoid making more requests for a while, to back off.
if err := rl.WaitN(ctx, clientErrRateCost); err != nil {
......@@ -532,11 +542,9 @@ func (s *SyncClient) peerLoop(ctx context.Context, id peer.ID) {
}
} else {
log.Debug("completed p2p sync request", "num", pr.num)
s.appScorer.onValidResponse(id)
}
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)
if err != nil {
......
......@@ -137,7 +137,7 @@ func TestSinglePeerSync(t *testing.T) {
hostA.SetStreamHandler(PayloadByNumberProtocolID(cfg.L2ChainID), payloadByNumber)
// 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)
cl.AddPeer(hostA.ID())
......@@ -190,7 +190,7 @@ func TestMultiPeerSync(t *testing.T) {
payloadByNumber := MakeStreamHandler(ctx, log.New("serve", "payloads_by_number"), srv.HandleSyncRequest)
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
}
......
......@@ -12,7 +12,7 @@ import (
"github.com/ethereum-optimism/optimism/op-node/sources"
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/hexutil"
......@@ -64,27 +64,27 @@ func NewConfig(ctx *cli.Context, log log.Logger) (*node.Config, error) {
Rollup: *rollupConfig,
Driver: *driverConfig,
RPC: node.RPCConfig{
ListenAddr: ctx.GlobalString(flags.RPCListenAddr.Name),
ListenPort: ctx.GlobalInt(flags.RPCListenPort.Name),
EnableAdmin: ctx.GlobalBool(flags.RPCEnableAdmin.Name),
ListenAddr: ctx.String(flags.RPCListenAddr.Name),
ListenPort: ctx.Int(flags.RPCListenPort.Name),
EnableAdmin: ctx.Bool(flags.RPCEnableAdmin.Name),
},
Metrics: node.MetricsConfig{
Enabled: ctx.GlobalBool(flags.MetricsEnabledFlag.Name),
ListenAddr: ctx.GlobalString(flags.MetricsAddrFlag.Name),
ListenPort: ctx.GlobalInt(flags.MetricsPortFlag.Name),
Enabled: ctx.Bool(flags.MetricsEnabledFlag.Name),
ListenAddr: ctx.String(flags.MetricsAddrFlag.Name),
ListenPort: ctx.Int(flags.MetricsPortFlag.Name),
},
Pprof: oppprof.CLIConfig{
Enabled: ctx.GlobalBool(flags.PprofEnabledFlag.Name),
ListenAddr: ctx.GlobalString(flags.PprofAddrFlag.Name),
ListenPort: ctx.GlobalInt(flags.PprofPortFlag.Name),
Enabled: ctx.Bool(flags.PprofEnabledFlag.Name),
ListenAddr: ctx.String(flags.PprofAddrFlag.Name),
ListenPort: ctx.Int(flags.PprofPortFlag.Name),
},
P2P: p2pConfig,
P2PSigner: p2pSignerSetup,
L1EpochPollInterval: ctx.GlobalDuration(flags.L1EpochPollIntervalFlag.Name),
L1EpochPollInterval: ctx.Duration(flags.L1EpochPollIntervalFlag.Name),
Heartbeat: node.HeartbeatConfig{
Enabled: ctx.GlobalBool(flags.HeartbeatEnabledFlag.Name),
Moniker: ctx.GlobalString(flags.HeartbeatMonikerFlag.Name),
URL: ctx.GlobalString(flags.HeartbeatURLFlag.Name),
Enabled: ctx.Bool(flags.HeartbeatEnabledFlag.Name),
Moniker: ctx.String(flags.HeartbeatMonikerFlag.Name),
URL: ctx.String(flags.HeartbeatURLFlag.Name),
},
}
if err := cfg.Check(); err != nil {
......@@ -95,18 +95,18 @@ func NewConfig(ctx *cli.Context, log log.Logger) (*node.Config, error) {
func NewL1EndpointConfig(ctx *cli.Context) *node.L1EndpointConfig {
return &node.L1EndpointConfig{
L1NodeAddr: ctx.GlobalString(flags.L1NodeAddr.Name),
L1TrustRPC: ctx.GlobalBool(flags.L1TrustRPC.Name),
L1RPCKind: sources.RPCProviderKind(strings.ToLower(ctx.GlobalString(flags.L1RPCProviderKind.Name))),
RateLimit: ctx.GlobalFloat64(flags.L1RPCRateLimit.Name),
BatchSize: ctx.GlobalInt(flags.L1RPCMaxBatchSize.Name),
L1NodeAddr: ctx.String(flags.L1NodeAddr.Name),
L1TrustRPC: ctx.Bool(flags.L1TrustRPC.Name),
L1RPCKind: sources.RPCProviderKind(strings.ToLower(ctx.String(flags.L1RPCProviderKind.Name))),
RateLimit: ctx.Float64(flags.L1RPCRateLimit.Name),
BatchSize: ctx.Int(flags.L1RPCMaxBatchSize.Name),
HttpPollInterval: ctx.Duration(flags.L1HTTPPollInterval.Name),
}
}
func NewL2EndpointConfig(ctx *cli.Context, log log.Logger) (*node.L2EndpointConfig, error) {
l2Addr := ctx.GlobalString(flags.L2EngineAddr.Name)
fileName := ctx.GlobalString(flags.L2EngineJWTSecret.Name)
l2Addr := ctx.String(flags.L2EngineAddr.Name)
fileName := ctx.String(flags.L2EngineJWTSecret.Name)
var secret [32]byte
fileName = strings.TrimSpace(fileName)
if fileName == "" {
......@@ -138,23 +138,23 @@ func NewL2EndpointConfig(ctx *cli.Context, log log.Logger) (*node.L2EndpointConf
// flag is set, otherwise nil.
func NewL2SyncEndpointConfig(ctx *cli.Context) *node.L2SyncEndpointConfig {
return &node.L2SyncEndpointConfig{
L2NodeAddr: ctx.GlobalString(flags.BackupL2UnsafeSyncRPC.Name),
TrustRPC: ctx.GlobalBool(flags.BackupL2UnsafeSyncRPCTrustRPC.Name),
L2NodeAddr: ctx.String(flags.BackupL2UnsafeSyncRPC.Name),
TrustRPC: ctx.Bool(flags.BackupL2UnsafeSyncRPCTrustRPC.Name),
}
}
func NewDriverConfig(ctx *cli.Context) *driver.Config {
return &driver.Config{
VerifierConfDepth: ctx.GlobalUint64(flags.VerifierL1Confs.Name),
SequencerConfDepth: ctx.GlobalUint64(flags.SequencerL1Confs.Name),
SequencerEnabled: ctx.GlobalBool(flags.SequencerEnabledFlag.Name),
SequencerStopped: ctx.GlobalBool(flags.SequencerStoppedFlag.Name),
SequencerMaxSafeLag: ctx.GlobalUint64(flags.SequencerMaxSafeLagFlag.Name),
VerifierConfDepth: ctx.Uint64(flags.VerifierL1Confs.Name),
SequencerConfDepth: ctx.Uint64(flags.SequencerL1Confs.Name),
SequencerEnabled: ctx.Bool(flags.SequencerEnabledFlag.Name),
SequencerStopped: ctx.Bool(flags.SequencerStoppedFlag.Name),
SequencerMaxSafeLag: ctx.Uint64(flags.SequencerMaxSafeLagFlag.Name),
}
}
func NewRollupConfig(ctx *cli.Context) (*rollup.Config, error) {
network := ctx.GlobalString(flags.Network.Name)
network := ctx.String(flags.Network.Name)
if network != "" {
config, err := chaincfg.GetRollupConfig(network)
if err != nil {
......@@ -164,7 +164,7 @@ func NewRollupConfig(ctx *cli.Context) (*rollup.Config, error) {
return &config, nil
}
rollupConfigPath := ctx.GlobalString(flags.RollupConfig.Name)
rollupConfigPath := ctx.String(flags.RollupConfig.Name)
file, err := os.Open(rollupConfigPath)
if err != nil {
return nil, fmt.Errorf("failed to read rollup config: %w", err)
......@@ -179,7 +179,7 @@ func NewRollupConfig(ctx *cli.Context) (*rollup.Config, error) {
}
func NewSnapshotLogger(ctx *cli.Context) (log.Logger, error) {
snapshotFile := ctx.GlobalString(flags.SnapshotLog.Name)
snapshotFile := ctx.String(flags.SnapshotLog.Name)
handler := log.DiscardHandler()
if snapshotFile != "" {
var err error
......
......@@ -10,7 +10,7 @@ import (
"github.com/ethereum-optimism/optimism/op-program/host/version"
oplog "github.com/ethereum-optimism/optimism/op-service/log"
"github.com/ethereum/go-ethereum/log"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
var (
......
......@@ -13,7 +13,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/params"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
var (
......@@ -118,23 +118,23 @@ func NewConfigFromCLI(ctx *cli.Context) (*Config, error) {
if err != nil {
return nil, err
}
l2Head := common.HexToHash(ctx.GlobalString(flags.L2Head.Name))
l2Head := common.HexToHash(ctx.String(flags.L2Head.Name))
if l2Head == (common.Hash{}) {
return nil, ErrInvalidL2Head
}
l2Claim := common.HexToHash(ctx.GlobalString(flags.L2Claim.Name))
l2Claim := common.HexToHash(ctx.String(flags.L2Claim.Name))
if l2Claim == (common.Hash{}) {
return nil, ErrInvalidL2Claim
}
l2ClaimBlockNum := ctx.GlobalUint64(flags.L2BlockNumber.Name)
l1Head := common.HexToHash(ctx.GlobalString(flags.L1Head.Name))
l2ClaimBlockNum := ctx.Uint64(flags.L2BlockNumber.Name)
l1Head := common.HexToHash(ctx.String(flags.L1Head.Name))
if l1Head == (common.Hash{}) {
return nil, ErrInvalidL1Head
}
l2GenesisPath := ctx.GlobalString(flags.L2GenesisPath.Name)
l2GenesisPath := ctx.String(flags.L2GenesisPath.Name)
var l2ChainConfig *params.ChainConfig
if l2GenesisPath == "" {
networkName := ctx.GlobalString(flags.Network.Name)
networkName := ctx.String(flags.Network.Name)
l2ChainConfig = L2ChainConfigsByName[networkName]
if l2ChainConfig == nil {
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) {
}
return &Config{
Rollup: rollupCfg,
DataDir: ctx.GlobalString(flags.DataDir.Name),
L2URL: ctx.GlobalString(flags.L2NodeAddr.Name),
DataDir: ctx.String(flags.DataDir.Name),
L2URL: ctx.String(flags.L2NodeAddr.Name),
L2ChainConfig: l2ChainConfig,
L2Head: l2Head,
L2Claim: l2Claim,
L2ClaimBlockNumber: l2ClaimBlockNum,
L1Head: l1Head,
L1URL: ctx.GlobalString(flags.L1NodeAddr.Name),
L1TrustRPC: ctx.GlobalBool(flags.L1TrustRPC.Name),
L1RPCKind: sources.RPCProviderKind(ctx.GlobalString(flags.L1RPCProviderKind.Name)),
ExecCmd: ctx.GlobalString(flags.Exec.Name),
ServerMode: ctx.GlobalBool(flags.Server.Name),
L1URL: ctx.String(flags.L1NodeAddr.Name),
L1TrustRPC: ctx.Bool(flags.L1TrustRPC.Name),
L1RPCKind: sources.RPCProviderKind(ctx.String(flags.L1RPCProviderKind.Name)),
ExecCmd: ctx.String(flags.Exec.Name),
ServerMode: ctx.Bool(flags.Server.Name),
}, nil
}
......
......@@ -4,7 +4,7 @@ import (
"fmt"
"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/sources"
......@@ -15,81 +15,85 @@ import (
const EnvVarPrefix = "OP_PROGRAM"
func prefixEnvVars(name string) []string {
return service.PrefixEnvVar(EnvVarPrefix, name)
}
var (
RollupConfig = cli.StringFlag{
RollupConfig = &cli.StringFlag{
Name: "rollup.config",
Usage: "Rollup chain parameters",
EnvVar: service.PrefixEnvVar(EnvVarPrefix, "ROLLUP_CONFIG"),
EnvVars: prefixEnvVars("ROLLUP_CONFIG"),
}
Network = cli.StringFlag{
Network = &cli.StringFlag{
Name: "network",
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",
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",
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",
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",
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",
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",
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",
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",
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",
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",
Usage: "The kind of RPC provider, used to inform optimal transactions receipts fetching, and thus reduce costs. Valid options: " +
openum.EnumString(sources.RPCProviderKinds),
EnvVar: service.PrefixEnvVar(EnvVarPrefix, "L1_RPC_KIND"),
EnvVars: prefixEnvVars("L1_RPC_KIND"),
Value: func() *sources.RPCProviderKind {
out := sources.RPCKindBasic
return &out
}(),
}
Exec = cli.StringFlag{
Exec = &cli.StringFlag{
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.",
EnvVar: service.PrefixEnvVar(EnvVarPrefix, "EXEC"),
EnvVars: prefixEnvVars("EXEC"),
}
Server = cli.BoolFlag{
Server = &cli.BoolFlag{
Name: "server",
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() {
}
func CheckRequired(ctx *cli.Context) error {
rollupConfig := ctx.GlobalString(RollupConfig.Name)
network := ctx.GlobalString(Network.Name)
rollupConfig := ctx.String(RollupConfig.Name)
network := ctx.String(Network.Name)
if rollupConfig == "" && network == "" {
return fmt.Errorf("flag %s or %s is required", RollupConfig.Name, Network.Name)
}
if rollupConfig != "" && network != "" {
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)
}
for _, flag := range requiredFlags {
if !ctx.IsSet(flag.GetName()) {
return fmt.Errorf("flag %s is required", flag.GetName())
if !ctx.IsSet(flag.Names()[0]) {
return fmt.Errorf("flag %s is required", flag.Names()[0])
}
}
return nil
......
......@@ -5,14 +5,14 @@ import (
"strings"
"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.
func TestUniqueFlags(t *testing.T) {
seenCLI := make(map[string]struct{})
for _, flag := range Flags {
name := flag.GetName()
name := flag.Names()[0]
if _, ok := seenCLI[name]; ok {
t.Errorf("duplicate flag %s", name)
continue
......@@ -38,22 +38,22 @@ func TestCorrectEnvVarPrefix(t *testing.T) {
for _, flag := range Flags {
envVar := envVarForFlag(flag)
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_") {
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, "__") {
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 {
values := reflect.ValueOf(flag)
envVarValue := values.FieldByName("EnvVar")
if envVarValue == (reflect.Value{}) {
envVarValue := values.Elem().FieldByName("EnvVars")
if envVarValue == (reflect.Value{}) || envVarValue.Len() == 0 {
return ""
}
return envVarValue.String()
return envVarValue.Index(0).String()
}
......@@ -8,7 +8,7 @@ import (
"github.com/ethereum-optimism/optimism/op-proposer/metrics"
"github.com/olekukonko/tablewriter"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
var Subcommands = cli.Commands{
......@@ -16,7 +16,7 @@ var Subcommands = cli.Commands{
Name: "metrics",
Usage: "Dumps a list of supported metrics to stdout",
Flags: []cli.Flag{
cli.StringFlag{
&cli.StringFlag{
Name: "format",
Value: "markdown",
Usage: "Output format (json|markdown)",
......
......@@ -4,7 +4,7 @@ import (
"fmt"
"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/flags"
......@@ -29,7 +29,7 @@ func main() {
app.Usage = "L2Output Submitter"
app.Description = "Service for generating and submitting L2 Output checkpoints to the L2OutputOracle contract"
app.Action = curryMain(Version)
app.Commands = []cli.Command{
app.Commands = []*cli.Command{
{
Name: "doc",
Subcommands: doc.Subcommands,
......
......@@ -4,7 +4,7 @@ import (
"fmt"
"time"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
opservice "github.com/ethereum-optimism/optimism/op-service"
oplog "github.com/ethereum-optimism/optimism/op-service/log"
......@@ -16,35 +16,39 @@ import (
const EnvVarPrefix = "OP_PROPOSER"
func prefixEnvVars(name string) []string {
return opservice.PrefixEnvVar(EnvVarPrefix, name)
}
var (
// Required Flags
L1EthRpcFlag = cli.StringFlag{
L1EthRpcFlag = &cli.StringFlag{
Name: "l1-eth-rpc",
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",
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",
Usage: "Address of the L2OutputOracle contract",
EnvVar: opservice.PrefixEnvVar(EnvVarPrefix, "L2OO_ADDRESS"),
EnvVars: prefixEnvVars("L2OO_ADDRESS"),
}
// Optional flags
PollIntervalFlag = cli.DurationFlag{
PollIntervalFlag = &cli.DurationFlag{
Name: "poll-interval",
Usage: "How frequently to poll L2 for new blocks",
Value: 6 * time.Second,
EnvVar: opservice.PrefixEnvVar(EnvVarPrefix, "POLL_INTERVAL"),
EnvVars: prefixEnvVars("POLL_INTERVAL"),
}
AllowNonFinalizedFlag = cli.BoolFlag{
AllowNonFinalizedFlag = &cli.BoolFlag{
Name: "allow-non-finalized",
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
L2OutputHDPathFlag = txmgr.L2OutputHDPathFlag
......@@ -77,8 +81,8 @@ var Flags []cli.Flag
func CheckRequired(ctx *cli.Context) error {
for _, f := range requiredFlags {
if !ctx.GlobalIsSet(f.GetName()) {
return fmt.Errorf("flag %s is required", f.GetName())
if !ctx.IsSet(f.Names()[0]) {
return fmt.Errorf("flag %s is required", f.Names()[0])
}
}
return nil
......
......@@ -5,7 +5,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"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-proposer/flags"
......@@ -86,13 +86,13 @@ func (c CLIConfig) Check() error {
func NewConfig(ctx *cli.Context) CLIConfig {
return CLIConfig{
// Required Flags
L1EthRpc: ctx.GlobalString(flags.L1EthRpcFlag.Name),
RollupRpc: ctx.GlobalString(flags.RollupRpcFlag.Name),
L2OOAddress: ctx.GlobalString(flags.L2OOAddressFlag.Name),
PollInterval: ctx.GlobalDuration(flags.PollIntervalFlag.Name),
L1EthRpc: ctx.String(flags.L1EthRpcFlag.Name),
RollupRpc: ctx.String(flags.RollupRpcFlag.Name),
L2OOAddress: ctx.String(flags.L2OOAddressFlag.Name),
PollInterval: ctx.Duration(flags.PollIntervalFlag.Name),
TxMgrConfig: txmgr.ReadCLIConfig(ctx),
// Optional Flags
AllowNonFinalized: ctx.GlobalBool(flags.AllowNonFinalizedFlag.Name),
AllowNonFinalized: ctx.Bool(flags.AllowNonFinalizedFlag.Name),
RPCConfig: oprpc.ReadCLIConfig(ctx),
LogConfig: oplog.ReadCLIConfig(ctx),
MetricsConfig: opmetrics.ReadCLIConfig(ctx),
......
......@@ -14,7 +14,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"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-node/eth"
......
......@@ -6,7 +6,7 @@ import (
"strings"
"github.com/ethereum/go-ethereum/log"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
"golang.org/x/term"
opservice "github.com/ethereum-optimism/optimism/op-service"
......@@ -20,22 +20,22 @@ const (
func CLIFlags(envPrefix string) []cli.Flag {
return []cli.Flag{
cli.StringFlag{
&cli.StringFlag{
Name: LevelFlagName,
Usage: "The lowest log level that will be output",
Value: "info",
EnvVar: opservice.PrefixEnvVar(envPrefix, "LOG_LEVEL"),
EnvVars: opservice.PrefixEnvVar(envPrefix, "LOG_LEVEL"),
},
cli.StringFlag{
&cli.StringFlag{
Name: FormatFlagName,
Usage: "Format the log output. Supported formats: 'text', 'terminal', 'logfmt', 'json', 'json-pretty',",
Value: "text",
EnvVar: opservice.PrefixEnvVar(envPrefix, "LOG_FORMAT"),
EnvVars: opservice.PrefixEnvVar(envPrefix, "LOG_FORMAT"),
},
cli.BoolFlag{
&cli.BoolFlag{
Name: ColorFlagName,
Usage: "Color the log output if in terminal mode",
EnvVar: opservice.PrefixEnvVar(envPrefix, "LOG_COLOR"),
EnvVars: opservice.PrefixEnvVar(envPrefix, "LOG_COLOR"),
},
}
}
......@@ -81,7 +81,7 @@ func DefaultCLIConfig() CLIConfig {
}
}
func ReadLocalCLIConfig(ctx *cli.Context) CLIConfig {
func ReadCLIConfig(ctx *cli.Context) CLIConfig {
cfg := DefaultCLIConfig()
cfg.Level = ctx.String(LevelFlagName)
cfg.Format = ctx.String(FormatFlagName)
......@@ -91,16 +91,6 @@ func ReadLocalCLIConfig(ctx *cli.Context) CLIConfig {
return cfg
}
func ReadCLIConfig(ctx *cli.Context) CLIConfig {
cfg := DefaultCLIConfig()
cfg.Level = ctx.GlobalString(LevelFlagName)
cfg.Format = ctx.GlobalString(FormatFlagName)
if ctx.IsSet(ColorFlagName) {
cfg.Color = ctx.GlobalBool(ColorFlagName)
}
return cfg
}
// Format turns a string and color into a structured Format object
func Format(lf string, color bool) log.Format {
switch lf {
......
......@@ -6,7 +6,7 @@ import (
opservice "github.com/ethereum-optimism/optimism/op-service"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
const (
......@@ -17,22 +17,22 @@ const (
func CLIFlags(envPrefix string) []cli.Flag {
return []cli.Flag{
cli.BoolFlag{
&cli.BoolFlag{
Name: EnabledFlagName,
Usage: "Enable the metrics server",
EnvVar: opservice.PrefixEnvVar(envPrefix, "METRICS_ENABLED"),
EnvVars: opservice.PrefixEnvVar(envPrefix, "METRICS_ENABLED"),
},
cli.StringFlag{
&cli.StringFlag{
Name: ListenAddrFlagName,
Usage: "Metrics listening address",
Value: "0.0.0.0",
EnvVar: opservice.PrefixEnvVar(envPrefix, "METRICS_ADDR"),
EnvVars: opservice.PrefixEnvVar(envPrefix, "METRICS_ADDR"),
},
cli.IntFlag{
&cli.IntFlag{
Name: PortFlagName,
Usage: "Metrics listening port",
Value: 7300,
EnvVar: opservice.PrefixEnvVar(envPrefix, "METRICS_PORT"),
EnvVars: opservice.PrefixEnvVar(envPrefix, "METRICS_PORT"),
},
}
}
......@@ -56,14 +56,6 @@ func (m CLIConfig) Check() error {
}
func ReadCLIConfig(ctx *cli.Context) CLIConfig {
return CLIConfig{
Enabled: ctx.GlobalBool(EnabledFlagName),
ListenAddr: ctx.GlobalString(ListenAddrFlagName),
ListenPort: ctx.GlobalInt(PortFlagName),
}
}
func ReadLocalCLIConfig(ctx *cli.Context) CLIConfig {
return CLIConfig{
Enabled: ctx.Bool(EnabledFlagName),
ListenAddr: ctx.String(ListenAddrFlagName),
......
......@@ -5,7 +5,7 @@ import (
"math"
opservice "github.com/ethereum-optimism/optimism/op-service"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
const (
......@@ -16,22 +16,22 @@ const (
func CLIFlags(envPrefix string) []cli.Flag {
return []cli.Flag{
cli.BoolFlag{
&cli.BoolFlag{
Name: EnabledFlagName,
Usage: "Enable the pprof server",
EnvVar: opservice.PrefixEnvVar(envPrefix, "PPROF_ENABLED"),
EnvVars: opservice.PrefixEnvVar(envPrefix, "PPROF_ENABLED"),
},
cli.StringFlag{
&cli.StringFlag{
Name: ListenAddrFlagName,
Usage: "pprof listening address",
Value: "0.0.0.0",
EnvVar: opservice.PrefixEnvVar(envPrefix, "PPROF_ADDR"),
EnvVars: opservice.PrefixEnvVar(envPrefix, "PPROF_ADDR"),
},
cli.IntFlag{
&cli.IntFlag{
Name: PortFlagName,
Usage: "pprof listening port",
Value: 6060,
EnvVar: opservice.PrefixEnvVar(envPrefix, "PPROF_PORT"),
EnvVars: opservice.PrefixEnvVar(envPrefix, "PPROF_PORT"),
},
}
}
......@@ -56,8 +56,8 @@ func (m CLIConfig) Check() error {
func ReadCLIConfig(ctx *cli.Context) CLIConfig {
return CLIConfig{
Enabled: ctx.GlobalBool(EnabledFlagName),
ListenAddr: ctx.GlobalString(ListenAddrFlagName),
ListenPort: ctx.GlobalInt(PortFlagName),
Enabled: ctx.Bool(EnabledFlagName),
ListenAddr: ctx.String(ListenAddrFlagName),
ListenPort: ctx.Int(PortFlagName),
}
}
......@@ -5,7 +5,7 @@ import (
"math"
opservice "github.com/ethereum-optimism/optimism/op-service"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
const (
......@@ -15,17 +15,17 @@ const (
func CLIFlags(envPrefix string) []cli.Flag {
return []cli.Flag{
cli.StringFlag{
&cli.StringFlag{
Name: ListenAddrFlagName,
Usage: "rpc listening address",
Value: "0.0.0.0",
EnvVar: opservice.PrefixEnvVar(envPrefix, "RPC_ADDR"),
EnvVars: opservice.PrefixEnvVar(envPrefix, "RPC_ADDR"),
},
cli.IntFlag{
&cli.IntFlag{
Name: PortFlagName,
Usage: "rpc listening port",
Value: 8545,
EnvVar: opservice.PrefixEnvVar(envPrefix, "RPC_PORT"),
EnvVars: opservice.PrefixEnvVar(envPrefix, "RPC_PORT"),
},
}
}
......@@ -45,7 +45,7 @@ func (c CLIConfig) Check() error {
func ReadCLIConfig(ctx *cli.Context) CLIConfig {
return CLIConfig{
ListenAddr: ctx.GlobalString(ListenAddrFlagName),
ListenPort: ctx.GlobalInt(PortFlagName),
ListenAddr: ctx.String(ListenAddrFlagName),
ListenPort: ctx.Int(PortFlagName),
}
}
......@@ -6,7 +6,7 @@ import (
"fmt"
"strings"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
opservice "github.com/ethereum-optimism/optimism/op-service"
)
......@@ -29,24 +29,27 @@ func CLIFlagsWithFlagPrefix(envPrefix string, flagPrefix string) []cli.Flag {
prefixFunc := func(flagName string) string {
return strings.Trim(fmt.Sprintf("%s.%s", flagPrefix, flagName), ".")
}
prefixEnvVars := func(name string) []string {
return opservice.PrefixEnvVar(envPrefix, name)
}
return []cli.Flag{
cli.StringFlag{
&cli.StringFlag{
Name: prefixFunc(TLSCaCertFlagName),
Usage: "tls ca cert path",
Value: "tls/ca.crt",
EnvVar: opservice.PrefixEnvVar(envPrefix, "TLS_CA"),
EnvVars: prefixEnvVars("TLS_CA"),
},
cli.StringFlag{
&cli.StringFlag{
Name: prefixFunc(TLSCertFlagName),
Usage: "tls cert path",
Value: "tls/tls.crt",
EnvVar: opservice.PrefixEnvVar(envPrefix, "TLS_CERT"),
EnvVars: prefixEnvVars("TLS_CERT"),
},
cli.StringFlag{
&cli.StringFlag{
Name: prefixFunc(TLSKeyFlagName),
Usage: "tls key",
Value: "tls/tls.key",
EnvVar: opservice.PrefixEnvVar(envPrefix, "TLS_KEY"),
EnvVars: prefixEnvVars("TLS_KEY"),
},
}
}
......@@ -73,9 +76,9 @@ func (c CLIConfig) TLSEnabled() bool {
// This should be used for server TLS configs, or when client and server tls configs are the same
func ReadCLIConfig(ctx *cli.Context) CLIConfig {
return CLIConfig{
TLSCaCert: ctx.GlobalString(TLSCaCertFlagName),
TLSCert: ctx.GlobalString(TLSCertFlagName),
TLSKey: ctx.GlobalString(TLSKeyFlagName),
TLSCaCert: ctx.String(TLSCaCertFlagName),
TLSCert: ctx.String(TLSCertFlagName),
TLSKey: ctx.String(TLSKeyFlagName),
}
}
......@@ -86,8 +89,8 @@ func ReadCLIConfigWithPrefix(ctx *cli.Context, flagPrefix string) CLIConfig {
return strings.Trim(fmt.Sprintf("%s.%s", flagPrefix, flagName), ".")
}
return CLIConfig{
TLSCaCert: ctx.GlobalString(prefixFunc(TLSCaCertFlagName)),
TLSCert: ctx.GlobalString(prefixFunc(TLSCertFlagName)),
TLSKey: ctx.GlobalString(prefixFunc(TLSKeyFlagName)),
TLSCaCert: ctx.String(prefixFunc(TLSCaCertFlagName)),
TLSCert: ctx.String(prefixFunc(TLSCertFlagName)),
TLSKey: ctx.String(prefixFunc(TLSKeyFlagName)),
}
}
......@@ -13,7 +13,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
const (
......@@ -34,78 +34,81 @@ const (
)
var (
SequencerHDPathFlag = cli.StringFlag{
SequencerHDPathFlag = &cli.StringFlag{
Name: "sequencer-hd-path",
Usage: "DEPRECATED: The HD path used to derive the sequencer wallet from the " +
"mnemonic. The mnemonic flag must also be set.",
EnvVar: "OP_BATCHER_SEQUENCER_HD_PATH",
EnvVars: []string{"OP_BATCHER_SEQUENCER_HD_PATH"},
}
L2OutputHDPathFlag = cli.StringFlag{
L2OutputHDPathFlag = &cli.StringFlag{
Name: "l2-output-hd-path",
Usage: "DEPRECATED:The HD path used to derive the l2output wallet from the " +
"mnemonic. The mnemonic flag must also be set.",
EnvVar: "OP_PROPOSER_L2_OUTPUT_HD_PATH",
EnvVars: []string{"OP_PROPOSER_L2_OUTPUT_HD_PATH"},
}
)
func CLIFlags(envPrefix string) []cli.Flag {
prefixEnvVars := func(name string) []string {
return opservice.PrefixEnvVar(envPrefix, name)
}
return append([]cli.Flag{
cli.StringFlag{
&cli.StringFlag{
Name: MnemonicFlagName,
Usage: "The mnemonic used to derive the wallets for either the service",
EnvVar: opservice.PrefixEnvVar(envPrefix, "MNEMONIC"),
EnvVars: prefixEnvVars("MNEMONIC"),
},
cli.StringFlag{
&cli.StringFlag{
Name: HDPathFlagName,
Usage: "The HD path used to derive the sequencer wallet from the mnemonic. The mnemonic flag must also be set.",
EnvVar: opservice.PrefixEnvVar(envPrefix, "HD_PATH"),
EnvVars: prefixEnvVars("HD_PATH"),
},
cli.StringFlag{
&cli.StringFlag{
Name: PrivateKeyFlagName,
Usage: "The private key to use with the service. Must not be used with mnemonic.",
EnvVar: opservice.PrefixEnvVar(envPrefix, "PRIVATE_KEY"),
EnvVars: prefixEnvVars("PRIVATE_KEY"),
},
cli.Uint64Flag{
&cli.Uint64Flag{
Name: NumConfirmationsFlagName,
Usage: "Number of confirmations which we will wait after sending a transaction",
Value: 10,
EnvVar: opservice.PrefixEnvVar(envPrefix, "NUM_CONFIRMATIONS"),
EnvVars: prefixEnvVars("NUM_CONFIRMATIONS"),
},
cli.Uint64Flag{
&cli.Uint64Flag{
Name: SafeAbortNonceTooLowCountFlagName,
Usage: "Number of ErrNonceTooLow observations required to give up on a tx at a particular nonce without receiving confirmation",
Value: 3,
EnvVar: opservice.PrefixEnvVar(envPrefix, "SAFE_ABORT_NONCE_TOO_LOW_COUNT"),
EnvVars: prefixEnvVars("SAFE_ABORT_NONCE_TOO_LOW_COUNT"),
},
cli.DurationFlag{
&cli.DurationFlag{
Name: ResubmissionTimeoutFlagName,
Usage: "Duration we will wait before resubmitting a transaction to L1",
Value: 48 * time.Second,
EnvVar: opservice.PrefixEnvVar(envPrefix, "RESUBMISSION_TIMEOUT"),
EnvVars: prefixEnvVars("RESUBMISSION_TIMEOUT"),
},
cli.DurationFlag{
&cli.DurationFlag{
Name: NetworkTimeoutFlagName,
Usage: "Timeout for all network operations",
Value: 2 * time.Second,
EnvVar: opservice.PrefixEnvVar(envPrefix, "NETWORK_TIMEOUT"),
EnvVars: prefixEnvVars("NETWORK_TIMEOUT"),
},
cli.DurationFlag{
&cli.DurationFlag{
Name: TxSendTimeoutFlagName,
Usage: "Timeout for sending transactions. If 0 it is disabled.",
Value: 0,
EnvVar: opservice.PrefixEnvVar(envPrefix, "TXMGR_TX_SEND_TIMEOUT"),
EnvVars: prefixEnvVars("TXMGR_TX_SEND_TIMEOUT"),
},
cli.DurationFlag{
&cli.DurationFlag{
Name: TxNotInMempoolTimeoutFlagName,
Usage: "Timeout for aborting a tx send if the tx does not make it to the mempool.",
Value: 2 * time.Minute,
EnvVar: opservice.PrefixEnvVar(envPrefix, "TXMGR_TX_NOT_IN_MEMPOOL_TIMEOUT"),
EnvVars: prefixEnvVars("TXMGR_TX_NOT_IN_MEMPOOL_TIMEOUT"),
},
cli.DurationFlag{
&cli.DurationFlag{
Name: ReceiptQueryIntervalFlagName,
Usage: "Frequency to poll for receipts",
Value: 12 * time.Second,
EnvVar: opservice.PrefixEnvVar(envPrefix, "TXMGR_RECEIPT_QUERY_INTERVAL"),
EnvVars: prefixEnvVars("TXMGR_RECEIPT_QUERY_INTERVAL"),
},
}, client.CLIFlags(envPrefix)...)
}
......@@ -157,20 +160,20 @@ func (m CLIConfig) Check() error {
func ReadCLIConfig(ctx *cli.Context) CLIConfig {
return CLIConfig{
L1RPCURL: ctx.GlobalString(L1RPCFlagName),
Mnemonic: ctx.GlobalString(MnemonicFlagName),
HDPath: ctx.GlobalString(HDPathFlagName),
SequencerHDPath: ctx.GlobalString(SequencerHDPathFlag.Name),
L2OutputHDPath: ctx.GlobalString(L2OutputHDPathFlag.Name),
PrivateKey: ctx.GlobalString(PrivateKeyFlagName),
L1RPCURL: ctx.String(L1RPCFlagName),
Mnemonic: ctx.String(MnemonicFlagName),
HDPath: ctx.String(HDPathFlagName),
SequencerHDPath: ctx.String(SequencerHDPathFlag.Name),
L2OutputHDPath: ctx.String(L2OutputHDPathFlag.Name),
PrivateKey: ctx.String(PrivateKeyFlagName),
SignerCLIConfig: client.ReadCLIConfig(ctx),
NumConfirmations: ctx.GlobalUint64(NumConfirmationsFlagName),
SafeAbortNonceTooLowCount: ctx.GlobalUint64(SafeAbortNonceTooLowCountFlagName),
ResubmissionTimeout: ctx.GlobalDuration(ResubmissionTimeoutFlagName),
ReceiptQueryInterval: ctx.GlobalDuration(ReceiptQueryIntervalFlagName),
NetworkTimeout: ctx.GlobalDuration(NetworkTimeoutFlagName),
TxSendTimeout: ctx.GlobalDuration(TxSendTimeoutFlagName),
TxNotInMempoolTimeout: ctx.GlobalDuration(TxNotInMempoolTimeoutFlagName),
NumConfirmations: ctx.Uint64(NumConfirmationsFlagName),
SafeAbortNonceTooLowCount: ctx.Uint64(SafeAbortNonceTooLowCountFlagName),
ResubmissionTimeout: ctx.Duration(ResubmissionTimeoutFlagName),
ReceiptQueryInterval: ctx.Duration(ReceiptQueryIntervalFlagName),
NetworkTimeout: ctx.Duration(NetworkTimeoutFlagName),
TxSendTimeout: ctx.Duration(TxSendTimeoutFlagName),
TxNotInMempoolTimeout: ctx.Duration(TxNotInMempoolTimeoutFlagName),
}
}
......
......@@ -13,11 +13,13 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
func PrefixEnvVar(prefix, suffix string) string {
return prefix + "_" + suffix
// PrefixEnvVar adds a prefix to the environment variable,
// and returns the env-var wrapped in a slice for usage with urfave CLI v2.
func PrefixEnvVar(prefix, suffix string) []string {
return []string{prefix + "_" + suffix}
}
// ValidateEnvVars logs all env vars that are found where the env var is
......@@ -33,8 +35,9 @@ func ValidateEnvVars(prefix string, flags []cli.Flag, log log.Logger) {
func cliFlagsToEnvVars(flags []cli.Flag) map[string]struct{} {
definedEnvVars := make(map[string]struct{})
for _, flag := range flags {
envVarField := reflect.ValueOf(flag).FieldByName("EnvVar")
if envVarField.IsValid() {
envVars := reflect.ValueOf(flag).Elem().FieldByName("EnvVars")
for i := 0; i < envVars.Len(); i++ {
envVarField := envVars.Index(i)
definedEnvVars[envVarField.String()] = struct{}{}
}
}
......
......@@ -4,16 +4,16 @@ import (
"testing"
"github.com/stretchr/testify/require"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
func TestCLIFlagsToEnvVars(t *testing.T) {
flags := []cli.Flag{
cli.StringFlag{
&cli.StringFlag{
Name: "test",
EnvVar: "OP_NODE_TEST_VAR",
EnvVars: []string{"OP_NODE_TEST_VAR"},
},
cli.IntFlag{
&cli.IntFlag{
Name: "no env var",
},
}
......
......@@ -3,7 +3,7 @@ package client
import (
"errors"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
opservice "github.com/ethereum-optimism/optimism/op-service"
optls "github.com/ethereum-optimism/optimism/op-service/tls"
......@@ -17,15 +17,15 @@ const (
func CLIFlags(envPrefix string) []cli.Flag {
envPrefix += "_SIGNER"
flags := []cli.Flag{
cli.StringFlag{
&cli.StringFlag{
Name: EndpointFlagName,
Usage: "Signer endpoint the client will connect to",
EnvVar: opservice.PrefixEnvVar(envPrefix, "ENDPOINT"),
EnvVars: opservice.PrefixEnvVar(envPrefix, "ENDPOINT"),
},
cli.StringFlag{
&cli.StringFlag{
Name: AddressFlagName,
Usage: "Address the signer is signing transactions for",
EnvVar: opservice.PrefixEnvVar(envPrefix, "ADDRESS"),
EnvVars: opservice.PrefixEnvVar(envPrefix, "ADDRESS"),
},
}
flags = append(flags, optls.CLIFlagsWithFlagPrefix(envPrefix, "signer")...)
......
......@@ -5,7 +5,7 @@ import (
"fmt"
"os"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
"github.com/ethereum/go-ethereum/log"
......@@ -29,7 +29,7 @@ func main() {
app.Before = func(c *cli.Context) error {
log.Root().SetHandler(
log.LvlFilterHandler(
oplog.Level(c.GlobalString(wheel.GlobalGethLogLvlFlag.Name)),
oplog.Level(c.String(wheel.GlobalGethLogLvlFlag.Name)),
log.StreamHandler(os.Stdout, log.TerminalFormat(true)),
),
)
......@@ -40,7 +40,7 @@ func main() {
})
app.Writer = os.Stdout
app.ErrWriter = os.Stderr
app.Commands = []cli.Command{
app.Commands = []*cli.Command{
wheel.CheatCmd,
wheel.EngineCmd,
}
......
This diff is collapsed.
......@@ -10,8 +10,8 @@ with priority on the `L2OutputOracle` and `OptimismPortal`.
#### Comments
We use [Seaport](https://github.com/ProjectOpenSea/seaport/blob/main/contracts/Seaport.sol)-style comments with some minor modifications.
Some basic rules:
Optimism smart contracts follow the triple-slash [solidity natspec comment style](https://docs.soliditylang.org/en/develop/natspec-format.html#documentation-example)
with additional rules. These are:
- Always use `@notice` since it has the same general effect as `@dev` but avoids confusion about when to use one over the other.
- Include a newline between `@notice` and the first `@param`.
......@@ -28,13 +28,19 @@ We also have the following custom tags:
#### Errors
- Use `require` statements when making simple assertions.
- Use `revert` if throwing an error where an assertion is not being made (no custom errors). See [here](https://github.com/ethereum-optimism/optimism/blob/861ae315a6db698a8c0adb1f8eab8311fd96be4c/packages/contracts-bedrock/contracts/L2/OVM_ETH.sol#L31) for an example of this in practice.
- Use `revert(string)` if throwing an error where an assertion is not being made (no custom errors).
See [here](https://github.com/ethereum-optimism/optimism/blob/861ae315a6db698a8c0adb1f8eab8311fd96be4c/packages/contracts-bedrock/contracts/L2/OVM_ETH.sol#L31)
for an example of this in practice.
- Error strings MUST have the format `"{ContractName}: {message}"` where `message` is a lower case string.
#### Function Parameters
- Function parameters should be prefixed with an underscore.
#### Function Return Arguments
- Arguments returned by functions should be suffixed with an underscore.
#### Event Parameters
- Event parameters should NOT be prefixed with an underscore.
......@@ -42,16 +48,21 @@ We also have the following custom tags:
#### Spacers
We use spacer variables to account for old storage slots that are no longer being used.
The name of a spacer variable MUST be in the format `spacer_<slot>_<offset>_<length>` where `<slot>` is the original storage slot number, `<offset>` is the original offset position within the storage slot, and `<length>` is the original size of the variable.
The name of a spacer variable MUST be in the format `spacer_<slot>_<offset>_<length>` where
`<slot>` is the original storage slot number, `<offset>` is the original offset position
within the storage slot, and `<length>` is the original size of the variable.
Spacers MUST be `private`.
### Proxy by Default
All contracts should be assumed to live behind proxies (except in certain special circumstances).
This means that new contracts MUST be built under the assumption of upgradeability.
We use a minimal [`Proxy`](./contracts/universal/Proxy.sol) contract designed to be owned by a corresponding [`ProxyAdmin`](./contracts/universal/ProxyAdmin.sol) which follow the interfaces of OpenZeppelin's `Proxy` and `ProxyAdmin` contracts, respectively.
We use a minimal [`Proxy`](./contracts/universal/Proxy.sol) contract designed to be owned by a
corresponding [`ProxyAdmin`](./contracts/universal/ProxyAdmin.sol) which follow the interfaces
of OpenZeppelin's `Proxy` and `ProxyAdmin` contracts, respectively.
Unless explicitly discussed otherwise, you MUST include the following basic upgradeability pattern for each new implementation contract:
Unless explicitly discussed otherwise, you MUST include the following basic upgradeability
pattern for each new implementation contract:
1. Extend OpenZeppelin's `Initializable` base contract.
2. Include a `uint8 public constant VERSION = X` at the TOP of your contract.
......@@ -60,11 +71,13 @@ Unless explicitly discussed otherwise, you MUST include the following basic upgr
### Versioning
All (non-library and non-abstract) contracts MUST extend the `Semver` base contract which exposes a `version()` function that returns a semver-compliant version string.
During the Bedrock development process the `Semver` value for all contracts SHOULD return `0.0.1` (this is not particularly important, but it's an easy standard to follow).
When the initial Bedrock upgrade is released, the `Semver` value MUST be updated to `1.0.0`.
All (non-library and non-abstract) contracts MUST extend the `Semver` base contract which
exposes a `version()` function that returns a semver-compliant version string.
Contracts must have a `Semver` of `1.0.0` or greater to be production ready. Contracts
with `Semver` values less than `1.0.0` should only be used locally or on devnets.
After the initial Bedrock upgrade, contracts MUST use the following versioning scheme:
Additionally, contracts MUST use the following versioning scheme:
- `patch` releases are to be used only for changes that do NOT modify contract bytecode (such as updating comments).
- `minor` releases are to be used for changes that modify bytecode OR changes that expand the contract ABI provided that these changes do NOT break the existing interface.
......@@ -72,7 +85,8 @@ After the initial Bedrock upgrade, contracts MUST use the following versioning s
#### Exceptions
We have made an exception to the `Semver` rule for the `WETH` contract to avoid making changes to a well-known, simple, and recognizable contract.
We have made an exception to the `Semver` rule for the `WETH` contract to avoid
making changes to a well-known, simple, and recognizable contract.
### Dependencies
......
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