From 66dc0b40d340e6da619a78998f405129d1cbb77f Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Wed, 6 May 2026 17:29:25 +0200 Subject: [PATCH 1/4] feat(pkg/sequencers): add queue limit in solo sequencer --- CHANGELOG.md | 1 + pkg/sequencers/common/errors.go | 10 ++++ pkg/sequencers/single/queue.go | 5 +- pkg/sequencers/single/sequencer.go | 3 +- pkg/sequencers/solo/sequencer.go | 87 ++++++++++++++++++++++++------ 5 files changed, 85 insertions(+), 21 deletions(-) create mode 100644 pkg/sequencers/common/errors.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 3cf651e997..1b99873e24 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changes +- Add max bytes contraints in simple solo sequnecer []() - Add support for otlp in execution/grpc. [#3300](https://github.com/evstack/ev-node/pull/3300) - Optimization of mutex usage in cache for reaper [#3286](https://github.com/evstack/ev-node/pull/3286) - Add Unix domain socket support for gRPC execution endpoints via `unix:///path/to/socket` [#3297](https://github.com/evstack/ev-node/pull/3297) diff --git a/pkg/sequencers/common/errors.go b/pkg/sequencers/common/errors.go new file mode 100644 index 0000000000..b960965442 --- /dev/null +++ b/pkg/sequencers/common/errors.go @@ -0,0 +1,10 @@ +package common + +import "errors" + +var ( + // ErrInvalidId is returned when the chain id is invalid + ErrInvalidID = errors.New("invalid chain id") + // ErrQueueFull is returned when the batch queue has reached its maximum size + ErrQueueFull = errors.New("sequencer queue full") +) diff --git a/pkg/sequencers/single/queue.go b/pkg/sequencers/single/queue.go index 40dcc5997f..6940e66c7f 100644 --- a/pkg/sequencers/single/queue.go +++ b/pkg/sequencers/single/queue.go @@ -4,7 +4,6 @@ import ( "context" "encoding/binary" "encoding/hex" - "errors" "fmt" "strconv" "sync" @@ -14,12 +13,12 @@ import ( "google.golang.org/protobuf/proto" coresequencer "github.com/evstack/ev-node/core/sequencer" + "github.com/evstack/ev-node/pkg/sequencers/common" "github.com/evstack/ev-node/pkg/store" pb "github.com/evstack/ev-node/types/pb/evnode/v1" ) -// ErrQueueFull is returned when the batch queue has reached its maximum size -var ErrQueueFull = errors.New("batch queue is full") +var ErrQueueFull = common.ErrQueueFull // initialSeqNum is the starting sequence number for new queues. // It is set to the middle of the uint64 range to allow for both diff --git a/pkg/sequencers/single/sequencer.go b/pkg/sequencers/single/sequencer.go index 5aec08984d..7b63334703 100644 --- a/pkg/sequencers/single/sequencer.go +++ b/pkg/sequencers/single/sequencer.go @@ -24,8 +24,7 @@ import ( "github.com/evstack/ev-node/types" ) -// ErrInvalidId is returned when the chain id is invalid -var ErrInvalidId = errors.New("invalid chain id") +var ErrInvalidId = seqcommon.ErrInvalidID // Catch-up state machine states const ( diff --git a/pkg/sequencers/solo/sequencer.go b/pkg/sequencers/solo/sequencer.go index 0fcae9f31c..22be26a111 100644 --- a/pkg/sequencers/solo/sequencer.go +++ b/pkg/sequencers/solo/sequencer.go @@ -3,7 +3,6 @@ package solo import ( "bytes" "context" - "errors" "sync" "sync/atomic" "time" @@ -12,9 +11,19 @@ import ( "github.com/evstack/ev-node/core/execution" coresequencer "github.com/evstack/ev-node/core/sequencer" + "github.com/evstack/ev-node/pkg/sequencers/common" ) -var ErrInvalidID = errors.New("invalid chain id") +var ( + ErrInvalidID = common.ErrInvalidID + ErrQueueFull = common.ErrQueueFull +) + +var ( + emptyBatch = &coresequencer.Batch{} + submitBatchResp = &coresequencer.SubmitBatchTxsResponse{} + verifyBatchOKResp = &coresequencer.VerifyBatchResponse{Status: true} +) var _ coresequencer.Sequencer = (*SoloSequencer)(nil) @@ -28,8 +37,10 @@ type SoloSequencer struct { daHeight atomic.Uint64 - mu sync.Mutex - queue [][]byte + mu sync.Mutex + queue [][]byte + queueBytes uint64 + maxQueueBytes uint64 // 0 = unbounded } func NewSoloSequencer( @@ -45,6 +56,16 @@ func NewSoloSequencer( } } +// SetMaxQueueBytes sets a soft cap on the sequencer's in-memory tx +// queue. SubmitBatchTxs admits txs in arrival order while the cap has +// room and returns ErrQueueFull as soon as one is rejected. A zero value +// disables the cap. Intended to be called once at startup. +func (s *SoloSequencer) SetMaxQueueBytes(n uint64) { + s.mu.Lock() + defer s.mu.Unlock() + s.maxQueueBytes = n +} + func (s *SoloSequencer) isValid(id []byte) bool { return bytes.Equal(s.id, id) } @@ -55,14 +76,38 @@ func (s *SoloSequencer) SubmitBatchTxs(ctx context.Context, req coresequencer.Su } if req.Batch == nil || len(req.Batch.Transactions) == 0 { - return &coresequencer.SubmitBatchTxsResponse{}, nil + return submitBatchResp, nil } s.mu.Lock() defer s.mu.Unlock() + if s.maxQueueBytes == 0 { + s.queue = append(s.queue, req.Batch.Transactions...) + return submitBatchResp, nil + } + + // All-or-nothing: if the whole incoming batch doesn't fit, reject + // it untouched. Partial admission would force the caller (e.g. + // the reaper bridging executor mempool → sequencer) to reason + // about which prefix was admitted and re-feed only the suffix on + // retry, which it doesn't currently do — leading to duplicate-tx + // resubmission on each retry. Rejecting the whole batch lets the + // reaper just retry with the same batch later when the queue has + // drained. + var batchBytes uint64 + for _, tx := range req.Batch.Transactions { + batchBytes += uint64(len(tx)) + } + + if s.queueBytes+batchBytes > s.maxQueueBytes { + return submitBatchResp, ErrQueueFull + } + s.queue = append(s.queue, req.Batch.Transactions...) - return &coresequencer.SubmitBatchTxsResponse{}, nil + s.queueBytes += batchBytes + + return submitBatchResp, nil } func (s *SoloSequencer) GetNextBatch(ctx context.Context, req coresequencer.GetNextBatchRequest) (*coresequencer.GetNextBatchResponse, error) { @@ -73,11 +118,12 @@ func (s *SoloSequencer) GetNextBatch(ctx context.Context, req coresequencer.GetN s.mu.Lock() txs := s.queue s.queue = nil + s.queueBytes = 0 s.mu.Unlock() if len(txs) == 0 { return &coresequencer.GetNextBatchResponse{ - Batch: &coresequencer.Batch{}, + Batch: emptyBatch, Timestamp: time.Now().UTC(), BatchData: req.LastBatchData, }, nil @@ -94,32 +140,41 @@ func (s *SoloSequencer) GetNextBatch(ctx context.Context, req coresequencer.GetN filterStatuses, err := s.executor.FilterTxs(ctx, txs, req.MaxBytes, maxGas, false) if err != nil { s.logger.Warn().Err(err).Msg("failed to filter transactions, proceeding with unfiltered") - filterStatuses = make([]execution.FilterStatus, len(txs)) - for i := range filterStatuses { - filterStatuses[i] = execution.FilterOK - } + return &coresequencer.GetNextBatchResponse{ + Batch: &coresequencer.Batch{Transactions: txs}, + Timestamp: time.Now().UTC(), + BatchData: req.LastBatchData, + }, nil } - var validTxs [][]byte + write := 0 var postponedTxs [][]byte for i, status := range filterStatuses { switch status { case execution.FilterOK: - validTxs = append(validTxs, txs[i]) + txs[write] = txs[i] + write++ case execution.FilterPostpone: postponedTxs = append(postponedTxs, txs[i]) - case execution.FilterRemove: } } if len(postponedTxs) > 0 { s.mu.Lock() s.queue = append(postponedTxs, s.queue...) + // Postponed txs were already in the queue's byte count when + // SubmitBatchTxs admitted them. We zeroed queueBytes on drain + // above, so re-queuing requires re-counting whatever survived. + var bytes uint64 + for _, tx := range postponedTxs { + bytes += uint64(len(tx)) + } + s.queueBytes += bytes s.mu.Unlock() } return &coresequencer.GetNextBatchResponse{ - Batch: &coresequencer.Batch{Transactions: validTxs}, + Batch: &coresequencer.Batch{Transactions: txs[:write]}, Timestamp: time.Now().UTC(), BatchData: req.LastBatchData, }, nil @@ -130,7 +185,7 @@ func (s *SoloSequencer) VerifyBatch(ctx context.Context, req coresequencer.Verif return nil, ErrInvalidID } - return &coresequencer.VerifyBatchResponse{Status: true}, nil + return verifyBatchOKResp, nil } func (s *SoloSequencer) SetDAHeight(height uint64) { From 26ebd0509a7f528321513cf14f5868945f602534 Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Wed, 6 May 2026 17:44:42 +0200 Subject: [PATCH 2/4] use option --- pkg/sequencers/solo/sequencer.go | 40 ++++++++---- pkg/sequencers/solo/sequencer_test.go | 88 +++++++++++++++++++++++++++ 2 files changed, 117 insertions(+), 11 deletions(-) diff --git a/pkg/sequencers/solo/sequencer.go b/pkg/sequencers/solo/sequencer.go index 22be26a111..8cf7249d52 100644 --- a/pkg/sequencers/solo/sequencer.go +++ b/pkg/sequencers/solo/sequencer.go @@ -27,6 +27,19 @@ var ( var _ coresequencer.Sequencer = (*SoloSequencer)(nil) +// Option configures a SoloSequencer. +type Option func(*SoloSequencer) + +// WithMaxQueueBytes sets a soft cap on the sequencer's in-memory tx queue. +// SubmitBatchTxs admits txs while the cap has room and returns ErrQueueFull +// when the incoming batch would exceed it. A zero value (default) disables +// the cap. +func WithMaxQueueBytes(n uint64) Option { + return func(s *SoloSequencer) { + s.maxQueueBytes = n + } +} + // SoloSequencer is a single-leader sequencer without forced inclusion // support. It maintains a simple in-memory queue of mempool transactions and // produces batches on demand. @@ -40,30 +53,35 @@ type SoloSequencer struct { mu sync.Mutex queue [][]byte queueBytes uint64 - maxQueueBytes uint64 // 0 = unbounded + maxQueueBytes uint64 } func NewSoloSequencer( logger zerolog.Logger, id []byte, executor execution.Executor, + opts ...Option, ) *SoloSequencer { - return &SoloSequencer{ + if executor == nil { + panic("solo: executor must not be nil") + } + + s := &SoloSequencer{ logger: logger, id: id, executor: executor, queue: make([][]byte, 0), } -} -// SetMaxQueueBytes sets a soft cap on the sequencer's in-memory tx -// queue. SubmitBatchTxs admits txs in arrival order while the cap has -// room and returns ErrQueueFull as soon as one is rejected. A zero value -// disables the cap. Intended to be called once at startup. -func (s *SoloSequencer) SetMaxQueueBytes(n uint64) { - s.mu.Lock() - defer s.mu.Unlock() - s.maxQueueBytes = n + for _, opt := range opts { + opt(s) + } + + logger.Debug(). + Uint64("max_queue_bytes", s.maxQueueBytes). + Msg("solo sequencer initialized") + + return s } func (s *SoloSequencer) isValid(id []byte) bool { diff --git a/pkg/sequencers/solo/sequencer_test.go b/pkg/sequencers/solo/sequencer_test.go index 7f3bc9e196..1d1274a0b6 100644 --- a/pkg/sequencers/solo/sequencer_test.go +++ b/pkg/sequencers/solo/sequencer_test.go @@ -217,3 +217,91 @@ func TestSoloSequencer_DAHeight(t *testing.T) { seq.SetDAHeight(42) assert.Equal(t, uint64(42), seq.GetDAHeight()) } + +func TestSoloSequencer_SubmitBatchTxs_QueueFull(t *testing.T) { + seq := NewSoloSequencer( + zerolog.Nop(), + []byte("test"), + createDefaultMockExecutor(t), + WithMaxQueueBytes(10), + ) + + _, err := seq.SubmitBatchTxs(context.Background(), coresequencer.SubmitBatchTxsRequest{ + Id: []byte("test"), + Batch: &coresequencer.Batch{Transactions: [][]byte{make([]byte, 6)}}, + }) + require.NoError(t, err) + + _, err = seq.SubmitBatchTxs(context.Background(), coresequencer.SubmitBatchTxsRequest{ + Id: []byte("test"), + Batch: &coresequencer.Batch{Transactions: [][]byte{make([]byte, 5)}}, + }) + assert.ErrorIs(t, err, ErrQueueFull) +} + +func TestSoloSequencer_SubmitBatchTxs_QueueFull_AllOrNothing(t *testing.T) { + seq := NewSoloSequencer( + zerolog.Nop(), + []byte("test"), + createDefaultMockExecutor(t), + WithMaxQueueBytes(5), + ) + + _, err := seq.SubmitBatchTxs(context.Background(), coresequencer.SubmitBatchTxsRequest{ + Id: []byte("test"), + Batch: &coresequencer.Batch{Transactions: [][]byte{{1}, {2}, {3}}}, + }) + require.NoError(t, err) + + _, err = seq.SubmitBatchTxs(context.Background(), coresequencer.SubmitBatchTxsRequest{ + Id: []byte("test"), + Batch: &coresequencer.Batch{Transactions: [][]byte{{4}, {5}, {6}}}, + }) + assert.ErrorIs(t, err, ErrQueueFull) + + assert.Len(t, seq.queue, 3, "queue should contain only the first batch") +} + +func TestSoloSequencer_SubmitBatchTxs_DrainReleasesCapacity(t *testing.T) { + seq := NewSoloSequencer( + zerolog.Nop(), + []byte("test"), + createDefaultMockExecutor(t), + WithMaxQueueBytes(10), + ) + + _, err := seq.SubmitBatchTxs(context.Background(), coresequencer.SubmitBatchTxsRequest{ + Id: []byte("test"), + Batch: &coresequencer.Batch{Transactions: [][]byte{make([]byte, 10)}}, + }) + require.NoError(t, err) + + _, err = seq.SubmitBatchTxs(context.Background(), coresequencer.SubmitBatchTxsRequest{ + Id: []byte("test"), + Batch: &coresequencer.Batch{Transactions: [][]byte{{1}}}, + }) + assert.ErrorIs(t, err, ErrQueueFull) + + _, err = seq.GetNextBatch(context.Background(), coresequencer.GetNextBatchRequest{Id: []byte("test")}) + require.NoError(t, err) + + _, err = seq.SubmitBatchTxs(context.Background(), coresequencer.SubmitBatchTxsRequest{ + Id: []byte("test"), + Batch: &coresequencer.Batch{Transactions: [][]byte{{1}}}, + }) + assert.NoError(t, err, "submit should succeed after queue is drained") +} + +func TestSoloSequencer_SubmitBatchTxs_UnboundedByDefault(t *testing.T) { + seq := newTestSequencer(t) + + bigTx := make([]byte, 1024*1024) + for i := 0; i < 10; i++ { + _, err := seq.SubmitBatchTxs(context.Background(), coresequencer.SubmitBatchTxsRequest{ + Id: []byte("test"), + Batch: &coresequencer.Batch{Transactions: [][]byte{bigTx}}, + }) + require.NoError(t, err) + } + assert.Len(t, seq.queue, 10) +} From 7d0fec93737b49275ae8be84854d91e2930c5748 Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Wed, 6 May 2026 17:45:39 +0200 Subject: [PATCH 3/4] cl --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b99873e24..de3cc9ee78 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changes -- Add max bytes contraints in simple solo sequnecer []() +- Add max bytes contraints in simple solo sequnecer [#3312](https://github.com/evstack/ev-node/pull/3312) - Add support for otlp in execution/grpc. [#3300](https://github.com/evstack/ev-node/pull/3300) - Optimization of mutex usage in cache for reaper [#3286](https://github.com/evstack/ev-node/pull/3286) - Add Unix domain socket support for gRPC execution endpoints via `unix:///path/to/socket` [#3297](https://github.com/evstack/ev-node/pull/3297) From 094dc443f57a3ecdea2542aee822d2f0ec56a7db Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Wed, 6 May 2026 18:04:08 +0200 Subject: [PATCH 4/4] move test files --- .mockery.yaml | 4 +- .../forced_inclusion_retriever_mock.go | 2 +- pkg/sequencers/based/sequencer_test.go | 41 +++++++++---------- 3 files changed, 23 insertions(+), 24 deletions(-) rename pkg/sequencers/{common => based}/forced_inclusion_retriever_mock.go (99%) diff --git a/.mockery.yaml b/.mockery.yaml index 106be368cb..36f4483c3f 100644 --- a/.mockery.yaml +++ b/.mockery.yaml @@ -97,6 +97,6 @@ packages: interfaces: ForcedInclusionRetriever: config: - dir: ./pkg/sequencers/common - pkgname: common + dir: ./pkg/sequencers/based + pkgname: based filename: forced_inclusion_retriever_mock.go diff --git a/pkg/sequencers/common/forced_inclusion_retriever_mock.go b/pkg/sequencers/based/forced_inclusion_retriever_mock.go similarity index 99% rename from pkg/sequencers/common/forced_inclusion_retriever_mock.go rename to pkg/sequencers/based/forced_inclusion_retriever_mock.go index e4dc2b0c8e..ada1bca82b 100644 --- a/pkg/sequencers/common/forced_inclusion_retriever_mock.go +++ b/pkg/sequencers/based/forced_inclusion_retriever_mock.go @@ -2,7 +2,7 @@ // github.com/vektra/mockery // template: testify -package common +package based import ( "context" diff --git a/pkg/sequencers/based/sequencer_test.go b/pkg/sequencers/based/sequencer_test.go index b8124f988b..2b947b2016 100644 --- a/pkg/sequencers/based/sequencer_test.go +++ b/pkg/sequencers/based/sequencer_test.go @@ -18,7 +18,6 @@ import ( "github.com/evstack/ev-node/pkg/config" datypes "github.com/evstack/ev-node/pkg/da/types" "github.com/evstack/ev-node/pkg/genesis" - "github.com/evstack/ev-node/pkg/sequencers/common" "github.com/evstack/ev-node/test/mocks" ) @@ -62,7 +61,7 @@ type testData struct { cancel context.CancelFunc } -func createTestSequencer(t *testing.T, mockRetriever *common.MockForcedInclusionRetriever, gen genesis.Genesis) (*BasedSequencer, testData) { +func createTestSequencer(t *testing.T, mockRetriever *MockForcedInclusionRetriever, gen genesis.Genesis) (*BasedSequencer, testData) { t.Helper() // Create in-memory datastore @@ -95,7 +94,7 @@ func createTestSequencer(t *testing.T, mockRetriever *common.MockForcedInclusion } func TestBasedSequencer_SubmitBatchTxs(t *testing.T) { - mockRetriever := common.NewMockForcedInclusionRetriever(t) + mockRetriever := NewMockForcedInclusionRetriever(t) gen := genesis.Genesis{ ChainID: "test-chain", DAEpochForcedInclusion: 10, @@ -122,7 +121,7 @@ func TestBasedSequencer_SubmitBatchTxs(t *testing.T) { func TestBasedSequencer_GetNextBatch_WithForcedTxs(t *testing.T) { testBlobs := [][]byte{[]byte("tx1"), []byte("tx2")} - mockRetriever := common.NewMockForcedInclusionRetriever(t) + mockRetriever := NewMockForcedInclusionRetriever(t) mockRetriever.On("RetrieveForcedIncludedTxs", mock.Anything, uint64(100)).Return(&block.ForcedInclusionEvent{ Txs: testBlobs, StartDaHeight: 100, @@ -158,7 +157,7 @@ func TestBasedSequencer_GetNextBatch_WithForcedTxs(t *testing.T) { } func TestBasedSequencer_GetNextBatch_EmptyDA(t *testing.T) { - mockRetriever := common.NewMockForcedInclusionRetriever(t) + mockRetriever := NewMockForcedInclusionRetriever(t) mockRetriever.On("RetrieveForcedIncludedTxs", mock.Anything, uint64(100)).Return(&block.ForcedInclusionEvent{ Txs: [][]byte{}, StartDaHeight: 100, @@ -189,7 +188,7 @@ func TestBasedSequencer_GetNextBatch_EmptyDA(t *testing.T) { } func TestBasedSequencer_GetNextBatch_NotConfigured(t *testing.T) { - mockRetriever := common.NewMockForcedInclusionRetriever(t) + mockRetriever := NewMockForcedInclusionRetriever(t) mockRetriever.On("RetrieveForcedIncludedTxs", mock.Anything, uint64(100)).Return(nil, block.ErrForceInclusionNotConfigured) gen := genesis.Genesis{ @@ -219,7 +218,7 @@ func TestBasedSequencer_GetNextBatch_WithMaxBytes(t *testing.T) { tx3 := make([]byte, 200) testBlobs := [][]byte{tx1, tx2, tx3} - mockRetriever := common.NewMockForcedInclusionRetriever(t) + mockRetriever := NewMockForcedInclusionRetriever(t) mockRetriever.On("RetrieveForcedIncludedTxs", mock.Anything, uint64(100)).Return(&block.ForcedInclusionEvent{ Txs: testBlobs, StartDaHeight: 100, @@ -274,7 +273,7 @@ func TestBasedSequencer_GetNextBatch_MultipleDABlocks(t *testing.T) { testBlobs1 := [][]byte{[]byte("tx1"), []byte("tx2")} testBlobs2 := [][]byte{[]byte("tx3"), []byte("tx4")} - mockRetriever := common.NewMockForcedInclusionRetriever(t) + mockRetriever := NewMockForcedInclusionRetriever(t) // First DA block mockRetriever.On("RetrieveForcedIncludedTxs", mock.Anything, uint64(100)).Return(&block.ForcedInclusionEvent{ Txs: testBlobs1, @@ -327,7 +326,7 @@ func TestBasedSequencer_GetNextBatch_ForcedInclusionExceedsMaxBytes(t *testing.T largeTx := make([]byte, 2000) testBlobs := [][]byte{largeTx} - mockRetriever := common.NewMockForcedInclusionRetriever(t) + mockRetriever := NewMockForcedInclusionRetriever(t) mockRetriever.On("RetrieveForcedIncludedTxs", mock.Anything, uint64(100)).Return(&block.ForcedInclusionEvent{ Txs: testBlobs, StartDaHeight: 100, @@ -358,7 +357,7 @@ func TestBasedSequencer_GetNextBatch_ForcedInclusionExceedsMaxBytes(t *testing.T } func TestBasedSequencer_VerifyBatch(t *testing.T) { - mockRetriever := common.NewMockForcedInclusionRetriever(t) + mockRetriever := NewMockForcedInclusionRetriever(t) gen := genesis.Genesis{ ChainID: "test-chain", DAEpochForcedInclusion: 10, @@ -379,7 +378,7 @@ func TestBasedSequencer_VerifyBatch(t *testing.T) { } func TestBasedSequencer_SetDAHeight(t *testing.T) { - mockRetriever := common.NewMockForcedInclusionRetriever(t) + mockRetriever := NewMockForcedInclusionRetriever(t) gen := genesis.Genesis{ ChainID: "test-chain", DAStartHeight: 100, @@ -397,7 +396,7 @@ func TestBasedSequencer_SetDAHeight(t *testing.T) { } func TestBasedSequencer_GetNextBatch_ErrorHandling(t *testing.T) { - mockRetriever := common.NewMockForcedInclusionRetriever(t) + mockRetriever := NewMockForcedInclusionRetriever(t) mockRetriever.On("RetrieveForcedIncludedTxs", mock.Anything, uint64(100)).Return(nil, block.ErrForceInclusionNotConfigured) gen := genesis.Genesis{ @@ -421,7 +420,7 @@ func TestBasedSequencer_GetNextBatch_ErrorHandling(t *testing.T) { } func TestBasedSequencer_GetNextBatch_HeightFromFuture(t *testing.T) { - mockRetriever := common.NewMockForcedInclusionRetriever(t) + mockRetriever := NewMockForcedInclusionRetriever(t) mockRetriever.On("RetrieveForcedIncludedTxs", mock.Anything, uint64(100)).Return(nil, datypes.ErrHeightFromFuture) gen := genesis.Genesis{ @@ -450,7 +449,7 @@ func TestBasedSequencer_GetNextBatch_HeightFromFuture(t *testing.T) { func TestBasedSequencer_CheckpointPersistence(t *testing.T) { testBlobs := [][]byte{[]byte("tx1"), []byte("tx2")} - mockRetriever := common.NewMockForcedInclusionRetriever(t) + mockRetriever := NewMockForcedInclusionRetriever(t) mockRetriever.On("RetrieveForcedIncludedTxs", mock.Anything, uint64(100)).Return(&block.ForcedInclusionEvent{ Txs: testBlobs, StartDaHeight: 100, @@ -525,7 +524,7 @@ func TestBasedSequencer_CrashRecoveryMidEpoch(t *testing.T) { testBlobs := [][]byte{[]byte("tx0"), []byte("tx1"), []byte("tx2"), []byte("tx3"), []byte("tx4")} - mockRetriever := common.NewMockForcedInclusionRetriever(t) + mockRetriever := NewMockForcedInclusionRetriever(t) // The epoch will be fetched twice: once before crash, once after restart mockRetriever.On("RetrieveForcedIncludedTxs", mock.Anything, uint64(100)).Return(&block.ForcedInclusionEvent{ Txs: testBlobs, @@ -650,7 +649,7 @@ func TestBasedSequencer_CrashRecoveryMidEpoch(t *testing.T) { } func TestBasedSequencer_GetNextBatch_EmptyDABatch_IncreasesDAHeight(t *testing.T) { - mockRetriever := common.NewMockForcedInclusionRetriever(t) + mockRetriever := NewMockForcedInclusionRetriever(t) // First DA block returns empty transactions mockRetriever.On("RetrieveForcedIncludedTxs", mock.Anything, uint64(100)).Return(&block.ForcedInclusionEvent{ @@ -715,7 +714,7 @@ func TestBasedSequencer_GetNextBatch_TimestampAdjustment(t *testing.T) { testBlobs := [][]byte{[]byte("tx1"), []byte("tx2"), []byte("tx3")} daEndTime := time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC) - mockRetriever := common.NewMockForcedInclusionRetriever(t) + mockRetriever := NewMockForcedInclusionRetriever(t) mockRetriever.On("RetrieveForcedIncludedTxs", mock.Anything, uint64(100)).Return(&block.ForcedInclusionEvent{ Txs: testBlobs, StartDaHeight: 100, @@ -759,7 +758,7 @@ func TestBasedSequencer_GetNextBatch_TimestampAdjustment_PartialBatch(t *testing testBlobs := [][]byte{tx1, tx2, tx3} daEndTime := time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC) - mockRetriever := common.NewMockForcedInclusionRetriever(t) + mockRetriever := NewMockForcedInclusionRetriever(t) mockRetriever.On("RetrieveForcedIncludedTxs", mock.Anything, uint64(100)).Return(&block.ForcedInclusionEvent{ Txs: testBlobs, StartDaHeight: 100, @@ -817,7 +816,7 @@ func TestBasedSequencer_GetNextBatch_TimestampAdjustment_EmptyBatch(t *testing.T // The checkpoint must still advance past the empty epoch. daEndTime := time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC) - mockRetriever := common.NewMockForcedInclusionRetriever(t) + mockRetriever := NewMockForcedInclusionRetriever(t) mockRetriever.On("RetrieveForcedIncludedTxs", mock.Anything, uint64(100)).Return(&block.ForcedInclusionEvent{ Txs: [][]byte{}, StartDaHeight: 100, @@ -869,7 +868,7 @@ func TestBasedSequencer_GetNextBatch_GasFilteringPreservesUnprocessedTxs(t *test testBlobs := [][]byte{tx0, tx1, tx2, tx3, tx4} - mockRetriever := common.NewMockForcedInclusionRetriever(t) + mockRetriever := NewMockForcedInclusionRetriever(t) // Only expect one call to retrieve - all txs come from one DA epoch mockRetriever.On("RetrieveForcedIncludedTxs", mock.Anything, uint64(100)).Return(&block.ForcedInclusionEvent{ Txs: testBlobs, @@ -988,7 +987,7 @@ func TestBasedSequencer_GetNextBatch_GasFilteringPreservesUnprocessedTxs(t *test assert.GreaterOrEqual(t, totalTxsProcessed, 3, "should process at least 3 valid transactions from the cache") } -func replaceWithMockRetriever(seq *BasedSequencer, mockRetriever *common.MockForcedInclusionRetriever) { +func replaceWithMockRetriever(seq *BasedSequencer, mockRetriever *MockForcedInclusionRetriever) { if seq.fiRetriever != nil { seq.fiRetriever.Stop() }