diff --git a/nil/services/synccommittee/internal/rpc/block_debug_rpc_test.go b/nil/services/synccommittee/internal/rpc/block_debug_rpc_test.go index 0a654e366..d493bb51d 100644 --- a/nil/services/synccommittee/internal/rpc/block_debug_rpc_test.go +++ b/nil/services/synccommittee/internal/rpc/block_debug_rpc_test.go @@ -129,6 +129,31 @@ func (s *BlockDebugRpcTestSuite) Test_GetBatchViews() { s.requireViewsEqualToLatest(views, batches) } +func (s *BlockDebugRpcTestSuite) Test_GetBatchViews_Parent_Batch_Is_Proposed() { + batches := testaide.NewBatchesSequence(3) + + err := s.storage.SetProvedStateRoot(s.context, batches[0].EarliestMainBlock().ParentHash) + s.Require().NoError(err) + + for _, batch := range batches { + err := s.storage.PutBlockBatch(s.context, batch) + s.Require().NoError(err) + } + + firstBatchId := batches[0].Id + err = s.storage.SetBatchAsProved(s.context, firstBatchId) + s.Require().NoError(err) + err = s.storage.SetBatchAsProposed(s.context, firstBatchId) + s.Require().NoError(err) + + request := public.DefaultBatchDebugRequest() + views, err := s.rpcClient.GetBatchViews(s.context, request) + s.Require().NoError(err) + s.Require().Len(views, 2) + + s.requireViewsEqualToLatest(views, batches[1:]) +} + func (s *BlockDebugRpcTestSuite) Test_GetBatchViews_Limit() { batches := testaide.NewBatchesSequence(10) for _, batch := range batches { diff --git a/nil/services/synccommittee/internal/storage/block_storage_op_batch.go b/nil/services/synccommittee/internal/storage/block_storage_op_batch.go index 239e6361d..091aa213c 100644 --- a/nil/services/synccommittee/internal/storage/block_storage_op_batch.go +++ b/nil/services/synccommittee/internal/storage/block_storage_op_batch.go @@ -114,6 +114,25 @@ func (o batchOp) getBatchesSeqReversed( } seenBatches[*nextBatchId] = true + nextBatchExists, err := o.batchExists(tx, *nextBatchId) + if err != nil { + yield(nil, err) + return + } + switch { + case nextBatchExists: + // Batch exists, continue traversing + case to == nil: + // We've reached the earliest batch in the storage, and its parent has already been removed + return + case to != nil: + traverseErr := fmt.Errorf( + "failed to traverse batch sequence from %s to %s: %w", from, to, o.errBatchNotFound(*nextBatchId), + ) + yield(nil, traverseErr) + return + } + nextBatchEntry, err := o.getBatchEntry(tx, *nextBatchId) if err != nil { yield(nil, err)