diff --git a/test/dummy.go b/test/dummy.go index 633d551..405decc 100644 --- a/test/dummy.go +++ b/test/dummy.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "errors" + "math" "sync" "time" @@ -30,17 +31,39 @@ func (tq *TransactionQueue) AddTransaction(tx sequencing.Tx) { tq.queue = append(tq.queue, tx) } +func totalBytes(data [][]byte) int { + total := 0 + for _, sub := range data { + total += len(sub) + } + return total +} + // GetNextBatch extracts a batch of transactions from the queue -func (tq *TransactionQueue) GetNextBatch() *sequencing.Batch { +func (tq *TransactionQueue) GetNextBatch(maxBytes uint64) *sequencing.Batch { tq.mu.Lock() defer tq.mu.Unlock() - size := len(tq.queue) - if size == 0 { + var batch [][]byte + batchSize := uint64(len(tq.queue)) + if batchSize == 0 { return &sequencing.Batch{Transactions: nil} } - batch := tq.queue[:size] - tq.queue = tq.queue[size:] + + if maxBytes == 0 { + maxBytes = math.MaxUint64 + } + + for { + batch = tq.queue[:batchSize] + blobSize := totalBytes(batch) + if uint64(blobSize) <= maxBytes { + break + } + batchSize = batchSize - 1 + } + + tq.queue = tq.queue[batchSize:] return &sequencing.Batch{Transactions: batch} } @@ -78,7 +101,7 @@ func (d *DummySequencer) GetNextBatch(ctx context.Context, req sequencing.GetNex } } - batch := d.tq.GetNextBatch() + 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 { diff --git a/test/dummy_test.go b/test/dummy_test.go index 0a899ec..c890029 100644 --- a/test/dummy_test.go +++ b/test/dummy_test.go @@ -2,6 +2,7 @@ package test import ( "context" + "math" "testing" "time" @@ -17,7 +18,7 @@ func TestTransactionQueue_AddTransaction(t *testing.T) { queue.AddTransaction(tx1) // Check that the transaction was added - batch := queue.GetNextBatch() + batch := queue.GetNextBatch(math.MaxUint64) assert.Equal(t, 1, len(batch.Transactions)) assert.Equal(t, tx1, batch.Transactions[0]) } @@ -32,13 +33,13 @@ func TestTransactionQueue_GetNextBatch(t *testing.T) { queue.AddTransaction(tx2) // Get next batch and check if all transactions are retrieved - batch := queue.GetNextBatch() + batch := queue.GetNextBatch(math.MaxUint64) assert.Equal(t, 2, len(batch.Transactions)) assert.Equal(t, tx1, batch.Transactions[0]) assert.Equal(t, tx2, batch.Transactions[1]) // Ensure the queue is empty after retrieving the batch - emptyBatch := queue.GetNextBatch() + emptyBatch := queue.GetNextBatch(math.MaxUint64) assert.Equal(t, 0, len(emptyBatch.Transactions)) } @@ -61,11 +62,73 @@ func TestDummySequencer_SubmitRollupTransaction(t *testing.T) { assert.NotNil(t, resp) // Check that the transaction is in the queue - batch := sequencer.tq.GetNextBatch() + batch := sequencer.tq.GetNextBatch(math.MaxUint64) assert.Equal(t, 1, len(batch.Transactions)) assert.Equal(t, tx, batch.Transactions[0]) } +func TestDummySequencer_SubmitEmptyTransaction(t *testing.T) { + sequencer := NewDummySequencer() + + // Define a test rollup ID and an empty transaction + rollupId := []byte("test_rollup_id") + tx := []byte("") + + // Submit an empty transaction + req := sequencing.SubmitRollupTransactionRequest{ + RollupId: rollupId, + Tx: tx, + } + resp, err := sequencer.SubmitRollupTransaction(context.Background(), req) + + // Assert no error (since the sequencer may accept empty transactions) and ensure the transaction was added + assert.NoError(t, err) + assert.NotNil(t, resp) + + // Check that the transaction is in the queue (even if empty) + batch := sequencer.tq.GetNextBatch(math.MaxUint64) + assert.Equal(t, 1, len(batch.Transactions)) + assert.Equal(t, tx, batch.Transactions[0]) +} + +func TestDummySequencer_SubmitMultipleTransactions(t *testing.T) { + sequencer := NewDummySequencer() + + // 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") + + // Submit multiple transactions + req1 := sequencing.SubmitRollupTransactionRequest{ + RollupId: rollupId, + Tx: tx1, + } + req2 := sequencing.SubmitRollupTransactionRequest{ + RollupId: rollupId, + Tx: tx2, + } + req3 := sequencing.SubmitRollupTransactionRequest{ + RollupId: rollupId, + Tx: tx3, + } + + _, err := sequencer.SubmitRollupTransaction(context.Background(), req1) + assert.NoError(t, err) + _, err = sequencer.SubmitRollupTransaction(context.Background(), req2) + assert.NoError(t, err) + _, err = sequencer.SubmitRollupTransaction(context.Background(), req3) + assert.NoError(t, err) + + // Check that all transactions are added to the queue + batch := sequencer.tq.GetNextBatch(math.MaxUint64) + assert.Equal(t, 3, len(batch.Transactions)) + assert.Equal(t, tx1, batch.Transactions[0]) + assert.Equal(t, tx2, batch.Transactions[1]) + assert.Equal(t, tx3, batch.Transactions[2]) +} + func TestDummySequencer_GetNextBatch(t *testing.T) { sequencer := NewDummySequencer() @@ -97,6 +160,24 @@ func TestDummySequencer_GetNextBatch(t *testing.T) { assert.WithinDuration(t, time.Now(), batchResp.Timestamp, time.Second) } +// Test retrieving a batch with no transactions +func TestDummySequencer_GetNextBatch_NoTransactions(t *testing.T) { + sequencer := NewDummySequencer() + + // Attempt to retrieve a batch with no transactions in the queue + getBatchReq := sequencing.GetNextBatchRequest{ + RollupId: []byte("test_rollup_id"), + LastBatchHash: nil, + } + batchResp, err := sequencer.GetNextBatch(context.Background(), getBatchReq) + + // Assert no error + assert.NoError(t, err) + // Assert that the batch is empty + assert.NotNil(t, batchResp) + assert.Equal(t, 0, len(batchResp.Batch.Transactions)) +} + func TestDummySequencer_GetNextBatch_LastBatchHashMismatch(t *testing.T) { sequencer := NewDummySequencer() @@ -122,6 +203,72 @@ func TestDummySequencer_GetNextBatch_LastBatchHashMismatch(t *testing.T) { assert.Equal(t, "lastBatch is supposed to be nil", err.Error()) } +// Test retrieving a batch with maxBytes limit +func TestDummySequencer_GetNextBatch_MaxBytesLimit(t *testing.T) { + sequencer := NewDummySequencer() + + // 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") + + // Submit multiple transactions + req1 := sequencing.SubmitRollupTransactionRequest{ + RollupId: rollupId, + Tx: tx1, + } + req2 := sequencing.SubmitRollupTransactionRequest{ + RollupId: rollupId, + Tx: tx2, + } + req3 := sequencing.SubmitRollupTransactionRequest{ + RollupId: rollupId, + Tx: tx3, + } + + _, err := sequencer.SubmitRollupTransaction(context.Background(), req1) + assert.NoError(t, err) + _, err = sequencer.SubmitRollupTransaction(context.Background(), req2) + assert.NoError(t, err) + _, err = sequencer.SubmitRollupTransaction(context.Background(), req3) + assert.NoError(t, err) + + // Retrieve the next batch with maxBytes limit (assuming maxBytes limits the size of transactions returned) + maxBytes := uint64(len(tx1) + len(tx2)) // Set maxBytes to the size of tx1 + tx2 + + getBatchReq := sequencing.GetNextBatchRequest{ + RollupId: rollupId, + LastBatchHash: nil, + MaxBytes: maxBytes, + } + batchResp, err := sequencer.GetNextBatch(context.Background(), getBatchReq) + + // Assert no error + assert.NoError(t, err) + // Assert that only two transactions (tx1 and tx2) are retrieved due to the maxBytes limit + assert.NotNil(t, batchResp) + assert.Equal(t, 2, len(batchResp.Batch.Transactions)) + assert.Equal(t, tx1, batchResp.Batch.Transactions[0]) + assert.Equal(t, tx2, batchResp.Batch.Transactions[1]) + + lastBatchHash, err := batchResp.Batch.Hash() + assert.NoError(t, err) + // Retrieve the remaining transaction (tx3) with another call + getBatchReq2 := sequencing.GetNextBatchRequest{ + RollupId: rollupId, + LastBatchHash: lastBatchHash, + MaxBytes: maxBytes, // This can be set higher to retrieve all remaining transactions + } + batchResp2, err2 := sequencer.GetNextBatch(context.Background(), getBatchReq2) + + // Assert no error and tx3 is retrieved + assert.NoError(t, err2) + assert.NotNil(t, batchResp2) + assert.Equal(t, 1, len(batchResp2.Batch.Transactions)) + assert.Equal(t, tx3, batchResp2.Batch.Transactions[0]) +} + func TestDummySequencer_VerifyBatch(t *testing.T) { sequencer := NewDummySequencer() @@ -156,6 +303,67 @@ func TestDummySequencer_VerifyBatch(t *testing.T) { assert.True(t, verifyResp.Status) } +func TestDummySequencer_VerifyEmptyBatch(t *testing.T) { + sequencer := NewDummySequencer() + + // Create a request with an empty batch hash + emptyBatchHash := []byte{} + verifyReq := sequencing.VerifyBatchRequest{ + BatchHash: emptyBatchHash, + } + + // Attempt to verify the empty batch + verifyResp, err := sequencer.VerifyBatch(context.Background(), verifyReq) + + // Assert that the batch is not verified (as it's empty) + assert.NoError(t, err) + assert.False(t, verifyResp.Status) +} + +func TestDummySequencer_VerifyBatchWithMultipleTransactions(t *testing.T) { + sequencer := NewDummySequencer() + + // Define a test rollup ID and multiple transactions + rollupId := []byte("test_rollup_id") + tx1 := []byte("transaction_1") + tx2 := []byte("transaction_2") + + // Submit multiple transactions + req1 := sequencing.SubmitRollupTransactionRequest{ + RollupId: rollupId, + Tx: tx1, + } + req2 := sequencing.SubmitRollupTransactionRequest{ + RollupId: rollupId, + Tx: tx2, + } + + _, err := sequencer.SubmitRollupTransaction(context.Background(), req1) + assert.NoError(t, err) + _, err = sequencer.SubmitRollupTransaction(context.Background(), req2) + assert.NoError(t, err) + + // Get the next batch to generate the batch hash + getBatchReq := sequencing.GetNextBatchRequest{ + RollupId: rollupId, + LastBatchHash: nil, + } + batchResp, err := sequencer.GetNextBatch(context.Background(), getBatchReq) + assert.NoError(t, err) + + batchHash, err := batchResp.Batch.Hash() + assert.NoError(t, err) + // Verify the batch using the batch hash + verifyReq := sequencing.VerifyBatchRequest{ + BatchHash: batchHash, + } + verifyResp, err := sequencer.VerifyBatch(context.Background(), verifyReq) + + // Assert that the batch with multiple transactions is verified successfully + assert.NoError(t, err) + assert.True(t, verifyResp.Status) +} + func TestDummySequencer_VerifyBatch_NotFound(t *testing.T) { sequencer := NewDummySequencer()