add engine_getBlobsV4

This commit is contained in:
healthykim 2026-03-31 20:27:13 +09:00
parent ff731ce79c
commit 6e00052cf5
3 changed files with 144 additions and 1 deletions

View file

@ -157,6 +157,11 @@ type BlobAndProofV2 struct {
CellProofs []hexutil.Bytes `json:"proofs"` // proofs MUST contain exactly CELLS_PER_EXT_BLOB cell proofs.
}
type BlobCellsAndProofsV1 struct {
BlobCells []hexutil.Bytes `json:"blob_cells"`
Proofs []hexutil.Bytes `json:"proofs"`
}
// JSON type overrides for ExecutionPayloadEnvelope.
type executionPayloadEnvelopeMarshaling struct {
BlockValue *hexutil.Big

View file

@ -1750,7 +1750,85 @@ func (p *BlobPool) GetBlobs(vhashes []common.Hash, version byte) ([]*kzg4844.Blo
return blobs, commitments, proofs, nil
}
// AvailableBlobs returns the number of blobs that are available in the subpool.
// 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
// was not available.
func (p *BlobPool) GetBlobCells(vhashes []common.Hash, mask types.CustodyBitmap) ([][]*kzg4844.Cell, [][]*kzg4844.Proof, error) {
var (
cells = make([][]*kzg4844.Cell, len(vhashes))
proofs = make([][]*kzg4844.Proof, len(vhashes))
vindex = make(map[common.Hash][]int) // Indices of versioned hashes in the request
filled = make(map[common.Hash]struct{})
)
for i, h := range vhashes {
vindex[h] = append(vindex[h], i)
}
requestedIndices := mask.Indices()
for _, vhash := range vhashes {
if _, ok := filled[vhash]; ok {
continue
}
p.lock.RLock()
txID, exists := p.lookup.storeidOfBlob(vhash)
p.lock.RUnlock()
if !exists {
continue
}
data, err := p.store.Get(txID)
if err != nil {
continue
}
var pooledTx pooledBlobTx
if err := rlp.DecodeBytes(data, &pooledTx); err != nil {
continue
}
sidecar := pooledTx.Sidecar
if sidecar == nil {
continue
}
tx := pooledTx.Transaction
cellsPerBlob := sidecar.Custody.OneCount()
storedIndices := sidecar.Custody.Indices()
for blobIdx, hash := range tx.BlobHashes() {
indices, ok := vindex[hash]
if !ok {
continue
}
filled[hash] = struct{}{}
blobCells := make([]*kzg4844.Cell, len(requestedIndices))
blobProofs := make([]*kzg4844.Proof, len(requestedIndices))
for i, cellIdx := range requestedIndices {
pos := -1
for k, storedIdx := range storedIndices {
if storedIdx == cellIdx {
pos = k
break
}
}
if pos >= 0 {
cell := sidecar.Cells[blobIdx*cellsPerBlob+pos]
blobCells[i] = &cell
proofIdx := blobIdx*kzg4844.CellProofsPerBlob + int(cellIdx)
if proofIdx < len(sidecar.Proofs) {
proof := sidecar.Proofs[proofIdx]
blobProofs[i] = &proof
}
}
}
for _, idx := range indices {
cells[idx] = blobCells
proofs[idx] = blobProofs
}
}
}
return cells, proofs, nil
}
func (p *BlobPool) AvailableBlobs(vhashes []common.Hash) int {
available := 0
for _, vhash := range vhashes {

View file

@ -660,6 +660,66 @@ func (api *ConsensusAPI) getBlobs(hashes []common.Hash, v2 bool) ([]*engine.Blob
return res, nil
}
// GetBlobsV4 returns cell-level blob data from the transaction pool.
// V4 returns only the requested cells as specified by the indices_bitarray.
func (api *ConsensusAPI) GetBlobsV4(hashes []common.Hash, indicesBitarray hexutil.Bytes) ([]*engine.BlobCellsAndProofsV1, error) {
head := api.eth.BlockChain().CurrentHeader()
// Sparse blobpool is not necessarily coupled with the Amsterdam fork and
// can technically be supported after the Osaka fork
// (where cell proofs are introduced).
if api.config().LatestFork(head.Time) < forks.Osaka {
return nil, nil
}
if len(hashes) > 128 {
return nil, engine.TooLargeRequest.With(fmt.Errorf("requested blob count too large: %v", len(hashes)))
}
if len(indicesBitarray) != 16 {
return nil, engine.InvalidParams.With(fmt.Errorf("indices_bitarray must be 16 bytes, got %d", len(indicesBitarray)))
}
var mask types.CustodyBitmap
copy(mask[:], indicesBitarray)
cells, proofs, err := api.eth.BlobTxPool().GetBlobCells(hashes, mask)
if err != nil {
return nil, engine.InvalidParams.With(err)
}
var (
res = make([]*engine.BlobCellsAndProofsV1, len(hashes))
hitCount int
)
getBlobsRequestedCounter.Inc(int64(len(hashes)))
for i := range hashes {
if cells[i] == nil || proofs[i] == nil {
continue
}
hitCount++
blobCells := make([]hexutil.Bytes, len(cells[i]))
for j, cell := range cells[i] {
if cell != nil {
blobCells[j] = cell[:]
}
}
blobProofs := make([]hexutil.Bytes, len(proofs[i]))
for j, proof := range proofs[i] {
if proof != nil {
blobProofs[j] = proof[:]
}
}
res[i] = &engine.BlobCellsAndProofsV1{
BlobCells: blobCells,
Proofs: blobProofs,
}
}
getBlobsAvailableCounter.Inc(int64(hitCount))
if hitCount == len(hashes) {
getBlobsRequestCompleteHit.Inc(1)
} else if hitCount > 0 {
getBlobsRequestPartialHit.Inc(1)
} else {
getBlobsRequestMiss.Inc(1)
}
return res, nil
}
// Helper for NewPayload* methods.
var invalidStatus = engine.PayloadStatusV1{Status: engine.INVALID}