From 7a1b11564c16f54dff0a2f578179c482d9f701bf Mon Sep 17 00:00:00 2001 From: spencer Date: Thu, 30 Jul 2026 17:39:50 +0200 Subject: [PATCH] core/txpool/blobpool: fall back to pool in GetCells for blob-mode cache entries (#35439) ## Description Blob mode cache entries (the default until a CL advertises `engine_getBlobsV4` via `engine_exchangeCapabilities`) carry no cells, but `Cache.GetCells` treated any entry as a hit and served null cells from the empty custody set instead of falling back to the pool. Found via the EELS execute-blobs simulator (EIP-8070 `engine_getBlobsV4` tests), which never calls `engine_exchangeCapabilities`: null cells appeared intermittently depending on the race with the 1s top-K preload. With the fix the suite passes 139/139 (from 87/139 on master). --- core/txpool/blobpool/cache.go | 5 ++++- core/txpool/blobpool/cache_test.go | 30 ++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) 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) {