diff --git a/core/txpool/blobpool/blobpool.go b/core/txpool/blobpool/blobpool.go index 2c50809716..a5a0344a40 100644 --- a/core/txpool/blobpool/blobpool.go +++ b/core/txpool/blobpool/blobpool.go @@ -1338,6 +1338,20 @@ func (p *BlobPool) HasBlobs(vhashes []common.Hash) bool { return true } +func (p *BlobPool) GetBlobCounts(vhashes []common.Hash) int { + count := 0 + for _, vhash := range vhashes { + // Retrieve the datastore item (in a short lock) + p.lock.RLock() + _, exists := p.lookup.storeidOfBlob(vhash) + p.lock.RUnlock() + if exists { + count += 1 + } + } + return count +} + // Add inserts a set of blob transactions into the pool if they pass validation (both // consensus validity and pool restrictions). // diff --git a/core/txpool/legacypool/legacypool.go b/core/txpool/legacypool/legacypool.go index bf1014d9e0..57f4d305fe 100644 --- a/core/txpool/legacypool/legacypool.go +++ b/core/txpool/legacypool/legacypool.go @@ -1074,6 +1074,12 @@ func (pool *LegacyPool) HasBlobs(vhashes []common.Hash) bool { return false } +// GetBlobCounts returns number of blobs in the subpool that matches the given +// versioned hashes, without performing db read. +func (pool *LegacyPool) GetBlobCounts(vhashes []common.Hash) int { + return 0 +} + // Has returns an indicator whether txpool has a transaction cached with the // given hash. func (pool *LegacyPool) Has(hash common.Hash) bool { diff --git a/core/txpool/subpool.go b/core/txpool/subpool.go index 90f1d5d9e5..7ba83283f7 100644 --- a/core/txpool/subpool.go +++ b/core/txpool/subpool.go @@ -141,6 +141,10 @@ type SubPool interface { // are in the sub pool. HasBlobs(vhashes []common.Hash) bool + // GetBlobCounts returns number of blobs in the subpool that matches the given + // versioned hashes, without performing db read. + GetBlobCounts(vhashes []common.Hash) int + // ValidateTxBasics checks whether a transaction is valid according to the consensus // rules, but does not check state-dependent validation such as sufficient balance. // This check is meant as a static check which can be performed without holding the diff --git a/core/txpool/txpool.go b/core/txpool/txpool.go index 666fdddcdb..5d32af1841 100644 --- a/core/txpool/txpool.go +++ b/core/txpool/txpool.go @@ -323,6 +323,17 @@ func (p *TxPool) GetBlobs(vhashes []common.Hash) []*types.BlobTxSidecar { return nil } +// GetBlobCounts returns a number of blobs that are present in the txpool for +// the given versioned hashes. +func (p *TxPool) GetBlobCounts(vhashes []common.Hash) int { + for _, subpool := range p.subpools { + if count := subpool.GetBlobCounts(vhashes); count != 0 { + return count + } + } + return 0 +} + // HasBlobs will return true if all the vhashes are available in the same subpool. func (p *TxPool) HasBlobs(vhashes []common.Hash) bool { for _, subpool := range p.subpools { diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index a24923f104..12db3b613d 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -38,6 +38,7 @@ import ( "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/internal/version" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/miner" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/params/forks" @@ -160,6 +161,12 @@ type ConsensusAPI struct { newPayloadLock sync.Mutex // Lock for the NewPayload method } +var ( + getBlobsV2RequestedTotal = metrics.NewRegisteredCounter("execution_get_blobs_requested_from_cl_total", nil) + getBlobsV2RequestedHit = metrics.NewRegisteredCounter("execution_get_blobs_requested_from_cl_hit", nil) + getBlobsV2RequestDuration = metrics.NewRegisteredHistogram("execution_get_blobs_request_duration_milliseconds", nil, metrics.NewExpDecaySample(1028, 0.015)) +) + // NewConsensusAPI creates a new consensus api for the given backend. // The underlying blockchain needs to have a valid terminal total difficulty set. func NewConsensusAPI(eth *eth.Ethereum) *ConsensusAPI { @@ -582,8 +589,19 @@ func (api *ConsensusAPI) GetBlobsV2(hashes []common.Hash) ([]*engine.BlobAndProo return nil, engine.TooLargeRequest.With(fmt.Errorf("requested blob count too large: %v", len(hashes))) } + start := time.Now() + defer func() { + duration := time.Since(start) + getBlobsV2RequestDuration.Update(duration.Milliseconds()) + }() + + getBlobsV2RequestedTotal.Inc(int64(len(hashes))) + // Optimization: check first if all blobs are available, if not, return empty response - if !api.eth.TxPool().HasBlobs(hashes) { + blobCounts := api.eth.TxPool().GetBlobCounts(hashes) + getBlobsV2RequestedHit.Inc(int64(blobCounts)) + + if blobCounts != len(hashes) { return nil, nil }