Skip to content
This repository was archived by the owner on May 2, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 5 additions & 7 deletions test/dummy.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import (
"context"
"encoding/hex"
"errors"
"fmt"
"math"
"reflect"
"sync"
"time"

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

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

batch := d.tq.GetNextBatch(req.MaxBytes)
batchRes := &sequencing.GetNextBatchResponse{Batch: batch, Timestamp: now}
// If there are no transactions, return empty batch without updating the last batch hash
if batch.Transactions == nil {
if len(batch.Transactions) == 0 {
return batchRes, nil
}

Expand Down
75 changes: 53 additions & 22 deletions test/dummy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ package test

import (
"context"
"crypto/rand"
"fmt"
"io"
"math"
"testing"
"time"
Expand All @@ -14,7 +17,8 @@ import (
func TestTransactionQueue_AddTransaction(t *testing.T) {
queue := NewTransactionQueue()

tx1 := []byte("transaction_1")
tx1, err := GenerateSecureRandomBytes(32)
assert.NoError(t, err)
queue.AddTransaction(tx1)

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

// Add multiple transactions
tx1 := []byte("transaction_1")
tx2 := []byte("transaction_2")
tx1, err := GenerateSecureRandomBytes(32)
assert.NoError(t, err)
tx2, err := GenerateSecureRandomBytes(32)
assert.NoError(t, err)
queue.AddTransaction(tx1)
queue.AddTransaction(tx2)

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

// Submit a transaction
Expand Down Expand Up @@ -92,9 +99,12 @@ func TestDummySequencer_SubmitEmptyTransaction(t *testing.T) {
func TestDummySequencer_SubmitMultipleTransactions(t *testing.T) {
// Define a test rollup ID and multiple transactions
rollupId := []byte("test_rollup_id")
tx1 := []byte("transaction_1")
tx2 := []byte("transaction_2")
tx3 := []byte("transaction_3")
tx1, err := GenerateSecureRandomBytes(32)
assert.NoError(t, err)
tx2, err := GenerateSecureRandomBytes(32)
assert.NoError(t, err)
tx3, err := GenerateSecureRandomBytes(32)
assert.NoError(t, err)
sequencer := NewDummySequencer(rollupId)

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

_, err := sequencer.SubmitRollupTransaction(context.Background(), req1)
_, err = sequencer.SubmitRollupTransaction(context.Background(), req1)
assert.NoError(t, err)
_, err = sequencer.SubmitRollupTransaction(context.Background(), req2)
assert.NoError(t, err)
Expand All @@ -129,13 +139,14 @@ func TestDummySequencer_SubmitMultipleTransactions(t *testing.T) {
func TestDummySequencer_GetNextBatch(t *testing.T) {
// Add a transaction to the queue
rollupId := []byte("test_rollup_id")
tx := []byte("test_transaction")
tx, err := GenerateSecureRandomBytes(32)
assert.NoError(t, err)
sequencer := NewDummySequencer(rollupId)
req := sequencing.SubmitRollupTransactionRequest{
RollupId: rollupId,
Tx: tx,
}
_, err := sequencer.SubmitRollupTransaction(context.Background(), req)
_, err = sequencer.SubmitRollupTransaction(context.Background(), req)
assert.NoError(t, err)

// Retrieve the next batch
Expand Down Expand Up @@ -178,12 +189,13 @@ func TestDummySequencer_GetNextBatch_LastBatchHashMismatch(t *testing.T) {
// Submit a transaction
rollupId := []byte("test_rollup_id")
sequencer := NewDummySequencer(rollupId)
tx := []byte("test_transaction")
tx, err := GenerateSecureRandomBytes(32)
assert.NoError(t, err)
req := sequencing.SubmitRollupTransactionRequest{
RollupId: rollupId,
Tx: tx,
}
_, err := sequencer.SubmitRollupTransaction(context.Background(), req)
_, err = sequencer.SubmitRollupTransaction(context.Background(), req)
assert.NoError(t, err)

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

// Assert that the batch hash mismatch error is returned
assert.Error(t, err)
assert.Equal(t, "lastBatch is supposed to be nil", err.Error())
assert.ErrorContains(t, err, "batch hash mismatch", "unexpected error message")
}

// Test retrieving a batch with maxBytes limit
func TestDummySequencer_GetNextBatch_MaxBytesLimit(t *testing.T) {
// Define a test rollup ID and multiple transactions
rollupId := []byte("test_rollup_id")
sequencer := NewDummySequencer(rollupId)
tx1 := []byte("transaction_1")
tx2 := []byte("transaction_2")
tx3 := []byte("transaction_3")
tx1, err := GenerateSecureRandomBytes(32)
assert.NoError(t, err)
tx2, err := GenerateSecureRandomBytes(32)
assert.NoError(t, err)
tx3, err := GenerateSecureRandomBytes(32)
assert.NoError(t, err)

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

_, err := sequencer.SubmitRollupTransaction(context.Background(), req1)
_, err = sequencer.SubmitRollupTransaction(context.Background(), req1)
assert.NoError(t, err)
_, err = sequencer.SubmitRollupTransaction(context.Background(), req2)
assert.NoError(t, err)
Expand Down Expand Up @@ -267,12 +282,13 @@ func TestDummySequencer_VerifyBatch(t *testing.T) {
// Add and retrieve a batch
rollupId := []byte("test_rollup_id")
sequencer := NewDummySequencer(rollupId)
tx := []byte("test_transaction")
tx, err := GenerateSecureRandomBytes(32)
assert.NoError(t, err)
req := sequencing.SubmitRollupTransactionRequest{
RollupId: rollupId,
Tx: tx,
}
_, err := sequencer.SubmitRollupTransaction(context.Background(), req)
_, err = sequencer.SubmitRollupTransaction(context.Background(), req)
assert.NoError(t, err)

// Get the next batch to generate batch hash
Expand Down Expand Up @@ -320,8 +336,10 @@ func TestDummySequencer_VerifyBatchWithMultipleTransactions(t *testing.T) {
// Define a test rollup ID and multiple transactions
rollupId := []byte("test_rollup_id")
sequencer := NewDummySequencer(rollupId)
tx1 := []byte("transaction_1")
tx2 := []byte("transaction_2")
tx1, err := GenerateSecureRandomBytes(32)
assert.NoError(t, err)
tx2, err := GenerateSecureRandomBytes(32)
assert.NoError(t, err)

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

_, err := sequencer.SubmitRollupTransaction(context.Background(), req1)
_, err = sequencer.SubmitRollupTransaction(context.Background(), req1)
assert.NoError(t, err)
_, err = sequencer.SubmitRollupTransaction(context.Background(), req2)
assert.NoError(t, err)
Expand Down Expand Up @@ -375,3 +393,16 @@ func TestDummySequencer_VerifyBatch_NotFound(t *testing.T) {
assert.NoError(t, err)
assert.False(t, verifyResp.Status)
}

// GenerateSecureRandomBytes generates cryptographically secure random bytes of the given length.
func GenerateSecureRandomBytes(length int) ([]byte, error) {
if length <= 0 {
return nil, fmt.Errorf("invalid length: %d, must be greater than 0", length)
}

buf := make([]byte, length)
if _, err := io.ReadFull(rand.Reader, buf); err != nil {
return nil, fmt.Errorf("failed to generate random bytes: %w", err)
}
return buf, nil
}
126 changes: 126 additions & 0 deletions test/multi_rollup_sequencer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package test

import (
"context"
"encoding/hex"
"fmt"
"reflect"
"sync"
"time"

"github.com/rollkit/go-sequencing"
)

// MultiRollupSequencer is a sequencer for testing that serves multiple rollups
type MultiRollupSequencer struct {
rollups map[string]*RollupData
rollupsMutex sync.RWMutex
}

// RollupData holds the data for a specific rollup, including its transaction queue, last batch hash, and seen batches.
type RollupData struct {
tq *TransactionQueue
lastBatchHash []byte
lastBatchHashMutex sync.RWMutex

seenBatches map[string]struct{}
seenBatchesMutex sync.Mutex
}

// SubmitRollupTransaction implements sequencing.Sequencer.
func (d *MultiRollupSequencer) SubmitRollupTransaction(ctx context.Context, req sequencing.SubmitRollupTransactionRequest) (*sequencing.SubmitRollupTransactionResponse, error) {
rollup, err := d.getOrCreateRollup(req.RollupId)
if err != nil {
return nil, err
}
rollup.tq.AddTransaction(req.Tx)
return &sequencing.SubmitRollupTransactionResponse{}, nil
}

// GetNextBatch implements sequencing.Sequencer.
func (d *MultiRollupSequencer) GetNextBatch(ctx context.Context, req sequencing.GetNextBatchRequest) (*sequencing.GetNextBatchResponse, error) {
rollup, err := d.getOrCreateRollup(req.RollupId)
if err != nil {
return nil, err
}

now := time.Now()
rollup.lastBatchHashMutex.RLock()
lastBatchHash := rollup.lastBatchHash
rollup.lastBatchHashMutex.RUnlock()

if !reflect.DeepEqual(lastBatchHash, req.LastBatchHash) {
return nil, fmt.Errorf("batch hash mismatch: lastBatchHash = %x, req.LastBatchHash = %x", lastBatchHash, req.LastBatchHash)
}

batch := rollup.tq.GetNextBatch(req.MaxBytes)
batchRes := &sequencing.GetNextBatchResponse{Batch: batch, Timestamp: now}
// If there are no transactions, return empty batch without updating the last batch hash
if len(batch.Transactions) == 0 {
return batchRes, nil
}

h, err := batch.Hash()
if err != nil {
return nil, err
}

rollup.lastBatchHashMutex.Lock()
rollup.lastBatchHash = h
rollup.lastBatchHashMutex.Unlock()

rollup.seenBatchesMutex.Lock()
rollup.seenBatches[hex.EncodeToString(h)] = struct{}{}
rollup.seenBatchesMutex.Unlock()
return batchRes, nil
}

// VerifyBatch implements sequencing.Sequencer.
func (d *MultiRollupSequencer) VerifyBatch(ctx context.Context, req sequencing.VerifyBatchRequest) (*sequencing.VerifyBatchResponse, error) {
rollup, err := d.getOrCreateRollup(req.RollupId)
if err != nil {
return nil, err
}

rollup.seenBatchesMutex.Lock()
defer rollup.seenBatchesMutex.Unlock()
key := hex.EncodeToString(req.BatchHash)
if _, exists := rollup.seenBatches[key]; exists {
return &sequencing.VerifyBatchResponse{Status: true}, nil
}
return &sequencing.VerifyBatchResponse{Status: false}, nil
}

// getOrCreateRollup returns the RollupData for a given rollupId, creating it if necessary.
func (d *MultiRollupSequencer) getOrCreateRollup(rollupId []byte) (*RollupData, error) {
rollupKey := hex.EncodeToString(rollupId)

d.rollupsMutex.Lock()
defer d.rollupsMutex.Unlock()
rollup, exists := d.rollups[rollupKey]
if exists {
return rollup, nil
}

// Double-check existence after acquiring write lock
if rollup, exists := d.rollups[rollupKey]; exists {
return rollup, nil
}

// Create a new RollupData if it doesn't exist
rollup = &RollupData{
tq: NewTransactionQueue(),
seenBatches: make(map[string]struct{}, 0),
}
d.rollups[rollupKey] = rollup
return rollup, nil
}

// NewMultiRollupSequencer creates a new MultiRollupSequencer
func NewMultiRollupSequencer() *MultiRollupSequencer {
return &MultiRollupSequencer{
rollups: make(map[string]*RollupData),
}
}

var _ sequencing.Sequencer = &MultiRollupSequencer{}
Loading
Loading