Temporary freezing of network transactions by delaying one block by 500% or more of the average block time of the preceding 24 hours beyond standard difficulty adjustments
Description
Brief/Intro
The default blob-mode frame sizing allows a non-first frame whose serialized size already consumes the full 130044-byte blob payload budget. Blob packing then adds the derivation prefix byte, producing a 130045-byte payload. The batcher only detects that overflow when BlobEncoder::encode_packed() runs, after the frame has already been selected as the first submission in an L1 blob candidate. No new L1 tx is created for that frame; it is requeued and retried indefinitely.
Vulnerability Details
The default encoder configuration makes the blob frame ceiling equal to the blob payload ceiling:
When a channel closes, the encoder drains frames using self.config.max_frame_size without reserving space for the blob derivation prefix:
ChannelOut::output_frame() subtracts the frame envelope and, only for the first frame, the compression-version byte. A non-first frame therefore carries max_frame_size - 23 bytes of compressed data:
Blob packing adds the derivation prefix byte and the per-frame envelope:
Concrete example:
default max_frame_size = 130044
non-first frame data bytes = 130044 - 23 = 130021
serialized frame size = 23 + 130021 = 130044
blob payload size = 1 + 23 + 130021 = 130045
blob max payload size = 130044
So the payload is deterministically one byte too large.
The submission queue does not reject an oversized first submission before blob encoding. The pre-pack check only runs once the candidate already contains at least one submission:
That produces the observed loop:
This is a pre-broadcast failure. It is not a malformed L1 blob tx that gets sent and later ignored. No new L1 tx hash is created for the oversized frame.
The pre-condition for such issue to happen is:
Blob DA mode is active.
The effective frame ceiling allows a serialized frame of 130044 bytes (target_frame_size = 130044 by default, and CLI wiring maps it to max_frame_size).
A channel produces enough compressed bytes after its first frame to create a full non-first frame.
That frame is selected as the first submission in an empty blob candidate.
Impact Details
The batcher can enter a deterministic encode-fail/requeue loop and stop making DA publication progress for the affected channel. Unsafe L2 blocks behind that channel are not published to L1 until the operator changes configuration or a code fix is deployed. Restarting with the same configuration does not solve the issue, because the same frame is derived again and fails at the same boundary.
This is operationally severe because the sequencer can continue accepting L2 transactions while the L1 publication path is stalled for that channel.
// base/crates/batcher/core/src/submissions.rs
while let Some(sub) = pipeline.next_submission() {
let sub_frame_size: usize =
sub.frames.iter().map(|f| BlobEncoder::FRAME_OVERHEAD + f.data.len()).sum();
if !ids.is_empty()
&& payload_size + sub_frame_size > BlobEncoder::BLOB_MAX_DATA_SIZE
{
pipeline.requeue(sub.id);
break;
}
payload_size += sub_frame_size;
ids.push(sub.id);
frames.extend(sub.frames);
}
let candidate = match da_type {
DaType::Blob => match BlobEncoder::encode_packed(&frames) {
Ok(blob) => TxCandidate { ... },
Err(e) => {
for id in ids {
pipeline.requeue(id);
}
drop(permit);
continue;
}
},
...
};
failed to encode frames to blob; requeueing without L1 tx submission
error=data too large: 130045 bytes exceeds maximum 130044
submissions=1
frames=1
blob_payload_size=130045
blob_max_data_size=130044
first_frame_number=Some(0)
first_frame_data_len=Some(130021)