Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
86ed975
create lazy aggregation adr proposal
Apr 29, 2025
ed5dce1
ammend based on feedback
Apr 30, 2025
ee3550d
Merge branch 'main' into marko/lazy-aggregation-adr
Apr 30, 2025
e7472d7
Update specs/lazy-adr/adr-021-lazy-aggregation.md
tac0turtle Apr 30, 2025
10ea3ae
Merge branch 'main' into marko/lazy-aggregation-adr
tac0turtle May 2, 2025
cb5fb90
lint fix
May 2, 2025
f68588b
Merge branch 'main' into marko/lazy-aggregation-adr
tac0turtle May 2, 2025
7fb554a
chore: empty blocks & lazy aggrgation (#2243)
tac0turtle May 2, 2025
4219dd2
remove unused
May 2, 2025
4949f09
reset block timer when no txs are present
May 2, 2025
95897a4
Simplify
Manav-Aggarwal May 2, 2025
0ea7657
Fix test
Manav-Aggarwal May 2, 2025
4d4d9d2
Add reaper logic in adr
Manav-Aggarwal May 2, 2025
b19a14e
Implement execValidate and insert back dataHash
Manav-Aggarwal May 2, 2025
d938c88
Add batchValidation in retriever
Manav-Aggarwal May 2, 2025
71dec18
modify validation logic
Manav-Aggarwal May 2, 2025
5a74dc4
Add DA Includer Loop
Manav-Aggarwal May 2, 2025
446cd75
Fix test syntax
Manav-Aggarwal May 2, 2025
eccf399
Add Validate method for signed header
Manav-Aggarwal May 2, 2025
05d2888
setDAIncluded for sequencers too
Manav-Aggarwal May 2, 2025
609d51d
Fix first retriever test
Manav-Aggarwal May 3, 2025
6cb6b1b
Refactor retriever and fix more tests
Manav-Aggarwal May 3, 2025
f616f69
fixed all retriever tests
Manav-Aggarwal May 3, 2025
6648f6c
Add more tests to retriever and handle empty blobs
Manav-Aggarwal May 3, 2025
91c30a8
make fromProto more robust
Manav-Aggarwal May 3, 2025
4dc7f9b
Add doc headers to add test methods in block package
Manav-Aggarwal May 3, 2025
1bf36e6
Make TestProcessNextDAHeader_MultipleHeadersAndBatches more robust
Manav-Aggarwal May 3, 2025
fe160ce
Fix from rebase
Manav-Aggarwal May 3, 2025
8c3006a
fix publishBlock tests
Manav-Aggarwal May 3, 2025
a16e63b
Add daIncluderTests
Manav-Aggarwal May 3, 2025
66cc82d
enable t.Parallel in all block tests
Manav-Aggarwal May 3, 2025
66a34c8
go mod tidy everywhere
Manav-Aggarwal May 3, 2025
76f652b
catch errcheck in setfinal and add test for it in daIncluder
Manav-Aggarwal May 3, 2025
3925c86
fix lint
Manav-Aggarwal May 3, 2025
b1c076e
remove gci from golintci
Manav-Aggarwal May 3, 2025
c0f82b3
Fix first test in sync_test
Manav-Aggarwal May 3, 2025
58ce301
Fix all sync tests
Manav-Aggarwal May 4, 2025
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
Prev Previous commit
Next Next commit
Add daIncluderTests
  • Loading branch information
Manav-Aggarwal committed May 3, 2025
commit a16e63b053ce93e1d80ae423840cddb3b1dc6a0d
17 changes: 10 additions & 7 deletions block/daIncluder.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ func (m *Manager) DAIncluderLoop(ctx context.Context) {
case <-m.daIncluderCh:
// proceed to check for DA inclusion
}

currentDAIncluded := m.GetDAIncludedHeight()
for {
nextHeight := currentDAIncluded + 1
header, data, err := m.store.GetBlockData(ctx, nextHeight)
if err != nil {
m.logger.Debug("no more blocks to check at this time", "height", nextHeight, "error", err)
// No more blocks to check at this time
break
}
Expand All @@ -31,7 +31,6 @@ func (m *Manager) DAIncluderLoop(ctx context.Context) {
if m.headerCache.IsDAIncluded(headerHash) && m.dataCache.IsDAIncluded(dataHash) {
// Both header and data are DA-included, so we can advance the height
if err := m.incrementDAIncludedHeight(ctx); err != nil {
m.logger.Error("failed to set DA included height", "height", nextHeight, "error", err)
break
}
currentDAIncluded = nextHeight
Expand All @@ -45,15 +44,19 @@ func (m *Manager) DAIncluderLoop(ctx context.Context) {

// incrementDAIncludedHeight sets the DA included height in the store
func (m *Manager) incrementDAIncludedHeight(ctx context.Context) error {
currentHeight := m.daIncludedHeight.Load()
currentHeight := m.GetDAIncludedHeight()
newHeight := currentHeight + 1
m.logger.Debug("setting final", "height", newHeight)
m.exec.SetFinal(ctx, newHeight)
if m.daIncludedHeight.CompareAndSwap(currentHeight, newHeight) {
m.logger.Debug("setting DA included height", "height", newHeight)
heightBytes := make([]byte, 8)
binary.LittleEndian.PutUint64(heightBytes, newHeight)
return m.store.SetMetadata(ctx, DAIncludedHeightKey, heightBytes)
m.logger.Debug("setting DA included height", "height", newHeight)
err := m.store.SetMetadata(ctx, DAIncludedHeightKey, heightBytes)
if err != nil {
m.logger.Error("failed to set DA included height", "height", newHeight, "error", err)
return err
}
m.logger.Debug("setting final", "height", newHeight)
m.exec.SetFinal(ctx, newHeight)
}
return nil
}
267 changes: 267 additions & 0 deletions block/daIncluder_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,267 @@
package block

import (
"context"
"encoding/binary"
"sync"
"testing"
"time"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"

"github.com/rollkit/rollkit/pkg/cache"
"github.com/rollkit/rollkit/test/mocks"
"github.com/rollkit/rollkit/types"
)

// newTestManager creates a Manager with mocked Store and Executor for testing DAIncluder logic.
func newTestManager(t *testing.T) (*Manager, *mocks.Store, *mocks.Executor, *MockLogger) {
store := mocks.NewStore(t)
exec := mocks.NewExecutor(t)
logger := new(MockLogger)
logger.On("Debug", mock.Anything, mock.Anything).Maybe()
logger.On("Info", mock.Anything, mock.Anything).Maybe()
logger.On("Warn", mock.Anything, mock.Anything).Maybe()
logger.On("Error", mock.Anything, mock.Anything).Maybe()
m := &Manager{
store: store,
headerCache: cache.NewCache[types.SignedHeader](),
dataCache: cache.NewCache[types.Data](),
daIncluderCh: make(chan struct{}, 1),
logger: logger,
exec: exec,
lastStateMtx: &sync.RWMutex{},
metrics: NopMetrics(),
}
return m, store, exec, logger
}

// TestDAIncluderLoop_AdvancesHeightWhenBothDAIncluded verifies that the DAIncluderLoop advances the DA included height
// when both the header and data for the next block are marked as DA-included in the caches.
func TestDAIncluderLoop_AdvancesHeightWhenBothDAIncluded(t *testing.T) {
t.Parallel()
m, store, exec, _ := newTestManager(t)
startDAIncludedHeight := uint64(4)
expectedDAIncludedHeight := uint64(startDAIncludedHeight + 1)
m.daIncludedHeight.Store(startDAIncludedHeight)

header, data := types.GetRandomBlock(5, 1, "testchain")
headerHash := header.Hash().String()
dataHash := data.DACommitment().String()
m.headerCache.SetDAIncluded(headerHash)
m.dataCache.SetDAIncluded(dataHash)

store.On("GetBlockData", mock.Anything, uint64(5)).Return(header, data, nil).Once()
store.On("GetBlockData", mock.Anything, uint64(6)).Return(nil, nil, assert.AnError).Once()
heightBytes := make([]byte, 8)
binary.LittleEndian.PutUint64(heightBytes, expectedDAIncludedHeight)
store.On("SetMetadata", mock.Anything, DAIncludedHeightKey, heightBytes).Return(nil).Once()
exec.On("SetFinal", mock.Anything, uint64(5)).Return(nil).Once()

ctx, loopCancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
defer loopCancel()

var wg sync.WaitGroup
wg.Add(1)

go func() {
defer wg.Done()
m.DAIncluderLoop(ctx)
}()

m.daIncluderCh <- struct{}{}

wg.Wait()

assert.Equal(t, expectedDAIncludedHeight, m.daIncludedHeight.Load())
store.AssertExpectations(t)
exec.AssertExpectations(t)
}

// TestDAIncluderLoop_StopsWhenHeaderNotDAIncluded verifies that the DAIncluderLoop does not advance the height
// if the header for the next block is not marked as DA-included in the cache.
func TestDAIncluderLoop_StopsWhenHeaderNotDAIncluded(t *testing.T) {
t.Parallel()
m, store, _, _ := newTestManager(t)
startDAIncludedHeight := uint64(4)
m.daIncludedHeight.Store(startDAIncludedHeight)

header, data := types.GetRandomBlock(5, 1, "testchain")
// m.headerCache.SetDAIncluded(headerHash) // Not set
m.dataCache.SetDAIncluded(data.DACommitment().String())

store.On("GetBlockData", mock.Anything, uint64(5)).Return(header, data, nil).Once()

ctx, loopCancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
defer loopCancel()

var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
m.DAIncluderLoop(ctx)
}()

m.daIncluderCh <- struct{}{}
wg.Wait()

assert.Equal(t, startDAIncludedHeight, m.GetDAIncludedHeight())
store.AssertExpectations(t)
}

// TestDAIncluderLoop_StopsWhenDataNotDAIncluded verifies that the DAIncluderLoop does not advance the height
// if the data for the next block is not marked as DA-included in the cache.
func TestDAIncluderLoop_StopsWhenDataNotDAIncluded(t *testing.T) {
t.Parallel()
m, store, _, _ := newTestManager(t)
startDAIncludedHeight := uint64(4)
m.daIncludedHeight.Store(startDAIncludedHeight)

header, data := types.GetRandomBlock(5, 1, "testchain")
headerHash := header.Hash().String()
m.headerCache.SetDAIncluded(headerHash)
// m.dataCache.SetDAIncluded(data.DACommitment().String()) // Not set

store.On("GetBlockData", mock.Anything, uint64(5)).Return(header, data, nil).Once()

ctx, loopCancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
defer loopCancel()

var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
m.DAIncluderLoop(ctx)
}()

m.daIncluderCh <- struct{}{}
wg.Wait()

assert.Equal(t, startDAIncludedHeight, m.GetDAIncludedHeight())
store.AssertExpectations(t)
}

// TestDAIncluderLoop_StopsOnGetBlockDataError verifies that the DAIncluderLoop stops advancing
// if GetBlockData returns an error for the next block height.
func TestDAIncluderLoop_StopsOnGetBlockDataError(t *testing.T) {
t.Parallel()
m, store, _, mockLogger := newTestManager(t)
startDAIncludedHeight := uint64(4)
m.daIncludedHeight.Store(startDAIncludedHeight)

store.On("GetBlockData", mock.Anything, uint64(5)).Return(nil, nil, assert.AnError).Once()

// Expect the debug log for no more blocks to check
mockLogger.ExpectedCalls = nil // Clear any previous expectations
mockLogger.On("Debug", "no more blocks to check at this time", mock.Anything).Once()

ctx, loopCancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
defer loopCancel()

var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
m.DAIncluderLoop(ctx)
}()

m.daIncluderCh <- struct{}{}
wg.Wait()

assert.Equal(t, startDAIncludedHeight, m.GetDAIncludedHeight())
store.AssertExpectations(t)
mockLogger.AssertExpectations(t)
}

// TestIncrementDAIncludedHeight_Success verifies that incrementDAIncludedHeight increments the height
// and calls SetMetadata and SetFinal when CompareAndSwap succeeds.
func TestIncrementDAIncludedHeight_Success(t *testing.T) {
t.Parallel()
m, store, exec, _ := newTestManager(t)
startDAIncludedHeight := uint64(4)
expectedDAIncludedHeight := uint64(startDAIncludedHeight + 1)
m.daIncludedHeight.Store(startDAIncludedHeight)

heightBytes := make([]byte, 8)
binary.LittleEndian.PutUint64(heightBytes, expectedDAIncludedHeight)
store.On("SetMetadata", mock.Anything, DAIncludedHeightKey, heightBytes).Return(nil).Once()
exec.On("SetFinal", mock.Anything, expectedDAIncludedHeight).Return(nil).Once()

err := m.incrementDAIncludedHeight(context.Background())
assert.NoError(t, err)
assert.Equal(t, expectedDAIncludedHeight, m.GetDAIncludedHeight())
store.AssertExpectations(t)
exec.AssertExpectations(t)
}

// TestIncrementDAIncludedHeight_SetMetadataError verifies that incrementDAIncludedHeight returns an error
// if SetMetadata fails after CompareAndSwap succeeds.
func TestIncrementDAIncludedHeight_SetMetadataError(t *testing.T) {
t.Parallel()
m, store, _, mockLogger := newTestManager(t)
startDAIncludedHeight := uint64(4)
expectedDAIncludedHeight := uint64(startDAIncludedHeight + 1)
m.daIncludedHeight.Store(startDAIncludedHeight)

heightBytes := make([]byte, 8)
binary.LittleEndian.PutUint64(heightBytes, expectedDAIncludedHeight)
store.On("SetMetadata", mock.Anything, DAIncludedHeightKey, heightBytes).Return(assert.AnError).Once()

// Expect the error log for failed to set DA included height
mockLogger.ExpectedCalls = nil // Clear any previous expectations
mockLogger.On("Error", "failed to set DA included height", []interface{}{"height", expectedDAIncludedHeight, "error", assert.AnError}).Once()
mockLogger.On("Debug", mock.Anything, mock.Anything).Maybe() // Allow other debug logs

err := m.incrementDAIncludedHeight(context.Background())
assert.Error(t, err)
store.AssertExpectations(t)
mockLogger.AssertExpectations(t)
}

// TestDAIncluderLoop_MultipleConsecutiveHeightsDAIncluded verifies that DAIncluderLoop advances the height
// multiple times in a single run when several consecutive blocks are DA-included.
func TestDAIncluderLoop_MultipleConsecutiveHeightsDAIncluded(t *testing.T) {
t.Parallel()
m, store, exec, _ := newTestManager(t)
startDAIncludedHeight := uint64(4)
numConsecutive := 10
numTxs := 5
m.daIncludedHeight.Store(startDAIncludedHeight)

headers := make([]*types.SignedHeader, numConsecutive)
dataBlocks := make([]*types.Data, numConsecutive)

for i := 0; i < numConsecutive; i++ {
height := startDAIncludedHeight + uint64(i+1)
headers[i], dataBlocks[i] = types.GetRandomBlock(height, numTxs, "testchain")
headerHash := headers[i].Hash().String()
dataHash := dataBlocks[i].DACommitment().String()
m.headerCache.SetDAIncluded(headerHash)
m.dataCache.SetDAIncluded(dataHash)
store.On("GetBlockData", mock.Anything, height).Return(headers[i], dataBlocks[i], nil).Once()
}
// Next height returns error
store.On("GetBlockData", mock.Anything, startDAIncludedHeight+uint64(numConsecutive+1)).Return(nil, nil, assert.AnError).Once()
store.On("SetMetadata", mock.Anything, DAIncludedHeightKey, mock.Anything).Return(nil).Times(numConsecutive)
exec.On("SetFinal", mock.Anything, mock.Anything).Return(nil).Times(numConsecutive)

expectedDAIncludedHeight := startDAIncludedHeight + uint64(numConsecutive)

ctx, loopCancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
defer loopCancel()

var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
m.DAIncluderLoop(ctx)
}()

m.daIncluderCh <- struct{}{}
wg.Wait()

assert.Equal(t, expectedDAIncludedHeight, m.GetDAIncludedHeight())
store.AssertExpectations(t)
exec.AssertExpectations(t)
}
Loading