diff --git a/core/txpool/blobpool/cache.go b/core/txpool/blobpool/cache.go index 2292ce1277..d0c1b02ce0 100644 --- a/core/txpool/blobpool/cache.go +++ b/core/txpool/blobpool/cache.go @@ -265,7 +265,10 @@ func (c *Cache) GetCells(vhashes []common.Hash, mask types.CustodyBitmap) ([][]* c.mu.Lock() for vhash, idxs := range indices { cached, ok := c.entries[vhash] - if !ok { + if !ok || cached.cell == nil { + // Entries loaded in blob mode carry no cells; fall back to + // the blobpool instead of serving null cells for a blob + // that is actually available. misses = append(misses, vhash) continue } diff --git a/core/txpool/blobpool/cache_test.go b/core/txpool/blobpool/cache_test.go index 1902f9b353..ac780b06ca 100644 --- a/core/txpool/blobpool/cache_test.go +++ b/core/txpool/blobpool/cache_test.go @@ -283,6 +283,36 @@ func TestCacheGetBlobs(t *testing.T) { tc.expectEntries(t, tc.vhashes[1]...) } +// TestCacheGetCellsBlobModeFallback checks that GetCells falls back to the +// blobpool for entries that were cached in blob mode (without cells) instead +// of serving null cells for a blob that is available in the pool. +func TestCacheGetCellsBlobModeFallback(t *testing.T) { + tc := newTestCache(t, []txSpec{ + {blobs: 1, tip: 300}, + }) + tc.expectEntries(t, tc.vhashes[0]...) // blob-mode entries, no cells + + cells, proofs, err := tc.GetCells(tc.vhashes[0], types.CustodyBitmapAll) + if err != nil { + t.Fatalf("GetCells: %v", err) + } + for i := range cells { + if len(cells[i]) == 0 { + t.Errorf("no cells returned for blob %d", i) + } + for j, cell := range cells[i] { + if cell == nil { + t.Errorf("cell %d of blob %d missing in GetCells response", j, i) + } + } + for j, proof := range proofs[i] { + if proof == nil { + t.Errorf("proof %d of blob %d missing in GetCells response", j, i) + } + } + } +} + // TestCacheTopKRefresh verifies that when a more profitable tx appears in the // pool, the next topK tick replaces the cached entry with the better one. func TestCacheTopKRefresh(t *testing.T) {