Commit 18f5efdb authored by Adrian Sutton's avatar Adrian Sutton

op-e2e: Add system tests for persisting the sequencer state

parent fe1fdf8a
package op_e2e
import (
"context"
"testing"
"github.com/ethereum-optimism/optimism/op-node/client"
"github.com/ethereum-optimism/optimism/op-node/node"
"github.com/ethereum-optimism/optimism/op-node/sources"
"github.com/ethereum-optimism/optimism/op-node/testlog"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rpc"
"github.com/stretchr/testify/require"
)
func TestPersistSequencerStateWhenChanged(t *testing.T) {
InitParallel(t)
ctx := context.Background()
dir := t.TempDir()
stateFile := dir + "/state.json"
cfg := DefaultSystemConfig(t)
// We don't need a verifier - just the sequencer is enough
delete(cfg.Nodes, "verifier")
cfg.Nodes["sequencer"].ConfigPersistence = node.NewConfigPersistence(stateFile)
sys, err := cfg.Start()
require.NoError(t, err)
defer sys.Close()
assertPersistedSequencerState(t, stateFile, node.StateStarted)
rollupRPCClient, err := rpc.DialContext(ctx, sys.RollupNodes["sequencer"].HTTPEndpoint())
require.Nil(t, err)
rollupClient := sources.NewRollupClient(client.NewBaseRPCClient(rollupRPCClient))
err = rollupClient.StartSequencer(ctx, common.Hash{0xaa})
require.ErrorContains(t, err, "sequencer already running")
head, err := rollupClient.StopSequencer(ctx)
require.NoError(t, err)
require.NotEqual(t, common.Hash{}, head)
assertPersistedSequencerState(t, stateFile, node.StateStopped)
}
func TestLoadSequencerStateOnStarted_Stopped(t *testing.T) {
InitParallel(t)
ctx := context.Background()
dir := t.TempDir()
stateFile := dir + "/state.json"
// Prepare the persisted state file with sequencer stopped
configReader := node.NewConfigPersistence(stateFile)
require.NoError(t, configReader.SequencerStopped())
cfg := DefaultSystemConfig(t)
// We don't need a verifier - just the sequencer is enough
delete(cfg.Nodes, "verifier")
logger := testlog.Logger(t, log.LvlInfo)
seqCfg := cfg.Nodes["sequencer"]
seqCfg.ConfigPersistence = node.NewConfigPersistence(stateFile)
require.NoError(t, seqCfg.LoadPersisted(logger))
sys, err := cfg.Start()
require.NoError(t, err)
defer sys.Close()
rollupRPCClient, err := rpc.DialContext(ctx, sys.RollupNodes["sequencer"].HTTPEndpoint())
require.Nil(t, err)
rollupClient := sources.NewRollupClient(client.NewBaseRPCClient(rollupRPCClient))
// Still persisted as stopped after startup
assertPersistedSequencerState(t, stateFile, node.StateStopped)
// Sequencer is really stopped
_, err = rollupClient.StopSequencer(ctx)
require.ErrorContains(t, err, "sequencer not running")
assertPersistedSequencerState(t, stateFile, node.StateStopped)
}
func TestLoadSequencerStateOnStarted_Started(t *testing.T) {
InitParallel(t)
ctx := context.Background()
dir := t.TempDir()
stateFile := dir + "/state.json"
// Prepare the persisted state file with sequencer stopped
configReader := node.NewConfigPersistence(stateFile)
require.NoError(t, configReader.SequencerStarted())
cfg := DefaultSystemConfig(t)
// We don't need a verifier - just the sequencer is enough
delete(cfg.Nodes, "verifier")
logger := testlog.Logger(t, log.LvlInfo)
seqCfg := cfg.Nodes["sequencer"]
seqCfg.Driver.SequencerStopped = true
seqCfg.ConfigPersistence = node.NewConfigPersistence(stateFile)
require.NoError(t, seqCfg.LoadPersisted(logger))
sys, err := cfg.Start()
require.NoError(t, err)
defer sys.Close()
rollupRPCClient, err := rpc.DialContext(ctx, sys.RollupNodes["sequencer"].HTTPEndpoint())
require.Nil(t, err)
rollupClient := sources.NewRollupClient(client.NewBaseRPCClient(rollupRPCClient))
// Still persisted as stopped after startup
assertPersistedSequencerState(t, stateFile, node.StateStarted)
// Sequencer is really stopped
err = rollupClient.StartSequencer(ctx, common.Hash{})
require.ErrorContains(t, err, "sequencer already running")
assertPersistedSequencerState(t, stateFile, node.StateStarted)
}
func assertPersistedSequencerState(t *testing.T, stateFile string, expected node.RunningState) {
configReader := node.NewConfigPersistence(stateFile)
state, err := configReader.SequencerState()
require.NoError(t, err)
require.Equalf(t, expected, state, "expected sequencer state %v but was %v", expected, state)
}
...@@ -6,10 +6,12 @@ import ( ...@@ -6,10 +6,12 @@ import (
"math" "math"
"time" "time"
"github.com/ethereum-optimism/optimism/op-node/flags"
"github.com/ethereum-optimism/optimism/op-node/p2p" "github.com/ethereum-optimism/optimism/op-node/p2p"
"github.com/ethereum-optimism/optimism/op-node/rollup" "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/driver"
oppprof "github.com/ethereum-optimism/optimism/op-service/pprof" oppprof "github.com/ethereum-optimism/optimism/op-service/pprof"
"github.com/ethereum/go-ethereum/log"
) )
type Config struct { type Config struct {
...@@ -77,6 +79,24 @@ type HeartbeatConfig struct { ...@@ -77,6 +79,24 @@ type HeartbeatConfig struct {
URL string URL string
} }
func (cfg *Config) LoadPersisted(log log.Logger) error {
if !cfg.Driver.SequencerEnabled {
return nil
}
if state, err := cfg.ConfigPersistence.SequencerState(); err != nil {
return err
} else if state != StateUnset {
stopped := state == StateStopped
if stopped != cfg.Driver.SequencerStopped {
log.Warn(fmt.Sprintf("Overriding %v with persisted state", flags.SequencerStoppedFlag.Name), "stopped", stopped)
}
cfg.Driver.SequencerStopped = stopped
} else {
log.Info("No persisted sequencer state loaded")
}
return nil
}
// Check verifies that the given configuration makes sense // Check verifies that the given configuration makes sense
func (cfg *Config) Check() error { func (cfg *Config) Check() error {
if err := cfg.L2.Check(); err != nil { if err := cfg.L2.Check(); err != nil {
......
...@@ -37,10 +37,7 @@ func NewConfig(ctx *cli.Context, log log.Logger) (*node.Config, error) { ...@@ -37,10 +37,7 @@ func NewConfig(ctx *cli.Context, log log.Logger) (*node.Config, error) {
configPersistence := NewConfigPersistence(ctx) configPersistence := NewConfigPersistence(ctx)
driverConfig, err := NewDriverConfig(ctx, log, configPersistence) driverConfig := NewDriverConfig(ctx)
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 {
...@@ -92,6 +89,11 @@ func NewConfig(ctx *cli.Context, log log.Logger) (*node.Config, error) { ...@@ -92,6 +89,11 @@ func NewConfig(ctx *cli.Context, log log.Logger) (*node.Config, error) {
}, },
ConfigPersistence: configPersistence, ConfigPersistence: configPersistence,
} }
if err := cfg.LoadPersisted(log); err != nil {
return nil, fmt.Errorf("failed to load driver config: %w", err)
}
if err := cfg.Check(); err != nil { if err := cfg.Check(); err != nil {
return nil, err return nil, err
} }
...@@ -156,28 +158,14 @@ func NewConfigPersistence(ctx *cli.Context) node.ConfigPersistence { ...@@ -156,28 +158,14 @@ func NewConfigPersistence(ctx *cli.Context) node.ConfigPersistence {
return node.NewConfigPersistence(stateFile) return node.NewConfigPersistence(stateFile)
} }
func NewDriverConfig(ctx *cli.Context, log log.Logger, config node.ConfigPersistence) (*driver.Config, error) { func NewDriverConfig(ctx *cli.Context) *driver.Config {
sequencerEnabled := ctx.Bool(flags.SequencerEnabledFlag.Name)
sequencerStopped := ctx.Bool(flags.SequencerStoppedFlag.Name)
if state, err := config.SequencerState(); err != nil {
return nil, err
} else if state != node.StateUnset {
stopped := state == node.StateStopped
if stopped != sequencerStopped && sequencerEnabled {
log.Warn(fmt.Sprintf("Overriding %v with persisted state", flags.SequencerStoppedFlag.Name), "stopped", stopped)
}
sequencerStopped = stopped
} else {
log.Info("No persisted sequencer state loaded")
}
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: sequencerEnabled, SequencerEnabled: ctx.Bool(flags.SequencerEnabledFlag.Name),
SequencerStopped: sequencerStopped, SequencerStopped: ctx.Bool(flags.SequencerStoppedFlag.Name),
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) {
......
...@@ -3,6 +3,7 @@ package sources ...@@ -3,6 +3,7 @@ package sources
import ( import (
"context" "context"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum-optimism/optimism/op-node/client" "github.com/ethereum-optimism/optimism/op-node/client"
...@@ -41,3 +42,13 @@ func (r *RollupClient) Version(ctx context.Context) (string, error) { ...@@ -41,3 +42,13 @@ func (r *RollupClient) Version(ctx context.Context) (string, error) {
err := r.rpc.CallContext(ctx, &output, "optimism_version") err := r.rpc.CallContext(ctx, &output, "optimism_version")
return output, err return output, err
} }
func (r *RollupClient) StartSequencer(ctx context.Context, unsafeHead common.Hash) error {
return r.rpc.CallContext(ctx, nil, "admin_startSequencer", unsafeHead)
}
func (r *RollupClient) StopSequencer(ctx context.Context) (common.Hash, error) {
var result common.Hash
err := r.rpc.CallContext(ctx, &result, "admin_stopSequencer")
return result, err
}
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