From 440f2d34dedbd620b21a3b8513c776f8a1f96f7f Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Wed, 10 Sep 2025 20:31:48 +0200 Subject: [PATCH 1/4] core/txpool/blobpool: expose sidecar version in blobTxMeta needed for filtering logic in block production Signed-off-by: Csaba Kiraly --- core/txpool/blobpool/blobpool.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/core/txpool/blobpool/blobpool.go b/core/txpool/blobpool/blobpool.go index 5cf1218b75..4e9696aed8 100644 --- a/core/txpool/blobpool/blobpool.go +++ b/core/txpool/blobpool/blobpool.go @@ -92,6 +92,7 @@ const ( type blobTxMeta struct { hash common.Hash // Transaction hash to maintain the lookup table vhashes []common.Hash // Blob versioned hashes to maintain the lookup table + version byte // Blob transaction version to determine proof type id uint64 // Storage ID in the pool's persistent store storageSize uint32 // Byte size in the pool's persistent store @@ -115,10 +116,16 @@ type blobTxMeta struct { // newBlobTxMeta retrieves the indexed metadata fields from a blob transaction // and assembles a helper struct to track in memory. +// Requires the transaction to have a sidecar (or that we introduce a special version tag for no-sidecar). func newBlobTxMeta(id uint64, size uint64, storageSize uint32, tx *types.Transaction) *blobTxMeta { + if tx.BlobTxSidecar() == nil { + // This should never happen, as the pool only admits blob transactions with a sidecar + panic("missing blob tx sidecar") + } meta := &blobTxMeta{ hash: tx.Hash(), vhashes: tx.BlobHashes(), + version: tx.BlobTxSidecar().Version, id: id, storageSize: storageSize, size: size, From 2adb5287048053630627ee9662243e53986c48e5 Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Wed, 10 Sep 2025 20:47:18 +0200 Subject: [PATCH 2/4] core/txpool: filter pending txs by blob sidecar version Signed-off-by: Csaba Kiraly --- core/txpool/blobpool/blobpool.go | 9 +++++++++ core/txpool/legacypool/legacypool.go | 2 +- core/txpool/subpool.go | 5 +++-- miner/worker.go | 8 ++++++-- 4 files changed, 19 insertions(+), 5 deletions(-) diff --git a/core/txpool/blobpool/blobpool.go b/core/txpool/blobpool/blobpool.go index 4e9696aed8..05ebacf9a8 100644 --- a/core/txpool/blobpool/blobpool.go +++ b/core/txpool/blobpool/blobpool.go @@ -1688,6 +1688,15 @@ func (p *BlobPool) Pending(filter txpool.PendingFilter) map[common.Address][]*tx for addr, txs := range p.index { lazies := make([]*txpool.LazyTransaction, 0, len(txs)) for _, tx := range txs { + + // Skip v0 or v1 blob transactions if not requested + if filter.OnlyBlobV0Txs && tx.version != types.BlobSidecarVersion0 { + continue + } + if filter.OnlyBlobV1Txs && tx.version != types.BlobSidecarVersion1 { + continue + } + // If transaction filtering was requested, discard badly priced ones if filter.MinTip != nil && filter.BaseFee != nil { if tx.execFeeCap.Lt(filter.BaseFee) { diff --git a/core/txpool/legacypool/legacypool.go b/core/txpool/legacypool/legacypool.go index 425def170b..36b57dc8b4 100644 --- a/core/txpool/legacypool/legacypool.go +++ b/core/txpool/legacypool/legacypool.go @@ -508,7 +508,7 @@ func (pool *LegacyPool) ContentFrom(addr common.Address) ([]*types.Transaction, func (pool *LegacyPool) Pending(filter txpool.PendingFilter) map[common.Address][]*txpool.LazyTransaction { // If only blob transactions are requested, this pool is unsuitable as it // contains none, don't even bother. - if filter.OnlyBlobTxs { + if filter.OnlyBlobV0Txs || filter.OnlyBlobV1Txs { return nil } pool.mu.Lock() diff --git a/core/txpool/subpool.go b/core/txpool/subpool.go index f1f6056686..a7d7c47932 100644 --- a/core/txpool/subpool.go +++ b/core/txpool/subpool.go @@ -78,8 +78,9 @@ type PendingFilter struct { BlobFee *uint256.Int // Minimum 4844 blobfee needed to include a blob transaction GasLimitCap uint64 // Maximum gas can be used for a single transaction execution (0 means no limit) - OnlyPlainTxs bool // Return only plain EVM transactions (peer-join announces, block space filling) - OnlyBlobTxs bool // Return only blob transactions (block blob-space filling) + OnlyPlainTxs bool // Return only plain EVM transactions (peer-join announces, block space filling) + OnlyBlobV0Txs bool // Return only V0 encoded blob transactions (block blob-space filling) + OnlyBlobV1Txs bool // Return only V1 encoded blob transactions (block blob-space filling) } // TxMetadata denotes the metadata of a transaction. diff --git a/miner/worker.go b/miner/worker.go index 6baec5e365..211442a266 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -481,10 +481,14 @@ func (miner *Miner) fillTransactions(interrupt *atomic.Int32, env *environment) if miner.chainConfig.IsOsaka(env.header.Number, env.header.Time) { filter.GasLimitCap = params.MaxTxGas } - filter.OnlyPlainTxs, filter.OnlyBlobTxs = true, false + filter.OnlyPlainTxs, filter.OnlyBlobV0Txs, filter.OnlyBlobV1Txs = true, false, false pendingPlainTxs := miner.txpool.Pending(filter) - filter.OnlyPlainTxs, filter.OnlyBlobTxs = false, true + if miner.chainConfig.IsOsaka(env.header.Number, env.header.Time) { + filter.OnlyPlainTxs, filter.OnlyBlobV0Txs, filter.OnlyBlobV1Txs = false, false, true + } else { + filter.OnlyPlainTxs, filter.OnlyBlobV0Txs, filter.OnlyBlobV1Txs = false, true, false + } pendingBlobTxs := miner.txpool.Pending(filter) // Split the pending transactions into locals and remotes. From b98b69e9de756314f5c60c4fb7ddc0f165f24ffb Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Wed, 10 Sep 2025 20:55:18 +0200 Subject: [PATCH 3/4] core/txpool/blobpool: ensure nonce continuity in pending filter Signed-off-by: Csaba Kiraly --- core/txpool/blobpool/blobpool.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/txpool/blobpool/blobpool.go b/core/txpool/blobpool/blobpool.go index 05ebacf9a8..d358c04c62 100644 --- a/core/txpool/blobpool/blobpool.go +++ b/core/txpool/blobpool/blobpool.go @@ -1691,10 +1691,10 @@ func (p *BlobPool) Pending(filter txpool.PendingFilter) map[common.Address][]*tx // Skip v0 or v1 blob transactions if not requested if filter.OnlyBlobV0Txs && tx.version != types.BlobSidecarVersion0 { - continue + break // skip the rest because of nonce ordering } if filter.OnlyBlobV1Txs && tx.version != types.BlobSidecarVersion1 { - continue + break // skip the rest because of nonce ordering } // If transaction filtering was requested, discard badly priced ones From d59174fa23a2c8b87e3ff8ca5f40b59773d2a718 Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Thu, 11 Sep 2025 17:19:29 +0200 Subject: [PATCH 4/4] core/txpool: simplify PendingFilter Signed-off-by: Csaba Kiraly --- core/txpool/blobpool/blobpool.go | 10 +++------- core/txpool/legacypool/legacypool.go | 2 +- core/txpool/subpool.go | 7 ++++--- eth/sync.go | 2 +- miner/worker.go | 7 ++++--- 5 files changed, 13 insertions(+), 15 deletions(-) diff --git a/core/txpool/blobpool/blobpool.go b/core/txpool/blobpool/blobpool.go index d358c04c62..722c176bb1 100644 --- a/core/txpool/blobpool/blobpool.go +++ b/core/txpool/blobpool/blobpool.go @@ -1667,7 +1667,7 @@ func (p *BlobPool) drop() { func (p *BlobPool) Pending(filter txpool.PendingFilter) map[common.Address][]*txpool.LazyTransaction { // If only plain transactions are requested, this pool is unsuitable as it // contains none, don't even bother. - if filter.OnlyPlainTxs { + if !filter.BlobTxs { return nil } // Track the amount of time waiting to retrieve the list of pending blob txs @@ -1688,12 +1688,8 @@ func (p *BlobPool) Pending(filter txpool.PendingFilter) map[common.Address][]*tx for addr, txs := range p.index { lazies := make([]*txpool.LazyTransaction, 0, len(txs)) for _, tx := range txs { - - // Skip v0 or v1 blob transactions if not requested - if filter.OnlyBlobV0Txs && tx.version != types.BlobSidecarVersion0 { - break // skip the rest because of nonce ordering - } - if filter.OnlyBlobV1Txs && tx.version != types.BlobSidecarVersion1 { + // Skip v0 or v1 blob transactions depending on the filter + if tx.version != filter.BlobVersion { break // skip the rest because of nonce ordering } diff --git a/core/txpool/legacypool/legacypool.go b/core/txpool/legacypool/legacypool.go index 36b57dc8b4..80a9faf23f 100644 --- a/core/txpool/legacypool/legacypool.go +++ b/core/txpool/legacypool/legacypool.go @@ -508,7 +508,7 @@ func (pool *LegacyPool) ContentFrom(addr common.Address) ([]*types.Transaction, func (pool *LegacyPool) Pending(filter txpool.PendingFilter) map[common.Address][]*txpool.LazyTransaction { // If only blob transactions are requested, this pool is unsuitable as it // contains none, don't even bother. - if filter.OnlyBlobV0Txs || filter.OnlyBlobV1Txs { + if filter.BlobTxs { return nil } pool.mu.Lock() diff --git a/core/txpool/subpool.go b/core/txpool/subpool.go index a7d7c47932..519ae7b989 100644 --- a/core/txpool/subpool.go +++ b/core/txpool/subpool.go @@ -78,9 +78,10 @@ type PendingFilter struct { BlobFee *uint256.Int // Minimum 4844 blobfee needed to include a blob transaction GasLimitCap uint64 // Maximum gas can be used for a single transaction execution (0 means no limit) - OnlyPlainTxs bool // Return only plain EVM transactions (peer-join announces, block space filling) - OnlyBlobV0Txs bool // Return only V0 encoded blob transactions (block blob-space filling) - OnlyBlobV1Txs bool // Return only V1 encoded blob transactions (block blob-space filling) + // When BlobTxs true, return only blob transactions (block blob-space filling) + // when false, return only non-blob txs (peer-join announces, block space filling) + BlobTxs bool + BlobVersion byte // Blob tx version to include. 0 means pre-Osaka, 1 means Osaka and later } // TxMetadata denotes the metadata of a transaction. diff --git a/eth/sync.go b/eth/sync.go index 61f2b2b376..ddae8443a3 100644 --- a/eth/sync.go +++ b/eth/sync.go @@ -25,7 +25,7 @@ import ( // syncTransactions starts sending all currently pending transactions to the given peer. func (h *handler) syncTransactions(p *eth.Peer) { var hashes []common.Hash - for _, batch := range h.txpool.Pending(txpool.PendingFilter{OnlyPlainTxs: true}) { + for _, batch := range h.txpool.Pending(txpool.PendingFilter{BlobTxs: false}) { for _, tx := range batch { hashes = append(hashes, tx.Hash) } diff --git a/miner/worker.go b/miner/worker.go index 211442a266..c0574eac23 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -481,13 +481,14 @@ func (miner *Miner) fillTransactions(interrupt *atomic.Int32, env *environment) if miner.chainConfig.IsOsaka(env.header.Number, env.header.Time) { filter.GasLimitCap = params.MaxTxGas } - filter.OnlyPlainTxs, filter.OnlyBlobV0Txs, filter.OnlyBlobV1Txs = true, false, false + filter.BlobTxs = false pendingPlainTxs := miner.txpool.Pending(filter) + filter.BlobTxs = true if miner.chainConfig.IsOsaka(env.header.Number, env.header.Time) { - filter.OnlyPlainTxs, filter.OnlyBlobV0Txs, filter.OnlyBlobV1Txs = false, false, true + filter.BlobVersion = types.BlobSidecarVersion1 } else { - filter.OnlyPlainTxs, filter.OnlyBlobV0Txs, filter.OnlyBlobV1Txs = false, true, false + filter.BlobVersion = types.BlobSidecarVersion0 } pendingBlobTxs := miner.txpool.Pending(filter)