From f45f8d4cb5158e1f6c4406ac9f3c0150c3fe80d4 Mon Sep 17 00:00:00 2001 From: Joshua Gutow Date: Wed, 12 Apr 2023 16:34:58 -0700 Subject: [PATCH] op-batcher,proposer: Add doc/metrics subcommand --- op-batcher/batcher/batch_submitter.go | 4 ++ op-batcher/cmd/doc/cmd.go | 54 ++++++++++++++++++++++++++ op-batcher/cmd/main.go | 12 ++++-- op-batcher/flags/flags.go | 38 ++++++++++-------- op-node/flags/flags.go | 26 ++----------- op-proposer/cmd/doc/cmd.go | 54 ++++++++++++++++++++++++++ op-proposer/cmd/main.go | 9 ++++- op-proposer/flags/flags.go | 35 ++++++++++------- op-proposer/metrics/metrics.go | 4 ++ op-service/txmgr/metrics/tx_metrics.go | 4 +- 10 files changed, 182 insertions(+), 58 deletions(-) create mode 100644 op-batcher/cmd/doc/cmd.go create mode 100644 op-proposer/cmd/doc/cmd.go diff --git a/op-batcher/batcher/batch_submitter.go b/op-batcher/batcher/batch_submitter.go index 1dd27aa905d3..a1b2c954b4a5 100644 --- a/op-batcher/batcher/batch_submitter.go +++ b/op-batcher/batcher/batch_submitter.go @@ -12,6 +12,7 @@ import ( gethrpc "github.com/ethereum/go-ethereum/rpc" "github.com/urfave/cli" + "github.com/ethereum-optimism/optimism/op-batcher/flags" "github.com/ethereum-optimism/optimism/op-batcher/metrics" "github.com/ethereum-optimism/optimism/op-batcher/rpc" oplog "github.com/ethereum-optimism/optimism/op-service/log" @@ -30,6 +31,9 @@ const ( // of a closure allows the parameters bound to the top-level main package, e.g. // GitVersion, to be captured and used once the function is executed. func Main(version string, cliCtx *cli.Context) error { + if err := flags.CheckRequired(cliCtx); err != nil { + return err + } cfg := NewConfig(cliCtx) if err := cfg.Check(); err != nil { return fmt.Errorf("invalid CLI flags: %w", err) diff --git a/op-batcher/cmd/doc/cmd.go b/op-batcher/cmd/doc/cmd.go new file mode 100644 index 000000000000..f5c8a6f5833c --- /dev/null +++ b/op-batcher/cmd/doc/cmd.go @@ -0,0 +1,54 @@ +package doc + +import ( + "encoding/json" + "fmt" + "os" + "strings" + + "github.com/ethereum-optimism/optimism/op-batcher/metrics" + "github.com/olekukonko/tablewriter" + "github.com/urfave/cli" +) + +var Subcommands = cli.Commands{ + { + Name: "metrics", + Usage: "Dumps a list of supported metrics to stdout", + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "format", + Value: "markdown", + Usage: "Output format (json|markdown)", + }, + }, + Action: func(ctx *cli.Context) error { + m := metrics.NewMetrics("default") + supportedMetrics := m.Document() + format := ctx.String("format") + + if format != "markdown" && format != "json" { + return fmt.Errorf("invalid format: %s", format) + } + + if format == "json" { + enc := json.NewEncoder(os.Stdout) + return enc.Encode(supportedMetrics) + } + + table := tablewriter.NewWriter(os.Stdout) + table.SetBorders(tablewriter.Border{Left: true, Top: false, Right: true, Bottom: false}) + table.SetCenterSeparator("|") + table.SetAutoWrapText(false) + table.SetHeader([]string{"Metric", "Description", "Labels", "Type"}) + var data [][]string + for _, metric := range supportedMetrics { + labels := strings.Join(metric.Labels, ",") + data = append(data, []string{metric.Name, metric.Help, labels, metric.Type}) + } + table.AppendBulk(data) + table.Render() + return nil + }, + }, +} diff --git a/op-batcher/cmd/main.go b/op-batcher/cmd/main.go index 84492a460f2b..69b5a494ac81 100644 --- a/op-batcher/cmd/main.go +++ b/op-batcher/cmd/main.go @@ -7,6 +7,7 @@ import ( "github.com/urfave/cli" "github.com/ethereum-optimism/optimism/op-batcher/batcher" + "github.com/ethereum-optimism/optimism/op-batcher/cmd/doc" "github.com/ethereum-optimism/optimism/op-batcher/flags" oplog "github.com/ethereum-optimism/optimism/op-service/log" "github.com/ethereum/go-ethereum/log" @@ -26,10 +27,15 @@ func main() { app.Version = fmt.Sprintf("%s-%s-%s", Version, GitCommit, GitDate) app.Name = "op-batcher" app.Usage = "Batch Submitter Service" - app.Description = "Service for generating and submitting L2 tx batches " + - "to L1" - + app.Description = "Service for generating and submitting L2 tx batches to L1" app.Action = curryMain(Version) + app.Commands = []cli.Command{ + { + Name: "doc", + Subcommands: doc.Subcommands, + }, + } + err := app.Run(os.Args) if err != nil { log.Crit("Application failed", "message", err) diff --git a/op-batcher/flags/flags.go b/op-batcher/flags/flags.go index 30e13969e460..a328bec1efe6 100644 --- a/op-batcher/flags/flags.go +++ b/op-batcher/flags/flags.go @@ -1,6 +1,8 @@ package flags import ( + "fmt" + "github.com/urfave/cli" "github.com/ethereum-optimism/optimism/op-batcher/rpc" @@ -17,37 +19,32 @@ const envVarPrefix = "OP_BATCHER" var ( // Required flags L1EthRpcFlag = cli.StringFlag{ - Name: "l1-eth-rpc", - Usage: "HTTP provider URL for L1", - Required: true, - EnvVar: opservice.PrefixEnvVar(envVarPrefix, "L1_ETH_RPC"), + Name: "l1-eth-rpc", + Usage: "HTTP provider URL for L1", + EnvVar: opservice.PrefixEnvVar(envVarPrefix, "L1_ETH_RPC"), } L2EthRpcFlag = cli.StringFlag{ - Name: "l2-eth-rpc", - Usage: "HTTP provider URL for L2 execution engine", - Required: true, - EnvVar: opservice.PrefixEnvVar(envVarPrefix, "L2_ETH_RPC"), + Name: "l2-eth-rpc", + Usage: "HTTP provider URL for L2 execution engine", + EnvVar: opservice.PrefixEnvVar(envVarPrefix, "L2_ETH_RPC"), } RollupRpcFlag = cli.StringFlag{ - Name: "rollup-rpc", - Usage: "HTTP provider URL for Rollup node", - Required: true, - EnvVar: opservice.PrefixEnvVar(envVarPrefix, "ROLLUP_RPC"), + Name: "rollup-rpc", + Usage: "HTTP provider URL for Rollup node", + EnvVar: opservice.PrefixEnvVar(envVarPrefix, "ROLLUP_RPC"), } 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.", - Required: true, - EnvVar: opservice.PrefixEnvVar(envVarPrefix, "SUB_SAFETY_MARGIN"), + EnvVar: opservice.PrefixEnvVar(envVarPrefix, "SUB_SAFETY_MARGIN"), } PollIntervalFlag = cli.DurationFlag{ Name: "poll-interval", Usage: "Delay between querying L2 for more transactions and " + "creating a new batch", - Required: true, - EnvVar: opservice.PrefixEnvVar(envVarPrefix, "POLL_INTERVAL"), + EnvVar: opservice.PrefixEnvVar(envVarPrefix, "POLL_INTERVAL"), } // Optional flags @@ -121,3 +118,12 @@ func init() { // Flags contains the list of configuration options available to the binary. 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()) + } + } + return nil +} diff --git a/op-node/flags/flags.go b/op-node/flags/flags.go index eaadc2241b9e..71baca5abea0 100644 --- a/op-node/flags/flags.go +++ b/op-node/flags/flags.go @@ -256,28 +256,10 @@ func init() { } func CheckRequired(ctx *cli.Context) error { - l1NodeAddr := ctx.GlobalString(L1NodeAddr.Name) - if l1NodeAddr == "" { - return fmt.Errorf("flag %s is required", L1NodeAddr.Name) - } - l2EngineAddr := ctx.GlobalString(L2EngineAddr.Name) - if l2EngineAddr == "" { - return fmt.Errorf("flag %s is required", L2EngineAddr.Name) - } - rollupConfig := ctx.GlobalString(RollupConfig.Name) - network := ctx.GlobalString(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) - } - rpcListenAddr := ctx.GlobalString(RPCListenAddr.Name) - if rpcListenAddr == "" { - return fmt.Errorf("flag %s is required", RPCListenAddr.Name) - } - if !ctx.GlobalIsSet(RPCListenPort.Name) { - return fmt.Errorf("flag %s is required", RPCListenPort.Name) + for _, f := range requiredFlags { + if !ctx.GlobalIsSet(f.GetName()) { + return fmt.Errorf("flag %s is required", f.GetName) + } } return nil } diff --git a/op-proposer/cmd/doc/cmd.go b/op-proposer/cmd/doc/cmd.go new file mode 100644 index 000000000000..2eb135c7e53a --- /dev/null +++ b/op-proposer/cmd/doc/cmd.go @@ -0,0 +1,54 @@ +package doc + +import ( + "encoding/json" + "fmt" + "os" + "strings" + + "github.com/ethereum-optimism/optimism/op-proposer/metrics" + "github.com/olekukonko/tablewriter" + "github.com/urfave/cli" +) + +var Subcommands = cli.Commands{ + { + Name: "metrics", + Usage: "Dumps a list of supported metrics to stdout", + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "format", + Value: "markdown", + Usage: "Output format (json|markdown)", + }, + }, + Action: func(ctx *cli.Context) error { + m := metrics.NewMetrics("default") + supportedMetrics := m.Document() + format := ctx.String("format") + + if format != "markdown" && format != "json" { + return fmt.Errorf("invalid format: %s", format) + } + + if format == "json" { + enc := json.NewEncoder(os.Stdout) + return enc.Encode(supportedMetrics) + } + + table := tablewriter.NewWriter(os.Stdout) + table.SetBorders(tablewriter.Border{Left: true, Top: false, Right: true, Bottom: false}) + table.SetCenterSeparator("|") + table.SetAutoWrapText(false) + table.SetHeader([]string{"Metric", "Description", "Labels", "Type"}) + var data [][]string + for _, metric := range supportedMetrics { + labels := strings.Join(metric.Labels, ",") + data = append(data, []string{metric.Name, metric.Help, labels, metric.Type}) + } + table.AppendBulk(data) + table.Render() + return nil + }, + }, +} diff --git a/op-proposer/cmd/main.go b/op-proposer/cmd/main.go index 6bd26c39ab4b..03ece09f48b3 100644 --- a/op-proposer/cmd/main.go +++ b/op-proposer/cmd/main.go @@ -6,6 +6,7 @@ import ( "github.com/urfave/cli" + "github.com/ethereum-optimism/optimism/op-proposer/cmd/doc" "github.com/ethereum-optimism/optimism/op-proposer/flags" "github.com/ethereum-optimism/optimism/op-proposer/proposer" oplog "github.com/ethereum-optimism/optimism/op-service/log" @@ -27,8 +28,14 @@ func main() { app.Name = "op-proposer" 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{ + { + Name: "doc", + Subcommands: doc.Subcommands, + }, + } + err := app.Run(os.Args) if err != nil { log.Crit("Application failed", "message", err) diff --git a/op-proposer/flags/flags.go b/op-proposer/flags/flags.go index f8b8772b9483..dcd5d9cfd047 100644 --- a/op-proposer/flags/flags.go +++ b/op-proposer/flags/flags.go @@ -1,6 +1,8 @@ package flags import ( + "fmt" + "github.com/urfave/cli" opservice "github.com/ethereum-optimism/optimism/op-service" @@ -16,29 +18,25 @@ const envVarPrefix = "OP_PROPOSER" var ( // Required Flags L1EthRpcFlag = cli.StringFlag{ - Name: "l1-eth-rpc", - Usage: "HTTP provider URL for L1", - Required: true, - EnvVar: opservice.PrefixEnvVar(envVarPrefix, "L1_ETH_RPC"), + Name: "l1-eth-rpc", + Usage: "HTTP provider URL for L1", + EnvVar: opservice.PrefixEnvVar(envVarPrefix, "L1_ETH_RPC"), } RollupRpcFlag = cli.StringFlag{ - Name: "rollup-rpc", - Usage: "HTTP provider URL for the rollup node", - Required: true, - EnvVar: opservice.PrefixEnvVar(envVarPrefix, "ROLLUP_RPC"), + Name: "rollup-rpc", + Usage: "HTTP provider URL for the rollup node", + EnvVar: opservice.PrefixEnvVar(envVarPrefix, "ROLLUP_RPC"), } L2OOAddressFlag = cli.StringFlag{ - Name: "l2oo-address", - Usage: "Address of the L2OutputOracle contract", - Required: true, - EnvVar: opservice.PrefixEnvVar(envVarPrefix, "L2OO_ADDRESS"), + Name: "l2oo-address", + Usage: "Address of the L2OutputOracle contract", + EnvVar: opservice.PrefixEnvVar(envVarPrefix, "L2OO_ADDRESS"), } PollIntervalFlag = cli.DurationFlag{ Name: "poll-interval", Usage: "Delay between querying L2 for more transactions and " + "creating a new batch", - Required: true, - EnvVar: opservice.PrefixEnvVar(envVarPrefix, "POLL_INTERVAL"), + EnvVar: opservice.PrefixEnvVar(envVarPrefix, "POLL_INTERVAL"), } // Optional flags AllowNonFinalizedFlag = cli.BoolFlag{ @@ -74,3 +72,12 @@ func init() { // Flags contains the list of configuration options available to the binary. 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()) + } + } + return nil +} diff --git a/op-proposer/metrics/metrics.go b/op-proposer/metrics/metrics.go index 41767e8bc6ca..1f550eeedd73 100644 --- a/op-proposer/metrics/metrics.go +++ b/op-proposer/metrics/metrics.go @@ -104,3 +104,7 @@ const ( func (m *Metrics) RecordL2BlocksProposed(l2ref eth.L2BlockRef) { m.RecordL2Ref(BlockProposed, l2ref) } + +func (m *Metrics) Document() []opmetrics.DocumentedMetric { + return m.factory.Document() +} diff --git a/op-service/txmgr/metrics/tx_metrics.go b/op-service/txmgr/metrics/tx_metrics.go index 77186af3d30d..a9d75f664e98 100644 --- a/op-service/txmgr/metrics/tx_metrics.go +++ b/op-service/txmgr/metrics/tx_metrics.go @@ -85,7 +85,7 @@ func MakeTxMetrics(ns string, factory metrics.Factory) TxMetrics { txPublishError: factory.NewCounterVec(prometheus.CounterOpts{ Namespace: ns, Name: "tx_publish_error_count", - Help: "Count of publish errors. Labells are sanitized error strings", + Help: "Count of publish errors. Labels are sanitized error strings", Subsystem: "txmgr", }, []string{"error"}), confirmEvent: metrics.NewEventVec(factory, ns, "txmgr", "confirm", "tx confirm", []string{"status"}), @@ -93,7 +93,7 @@ func MakeTxMetrics(ns string, factory metrics.Factory) TxMetrics { rpcError: factory.NewCounter(prometheus.CounterOpts{ Namespace: ns, Name: "rpc_error_count", - Help: "Temporrary: Count of RPC errors (like timeouts) that have occurrred", + Help: "Temporary: Count of RPC errors (like timeouts) that have occurred", Subsystem: "txmgr", }), }