Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: adding multi-rollup sequencer for the purpose of testing #18

Merged
merged 9 commits into from
Oct 18, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
134 changes: 134 additions & 0 deletions test/multi_rollup_sequencer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
package test

import (
"bytes"
"context"
"encoding/hex"
"errors"
"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
gupadhyaya marked this conversation as resolved.
Show resolved Hide resolved
}

// 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
}
gupadhyaya marked this conversation as resolved.
Show resolved Hide resolved
gupadhyaya marked this conversation as resolved.
Show resolved Hide resolved

// 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 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")
}
gupadhyaya marked this conversation as resolved.
Show resolved Hide resolved
gupadhyaya marked this conversation as resolved.
Show resolved Hide resolved

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 batch.Transactions == nil {
return batchRes, nil
gupadhyaya marked this conversation as resolved.
Show resolved Hide resolved
}

h, err := batch.Hash()
if err != nil {
return nil, err
}
gupadhyaya marked this conversation as resolved.
Show resolved Hide resolved

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
}
gupadhyaya marked this conversation as resolved.
Show resolved Hide resolved
gupadhyaya marked this conversation as resolved.
Show resolved Hide resolved

// 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.RLock()
rollup, exists := d.rollups[rollupKey]
d.rollupsMutex.RUnlock()

if exists {
return rollup, nil
}

d.rollupsMutex.Lock()
defer d.rollupsMutex.Unlock()
gupadhyaya marked this conversation as resolved.
Show resolved Hide resolved

// 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{}
150 changes: 150 additions & 0 deletions test/multi_rollup_sequencer_test.go
gupadhyaya marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
package test

import (
"context"
"math"
"testing"

"github.com/rollkit/go-sequencing"
"github.com/stretchr/testify/assert"
)

func TestMultiRollupSequencer_SubmitRollupTransaction(t *testing.T) {
sequencer := NewMultiRollupSequencer()

rollupId := []byte("test-rollup")
tx := []byte("transaction data")
gupadhyaya marked this conversation as resolved.
Show resolved Hide resolved

// Submit the transaction
req := sequencing.SubmitRollupTransactionRequest{
RollupId: rollupId,
Tx: tx,
}

res, err := sequencer.SubmitRollupTransaction(context.Background(), req)

assert.NoError(t, err)
assert.NotNil(t, res)

// Ensure that the transaction was added to the transaction queue for the rollup
rollup, _ := sequencer.getOrCreateRollup(rollupId)
nextBatch := rollup.tq.GetNextBatch(math.MaxInt32)

assert.Equal(t, 1, len(nextBatch.Transactions))
assert.Equal(t, tx, nextBatch.Transactions[0])
}

func TestMultiRollupSequencer_GetNextBatch(t *testing.T) {
sequencer := NewMultiRollupSequencer()

rollupId := []byte("test-rollup")
tx := []byte("transaction data")

// Submit the transaction
req := sequencing.SubmitRollupTransactionRequest{
RollupId: rollupId,
Tx: tx,
}
_, err := sequencer.SubmitRollupTransaction(context.Background(), req)
assert.NoError(t, err)

// Get next batch
getBatchReq := sequencing.GetNextBatchRequest{
RollupId: rollupId,
LastBatchHash: nil,
MaxBytes: math.MaxInt32,
}
batchRes, err := sequencer.GetNextBatch(context.Background(), getBatchReq)
assert.NoError(t, err)

// Verify that the batch contains the transaction
assert.NotNil(t, batchRes.Batch)
assert.Equal(t, 1, len(batchRes.Batch.Transactions))
assert.Equal(t, tx, batchRes.Batch.Transactions[0])
}

func TestMultiRollupSequencer_VerifyBatch(t *testing.T) {
sequencer := NewMultiRollupSequencer()

rollupId := []byte("test-rollup")
tx := []byte("transaction data")

// Submit the transaction
req := sequencing.SubmitRollupTransactionRequest{
RollupId: rollupId,
Tx: tx,
}
_, err := sequencer.SubmitRollupTransaction(context.Background(), req)
assert.NoError(t, err)

// Get the next batch to update the last batch hash
getBatchReq := sequencing.GetNextBatchRequest{
RollupId: rollupId,
LastBatchHash: nil,
MaxBytes: math.MaxInt32,
}
batchRes, err := sequencer.GetNextBatch(context.Background(), getBatchReq)
assert.NoError(t, err)

bHash, err := batchRes.Batch.Hash()
assert.NoError(t, err)

// Verify the batch
verifyReq := sequencing.VerifyBatchRequest{
RollupId: rollupId,
BatchHash: bHash,
}

verifyRes, err := sequencer.VerifyBatch(context.Background(), verifyReq)
assert.NoError(t, err)
assert.True(t, verifyRes.Status)
}

func TestMultiRollupSequencer_MultipleRollups(t *testing.T) {
sequencer := NewMultiRollupSequencer()

rollupId1 := []byte("rollup-1")
rollupId2 := []byte("rollup-2")
tx1 := []byte("tx data 1")
tx2 := []byte("tx data 2")

// Submit transactions for two different rollups
req1 := sequencing.SubmitRollupTransactionRequest{
RollupId: rollupId1,
Tx: tx1,
}
req2 := sequencing.SubmitRollupTransactionRequest{
RollupId: rollupId2,
Tx: tx2,
}

_, err := sequencer.SubmitRollupTransaction(context.Background(), req1)
assert.NoError(t, err)

_, err = sequencer.SubmitRollupTransaction(context.Background(), req2)
assert.NoError(t, err)

// Get next batch for rollup 1
getBatchReq1 := sequencing.GetNextBatchRequest{
RollupId: rollupId1,
LastBatchHash: nil,
MaxBytes: math.MaxInt32,
}
batchRes1, err := sequencer.GetNextBatch(context.Background(), getBatchReq1)
assert.NoError(t, err)

assert.Equal(t, 1, len(batchRes1.Batch.Transactions))
assert.Equal(t, tx1, batchRes1.Batch.Transactions[0])

// Get next batch for rollup 2
getBatchReq2 := sequencing.GetNextBatchRequest{
RollupId: rollupId2,
LastBatchHash: nil,
MaxBytes: math.MaxInt32,
}
batchRes2, err := sequencer.GetNextBatch(context.Background(), getBatchReq2)
assert.NoError(t, err)

assert.Equal(t, 1, len(batchRes2.Batch.Transactions))
assert.Equal(t, tx2, batchRes2.Batch.Transactions[0])
}
Loading