Skip to content

Commit f59b331

Browse files
tac0turtleclaude
andauthored
fix(evm): don't create single sequencer for non-aggregator nodes (#3389)
* fix(evm): don't create single sequencer for non-aggregator nodes P2P-only followers (no DA address) crashed on startup with a nil pointer dereference: the EVM app's createSequencer unconditionally built a single sequencer, whose forced-inclusion retriever calls GetForcedInclusionNamespace on the nil DA client. Mirror the testapp guard from #3386 by skipping sequencer creation for non-aggregators, and fail fast in single.NewSequencer when the DA client is nil so future regressions surface as a clean error instead of a SIGSEGV. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix: keep sequencer for promotable followers and require DA for promotable mode Promotable nodes start as followers but can be promoted to proposer at runtime, which hands the sequencer to the aggregator components. The non-aggregator guard (added to testapp in #3386 and mirrored here for the EVM app) would leave promotable nodes with a nil sequencer and break promotion. Create the sequencer for promotable nodes too, and require a DA address for promotable mode at config validation, same as aggregator mode. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
1 parent 8e8cdde commit f59b331

6 files changed

Lines changed: 34 additions & 13 deletions

File tree

apps/evm/cmd/run.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,12 @@ func createSequencer(
160160
daClient block.FullDAClient,
161161
executor execution.Executor,
162162
) (coresequencer.Sequencer, error) {
163+
// Only aggregators and promotable nodes need a sequencer; promotable
164+
// followers must have one ready before they are promoted to proposer.
165+
if !nodeConfig.Node.Aggregator && !nodeConfig.Node.Promotable {
166+
return nil, nil
167+
}
168+
163169
if nodeConfig.Node.BasedSequencer {
164170
// Based sequencer mode - fetch transactions only from DA
165171
if !nodeConfig.Node.Aggregator {

apps/testapp/cmd/run.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,9 @@ func createSequencer(
113113
genesis genesis.Genesis,
114114
executor execution.Executor,
115115
) (coresequencer.Sequencer, error) {
116-
if !nodeConfig.Node.Aggregator {
116+
// Only aggregators and promotable nodes need a sequencer; promotable
117+
// followers must have one ready before they are promoted to proposer.
118+
if !nodeConfig.Node.Aggregator && !nodeConfig.Node.Promotable {
117119
return nil, nil
118120
}
119121

pkg/cmd/run_node.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@ func ParseStartConfig(cmd *cobra.Command) (rollconf.Config, error) {
4848
if err != nil {
4949
return rollconf.Config{}, err
5050
}
51-
if nodeConfig.Node.Aggregator && nodeConfig.GetDAAddress() == "" {
52-
return rollconf.Config{}, fmt.Errorf("DA address is required when aggregator mode is enabled")
51+
if (nodeConfig.Node.Aggregator || nodeConfig.Node.Promotable) && nodeConfig.GetDAAddress() == "" {
52+
return rollconf.Config{}, fmt.Errorf("DA address is required when aggregator or promotable mode is enabled")
5353
}
5454

5555
return nodeConfig, nil

pkg/cmd/run_node_test.go

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -175,20 +175,22 @@ func TestAggregatorFlagInvariants(t *testing.T) {
175175
}
176176

177177
func TestParseStartConfig_AggregatorRequiresDAAddress(t *testing.T) {
178-
executor, sequencer, keyProvider, nodeKey, ds, stopDAHeightTicker := createTestComponents(context.Background(), t)
179-
defer stopDAHeightTicker()
178+
for _, flag := range []string{"--rollkit.node.aggregator", "--rollkit.node.promotable"} {
179+
executor, sequencer, keyProvider, nodeKey, ds, stopDAHeightTicker := createTestComponents(context.Background(), t)
180+
defer stopDAHeightTicker()
180181

181-
nodeConfig := rollconf.DefaultConfig()
182-
nodeConfig.RootDir = t.TempDir()
182+
nodeConfig := rollconf.DefaultConfig()
183+
nodeConfig.RootDir = t.TempDir()
183184

184-
cmd := newRunNodeCmd(t.Context(), executor, sequencer, keyProvider, nodeKey, ds, nodeConfig)
185-
require.NoError(t, cmd.ParseFlags([]string{"start", "--rollkit.node.aggregator"}))
185+
cmd := newRunNodeCmd(t.Context(), executor, sequencer, keyProvider, nodeKey, ds, nodeConfig)
186+
require.NoError(t, cmd.ParseFlags([]string{"start", flag}))
186187

187-
_, err := ParseConfig(cmd)
188-
require.NoError(t, err)
188+
_, err := ParseConfig(cmd)
189+
require.NoError(t, err)
189190

190-
_, err = ParseStartConfig(cmd)
191-
require.EqualError(t, err, "DA address is required when aggregator mode is enabled")
191+
_, err = ParseStartConfig(cmd)
192+
require.EqualError(t, err, "DA address is required when aggregator or promotable mode is enabled")
193+
}
192194
}
193195

194196
func TestPromotableFlagInvariants(t *testing.T) {

pkg/sequencers/single/sequencer.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,10 @@ func NewSequencer(
7878
genesis genesis.Genesis,
7979
executor execution.Executor,
8080
) (*Sequencer, error) {
81+
if daClient == nil {
82+
return nil, errors.New("single sequencer requires a DA client")
83+
}
84+
8185
s := &Sequencer{
8286
db: db,
8387
logger: logger,

pkg/sequencers/single/sequencer_test.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,13 @@ func newTestSequencer(t *testing.T, db ds.Batching, daClient block.FullDAClient)
114114
return seq
115115
}
116116

117+
func TestNewSequencer_NilDAClient(t *testing.T) {
118+
mockExec := createDefaultMockExecutor(t)
119+
seq, err := NewSequencer(zerolog.Nop(), ds.NewMapDatastore(), nil, config.DefaultConfig(), []byte("test"), 1000, genesis.Genesis{}, mockExec)
120+
require.Error(t, err)
121+
require.Nil(t, seq)
122+
}
123+
117124
func TestSequencer_SubmitBatchTxs(t *testing.T) {
118125
dummyDA := newDummyDA(100_000_000)
119126
db := ds.NewMapDatastore()

0 commit comments

Comments
 (0)