diff --git a/apps/evm/cmd/run.go b/apps/evm/cmd/run.go index ccb45b5a02..e573b2da7a 100644 --- a/apps/evm/cmd/run.go +++ b/apps/evm/cmd/run.go @@ -160,6 +160,12 @@ func createSequencer( daClient block.FullDAClient, executor execution.Executor, ) (coresequencer.Sequencer, error) { + // Only aggregators and promotable nodes need a sequencer; promotable + // followers must have one ready before they are promoted to proposer. + if !nodeConfig.Node.Aggregator && !nodeConfig.Node.Promotable { + return nil, nil + } + if nodeConfig.Node.BasedSequencer { // Based sequencer mode - fetch transactions only from DA if !nodeConfig.Node.Aggregator { diff --git a/apps/testapp/cmd/run.go b/apps/testapp/cmd/run.go index 6eaaa33683..a89fc0f5b4 100644 --- a/apps/testapp/cmd/run.go +++ b/apps/testapp/cmd/run.go @@ -113,7 +113,9 @@ func createSequencer( genesis genesis.Genesis, executor execution.Executor, ) (coresequencer.Sequencer, error) { - if !nodeConfig.Node.Aggregator { + // Only aggregators and promotable nodes need a sequencer; promotable + // followers must have one ready before they are promoted to proposer. + if !nodeConfig.Node.Aggregator && !nodeConfig.Node.Promotable { return nil, nil } diff --git a/pkg/cmd/run_node.go b/pkg/cmd/run_node.go index fd98b0ee25..27dff229f0 100644 --- a/pkg/cmd/run_node.go +++ b/pkg/cmd/run_node.go @@ -48,8 +48,8 @@ func ParseStartConfig(cmd *cobra.Command) (rollconf.Config, error) { if err != nil { return rollconf.Config{}, err } - if nodeConfig.Node.Aggregator && nodeConfig.GetDAAddress() == "" { - return rollconf.Config{}, fmt.Errorf("DA address is required when aggregator mode is enabled") + if (nodeConfig.Node.Aggregator || nodeConfig.Node.Promotable) && nodeConfig.GetDAAddress() == "" { + return rollconf.Config{}, fmt.Errorf("DA address is required when aggregator or promotable mode is enabled") } return nodeConfig, nil diff --git a/pkg/cmd/run_node_test.go b/pkg/cmd/run_node_test.go index 6de41048fe..fef1300157 100644 --- a/pkg/cmd/run_node_test.go +++ b/pkg/cmd/run_node_test.go @@ -175,20 +175,22 @@ func TestAggregatorFlagInvariants(t *testing.T) { } func TestParseStartConfig_AggregatorRequiresDAAddress(t *testing.T) { - executor, sequencer, keyProvider, nodeKey, ds, stopDAHeightTicker := createTestComponents(context.Background(), t) - defer stopDAHeightTicker() + for _, flag := range []string{"--rollkit.node.aggregator", "--rollkit.node.promotable"} { + executor, sequencer, keyProvider, nodeKey, ds, stopDAHeightTicker := createTestComponents(context.Background(), t) + defer stopDAHeightTicker() - nodeConfig := rollconf.DefaultConfig() - nodeConfig.RootDir = t.TempDir() + nodeConfig := rollconf.DefaultConfig() + nodeConfig.RootDir = t.TempDir() - cmd := newRunNodeCmd(t.Context(), executor, sequencer, keyProvider, nodeKey, ds, nodeConfig) - require.NoError(t, cmd.ParseFlags([]string{"start", "--rollkit.node.aggregator"})) + cmd := newRunNodeCmd(t.Context(), executor, sequencer, keyProvider, nodeKey, ds, nodeConfig) + require.NoError(t, cmd.ParseFlags([]string{"start", flag})) - _, err := ParseConfig(cmd) - require.NoError(t, err) + _, err := ParseConfig(cmd) + require.NoError(t, err) - _, err = ParseStartConfig(cmd) - require.EqualError(t, err, "DA address is required when aggregator mode is enabled") + _, err = ParseStartConfig(cmd) + require.EqualError(t, err, "DA address is required when aggregator or promotable mode is enabled") + } } func TestPromotableFlagInvariants(t *testing.T) { diff --git a/pkg/sequencers/single/sequencer.go b/pkg/sequencers/single/sequencer.go index dd30e73f41..e7277ff651 100644 --- a/pkg/sequencers/single/sequencer.go +++ b/pkg/sequencers/single/sequencer.go @@ -78,6 +78,10 @@ func NewSequencer( genesis genesis.Genesis, executor execution.Executor, ) (*Sequencer, error) { + if daClient == nil { + return nil, errors.New("single sequencer requires a DA client") + } + s := &Sequencer{ db: db, logger: logger, diff --git a/pkg/sequencers/single/sequencer_test.go b/pkg/sequencers/single/sequencer_test.go index 1891cbba99..f4109b02c3 100644 --- a/pkg/sequencers/single/sequencer_test.go +++ b/pkg/sequencers/single/sequencer_test.go @@ -114,6 +114,13 @@ func newTestSequencer(t *testing.T, db ds.Batching, daClient block.FullDAClient) return seq } +func TestNewSequencer_NilDAClient(t *testing.T) { + mockExec := createDefaultMockExecutor(t) + seq, err := NewSequencer(zerolog.Nop(), ds.NewMapDatastore(), nil, config.DefaultConfig(), []byte("test"), 1000, genesis.Genesis{}, mockExec) + require.Error(t, err) + require.Nil(t, seq) +} + func TestSequencer_SubmitBatchTxs(t *testing.T) { dummyDA := newDummyDA(100_000_000) db := ds.NewMapDatastore()