Skip to content
This repository was archived by the owner on May 2, 2025. It is now read-only.

Commit 0f699be

Browse files
authored
feat: adding multi-rollup sequencer for the purpose of testing (#18)
* adding multi-rollup sequencer for the purpose of testing * lint * minor fix * fix tests and checks * DeepEqual * more deepequal * adding multi-rollup test * add test
1 parent 3682ffb commit 0f699be

File tree

4 files changed

+396
-29
lines changed

4 files changed

+396
-29
lines changed

test/dummy.go

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ import (
55
"context"
66
"encoding/hex"
77
"errors"
8+
"fmt"
89
"math"
10+
"reflect"
911
"sync"
1012
"time"
1113

@@ -105,18 +107,14 @@ func (d *DummySequencer) GetNextBatch(ctx context.Context, req sequencing.GetNex
105107
lastBatchHash := d.lastBatchHash
106108
d.lastBatchHashMutex.RUnlock()
107109

108-
if lastBatchHash == nil && req.LastBatchHash != nil {
109-
return nil, errors.New("lastBatch is supposed to be nil")
110-
} else if lastBatchHash != nil && req.LastBatchHash == nil {
111-
return nil, errors.New("lastBatch is not supposed to be nil")
112-
} else if !bytes.Equal(lastBatchHash, req.LastBatchHash) {
113-
return nil, errors.New("supplied lastBatch does not match with sequencer last batch")
110+
if !reflect.DeepEqual(lastBatchHash, req.LastBatchHash) {
111+
return nil, fmt.Errorf("batch hash mismatch: lastBatchHash = %x, req.LastBatchHash = %x", lastBatchHash, req.LastBatchHash)
114112
}
115113

116114
batch := d.tq.GetNextBatch(req.MaxBytes)
117115
batchRes := &sequencing.GetNextBatchResponse{Batch: batch, Timestamp: now}
118116
// If there are no transactions, return empty batch without updating the last batch hash
119-
if batch.Transactions == nil {
117+
if len(batch.Transactions) == 0 {
120118
return batchRes, nil
121119
}
122120

test/dummy_test.go

Lines changed: 53 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ package test
22

33
import (
44
"context"
5+
"crypto/rand"
6+
"fmt"
7+
"io"
58
"math"
69
"testing"
710
"time"
@@ -14,7 +17,8 @@ import (
1417
func TestTransactionQueue_AddTransaction(t *testing.T) {
1518
queue := NewTransactionQueue()
1619

17-
tx1 := []byte("transaction_1")
20+
tx1, err := GenerateSecureRandomBytes(32)
21+
assert.NoError(t, err)
1822
queue.AddTransaction(tx1)
1923

2024
// Check that the transaction was added
@@ -27,8 +31,10 @@ func TestTransactionQueue_GetNextBatch(t *testing.T) {
2731
queue := NewTransactionQueue()
2832

2933
// Add multiple transactions
30-
tx1 := []byte("transaction_1")
31-
tx2 := []byte("transaction_2")
34+
tx1, err := GenerateSecureRandomBytes(32)
35+
assert.NoError(t, err)
36+
tx2, err := GenerateSecureRandomBytes(32)
37+
assert.NoError(t, err)
3238
queue.AddTransaction(tx1)
3339
queue.AddTransaction(tx2)
3440

@@ -46,7 +52,8 @@ func TestTransactionQueue_GetNextBatch(t *testing.T) {
4652
func TestDummySequencer_SubmitRollupTransaction(t *testing.T) {
4753
// Define a test rollup ID and transaction
4854
rollupId := []byte("test_rollup_id")
49-
tx := []byte("test_transaction")
55+
tx, err := GenerateSecureRandomBytes(32)
56+
assert.NoError(t, err)
5057
sequencer := NewDummySequencer(rollupId)
5158

5259
// Submit a transaction
@@ -92,9 +99,12 @@ func TestDummySequencer_SubmitEmptyTransaction(t *testing.T) {
9299
func TestDummySequencer_SubmitMultipleTransactions(t *testing.T) {
93100
// Define a test rollup ID and multiple transactions
94101
rollupId := []byte("test_rollup_id")
95-
tx1 := []byte("transaction_1")
96-
tx2 := []byte("transaction_2")
97-
tx3 := []byte("transaction_3")
102+
tx1, err := GenerateSecureRandomBytes(32)
103+
assert.NoError(t, err)
104+
tx2, err := GenerateSecureRandomBytes(32)
105+
assert.NoError(t, err)
106+
tx3, err := GenerateSecureRandomBytes(32)
107+
assert.NoError(t, err)
98108
sequencer := NewDummySequencer(rollupId)
99109

100110
// Submit multiple transactions
@@ -111,7 +121,7 @@ func TestDummySequencer_SubmitMultipleTransactions(t *testing.T) {
111121
Tx: tx3,
112122
}
113123

114-
_, err := sequencer.SubmitRollupTransaction(context.Background(), req1)
124+
_, err = sequencer.SubmitRollupTransaction(context.Background(), req1)
115125
assert.NoError(t, err)
116126
_, err = sequencer.SubmitRollupTransaction(context.Background(), req2)
117127
assert.NoError(t, err)
@@ -129,13 +139,14 @@ func TestDummySequencer_SubmitMultipleTransactions(t *testing.T) {
129139
func TestDummySequencer_GetNextBatch(t *testing.T) {
130140
// Add a transaction to the queue
131141
rollupId := []byte("test_rollup_id")
132-
tx := []byte("test_transaction")
142+
tx, err := GenerateSecureRandomBytes(32)
143+
assert.NoError(t, err)
133144
sequencer := NewDummySequencer(rollupId)
134145
req := sequencing.SubmitRollupTransactionRequest{
135146
RollupId: rollupId,
136147
Tx: tx,
137148
}
138-
_, err := sequencer.SubmitRollupTransaction(context.Background(), req)
149+
_, err = sequencer.SubmitRollupTransaction(context.Background(), req)
139150
assert.NoError(t, err)
140151

141152
// Retrieve the next batch
@@ -178,12 +189,13 @@ func TestDummySequencer_GetNextBatch_LastBatchHashMismatch(t *testing.T) {
178189
// Submit a transaction
179190
rollupId := []byte("test_rollup_id")
180191
sequencer := NewDummySequencer(rollupId)
181-
tx := []byte("test_transaction")
192+
tx, err := GenerateSecureRandomBytes(32)
193+
assert.NoError(t, err)
182194
req := sequencing.SubmitRollupTransactionRequest{
183195
RollupId: rollupId,
184196
Tx: tx,
185197
}
186-
_, err := sequencer.SubmitRollupTransaction(context.Background(), req)
198+
_, err = sequencer.SubmitRollupTransaction(context.Background(), req)
187199
assert.NoError(t, err)
188200

189201
// Retrieve the next batch
@@ -195,17 +207,20 @@ func TestDummySequencer_GetNextBatch_LastBatchHashMismatch(t *testing.T) {
195207

196208
// Assert that the batch hash mismatch error is returned
197209
assert.Error(t, err)
198-
assert.Equal(t, "lastBatch is supposed to be nil", err.Error())
210+
assert.ErrorContains(t, err, "batch hash mismatch", "unexpected error message")
199211
}
200212

201213
// Test retrieving a batch with maxBytes limit
202214
func TestDummySequencer_GetNextBatch_MaxBytesLimit(t *testing.T) {
203215
// Define a test rollup ID and multiple transactions
204216
rollupId := []byte("test_rollup_id")
205217
sequencer := NewDummySequencer(rollupId)
206-
tx1 := []byte("transaction_1")
207-
tx2 := []byte("transaction_2")
208-
tx3 := []byte("transaction_3")
218+
tx1, err := GenerateSecureRandomBytes(32)
219+
assert.NoError(t, err)
220+
tx2, err := GenerateSecureRandomBytes(32)
221+
assert.NoError(t, err)
222+
tx3, err := GenerateSecureRandomBytes(32)
223+
assert.NoError(t, err)
209224

210225
// Submit multiple transactions
211226
req1 := sequencing.SubmitRollupTransactionRequest{
@@ -221,7 +236,7 @@ func TestDummySequencer_GetNextBatch_MaxBytesLimit(t *testing.T) {
221236
Tx: tx3,
222237
}
223238

224-
_, err := sequencer.SubmitRollupTransaction(context.Background(), req1)
239+
_, err = sequencer.SubmitRollupTransaction(context.Background(), req1)
225240
assert.NoError(t, err)
226241
_, err = sequencer.SubmitRollupTransaction(context.Background(), req2)
227242
assert.NoError(t, err)
@@ -267,12 +282,13 @@ func TestDummySequencer_VerifyBatch(t *testing.T) {
267282
// Add and retrieve a batch
268283
rollupId := []byte("test_rollup_id")
269284
sequencer := NewDummySequencer(rollupId)
270-
tx := []byte("test_transaction")
285+
tx, err := GenerateSecureRandomBytes(32)
286+
assert.NoError(t, err)
271287
req := sequencing.SubmitRollupTransactionRequest{
272288
RollupId: rollupId,
273289
Tx: tx,
274290
}
275-
_, err := sequencer.SubmitRollupTransaction(context.Background(), req)
291+
_, err = sequencer.SubmitRollupTransaction(context.Background(), req)
276292
assert.NoError(t, err)
277293

278294
// Get the next batch to generate batch hash
@@ -320,8 +336,10 @@ func TestDummySequencer_VerifyBatchWithMultipleTransactions(t *testing.T) {
320336
// Define a test rollup ID and multiple transactions
321337
rollupId := []byte("test_rollup_id")
322338
sequencer := NewDummySequencer(rollupId)
323-
tx1 := []byte("transaction_1")
324-
tx2 := []byte("transaction_2")
339+
tx1, err := GenerateSecureRandomBytes(32)
340+
assert.NoError(t, err)
341+
tx2, err := GenerateSecureRandomBytes(32)
342+
assert.NoError(t, err)
325343

326344
// Submit multiple transactions
327345
req1 := sequencing.SubmitRollupTransactionRequest{
@@ -333,7 +351,7 @@ func TestDummySequencer_VerifyBatchWithMultipleTransactions(t *testing.T) {
333351
Tx: tx2,
334352
}
335353

336-
_, err := sequencer.SubmitRollupTransaction(context.Background(), req1)
354+
_, err = sequencer.SubmitRollupTransaction(context.Background(), req1)
337355
assert.NoError(t, err)
338356
_, err = sequencer.SubmitRollupTransaction(context.Background(), req2)
339357
assert.NoError(t, err)
@@ -375,3 +393,16 @@ func TestDummySequencer_VerifyBatch_NotFound(t *testing.T) {
375393
assert.NoError(t, err)
376394
assert.False(t, verifyResp.Status)
377395
}
396+
397+
// GenerateSecureRandomBytes generates cryptographically secure random bytes of the given length.
398+
func GenerateSecureRandomBytes(length int) ([]byte, error) {
399+
if length <= 0 {
400+
return nil, fmt.Errorf("invalid length: %d, must be greater than 0", length)
401+
}
402+
403+
buf := make([]byte, length)
404+
if _, err := io.ReadFull(rand.Reader, buf); err != nil {
405+
return nil, fmt.Errorf("failed to generate random bytes: %w", err)
406+
}
407+
return buf, nil
408+
}

test/multi_rollup_sequencer.go

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
package test
2+
3+
import (
4+
"context"
5+
"encoding/hex"
6+
"fmt"
7+
"reflect"
8+
"sync"
9+
"time"
10+
11+
"github.com/rollkit/go-sequencing"
12+
)
13+
14+
// MultiRollupSequencer is a sequencer for testing that serves multiple rollups
15+
type MultiRollupSequencer struct {
16+
rollups map[string]*RollupData
17+
rollupsMutex sync.RWMutex
18+
}
19+
20+
// RollupData holds the data for a specific rollup, including its transaction queue, last batch hash, and seen batches.
21+
type RollupData struct {
22+
tq *TransactionQueue
23+
lastBatchHash []byte
24+
lastBatchHashMutex sync.RWMutex
25+
26+
seenBatches map[string]struct{}
27+
seenBatchesMutex sync.Mutex
28+
}
29+
30+
// SubmitRollupTransaction implements sequencing.Sequencer.
31+
func (d *MultiRollupSequencer) SubmitRollupTransaction(ctx context.Context, req sequencing.SubmitRollupTransactionRequest) (*sequencing.SubmitRollupTransactionResponse, error) {
32+
rollup, err := d.getOrCreateRollup(req.RollupId)
33+
if err != nil {
34+
return nil, err
35+
}
36+
rollup.tq.AddTransaction(req.Tx)
37+
return &sequencing.SubmitRollupTransactionResponse{}, nil
38+
}
39+
40+
// GetNextBatch implements sequencing.Sequencer.
41+
func (d *MultiRollupSequencer) GetNextBatch(ctx context.Context, req sequencing.GetNextBatchRequest) (*sequencing.GetNextBatchResponse, error) {
42+
rollup, err := d.getOrCreateRollup(req.RollupId)
43+
if err != nil {
44+
return nil, err
45+
}
46+
47+
now := time.Now()
48+
rollup.lastBatchHashMutex.RLock()
49+
lastBatchHash := rollup.lastBatchHash
50+
rollup.lastBatchHashMutex.RUnlock()
51+
52+
if !reflect.DeepEqual(lastBatchHash, req.LastBatchHash) {
53+
return nil, fmt.Errorf("batch hash mismatch: lastBatchHash = %x, req.LastBatchHash = %x", lastBatchHash, req.LastBatchHash)
54+
}
55+
56+
batch := rollup.tq.GetNextBatch(req.MaxBytes)
57+
batchRes := &sequencing.GetNextBatchResponse{Batch: batch, Timestamp: now}
58+
// If there are no transactions, return empty batch without updating the last batch hash
59+
if len(batch.Transactions) == 0 {
60+
return batchRes, nil
61+
}
62+
63+
h, err := batch.Hash()
64+
if err != nil {
65+
return nil, err
66+
}
67+
68+
rollup.lastBatchHashMutex.Lock()
69+
rollup.lastBatchHash = h
70+
rollup.lastBatchHashMutex.Unlock()
71+
72+
rollup.seenBatchesMutex.Lock()
73+
rollup.seenBatches[hex.EncodeToString(h)] = struct{}{}
74+
rollup.seenBatchesMutex.Unlock()
75+
return batchRes, nil
76+
}
77+
78+
// VerifyBatch implements sequencing.Sequencer.
79+
func (d *MultiRollupSequencer) VerifyBatch(ctx context.Context, req sequencing.VerifyBatchRequest) (*sequencing.VerifyBatchResponse, error) {
80+
rollup, err := d.getOrCreateRollup(req.RollupId)
81+
if err != nil {
82+
return nil, err
83+
}
84+
85+
rollup.seenBatchesMutex.Lock()
86+
defer rollup.seenBatchesMutex.Unlock()
87+
key := hex.EncodeToString(req.BatchHash)
88+
if _, exists := rollup.seenBatches[key]; exists {
89+
return &sequencing.VerifyBatchResponse{Status: true}, nil
90+
}
91+
return &sequencing.VerifyBatchResponse{Status: false}, nil
92+
}
93+
94+
// getOrCreateRollup returns the RollupData for a given rollupId, creating it if necessary.
95+
func (d *MultiRollupSequencer) getOrCreateRollup(rollupId []byte) (*RollupData, error) {
96+
rollupKey := hex.EncodeToString(rollupId)
97+
98+
d.rollupsMutex.Lock()
99+
defer d.rollupsMutex.Unlock()
100+
rollup, exists := d.rollups[rollupKey]
101+
if exists {
102+
return rollup, nil
103+
}
104+
105+
// Double-check existence after acquiring write lock
106+
if rollup, exists := d.rollups[rollupKey]; exists {
107+
return rollup, nil
108+
}
109+
110+
// Create a new RollupData if it doesn't exist
111+
rollup = &RollupData{
112+
tq: NewTransactionQueue(),
113+
seenBatches: make(map[string]struct{}, 0),
114+
}
115+
d.rollups[rollupKey] = rollup
116+
return rollup, nil
117+
}
118+
119+
// NewMultiRollupSequencer creates a new MultiRollupSequencer
120+
func NewMultiRollupSequencer() *MultiRollupSequencer {
121+
return &MultiRollupSequencer{
122+
rollups: make(map[string]*RollupData),
123+
}
124+
}
125+
126+
var _ sequencing.Sequencer = &MultiRollupSequencer{}

0 commit comments

Comments
 (0)