Commit 4352684f authored by Adrian Sutton's avatar Adrian Sutton

op-node: Add CLI flag to persist changes made via RPC Admin APIs.

parent 449321bd
...@@ -170,6 +170,7 @@ func DefaultSystemConfig(t *testing.T) SystemConfig { ...@@ -170,6 +170,7 @@ func DefaultSystemConfig(t *testing.T) SystemConfig {
EnableAdmin: true, EnableAdmin: true,
}, },
L1EpochPollInterval: time.Second * 4, L1EpochPollInterval: time.Second * 4,
ConfigPersistence: &rollupNode.DisabledConfigPersistence{},
}, },
"verifier": { "verifier": {
Driver: driver.Config{ Driver: driver.Config{
...@@ -178,6 +179,7 @@ func DefaultSystemConfig(t *testing.T) SystemConfig { ...@@ -178,6 +179,7 @@ func DefaultSystemConfig(t *testing.T) SystemConfig {
SequencerEnabled: false, SequencerEnabled: false,
}, },
L1EpochPollInterval: time.Second * 4, L1EpochPollInterval: time.Second * 4,
ConfigPersistence: &rollupNode.DisabledConfigPersistence{},
}, },
}, },
Loggers: map[string]log.Logger{ Loggers: map[string]log.Logger{
......
...@@ -59,6 +59,11 @@ var ( ...@@ -59,6 +59,11 @@ var (
Usage: "Enable the admin API (experimental)", Usage: "Enable the admin API (experimental)",
EnvVars: prefixEnvVars("RPC_ENABLE_ADMIN"), EnvVars: prefixEnvVars("RPC_ENABLE_ADMIN"),
} }
RPCAdminPersistence = &cli.StringFlag{
Name: "rpc.admin-state",
Usage: "File path used to persist state changes made via the admin API so they persist across restarts",
EnvVars: prefixEnvVars("RPC_ADMIN_STATE"),
}
/* Optional Flags */ /* Optional Flags */
L1TrustRPC = &cli.BoolFlag{ L1TrustRPC = &cli.BoolFlag{
...@@ -233,6 +238,7 @@ var optionalFlags = []cli.Flag{ ...@@ -233,6 +238,7 @@ var optionalFlags = []cli.Flag{
SequencerL1Confs, SequencerL1Confs,
L1EpochPollIntervalFlag, L1EpochPollIntervalFlag,
RPCEnableAdmin, RPCEnableAdmin,
RPCAdminPersistence,
MetricsEnabledFlag, MetricsEnabledFlag,
MetricsAddrFlag, MetricsAddrFlag,
MetricsPortFlag, MetricsPortFlag,
......
...@@ -36,6 +36,8 @@ type Config struct { ...@@ -36,6 +36,8 @@ type Config struct {
// Used to poll the L1 for new finalized or safe blocks // Used to poll the L1 for new finalized or safe blocks
L1EpochPollInterval time.Duration L1EpochPollInterval time.Duration
ConfigPersistence ConfigPersistence
// Optional // Optional
Tracer Tracer Tracer Tracer
Heartbeat HeartbeatConfig Heartbeat HeartbeatConfig
......
...@@ -12,9 +12,9 @@ import ( ...@@ -12,9 +12,9 @@ import (
type RunningState int type RunningState int
const ( const (
Unset RunningState = iota StateUnset RunningState = iota
Started StateStarted
Stopped StateStopped
) )
type persistedState struct { type persistedState struct {
...@@ -35,8 +35,8 @@ type ActiveConfigPersistence struct { ...@@ -35,8 +35,8 @@ type ActiveConfigPersistence struct {
file string file string
} }
func NewConfigPersistence(file string) (*ActiveConfigPersistence, error) { func NewConfigPersistence(file string) *ActiveConfigPersistence {
return &ActiveConfigPersistence{file: file}, nil return &ActiveConfigPersistence{file: file}
} }
func (p *ActiveConfigPersistence) SequencerStarted() error { func (p *ActiveConfigPersistence) SequencerStarted() error {
...@@ -88,15 +88,15 @@ func (p *ActiveConfigPersistence) persist(sequencerStarted bool) error { ...@@ -88,15 +88,15 @@ func (p *ActiveConfigPersistence) persist(sequencerStarted bool) error {
func (p *ActiveConfigPersistence) SequencerState() (RunningState, error) { func (p *ActiveConfigPersistence) SequencerState() (RunningState, error) {
config, err := p.read() config, err := p.read()
if err != nil { if err != nil {
return Unset, err return StateUnset, err
} }
if config.SequencerStarted == nil { if config.SequencerStarted == nil {
return Unset, nil return StateUnset, nil
} else if *config.SequencerStarted { } else if *config.SequencerStarted {
return Started, nil return StateStarted, nil
} else { } else {
return Stopped, nil return StateStopped, nil
} }
} }
...@@ -122,7 +122,7 @@ type DisabledConfigPersistence struct { ...@@ -122,7 +122,7 @@ type DisabledConfigPersistence struct {
} }
func (d DisabledConfigPersistence) SequencerState() (RunningState, error) { func (d DisabledConfigPersistence) SequencerState() (RunningState, error) {
return Unset, nil return StateUnset, nil
} }
func (d DisabledConfigPersistence) SequencerStarted() error { func (d DisabledConfigPersistence) SequencerStarted() error {
......
...@@ -9,8 +9,7 @@ import ( ...@@ -9,8 +9,7 @@ import (
func TestActive(t *testing.T) { func TestActive(t *testing.T) {
create := func() *ActiveConfigPersistence { create := func() *ActiveConfigPersistence {
dir := t.TempDir() dir := t.TempDir()
config, err := NewConfigPersistence(dir + "/state") config := NewConfigPersistence(dir + "/state")
require.NoError(t, err)
return config return config
} }
...@@ -18,7 +17,7 @@ func TestActive(t *testing.T) { ...@@ -18,7 +17,7 @@ func TestActive(t *testing.T) {
config := create() config := create()
state, err := config.SequencerState() state, err := config.SequencerState()
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, Unset, state) require.Equal(t, StateUnset, state)
}) })
t.Run("PersistSequencerStarted", func(t *testing.T) { t.Run("PersistSequencerStarted", func(t *testing.T) {
...@@ -26,13 +25,12 @@ func TestActive(t *testing.T) { ...@@ -26,13 +25,12 @@ func TestActive(t *testing.T) {
require.NoError(t, config1.SequencerStarted()) require.NoError(t, config1.SequencerStarted())
state, err := config1.SequencerState() state, err := config1.SequencerState()
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, Started, state) require.Equal(t, StateStarted, state)
config2, err := NewConfigPersistence(config1.file) config2 := NewConfigPersistence(config1.file)
require.NoError(t, err)
state, err = config2.SequencerState() state, err = config2.SequencerState()
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, Started, state) require.Equal(t, StateStarted, state)
}) })
t.Run("PersistSequencerStopped", func(t *testing.T) { t.Run("PersistSequencerStopped", func(t *testing.T) {
...@@ -40,13 +38,12 @@ func TestActive(t *testing.T) { ...@@ -40,13 +38,12 @@ func TestActive(t *testing.T) {
require.NoError(t, config1.SequencerStopped()) require.NoError(t, config1.SequencerStopped())
state, err := config1.SequencerState() state, err := config1.SequencerState()
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, Stopped, state) require.Equal(t, StateStopped, state)
config2, err := NewConfigPersistence(config1.file) config2 := NewConfigPersistence(config1.file)
require.NoError(t, err)
state, err = config2.SequencerState() state, err = config2.SequencerState()
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, Stopped, state) require.Equal(t, StateStopped, state)
}) })
t.Run("PersistMultipleChanges", func(t *testing.T) { t.Run("PersistMultipleChanges", func(t *testing.T) {
...@@ -54,23 +51,22 @@ func TestActive(t *testing.T) { ...@@ -54,23 +51,22 @@ func TestActive(t *testing.T) {
require.NoError(t, config.SequencerStarted()) require.NoError(t, config.SequencerStarted())
state, err := config.SequencerState() state, err := config.SequencerState()
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, Started, state) require.Equal(t, StateStarted, state)
require.NoError(t, config.SequencerStopped()) require.NoError(t, config.SequencerStopped())
state, err = config.SequencerState() state, err = config.SequencerState()
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, Stopped, state) require.Equal(t, StateStopped, state)
}) })
t.Run("CreateParentDirs", func(t *testing.T) { t.Run("CreateParentDirs", func(t *testing.T) {
dir := t.TempDir() dir := t.TempDir()
config, err := NewConfigPersistence(dir + "/some/dir/state") config := NewConfigPersistence(dir + "/some/dir/state")
require.NoError(t, err)
// Should be unset before file exists // Should be unset before file exists
state, err := config.SequencerState() state, err := config.SequencerState()
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, Unset, state) require.Equal(t, StateUnset, state)
require.NoFileExists(t, config.file) require.NoFileExists(t, config.file)
// Should create directories when updating // Should create directories when updating
...@@ -78,7 +74,7 @@ func TestActive(t *testing.T) { ...@@ -78,7 +74,7 @@ func TestActive(t *testing.T) {
require.FileExists(t, config.file) require.FileExists(t, config.file)
state, err = config.SequencerState() state, err = config.SequencerState()
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, Started, state) require.Equal(t, StateStarted, state)
}) })
} }
...@@ -86,15 +82,15 @@ func TestDisabledConfigPersistence_AlwaysUnset(t *testing.T) { ...@@ -86,15 +82,15 @@ func TestDisabledConfigPersistence_AlwaysUnset(t *testing.T) {
config := DisabledConfigPersistence{} config := DisabledConfigPersistence{}
state, err := config.SequencerState() state, err := config.SequencerState()
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, Unset, state) require.Equal(t, StateUnset, state)
require.NoError(t, config.SequencerStarted()) require.NoError(t, config.SequencerStarted())
state, err = config.SequencerState() state, err = config.SequencerState()
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, Unset, state) require.Equal(t, StateUnset, state)
require.NoError(t, config.SequencerStopped()) require.NoError(t, config.SequencerStopped())
state, err = config.SequencerState() state, err = config.SequencerState()
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, Unset, state) require.Equal(t, StateUnset, state)
} }
...@@ -199,7 +199,7 @@ func (n *OpNode) initL2(ctx context.Context, cfg *Config, snapshotLog log.Logger ...@@ -199,7 +199,7 @@ func (n *OpNode) initL2(ctx context.Context, cfg *Config, snapshotLog log.Logger
return err return err
} }
n.l2Driver = driver.NewDriver(&cfg.Driver, &cfg.Rollup, n.l2Source, n.l1Source, n, n, n.log, snapshotLog, n.metrics, DisabledConfigPersistence{}) n.l2Driver = driver.NewDriver(&cfg.Driver, &cfg.Rollup, n.l2Source, n.l1Source, n, n, n.log, snapshotLog, n.metrics, cfg.ConfigPersistence)
return nil return nil
} }
......
...@@ -91,6 +91,8 @@ type Driver struct { ...@@ -91,6 +91,8 @@ type Driver struct {
func (s *Driver) Start() error { func (s *Driver) Start() error {
s.derivation.Reset() s.derivation.Reset()
log.Info("Starting driver", "sequencerEnabled", s.driverConfig.SequencerEnabled, "sequencerStopped", s.driverConfig.SequencerStopped)
s.wg.Add(1) s.wg.Add(1)
go s.eventLoop() go s.eventLoop()
......
...@@ -11,7 +11,6 @@ import ( ...@@ -11,7 +11,6 @@ import (
"github.com/ethereum-optimism/optimism/op-node/chaincfg" "github.com/ethereum-optimism/optimism/op-node/chaincfg"
"github.com/ethereum-optimism/optimism/op-node/sources" "github.com/ethereum-optimism/optimism/op-node/sources"
oppprof "github.com/ethereum-optimism/optimism/op-service/pprof" oppprof "github.com/ethereum-optimism/optimism/op-service/pprof"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
...@@ -36,7 +35,12 @@ func NewConfig(ctx *cli.Context, log log.Logger) (*node.Config, error) { ...@@ -36,7 +35,12 @@ func NewConfig(ctx *cli.Context, log log.Logger) (*node.Config, error) {
return nil, err return nil, err
} }
driverConfig := NewDriverConfig(ctx) configPersistence := NewConfigPersistence(ctx)
driverConfig, err := NewDriverConfig(ctx, configPersistence)
if err != nil {
return nil, fmt.Errorf("failed to load driver config: %w", err)
}
p2pSignerSetup, err := p2pcli.LoadSignerSetup(ctx) p2pSignerSetup, err := p2pcli.LoadSignerSetup(ctx)
if err != nil { if err != nil {
...@@ -86,6 +90,7 @@ func NewConfig(ctx *cli.Context, log log.Logger) (*node.Config, error) { ...@@ -86,6 +90,7 @@ func NewConfig(ctx *cli.Context, log log.Logger) (*node.Config, error) {
Moniker: ctx.String(flags.HeartbeatMonikerFlag.Name), Moniker: ctx.String(flags.HeartbeatMonikerFlag.Name),
URL: ctx.String(flags.HeartbeatURLFlag.Name), URL: ctx.String(flags.HeartbeatURLFlag.Name),
}, },
ConfigPersistence: configPersistence,
} }
if err := cfg.Check(); err != nil { if err := cfg.Check(); err != nil {
return nil, err return nil, err
...@@ -143,14 +148,29 @@ func NewL2SyncEndpointConfig(ctx *cli.Context) *node.L2SyncEndpointConfig { ...@@ -143,14 +148,29 @@ func NewL2SyncEndpointConfig(ctx *cli.Context) *node.L2SyncEndpointConfig {
} }
} }
func NewDriverConfig(ctx *cli.Context) *driver.Config { func NewConfigPersistence(ctx *cli.Context) node.ConfigPersistence {
stateFile := ctx.String(flags.RPCAdminPersistence.Name)
if stateFile == "" {
return node.DisabledConfigPersistence{}
}
return node.NewConfigPersistence(stateFile)
}
func NewDriverConfig(ctx *cli.Context, config node.ConfigPersistence) (*driver.Config, error) {
sequencerStopped := ctx.Bool(flags.SequencerStoppedFlag.Name)
if state, err := config.SequencerState(); err != nil {
return nil, err
} else if state != node.StateUnset {
sequencerStopped = state == node.StateStopped
}
return &driver.Config{ return &driver.Config{
VerifierConfDepth: ctx.Uint64(flags.VerifierL1Confs.Name), VerifierConfDepth: ctx.Uint64(flags.VerifierL1Confs.Name),
SequencerConfDepth: ctx.Uint64(flags.SequencerL1Confs.Name), SequencerConfDepth: ctx.Uint64(flags.SequencerL1Confs.Name),
SequencerEnabled: ctx.Bool(flags.SequencerEnabledFlag.Name), SequencerEnabled: ctx.Bool(flags.SequencerEnabledFlag.Name),
SequencerStopped: ctx.Bool(flags.SequencerStoppedFlag.Name), SequencerStopped: sequencerStopped,
SequencerMaxSafeLag: ctx.Uint64(flags.SequencerMaxSafeLagFlag.Name), SequencerMaxSafeLag: ctx.Uint64(flags.SequencerMaxSafeLagFlag.Name),
} }, nil
} }
func NewRollupConfig(ctx *cli.Context) (*rollup.Config, error) { func NewRollupConfig(ctx *cli.Context) (*rollup.Config, error) {
......
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