core/txpool, eth: remove GetCells

This commit is contained in:
healthykim 2026-04-21 15:53:52 +02:00
parent 6bbcbe6a65
commit b2c50675ca
7 changed files with 121 additions and 77 deletions

View file

@ -1671,6 +1671,17 @@ func (p *BlobPool) GetBlobs(vhashes []common.Hash, version byte) ([]*kzg4844.Blo
return blobs, commitments, proofs, nil
}
// GetBlobHashes returns the blob versioned hashes for a given transaction hash.
func (p *BlobPool) GetBlobHashes(txHash common.Hash) []common.Hash {
p.lock.RLock()
defer p.lock.RUnlock()
vhashes, ok := p.lookup.blobHashesOfTx(txHash)
if !ok {
return nil
}
return vhashes
}
// GetBlobCells returns cells for the given versioned blob hashes,
// filtered by the requested cell indices(mask).
// Each entry in the result corresponds to one vhash. Nil entries mean the blob
@ -2436,38 +2447,3 @@ func (p *BlobPool) GetCustody(hash common.Hash) *types.CustodyBitmap {
}
return nil
}
// GetCells returns the cells matching the given custody bitmap for a transaction.
func (p *BlobPool) GetCells(hash common.Hash, mask types.CustodyBitmap) ([]kzg4844.Cell, error) {
p.lock.RLock()
defer p.lock.RUnlock()
id, ok := p.lookup.storeidOfTx(hash)
if !ok {
return nil, errors.New("requested cells don't exist")
}
data, err := p.store.Get(id)
if err != nil {
return nil, errors.New("tracked blob transaction missing from store")
}
// Decode the blob transaction
var pooledTx PooledBlobTx
if err := rlp.DecodeBytes(data, &pooledTx); err != nil {
return nil, errors.New("blobs corrupted for traced transaction")
}
tx := pooledTx.Transaction
sidecar := pooledTx.Sidecar
// Return cells in blob-major order: [blob0_cell0, blob0_cell1, ..., blob1_cell0, ...]
cellsPerBlob := sidecar.Custody.OneCount()
cells := make([]kzg4844.Cell, 0, mask.OneCount()*len(tx.BlobHashes()))
for blobIdx := 0; blobIdx < len(tx.BlobHashes()); blobIdx++ {
for cellIdx, custodyIdx := range sidecar.Custody.Indices() {
if mask.IsSet(custodyIdx) {
cells = append(cells, sidecar.Cells[blobIdx*cellsPerBlob+cellIdx])
}
}
}
if len(cells) != mask.OneCount()*len(tx.BlobHashes()) {
return nil, fmt.Errorf("not enough cells: tx %s, needed %d, have %d", tx.Hash(), len(tx.BlobHashes())*mask.OneCount(), len(cells))
}
return cells, nil
}

View file

@ -2227,33 +2227,32 @@ func TestGetCells(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cells, err := pool.GetCells(tt.hash, tt.mask)
if err != nil && !tt.shouldFail {
t.Errorf("expected to success, got %v", err)
vhashes := pool.GetBlobHashes(tt.hash)
if tt.shouldFail {
if vhashes != nil {
t.Errorf("expected nil vhashes for non-existent tx")
}
return
}
if err == nil && tt.shouldFail {
t.Errorf("expected to fail, got %v", err)
if vhashes == nil {
t.Fatalf("expected vhashes, got nil")
}
if len(cells) != tt.expectedLen {
t.Errorf("expected %d cells, got %d", tt.expectedLen, len(cells))
blobCells, _, err := pool.GetBlobCells(vhashes, tt.mask)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if tt.expectedLen > 0 && tt.expectedLen%3 == 0 {
blobCount := 3
cellsPerBlob := tt.expectedLen / blobCount
for blobIdx := 0; blobIdx < blobCount; blobIdx++ {
startIdx := blobIdx * cellsPerBlob
endIdx := startIdx + cellsPerBlob
if endIdx > len(cells) {
t.Errorf("blob %d: expected cells up to index %d, but only have %d cells",
blobIdx, endIdx-1, len(cells))
// Count total non-nil cells across all blobs
totalCells := 0
for _, bc := range blobCells {
for _, c := range bc {
if c != nil {
totalCells++
}
}
}
if totalCells != tt.expectedLen {
t.Errorf("expected %d cells, got %d", tt.expectedLen, totalCells)
}
})
}
}

View file

@ -26,6 +26,7 @@ type txMetadata struct {
size uint64 // the RLP encoded size of transaction (blobs are included)
sizeWithoutBlob uint64 // the RLP encoded size without blob data (for ETH/72 announcements)
custody types.CustodyBitmap
vhashes []common.Hash // blob versioned hashes for the transaction
}
// lookup maps blob versioned hashes to transaction hashes that include them,
@ -59,6 +60,15 @@ func (l *lookup) storeidOfTx(txhash common.Hash) (uint64, bool) {
return meta.id, true
}
// blobHashesOfTx returns the blob versioned hashes for a transaction.
func (l *lookup) blobHashesOfTx(txhash common.Hash) ([]common.Hash, bool) {
meta, ok := l.txIndex[txhash]
if !ok {
return nil, false
}
return meta.vhashes, true
}
// storeidOfBlob returns the datastore storage item id of a blob.
func (l *lookup) storeidOfBlob(vhash common.Hash) (uint64, bool) {
// If the blob is unknown, return a miss
@ -98,6 +108,7 @@ func (l *lookup) track(tx *blobTxMeta) {
size: tx.size,
sizeWithoutBlob: tx.sizeWithoutBlob,
custody: *tx.custody,
vhashes: tx.vhashes,
}
}

View file

@ -103,7 +103,8 @@ type txPool interface {
// support cell-based blob data availability.
type blobPool interface {
Has(hash common.Hash) bool
GetCells(hash common.Hash, mask types.CustodyBitmap) ([]kzg4844.Cell, error)
GetBlobHashes(hash common.Hash) []common.Hash
GetBlobCells(vhashes []common.Hash, mask types.CustodyBitmap) ([][]*kzg4844.Cell, [][]*kzg4844.Proof, error)
HasPayload(hash common.Hash) bool
GetCustody(hash common.Hash) *types.CustodyBitmap
AddPooledTx(pooledTx *blobpool.PooledBlobTx) error

View file

@ -17,7 +17,6 @@
package eth
import (
"errors"
"maps"
"math/big"
"math/rand"
@ -181,28 +180,59 @@ func (p *testTxPool) Pending(filter txpool.PendingFilter) (map[common.Address][]
func (p *testTxPool) SubscribeTransactions(ch chan<- core.NewTxsEvent, reorgs bool) event.Subscription {
return p.txFeed.Subscribe(ch)
}
func (p *testTxPool) GetCells(hash common.Hash, mask types.CustodyBitmap) ([]kzg4844.Cell, error) {
func (p *testTxPool) GetBlobHashes(hash common.Hash) []common.Hash {
p.lock.RLock()
defer p.lock.RUnlock()
_, exists := p.txPool[hash]
tx, exists := p.txPool[hash]
if !exists {
return nil, errors.New("Requested tx does not exist")
return nil
}
return tx.BlobHashes()
}
var cells []kzg4844.Cell
func (p *testTxPool) GetBlobCells(vhashes []common.Hash, mask types.CustodyBitmap) ([][]*kzg4844.Cell, [][]*kzg4844.Proof, error) {
p.lock.RLock()
defer p.lock.RUnlock()
if cells, exists = p.cellPool[hash]; !exists {
return nil, errors.New("Requested cells do not exist")
}
requestedIndices := mask.Indices()
cells := make([][]*kzg4844.Cell, len(vhashes))
proofs := make([][]*kzg4844.Proof, len(vhashes))
result := make([]kzg4844.Cell, 0, mask.OneCount())
for _, idx := range mask.Indices() {
if int(idx) < len(cells) {
result = append(result, cells[idx])
for i, vhash := range vhashes {
// Find the tx containing this versioned hash
var foundTx *types.Transaction
var blobIdx int
for _, tx := range p.txPool {
for j, bh := range tx.BlobHashes() {
if bh == vhash {
foundTx = tx
blobIdx = j
break
}
}
if foundTx != nil {
break
}
}
if foundTx == nil {
continue
}
txCells, ok := p.cellPool[foundTx.Hash()]
if !ok {
continue
}
_ = blobIdx // cells in the mock are stored flat by cell index
blobCells := make([]*kzg4844.Cell, len(requestedIndices))
for j, idx := range requestedIndices {
if int(idx) < len(txCells) {
cell := txCells[idx]
blobCells[j] = &cell
}
}
cells[i] = blobCells
}
return result, nil
return cells, proofs, nil
}
func (p *testTxPool) GetCustody(hash common.Hash) *types.CustodyBitmap {

View file

@ -89,8 +89,10 @@ type Backend interface {
// BlobPool defines the methods needed by the protocol handler to serve cell requests.
type BlobPool interface {
// GetCells retrieves cells for a given transaction hash filtered by the custody bitmap.
GetCells(hash common.Hash, mask types.CustodyBitmap) ([]kzg4844.Cell, error)
// GetBlobHashes returns the blob versioned hashes for a given transaction hash.
GetBlobHashes(hash common.Hash) []common.Hash
// GetBlobCells retrieves cells and proofs for given versioned blob hashes filtered by the custody bitmap.
GetBlobCells(vhashes []common.Hash, mask types.CustodyBitmap) ([][]*kzg4844.Cell, [][]*kzg4844.Proof, error)
// GetCustody returns the custody bitmap for a given transaction hash.
GetCustody(hash common.Hash) *types.CustodyBitmap
// Has returns whether the blob pool contains a transaction with the given hash.

View file

@ -623,14 +623,39 @@ func answerGetCells(backend Backend, query GetCellsRequest) ([]common.Hash, [][]
if cellCounts >= maxCells {
break
}
cell, _ := backend.BlobPool().GetCells(hash, query.Mask)
if len(cell) == 0 {
// skip this tx
// Look up the blob versioned hashes for this transaction
vhashes := backend.BlobPool().GetBlobHashes(hash)
if len(vhashes) == 0 {
continue
}
blobCells, _, _ := backend.BlobPool().GetBlobCells(vhashes, query.Mask)
// Flatten per-blob cells into a single slice. If any blob has a nil
// entry (unavailable cell), skip the entire transaction.
var flat []kzg4844.Cell
skip := false
for _, bc := range blobCells {
if bc == nil {
skip = true
break
}
for _, c := range bc {
if c == nil {
skip = true
break
}
flat = append(flat, *c)
}
if skip {
break
}
}
if skip || len(flat) == 0 {
continue
}
hashes = append(hashes, hash)
cells = append(cells, cell)
cellCounts += len(cell)
cells = append(cells, flat)
cellCounts += len(flat)
}
return hashes, cells, query.Mask
}