Commit f3da73dd authored by Francis Li's avatar Francis Li Committed by GitHub

[op-conductor] Initial setup (#8642)

* Initial setup for op-conductor

* Rework based on feedback

* Update README

* Update README

* Rework flags structure

* Add tests

* Add CI configs

* Add dockerfile

* Remove TODO

---------
Co-authored-by: default avatarMatthew Slipper <me@matthewslipper.com>
parent 7e571097
......@@ -1493,6 +1493,10 @@ workflows:
name: op-challenger-tests
module: op-challenger
requires: ["op-stack-go-lint"]
- go-test:
name: op-conductor-tests
module: op-conductor
requires: ["op-stack-go-lint"]
- go-test:
name: op-program-tests
module: op-program
......@@ -1547,6 +1551,7 @@ workflows:
- op-node-tests
- op-proposer-tests
- op-challenger-tests
- op-conductor-tests
- op-program-tests
- op-program-compat
- op-service-tests
......@@ -1589,6 +1594,12 @@ workflows:
docker_tags: <<pipeline.git.revision>>,<<pipeline.git.branch>>
requires: ['op-stack-go-docker-build']
save_image_tag: <<pipeline.git.revision>> # for devnet later
- docker-build:
name: op-conductor-docker-build
docker_name: op-conductor
docker_tags: <<pipeline.git.revision>>,<<pipeline.git.branch>>
requires: ['op-stack-go-docker-build']
save_image_tag: <<pipeline.git.revision>> # for devnet later
- docker-build:
name: op-heartbeat-docker-build
docker_name: op-heartbeat
......@@ -1717,6 +1728,21 @@ workflows:
release: true
context:
- oplabs-gcr-release
- docker-build:
name: op-conductor-docker-release
filters:
tags:
only: /^op-conductor\/v.*/
branches:
ignore: /.*/
docker_name: op-conductor
docker_tags: <<pipeline.git.revision>>
requires: ['op-stack-go-docker-build-release']
platforms: "linux/amd64,linux/arm64"
publish: true
release: true
context:
- oplabs-gcr-release
- docker-build:
name: op-ufm-docker-release
filters:
......@@ -1897,6 +1923,16 @@ workflows:
context:
- oplabs-gcr
- slack
- docker-build:
name: op-conductor-docker-publish
docker_name: op-conductor
docker_tags: <<pipeline.git.revision>>,<<pipeline.git.branch>>
requires: [ 'op-stack-go-docker-build-publish' ]
platforms: "linux/amd64,linux/arm64"
publish: true
context:
- oplabs-gcr
- slack
- docker-build:
name: op-heartbeat-docker-publish
docker_name: op-heartbeat
......
......@@ -93,6 +93,19 @@ target "op-challenger" {
tags = [for tag in split(",", IMAGE_TAGS) : "${REGISTRY}/${REPOSITORY}/op-challenger:${tag}"]
}
target "op-conductor" {
dockerfile = "Dockerfile"
context = "./op-conductor"
args = {
OP_STACK_GO_BUILDER = "op-stack-go"
}
contexts = {
op-stack-go: "target:op-stack-go"
}
platforms = split(",", PLATFORMS)
tags = [for tag in split(",", IMAGE_TAGS) : "${REGISTRY}/${REPOSITORY}/op-conductor:${tag}"]
}
target "op-heartbeat" {
dockerfile = "Dockerfile"
context = "./op-heartbeat"
......
package flags
import (
"fmt"
"strings"
"github.com/urfave/cli/v2"
"github.com/ethereum-optimism/optimism/op-node/chaincfg"
"github.com/ethereum-optimism/optimism/op-node/flags"
opservice "github.com/ethereum-optimism/optimism/op-service"
opflags "github.com/ethereum-optimism/optimism/op-service/flags"
oplog "github.com/ethereum-optimism/optimism/op-service/log"
opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics"
"github.com/urfave/cli/v2"
)
const envVarPrefix = "OP_BOOTNODE"
func prefixEnvVars(name string) []string {
return opservice.PrefixEnvVar(envVarPrefix, name)
}
var (
RollupConfig = &cli.StringFlag{
Name: flags.RollupConfig.Name,
Usage: "Rollup chain parameters",
EnvVars: prefixEnvVars("ROLLUP_CONFIG"),
}
Network = &cli.StringFlag{
Name: flags.Network.Name,
Usage: fmt.Sprintf("Predefined network selection. Available networks: %s", strings.Join(chaincfg.AvailableNetworks(), ", ")),
EnvVars: prefixEnvVars("NETWORK"),
}
)
var Flags = []cli.Flag{
RollupConfig,
Network,
opflags.CLINetworkFlag(envVarPrefix),
opflags.CLIRollupConfigFlag(envVarPrefix),
}
func init() {
......
ARG OP_STACK_GO_BUILDER=us-docker.pkg.dev/oplabs-tools-artifacts/images/op-stack-go:latest
FROM $OP_STACK_GO_BUILDER as builder
# See "make golang-docker" and /ops/docker/op-stack-go
FROM alpine:3.18
COPY --from=builder /usr/local/bin/op-conductor /usr/local/bin/op-conductor
CMD ["op-conductor"]
GITCOMMIT ?= $(shell git rev-parse HEAD)
GITDATE ?= $(shell git show -s --format='%ct')
VERSION := v0.0.0
LDFLAGSSTRING +=-X main.GitCommit=$(GITCOMMIT)
LDFLAGSSTRING +=-X main.GitDate=$(GITDATE)
LDFLAGSSTRING +=-X main.Version=$(VERSION)
LDFLAGS := -ldflags "$(LDFLAGSSTRING)"
op-conductor:
env GO111MODULE=on GOOS=$(TARGETOS) GOARCH=$(TARGETARCH) go build -v $(LDFLAGS) -o ./bin/op-conductor ./cmd
clean:
rm bin/op-conductor
test:
go test -v ./...
.PHONY: \
clean \
op-conductor \
test
# op-conductor
op-conductor is an auxiliary service designed to enhance the reliability and availability of a sequencer in
high-availability setups, thereby minimizing the risks associated with single point of failure.
It is important to note, however, that this design does not incorporate Byzantine fault tolerance.
This means it operates under the assumption that all participating nodes are honest.
## Summary
The design will provide below guarantees:
1. No unsafe reorgs
2. No unsafe head stall during network partition
3. 100% uptime with no more than 1 node failure (for a standard 3 node setup)
## Design
![op-conductor architecture](./assets/op-conductor.svg)
On a high level, op-conductor serves the following functions:
1. serves as a (raft) consensus layer participant to determine
1. leader of the sequencers
2. store latest unsafe block within its state machine.
2. serves rpc requests for
1. admin rpc for manual recovery scenarios such as stop leadership vote, remove itself from cluster, etc
2. health rpc for op-node to determine if it should allow publish txs / unsafe blocks
3. monitor sequencer (op-node) health
4. control loop => control sequencer (op-node) status (start / stop) based on different scenarios
This is initial version of README, more details will be added later.
This diff is collapsed.
package main
import (
"context"
"fmt"
"os"
"github.com/ethereum/go-ethereum/log"
"github.com/urfave/cli/v2"
"github.com/ethereum-optimism/optimism/op-conductor/conductor"
"github.com/ethereum-optimism/optimism/op-conductor/flags"
opservice "github.com/ethereum-optimism/optimism/op-service"
"github.com/ethereum-optimism/optimism/op-service/cliapp"
oplog "github.com/ethereum-optimism/optimism/op-service/log"
"github.com/ethereum-optimism/optimism/op-service/opio"
)
var (
Version = "v0.0.1"
GitCommit = ""
GitDate = ""
)
func main() {
oplog.SetupDefaults()
app := cli.NewApp()
app.Flags = cliapp.ProtectFlags(flags.Flags)
app.Version = opservice.FormatVersion(Version, GitCommit, GitDate, "")
app.Name = "op-conductor"
app.Usage = "Optimism Sequencer Conductor Service"
app.Description = "op-conductor help sequencer to run in highly available mode"
app.Action = cliapp.LifecycleCmd(OpConductorMain)
app.Commands = []*cli.Command{}
ctx := opio.WithInterruptBlocker(context.Background())
err := app.RunContext(ctx, os.Args)
if err != nil {
log.Crit("Application failed", "message", err)
}
}
func OpConductorMain(ctx *cli.Context, closeApp context.CancelCauseFunc) (cliapp.Lifecycle, error) {
logCfg := oplog.ReadCLIConfig(ctx)
log := oplog.NewLogger(oplog.AppOut(ctx), logCfg)
oplog.SetGlobalLogHandler(log.GetHandler())
opservice.ValidateEnvVars(flags.EnvVarPrefix, flags.Flags, log)
cfg, err := conductor.NewConfig(ctx, log)
if err != nil {
return nil, fmt.Errorf("failed to read config: %w", err)
}
c, err := conductor.New(ctx.Context, cfg, log, Version)
if err != nil {
return nil, fmt.Errorf("failed to create conductor: %w", err)
}
return c, nil
}
package conductor
import (
"fmt"
"github.com/ethereum/go-ethereum/log"
"github.com/pkg/errors"
"github.com/urfave/cli/v2"
"github.com/ethereum-optimism/optimism/op-conductor/flags"
opnode "github.com/ethereum-optimism/optimism/op-node"
"github.com/ethereum-optimism/optimism/op-node/rollup"
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"
oprpc "github.com/ethereum-optimism/optimism/op-service/rpc"
)
type Config struct {
// ConsensusAddr is the address to listen for consensus connections.
ConsensusAddr string
// ConsensusPort is the port to listen for consensus connections.
ConsensusPort int
// RaftServerID is the unique ID for this server used by raft consensus.
RaftServerID string
// RaftStorageDir is the directory to store raft data.
RaftStorageDir string
// RaftBootstrap is true if this node should bootstrap a new raft cluster.
RaftBootstrap bool
// NodeRPC is the HTTP provider URL for op-node.
NodeRPC string
RollupCfg rollup.Config
LogConfig oplog.CLIConfig
MetricsConfig opmetrics.CLIConfig
PprofConfig oppprof.CLIConfig
RPC oprpc.CLIConfig
}
// Check validates the CLIConfig.
func (c *Config) Check() error {
if c.ConsensusAddr == "" {
return fmt.Errorf("missing consensus address")
}
if c.ConsensusPort == 0 {
return fmt.Errorf("missing consensus port")
}
if c.RaftServerID == "" {
return fmt.Errorf("missing raft server ID")
}
if c.RaftStorageDir == "" {
return fmt.Errorf("missing raft storage directory")
}
if c.NodeRPC == "" {
return fmt.Errorf("missing node RPC")
}
if err := c.RollupCfg.Check(); err != nil {
return errors.Wrap(err, "invalid rollup config")
}
if err := c.MetricsConfig.Check(); err != nil {
return errors.Wrap(err, "invalid metrics config")
}
if err := c.PprofConfig.Check(); err != nil {
return errors.Wrap(err, "invalid pprof config")
}
if err := c.RPC.Check(); err != nil {
return errors.Wrap(err, "invalid rpc config")
}
return nil
}
// NewConfig parses the Config from the provided flags or environment variables.
func NewConfig(ctx *cli.Context, log log.Logger) (*Config, error) {
if err := flags.CheckRequired(ctx); err != nil {
return nil, errors.Wrap(err, "missing required flags")
}
rollupCfg, err := opnode.NewRollupConfig(log, ctx)
if err != nil {
return nil, errors.Wrap(err, "failed to load rollup config")
}
return &Config{
ConsensusAddr: ctx.String(flags.ConsensusAddr.Name),
ConsensusPort: ctx.Int(flags.ConsensusPort.Name),
RaftServerID: ctx.String(flags.RaftServerID.Name),
RaftStorageDir: ctx.String(flags.RaftStorageDir.Name),
NodeRPC: ctx.String(flags.NodeRPC.Name),
RollupCfg: *rollupCfg,
LogConfig: oplog.ReadCLIConfig(ctx),
MetricsConfig: opmetrics.ReadCLIConfig(ctx),
PprofConfig: oppprof.ReadCLIConfig(ctx),
RPC: oprpc.ReadCLIConfig(ctx),
}, nil
}
package conductor
import (
"context"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum-optimism/optimism/op-service/cliapp"
)
// New creates a new OpConductor instance.
func New(ctx context.Context, cfg *Config, log log.Logger, version string) (*OpConductor, error) {
panic("unimplemented")
}
// OpConductor represents a full conductor instance and its resources, it does:
// 1. performs health checks on sequencer
// 2. participate in consensus protocol for leader election
// 3. and control sequencer state based on leader and sequencer health status.
//
// OpConductor has three states:
// 1. running: it is running normally, which executes control loop and participates in leader election.
// 2. paused: control loop (sequencer start/stop) is paused, but it still participates in leader election.
// it is paused for disaster recovery situation
// 3. stopped: it is stopped, which means it is not participating in leader election and control loop. OpConductor cannot be started again from stopped mode.
type OpConductor struct{}
var _ cliapp.Lifecycle = (*OpConductor)(nil)
// Start implements cliapp.Lifecycle.
func (*OpConductor) Start(ctx context.Context) error {
panic("unimplemented")
}
// Stop implements cliapp.Lifecycle.
func (*OpConductor) Stop(ctx context.Context) error {
panic("unimplemented")
}
// Stopped implements cliapp.Lifecycle.
func (*OpConductor) Stopped() bool {
panic("unimplemented")
}
package flags
import (
"fmt"
"github.com/urfave/cli/v2"
opservice "github.com/ethereum-optimism/optimism/op-service"
opflags "github.com/ethereum-optimism/optimism/op-service/flags"
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"
oprpc "github.com/ethereum-optimism/optimism/op-service/rpc"
)
const EnvVarPrefix = "OP_CONDUCTOR"
var (
ConsensusAddr = &cli.StringFlag{
Name: "consensus.addr",
Usage: "Address to listen for consensus connections",
EnvVars: opservice.PrefixEnvVar(EnvVarPrefix, "CONSENSUS_ADDR"),
Value: "127.0.0.1",
}
ConsensusPort = &cli.IntFlag{
Name: "consensus.port",
Usage: "Port to listen for consensus connections",
EnvVars: opservice.PrefixEnvVar(EnvVarPrefix, "CONSENSUS_PORT"),
Value: 50050,
}
RaftServerID = &cli.StringFlag{
Name: "raft.server.id",
Usage: "Unique ID for this server used by raft consensus",
EnvVars: opservice.PrefixEnvVar(EnvVarPrefix, "RAFT_SERVER_ID"),
}
RaftStorageDir = &cli.StringFlag{
Name: "raft.storage.dir",
Usage: "Directory to store raft data",
EnvVars: opservice.PrefixEnvVar(EnvVarPrefix, "RAFT_STORAGE_DIR"),
}
NodeRPC = &cli.StringFlag{
Name: "node.rpc",
Usage: "HTTP provider URL for op-node",
EnvVars: opservice.PrefixEnvVar(EnvVarPrefix, "NODE_RPC"),
}
)
var requiredFlags = []cli.Flag{
ConsensusAddr,
ConsensusPort,
RaftServerID,
RaftStorageDir,
NodeRPC,
}
var optionalFlags = []cli.Flag{}
func init() {
optionalFlags = append(optionalFlags, oprpc.CLIFlags(EnvVarPrefix)...)
optionalFlags = append(optionalFlags, oplog.CLIFlags(EnvVarPrefix)...)
optionalFlags = append(optionalFlags, opmetrics.CLIFlags(EnvVarPrefix)...)
optionalFlags = append(optionalFlags, oppprof.CLIFlags(EnvVarPrefix)...)
optionalFlags = append(optionalFlags, opflags.CLIFlags(EnvVarPrefix)...)
Flags = append(requiredFlags, optionalFlags...)
}
var Flags []cli.Flag
func CheckRequired(ctx *cli.Context) error {
for _, f := range requiredFlags {
if !ctx.IsSet(f.Names()[0]) {
return fmt.Errorf("flag %s is required", f.Names()[0])
}
}
return opflags.CheckRequiredXor(ctx)
}
package flags
import (
"strings"
"testing"
"github.com/stretchr/testify/require"
"github.com/urfave/cli/v2"
opservice "github.com/ethereum-optimism/optimism/op-service"
)
// TestOptionalFlagsDontSetRequired asserts that all flags deemed optional set
// the Required field to false.
func TestOptionalFlagsDontSetRequired(t *testing.T) {
for _, flag := range optionalFlags {
reqFlag, ok := flag.(cli.RequiredFlag)
require.True(t, ok)
require.False(t, reqFlag.IsRequired())
}
}
// 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.Names()[0]
if _, ok := seenCLI[name]; ok {
t.Errorf("duplicate flag %s", name)
continue
}
seenCLI[name] = struct{}{}
}
}
// TestBetaFlags test that all flags starting with "beta." have "BETA_" in the env var, and vice versa.
func TestBetaFlags(t *testing.T) {
for _, flag := range Flags {
envFlag, ok := flag.(interface {
GetEnvVars() []string
})
if !ok || len(envFlag.GetEnvVars()) == 0 { // skip flags without env-var support
continue
}
name := flag.Names()[0]
envName := envFlag.GetEnvVars()[0]
if strings.HasPrefix(name, "beta.") {
require.Contains(t, envName, "BETA_", "%q flag must contain BETA in env var to match \"beta.\" flag name", name)
}
if strings.Contains(envName, "BETA_") {
require.True(t, strings.HasPrefix(name, "beta."), "%q flag must start with \"beta.\" in flag name to match \"BETA_\" env var", name)
}
}
}
func TestHasEnvVar(t *testing.T) {
for _, flag := range Flags {
flag := flag
flagName := flag.Names()[0]
t.Run(flagName, func(t *testing.T) {
envFlagGetter, ok := flag.(interface {
GetEnvVars() []string
})
envFlags := envFlagGetter.GetEnvVars()
require.True(t, ok, "must be able to cast the flag to an EnvVar interface")
require.Equal(t, 1, len(envFlags), "flags should have exactly one env var")
})
}
}
func TestEnvVarFormat(t *testing.T) {
for _, flag := range Flags {
flag := flag
flagName := flag.Names()[0]
t.Run(flagName, func(t *testing.T) {
envFlagGetter, ok := flag.(interface {
GetEnvVars() []string
})
envFlags := envFlagGetter.GetEnvVars()
require.True(t, ok, "must be able to cast the flag to an EnvVar interface")
require.Equal(t, 1, len(envFlags), "flags should have exactly one env var")
expectedEnvVar := opservice.FlagNameToEnvVarName(flagName, "OP_CONDUCTOR")
require.Equal(t, expectedEnvVar, envFlags[0])
})
}
}
......@@ -5,10 +5,12 @@ import (
"errors"
"fmt"
"github.com/urfave/cli/v2"
opnode "github.com/ethereum-optimism/optimism/op-node"
"github.com/ethereum-optimism/optimism/op-node/flags"
opflags "github.com/ethereum-optimism/optimism/op-service/flags"
oplog "github.com/ethereum-optimism/optimism/op-service/log"
"github.com/urfave/cli/v2"
)
var Subcommands = []*cli.Command{
......@@ -16,13 +18,13 @@ var Subcommands = []*cli.Command{
Name: "dump-rollup-config",
Usage: "Dumps network configs",
Flags: []cli.Flag{
flags.Network,
opflags.CLINetworkFlag(flags.EnvVarPrefix),
},
Action: func(ctx *cli.Context) error {
logCfg := oplog.ReadCLIConfig(ctx)
logger := oplog.NewLogger(oplog.AppOut(ctx), logCfg)
network := ctx.String(flags.Network.Name)
network := ctx.String(opflags.NetworkFlagName)
if network == "" {
return errors.New("must specify a network name")
}
......
......@@ -2,16 +2,15 @@ package flags
import (
"fmt"
"strings"
"time"
"github.com/ethereum-optimism/optimism/op-node/chaincfg"
"github.com/urfave/cli/v2"
"github.com/ethereum-optimism/optimism/op-node/rollup/sync"
openum "github.com/ethereum-optimism/optimism/op-service/enum"
opflags "github.com/ethereum-optimism/optimism/op-service/flags"
oplog "github.com/ethereum-optimism/optimism/op-service/log"
"github.com/ethereum-optimism/optimism/op-service/sources"
"github.com/urfave/cli/v2"
)
// Flags
......@@ -42,16 +41,6 @@ var (
Value: "",
Destination: new(string),
}
RollupConfig = &cli.StringFlag{
Name: "rollup.config",
Usage: "Rollup chain parameters",
EnvVars: prefixEnvVars("ROLLUP_CONFIG"),
}
Network = &cli.StringFlag{
Name: "network",
Usage: fmt.Sprintf("Predefined network selection. Available networks: %s", strings.Join(chaincfg.AvailableNetworks(), ", ")),
EnvVars: prefixEnvVars("NETWORK"),
}
/* Optional Flags */
SyncModeFlag = &cli.GenericFlag{
Name: "syncmode",
......@@ -235,18 +224,6 @@ var (
Usage: "Load protocol versions from the superchain L1 ProtocolVersions contract (if available), and report in logs and metrics",
EnvVars: prefixEnvVars("ROLLUP_LOAD_PROTOCOL_VERSIONS"),
}
CanyonOverrideFlag = &cli.Uint64Flag{
Name: "override.canyon",
Usage: "Manually specify the Canyon fork timestamp, overriding the bundled setting",
EnvVars: prefixEnvVars("OVERRIDE_CANYON"),
Hidden: false,
}
DeltaOverrideFlag = &cli.Uint64Flag{
Name: "override.delta",
Usage: "Manually specify the Delta fork timestamp, overriding the bundled setting",
EnvVars: prefixEnvVars("OVERRIDE_DELTA"),
Hidden: false,
}
/* Deprecated Flags */
L2EngineSyncEnabled = &cli.BoolFlag{
Name: "l2.engine-sync",
......@@ -294,8 +271,6 @@ var optionalFlags = []cli.Flag{
SyncModeFlag,
RPCListenAddr,
RPCListenPort,
RollupConfig,
Network,
L1TrustRPC,
L1RPCProviderKind,
L1RPCRateLimit,
......@@ -324,8 +299,6 @@ var optionalFlags = []cli.Flag{
RollupHalt,
RollupLoadProtocolVersions,
L1RethDBPath,
CanyonOverrideFlag,
DeltaOverrideFlag,
}
var DeprecatedFlags = []cli.Flag{
......@@ -345,34 +318,15 @@ func init() {
optionalFlags = append(optionalFlags, P2PFlags(EnvVarPrefix)...)
optionalFlags = append(optionalFlags, oplog.CLIFlags(EnvVarPrefix)...)
optionalFlags = append(optionalFlags, DeprecatedFlags...)
optionalFlags = append(optionalFlags, opflags.CLIFlags(EnvVarPrefix)...)
Flags = append(requiredFlags, optionalFlags...)
}
// This checks flags that are exclusive & required. Specifically for each
// set of flags, exactly one flag must be set.
var requiredXorFlags = [][]string{
{
RollupConfig.Name,
Network.Name,
},
}
func CheckRequired(ctx *cli.Context) error {
for _, f := range requiredFlags {
if !ctx.IsSet(f.Names()[0]) {
return fmt.Errorf("flag %s is required", f.Names()[0])
}
}
for _, flagNames := range requiredXorFlags {
setCount := 0
for _, f := range flagNames {
if ctx.IsSet(f) {
setCount += 1
}
}
if setCount != 1 {
return fmt.Errorf("exactly one of the flags %v is required", flagNames)
}
}
return nil
return opflags.CheckRequiredXor(ctx)
}
......@@ -9,21 +9,21 @@ import (
"os"
"strings"
"github.com/ethereum-optimism/optimism/op-node/chaincfg"
oppprof "github.com/ethereum-optimism/optimism/op-service/pprof"
"github.com/ethereum-optimism/optimism/op-service/sources"
"github.com/urfave/cli/v2"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/log"
"github.com/urfave/cli/v2"
"github.com/ethereum-optimism/optimism/op-node/chaincfg"
"github.com/ethereum-optimism/optimism/op-node/flags"
"github.com/ethereum-optimism/optimism/op-node/node"
p2pcli "github.com/ethereum-optimism/optimism/op-node/p2p/cli"
"github.com/ethereum-optimism/optimism/op-node/rollup"
"github.com/ethereum-optimism/optimism/op-node/rollup/driver"
"github.com/ethereum-optimism/optimism/op-node/rollup/sync"
opflags "github.com/ethereum-optimism/optimism/op-service/flags"
oppprof "github.com/ethereum-optimism/optimism/op-service/pprof"
"github.com/ethereum-optimism/optimism/op-service/sources"
)
// NewConfig creates a Config from the provided flags or environment variables.
......@@ -149,7 +149,7 @@ func NewL2EndpointConfig(ctx *cli.Context, log log.Logger) (*node.L2EndpointConf
if _, err := io.ReadFull(rand.Reader, secret[:]); err != nil {
return nil, fmt.Errorf("failed to generate jwt secret: %w", err)
}
if err := os.WriteFile(fileName, []byte(hexutil.Encode(secret[:])), 0600); err != nil {
if err := os.WriteFile(fileName, []byte(hexutil.Encode(secret[:])), 0o600); err != nil {
return nil, err
}
}
......@@ -179,8 +179,8 @@ func NewDriverConfig(ctx *cli.Context) *driver.Config {
}
func NewRollupConfig(log log.Logger, ctx *cli.Context) (*rollup.Config, error) {
network := ctx.String(flags.Network.Name)
rollupConfigPath := ctx.String(flags.RollupConfig.Name)
network := ctx.String(opflags.NetworkFlagName)
rollupConfigPath := ctx.String(opflags.RollupConfigFlagName)
if ctx.Bool(flags.BetaExtraNetworks.Name) {
log.Warn("The beta.extra-networks flag is deprecated and can be omitted safely.")
}
......@@ -214,12 +214,12 @@ Conflicting configuration is deprecated, and will stop the op-node from starting
}
func applyOverrides(ctx *cli.Context, rollupConfig *rollup.Config) {
if ctx.IsSet(flags.CanyonOverrideFlag.Name) {
canyon := ctx.Uint64(flags.CanyonOverrideFlag.Name)
if ctx.IsSet(opflags.CanyonOverrideFlagName) {
canyon := ctx.Uint64(opflags.CanyonOverrideFlagName)
rollupConfig.CanyonTime = &canyon
}
if ctx.IsSet(flags.DeltaOverrideFlag.Name) {
delta := ctx.Uint64(flags.DeltaOverrideFlag.Name)
if ctx.IsSet(opflags.DeltaOverrideFlagName) {
delta := ctx.Uint64(opflags.DeltaOverrideFlagName)
rollupConfig.DeltaTime = &delta
}
}
......
package flags
import (
"fmt"
"strings"
"github.com/urfave/cli/v2"
"github.com/ethereum-optimism/optimism/op-node/chaincfg"
opservice "github.com/ethereum-optimism/optimism/op-service"
)
const (
RollupConfigFlagName = "rollup.config"
NetworkFlagName = "network"
CanyonOverrideFlagName = "override.canyon"
DeltaOverrideFlagName = "override.delta"
)
func CLIFlags(envPrefix string) []cli.Flag {
return []cli.Flag{
&cli.Uint64Flag{
Name: CanyonOverrideFlagName,
Usage: "Manually specify the Canyon fork timestamp, overriding the bundled setting",
EnvVars: opservice.PrefixEnvVar(envPrefix, "OVERRIDE_CANYON"),
Hidden: false,
},
&cli.Uint64Flag{
Name: DeltaOverrideFlagName,
Usage: "Manually specify the Delta fork timestamp, overriding the bundled setting",
EnvVars: opservice.PrefixEnvVar(envPrefix, "OVERRIDE_DELTA"),
Hidden: false,
},
CLINetworkFlag(envPrefix),
CLIRollupConfigFlag(envPrefix),
}
}
func CLINetworkFlag(envPrefix string) cli.Flag {
return &cli.StringFlag{
Name: NetworkFlagName,
Usage: fmt.Sprintf("Predefined network selection. Available networks: %s", strings.Join(chaincfg.AvailableNetworks(), ", ")),
EnvVars: opservice.PrefixEnvVar(envPrefix, "NETWORK"),
}
}
func CLIRollupConfigFlag(envPrefix string) cli.Flag {
return &cli.StringFlag{
Name: RollupConfigFlagName,
Usage: "Rollup chain parameters",
EnvVars: opservice.PrefixEnvVar(envPrefix, "ROLLUP_CONFIG"),
}
}
// This checks flags that are exclusive & required. Specifically for each
// set of flags, exactly one flag must be set.
var requiredXorFlags = [][]string{
{
RollupConfigFlagName,
NetworkFlagName,
},
}
func CheckRequiredXor(ctx *cli.Context) error {
for _, flagSet := range requiredXorFlags {
var setCount int
for _, flagName := range flagSet {
if ctx.IsSet(flagName) {
setCount++
}
}
if setCount != 1 {
return fmt.Errorf("exactly one of the following flags must be set: %s", strings.Join(flagSet, ", "))
}
}
return nil
}
......@@ -33,6 +33,7 @@ ARG OP_NODE_VERSION=v0.0.0
ARG OP_CHALLENGER_VERSION=v0.0.0
ARG OP_BATCHER_VERSION=v0.0.0
ARG OP_PROPOSER_VERSION=v0.0.0
ARG OP_CONDUCTOR_VERSION=v0.0.0
# separate docker-builds:
......@@ -66,6 +67,8 @@ RUN --mount=type=cache,target=/root/.cache/go-build cd op-batcher && make op-bat
GOOS=$TARGETOS GOARCH=$TARGETARCH GITCOMMIT=$GIT_COMMIT GITDATE=$GIT_DATE VERSION="$OP_BATCHER_VERSION"
RUN --mount=type=cache,target=/root/.cache/go-build cd op-proposer && make op-proposer \
GOOS=$TARGETOS GOARCH=$TARGETARCH GITCOMMIT=$GIT_COMMIT GITDATE=$GIT_DATE VERSION="$OP_PROPOSER_VERSION"
RUN --mount=type=cache,target=/root/.cache/go-build cd op-conductor && make op-conductor \
GOOS=$TARGETOS GOARCH=$TARGETARCH GITCOMMIT=$GIT_COMMIT GITDATE=$GIT_DATE VERSION="$OP_CONDUCTOR_VERSION"
FROM alpine:3.18
......@@ -80,5 +83,6 @@ COPY --from=builder /app/op-node/bin/op-node /usr/local/bin/
COPY --from=builder /app/op-challenger/bin/op-challenger /usr/local/bin/
COPY --from=builder /app/op-batcher/bin/op-batcher /usr/local/bin/
COPY --from=builder /app/op-proposer/bin/op-proposer /usr/local/bin/
COPY --from=builder /app/op-conductor/bin/op-conductor /usr/local/bin/
......@@ -8,6 +8,7 @@
!/op-bootnode
!/op-chain-ops
!/op-challenger
!/op-conductor
!/op-heartbeat
!/op-node
!/op-preimage
......
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