Skip to content

EIP-8141: blob-pool routing, sidecar, and production budget for blob-carrying frame transactions - #12709

Draft
Marchhill wants to merge 2 commits into
eip8141-blob-supportfrom
eip8141-blob-network
Draft

EIP-8141: blob-pool routing, sidecar, and production budget for blob-carrying frame transactions#12709
Marchhill wants to merge 2 commits into
eip8141-blob-supportfrom
eip8141-blob-network

Conversation

@Marchhill

Copy link
Copy Markdown
Contributor

Completes the deferred non-consensus blob layer for EIP-8141 blob-carrying frame transactions, stacked on top of #12701 (which added the consensus-correct blob accounting/validation/settlement/sig-hash). Addresses the EIP8141: deferral markers left by #12701.

What this does

  • Blob-pool routing — new instance-level Transaction.CarriesBlobs gate (BlobVersionedHashes present); the pool routes on it so a blob-carrying frame tx (type 6) lands in the blob pool exactly like a type-3 tx. The type-level SupportsBlobs() that networking/decode paths key on is left untouched, so type-3 routing is unchanged. The same gate drives TxTypeTxFilter conflict detection, snapshot invalidation, eviction cascade, metrics, and ContainsTx.
  • Production blob-budget metering — because frame-blob txs now sit in the blob pool, they flow through the blob-selection path in TxPoolTxSource and are metered against the block blob budget (MaxBlobGasPerBlock / MaxProductionBlobCount), so a produced block can never exceed it. The normal-pool picker guard is reworded and kept as defense-in-depth.
  • Networking (conservative) — blob-carrying frame txs are withheld from gossip (SpecDrivenTxGossipPolicy) until the sidecar wire format lands, rather than leaking them in bare consensus form.

Deferred (scope note)

The EIP-7594 sidecar / network-wrapper wire format for type 6 (decode/encode + mempool acceptance of the wrapper form) is deferred with documented EIP8141: markers — the wire format is the risky piece and is left for a focused follow-up. Real-world production of frame-blob txs is gated on it (the blobs bundle cannot be built without the sidecar); the metering and selection logic here already work end-to-end once a wrapper is present, as the production test demonstrates.

Tests

  • A blob-carrying frame tx routes to the blob pool; a plain frame tx and a type-3 tx route unchanged (regression).
  • A frame-blob tx carrying a sidecar is metered/selected within the blob budget and excluded when it would overflow it.

Draft.

@claude

claude Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Claude finished @Marchhill's task in 9m 25s —— View job


Review: EIP-8141 blob-pool routing & production blob budget

  • Gather context (diff vs origin/eip8141-blob-support)
  • Review Transaction.CarriesBlobs gate + TxPool routing
  • Review production blob-budget metering
  • Review gossip policy / validator changes
  • Review tests
  • Post findings (7 inline comments)

The core idea is right, and the production side is genuinely safe: because TxPoolTxSource.ResolveBlob requires a ShardBlobNetworkWrapper (TxPoolTxSource.cs:115), a sidecar-less frame-blob tx can never be produced, and the blob-selection path does meter GetBlobCount() against MaxProductionBlobCount — the new TxPoolSourceTests case demonstrates that end to end. The picker guard as defense-in-depth is a good call.

The problem is that routing was moved to CarriesBlobs at the membership sites, but every rule that governs the blob pool was left on SupportsBlobs. The result is a class of transaction that lives in the blob pool while being admitted, limited, expired, persisted, and served under normal-pool rules.

Findings

# Severity Finding
1 High Admission filters still pick the wrong pool/limit: FeeTooLowFilter:44, BalanceTooLowFilter:39, FutureNonceFilter:15, GapNonceFilter:24,29, SizeTxFilter:19, NotSupportedTxFilter:21. Net effect: a sender gets unlimited pending frame-blob txs (MaxPendingTxsPerSender default 0 instead of MaxPendingBlobTxsPerSender = 16), with cumulative-balance accounting reading an always-empty normal-pool bucket, and no blob-pool fee floor. (details)
2 High Frame-tx expiry eviction is silently disabled for these txs: RemoveExpiredFrameTransactions scans _transactions only (TxPool.cs:496) and _expiringFrameTxCount is only fed by _transactions.Inserted/Removed (TxPool.cs:148), so the pass short-circuits. Expired blob-carrying frame txs are never removed. (details)
3 High The default BlobsSupport = StorageWithReorgs path loses the tx type — LightTransaction hard-codes Type = TxType.Blob and LightTxDecoder never persists the type — and writes every blob-less type-6 tx to the blob DB. All three new routing tests pin BlobsSupportMode.InMemory, so the production path is untested. (details)
4 Medium Reorg/processed-tx paths not switched: RemoveProcessedTransactions:414 skips blob metrics and the processed-blob store; ReAddReorganisedTransactions:361 re-submits the tx from the block body (bare, no sidecar) back into the blob pool. (details)
5 Medium The gossip withhold only covers announce/broadcast. GetPooledTransactions is served from TryGetPendingTransaction, and SyncPeerProtocolHandlerBase.cs:199,234 guard on !tx.SupportsBlobs — so the bare consensus form still goes on the wire, which is what this change set out to prevent. (details)
6 Low SpecDrivenTxGossipPolicy ternary-returning-false; collapses to tx.SupportsBlobs ? proofVersionMatch : !tx.CarriesBlobs (suggestion posted inline).
7 Low CarriesBlobs duplicates the inline predicates it documents itself as mirroring (BlockValidator.cs:353, TransactionExtensions.cs:79, picker :76); BlobTxPriorityComparer:28 still on SupportsBlobs. (details)
8 Low TxValidator.cs:91 comment claims a wrapper on a frame tx is "rejected … by NonBlobFieldsTxValidator's absence" — an absent validator rejects nothing; the real invariant is that FrameTxDecoder never produces a wrapper. (details)

Why 1–3 compound

No validator requires the mempool form for type 6 (MempoolBlobTxValidator is type-3 only; MempoolBlobTxProofVersionValidator early-returns at TxValidator.cs:381 on !SupportsBlobs). So a peer can send a bare type-6 tx carrying only versioned hashes, and it will be accepted with no per-sender cap (#1), never expire out (#2), and be written to the 16K-slot persistent blob DB (#3) — permanently occupying blob-pool capacity it can never use. Practical exposure is devnet-only today thanks to the IsEip8141Enabled gate in NotSupportedTxFilter, which is why I have not marked it Critical, but I would not land the routing without either closing #1/#2 or scoping routing to BlobsSupportMode.InMemory with an explicit EIP8141: marker.

Suggested test additions

  • persistent (StorageWithReorgs) round-trip of a blob-carrying frame tx, asserting the type survives storage
  • expired blob-carrying frame tx is evicted on a new head
  • per-sender blob limit and cumulative-balance rejection for multiple type-6 blob txs from one sender
  • reorg re-add of a blob-carrying frame tx

I did not run a build or the test suite for this review — findings are from source reading.
· branch eip8141-blob-network

bool eip1559Enabled = _specProvider.GetCurrentHeadSpec().IsEip1559Enabled;
UInt256 effectiveGasPrice = tx.CalculateEffectiveGasPrice(eip1559Enabled, _headInfo.CurrentBaseFee);
TxDistinctSortedPool relevantPool = (tx.SupportsBlobs ? _blobTransactions : _transactions);
TxDistinctSortedPool relevantPool = (tx.CarriesBlobs ? _blobTransactions : _transactions);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

High — the default (persistent) blob pool loses the transaction type, and that path is untested.

BlobsSupport defaults to StorageWithReorgs (TxPoolConfig.cs:14), so in production _blobTransactions is a PersistentBlobTxDistinctSortedPool. Its InsertCore stores new LightTransaction(fullBlobTx) in memory and writes the tx to the blob DB:

// PersistentBlobTxDistinctSortedPool.cs:62
if (base.InsertCore(hash, new LightTransaction(fullBlobTx), groupKey))
{
    _blobTxCache.Set(fullBlobTx.Hash, fullBlobTx);
    _blobTxStorage.Add(fullBlobTx);

and LightTransaction hard-codes the type (LightTransaction.cs:18 and again in the deserializing ctor):

Type = TxType.Blob;

Consequences for a routed type-6 tx:

  • the in-memory blob-pool entry claims TxType.Blob, so every SupportsBlobs-keyed branch that sees the light tx takes the type-3 path (and after a node restart LightTxDecoder.Decode rebuilds it as type 3 — the type is not persisted in the light record at all, only in the full-tx record);
  • LightTxDecoder.Encode dereferences tx.MaxFeePerBlobGas!.Value, which is only non-null for frame txs that came off the wire;
  • every blob-less type-6 tx is written to the FullBlobTxs/LightBlobTxs columns and reloaded on every restart, even though ResolveBlob in TxPoolTxSource can never produce it (no NetworkWrapper), so the slot and the disk entry are held indefinitely.

The three new routing tests all pin BlobsSupport = BlobsSupportMode.InMemory (TxPoolTests.Blobs.cs:1420/1439/1458), so none of this is covered. Please add a StorageWithReorgs case (submit → restart-equivalent reload → assert type/round-trip) or explicitly restrict routing to the in-memory mode until the sidecar lands.

Fix this →

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The in-memory routing/admission/eviction consistency is now fixed (filters, expiry pass, reorg and processed-tx accounting all key on CarriesBlobs). On the persistent-pool specifics, two of the cited failure modes don't hold as stated:

  • LightTxDecoder.Encode does not NRE here: only blob-carrying frame txs reach the blob pool, and validation now requires a non-null MaxFeePerBlobGas for them (the decoder also defaults it to 0), so MaxFeePerBlobGas!.Value / BlobVersionedHashes! are always non-null on this path.
  • Blob-less type-6 txs are not written to the blob DB: routing keys on CarriesBlobs, so they go to the normal pool.

The real residual is that the light in-memory record is typed Blob and doesn't carry the frame body, so on restart a persisted blob-carrying frame tx reloads as a type-3 light entry (the full tx is still stored via the full TxDecoder). That's latent today: such a tx can't be included (production guard) or resolved (no sidecar). Persisting the type/body through LightTransaction+LightTxDecoder is a storage-format change entangled with the deferred type-6 sidecar wire format, so I'm leaving it out of this PR rather than encoding a format that isn't settled — and not adding a test that would pin the current reload behaviour.

TxDistinctSortedPool otherTxTypePool = tx.SupportsBlobs ? txs : blobTxs;
// EIP-8141: route on the instance-level blob gate so a blob-carrying frame tx is treated as a
// blob-pool member (conflicts with the sender's normal-pool txs), mirroring type-3 routing.
TxDistinctSortedPool otherTxTypePool = tx.CarriesBlobs ? txs : blobTxs;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

High — routing moved but the admission filters did not: a frame-blob tx is now admitted under normal-pool rules into the blob pool.

TxTypeTxFilter, AddCore, the snapshot/eviction/metrics sites were switched to CarriesBlobs, but every filter that picks a pool or a limit still keys on the type-level SupportsBlobs (false for type 6):

site effect for a blob-carrying frame tx
FeeTooLowFilter.cs:44 fullness / worst-fee check runs against _txs, so the fee floor that protects a full blob pool is skipped
BalanceTooLowFilter.cs:39 cumulative-cost bucket is read from _txs; TxTypeTxFilter guarantees that bucket is empty, so the sender's existing blob-pool frame txs are never counted — balance is checked per-tx only
FutureNonceFilter.cs:15 uses MaxPendingTxsPerSender (default 0 = unlimited) instead of MaxPendingBlobTxsPerSender (default 16)
GapNonceFilter.cs:24,29 gap allowance keyed on _txs.IsFull() and bucket count read from _txs — gaps are allowed in a pool whose eviction cascade exists precisely to prevent them (TxPool.cs:882)
SizeTxFilter.cs:19 MaxTxSize instead of MaxBlobTxSize (bites once a sidecar exists)
NotSupportedTxFilter.cs:21 with BlobsSupport=Disabled the tx is not rejected; it is routed to a capacity-0 blob pool and comes back as FeeTooLowToCompete instead of NotSupportedTxType

Taken together with the fact that no validator requires the mempool form for type 6 (TxValidator.cs:94, and MempoolBlobTxProofVersionValidator early-returns on !SupportsBlobs), a single sender can push an unbounded number of blob-less type-6 txs — none individually priced against the blob pool and none charged for their siblings — into the 16K-slot persistent blob pool, each also written to the blob DB. Exposure is currently limited to devnets by the IsEip8141Enabled gate in NotSupportedTxFilter, but this should be closed (or the per-sender/balance/fee filters explicitly deferred with a marker) before the routing lands.

Fix this →

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed. FeeTooLowFilter, BalanceTooLowFilter, FutureNonceFilter, GapNonceFilter, SizeTxFilter and NotSupportedTxFilter now key on CarriesBlobs, so a blob-carrying frame tx picks the blob pool/limit, counts against MaxPendingBlobTxsPerSender, and is rejected as NotSupportedTxType under BlobsSupport=Disabled. Added routing tests for the per-sender blob limit and the blobs-disabled rejection.

updateTx(transactions, tx, null, lastElement);
// evict all following txs to prevent nonce gaps between blob tx
evictNextTxs |= tx.SupportsBlobs;
evictNextTxs |= tx.CarriesBlobs;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

High — routing to the blob pool silently disables frame-tx expiry eviction.

The on-head eviction pass added for EIP-8141 only ever looks at the normal pool:

// TxPool.cs:496 (RemoveExpiredFrameTransactions)
Transaction[] snapshot = _transactions.GetSnapshot();

and its fast-path counter is only fed by the normal pool's events (TxPool.cs:148-149):

_transactions.Inserted += OnInsertedTx;   // increments _expiringFrameTxCount
_transactions.Removed  += OnRemovedTx;

So after this PR a blob-carrying frame tx with an expiry-verifier deadline (a) never increments _expiringFrameTxCount, so the pass short-circuits at TxPool.cs:489 even when such txs are pending, and (b) is not in the scanned snapshot anyway. An expired blob-carrying frame tx therefore stays in the (persistent) blob pool forever — it can never be included, ResolveBlob will never produce it, and nothing evicts it. That removes the only bound on the occupancy problem described in the TxTypeTxFilter comment.

Either scan _blobTransactions in the same pass and subscribe the blob pool's Inserted/Removed to the same handlers, or add a regression test asserting an expired blob-carrying frame tx is evicted on a new head.

Fix this →

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed. The blob pool's Inserted/Removed now feed the same expiry counter, and RemoveExpiredFrameTransactions scans both pools. Added a regression test asserting an expired blob-carrying frame tx is evicted from the blob pool on a new head.

Interlocked.Increment(ref _pendingTransactionsAdded);
if (tx.Supports1559) { Metrics.Pending1559TransactionsAdded++; }
if (tx.SupportsBlobs) { Metrics.PendingBlobTransactionsAdded++; }
if (tx.CarriesBlobs) { Metrics.PendingBlobTransactionsAdded++; }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Medium — the block-processing / reorg paths were not switched, so they now disagree with the routing.

Two sites still key on SupportsBlobs and are reached with the block's consensus-form tx:

  • RemoveProcessedTransactions (TxPool.cs:414) — a blob-carrying frame tx is not counted in blobTxs/blobs metrics (inconsistent with the Metrics.PendingBlobTransactionsAdded increment on this line) and is not written to the processed-blob-txs store, so it cannot be restored via TryGetBlobTransactionsFromBlock.
  • ReAddReorganisedTransactions (TxPool.cs:361) — because SupportsBlobs is false the continue is skipped, so on a reorg the tx is re-submitted from the block body, i.e. in bare consensus form with no sidecar, and now lands back in the blob pool (and, under the default persistent mode, back in the blob DB) as a permanently unusable entry.

Since blob-carrying frame txs cannot be produced yet, the cleanest resolution may be to make both sites use CarriesBlobs too so the two pools stay symmetric.

Fix this →

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed. ReAddReorganisedTransactions and RemoveProcessedTransactions now key on CarriesBlobs, so a blob-carrying frame tx is not re-submitted in bare consensus form on a reorg and is counted/saved consistently with the blob metrics.

// EIP8141: a blob-carrying frame tx (type 6) has no EIP-7594 sidecar network wrapper yet, so it
// cannot participate in the announce-by-hash / serve-with-sidecar blob gossip protocol. Withhold
// it from gossip until that wire format lands, rather than leaking it in bare consensus form.
tx.CarriesBlobs && !tx.SupportsBlobs

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Medium — the gossip policy only covers the announce/broadcast path, not the request/response path.

ShouldGossipTransaction is consulted from TxBroadcaster only (TxBroadcaster.cs:87,350). A peer that already knows the hash and sends GetPooledTransactions is served from TryGetPendingTransaction, which now reaches the blob pool (TxPool.cs:975-978), and the send-side blob guards also key on the type:

// SyncPeerProtocolHandlerBase.cs:199 / :234
if (!tx.SupportsBlobs) //additional protection from sending full tx with blob

SupportsBlobs is false for type 6, so the bare consensus form is still put on the wire — the exact leak this change is meant to prevent. Also TxBroadcaster.cs:123 stores the frame-blob tx in full (not as a LightTransaction) in the persistent-broadcast set, and TxBroadcaster.cs:370 will hand it back from TryGetPersistentTx. Consider gating on CarriesBlobs in those guards as well, or documenting that only announcements are withheld.

Low (style) — the ternary-returning-false is hard to read; the whole predicate collapses to:

Suggested change
tx.CarriesBlobs && !tx.SupportsBlobs
// EIP8141: a blob-carrying frame tx (type 6) has no EIP-7594 sidecar network wrapper yet, so it
// cannot participate in the announce-by-hash / serve-with-sidecar blob gossip protocol. Withhold
// it from gossip until that wire format lands, rather than leaking it in bare consensus form.
tx.SupportsBlobs
? tx.GetProofVersion() == ChainHeadInfoProvider.CurrentProofVersion
: !tx.CarriesBlobs;

Fix this →

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The send-side guards (SyncPeerProtocolHandlerBase 199/234) now key on CarriesBlobs, so the announce/broadcast paths never put a blob-carrying frame tx on the wire in full form, and I simplified the gossip-policy ternary as suggested. FulfillPooledTransactionsRequest serves whatever the pool returns and predates this routing change; withholding there touches the request/response protocol path and the persistent-broadcast light storage, which I'm leaving with the deferred sidecar work — for now only announcements are withheld.

// signature list) — signature/intrinsic-gas validators do not apply; per-frame gas and
// signature validation happen during processing.
// EIP8141: a blob-carrying frame tx currently has no EIP-7594 sidecar network wrapper, so no
// sidecar/proof validator is registered here (a wrapper on a frame tx is rejected as malformed

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Low — this comment asserts a guarantee that is not enforced.

"a wrapper on a frame tx is rejected as malformed by NonBlobFieldsTxValidator's absence" is backwards: NonBlobFieldsTxValidator is deliberately not in the type-6 chain, and the absence of a validator cannot reject anything. What actually holds today is narrower and worth stating plainly: FrameTxDecoder never produces a NetworkWrapper, so a frame tx off the wire cannot carry one — and consequently nothing requires the mempool form for type 6 either (MempoolBlobTxValidator is type-3 only, and MempoolBlobTxProofVersionValidator early-returns at line 381 on !SupportsBlobs). Per .agents/rules/coding-style.md, a comment that contradicts the code is worse than none.

Note this is also the property that lets a bare, blob-less type-6 tx be admitted straight into the blob pool — see the TxTypeTxFilter comment.

Fix this →

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reworded to state the actual invariant: FrameTxDecoder never decodes a network wrapper for a type-6 tx, so no sidecar/proof validator is registered and no mempool-form requirement exists yet.

/// and decode paths key on. Mirrors the accounting predicate used in block validation and the blob
/// gas calculator so a blob-carrying frame tx counts towards the block blob budget like a type-3 tx.
/// </remarks>
public bool CarriesBlobs => BlobVersionedHashes is { Length: > 0 };

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Low — the new property duplicates predicates it says it mirrors.

The XML doc says it "mirrors the accounting predicate used in block validation and the blob gas calculator", but those inline copies are left in place, so the repo now has four spellings of the same rule:

  • BlockValidator.cs:353if (transaction.BlobVersionedHashes is not { Length: > 0 })
  • TransactionExtensions.cs:79if (tx.BlobVersionedHashes is { Length: > 0 })
  • BlockProcessor.BlockProductionTransactionPicker.cs:76currentTx.Type == TxType.FrameTx && currentTx.BlobVersionedHashes is { Length: > 0 }

Folding those onto CarriesBlobs is exactly the kind of change that makes the new property pay for itself (AGENTS.md: prefer the option that removes code; DRY).

Low, separateBlobTxPriorityComparer (Nethermind.Consensus/Comparers/BlobTxPriorityComparer.cs:28) still compares on SupportsBlobs, so inside the producer comparer a blob-carrying frame tx loses the blob tie-break to an equally-priced type-3 tx. Minor, but it contradicts the "metered/selected exactly like a type-3 tx" goal.

Fix this →

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed. Folded BlockValidator, TransactionExtensions and both picker sites onto CarriesBlobs, and switched BlobTxPriorityComparer to CarriesBlobs so a blob-carrying frame tx gets the same producer tie-break as a type-3 tx.

…lob-carrying frame transactions

Complete the non-consensus blob layer for EIP-8141 frame transactions on top
of the consensus-correct accounting/validation/settlement in PR #12701.

- Blob-pool routing: introduce an instance-level Transaction.CarriesBlobs gate
  (BlobVersionedHashes present) and route on it at the pool-membership sites so a
  blob-carrying frame tx (type 6) lands in the blob pool like a type-3 tx, without
  touching the type-level SupportsBlobs() that networking/decode paths key on.
  Type-3 routing is unchanged. TxTypeTxFilter, snapshot invalidation, eviction
  cascade, metrics, and ContainsTx follow the same gate.
- Production blob-budget metering: routing feeds frame-blob txs through the blob
  selection path in TxPoolTxSource, so they are metered against the block blob
  budget (MaxBlobGasPerBlock / MaxProductionBlobCount) and a produced block never
  exceeds it. The normal-pool picker guard is kept as defense in depth for any
  frame-blob tx that reaches production without a resolvable sidecar.
- Networking: withhold blob-carrying frame txs from gossip until the EIP-7594
  sidecar wire format exists, rather than leaking them in bare consensus form.

Deferred (documented markers): the EIP-7594 sidecar / network-wrapper wire format
for type 6 (decode/encode + mempool acceptance of the wrapper form). Production of
frame-blob txs is gated on it, since the blobs bundle cannot be built without the
sidecar.

Tests: frame-blob tx routes to the blob pool while a plain frame tx and a type-3
tx route unchanged; a frame-blob tx with a sidecar is metered/selected within the
blob budget and excluded when it would overflow it.
…sistently

Address review follow-ups on the blob-network PR:
- switch the admission filters (fee/balance/future-nonce/gap-nonce/size/
  not-supported) to the instance blob predicate so a blob-carrying frame tx
  obeys blob-pool limits and is rejected when blobs are disabled
- scan the blob pool in the frame-expiry eviction pass and feed its
  inserts/removals to the expiry counter
- align the reorg re-add and processed-tx accounting with the routing predicate
- withhold blob-carrying frame txs from the peer send path and simplify the
  gossip policy predicate
- fold duplicate blob predicates onto CarriesBlobs and correct the type-6
  validator comment
- add blob-pool routing regression tests (expiry eviction, blobs-disabled
  rejection, per-sender blob limit)
@Marchhill
Marchhill force-pushed the eip8141-blob-network branch from 055d6d5 to d0e510b Compare August 6, 2026 20:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant