For the complete documentation index, see llms.txt. This page is also available as Markdown.

74742 bc low blob mode frame size mismatch can stall l1 blob publication before submission

Submitted on Apr 24th 2026 at 15:57:29 UTC by @y4y for Audit Comp | Base Azul

  • Report ID: #74742

  • Report Type: Blockchain/DLT

  • Report severity: Low

  • Target: https://github.com/base/base/tree/v0.8.0-rc.24

  • Impacts:

    • 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:

// base/crates/batcher/encoder/src/config.rs
impl Default for EncoderConfig {
    fn default() -> Self {
        Self {
            target_frame_size: 130_044,
            max_frame_size: 130_044,
            max_channel_duration: 2,
            sub_safety_margin: 0,
            target_num_frames: 1,
            batch_type: BatchType::Single,
            da_type: DaType::Blob,
            approx_compr_ratio: 0.6,
            max_l1_tx_size_bytes: None,
        }
    }
}

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.

References

  • base/crates/batcher/encoder/src/config.rs:94-134

  • base/crates/batcher/encoder/src/encoder.rs:229-239

  • base/crates/batcher/comp/src/channel_out.rs:140-171

  • base/crates/batcher/blobs/src/encoder.rs:30-68

  • base/crates/batcher/core/src/submissions.rs:61-133

Proof of Concept

There are multiple changes to the codebase: This change improves observability only. It does not tighten limits or alter blob packing behavior.


File: base/crates/batcher/core/tests/h01_blob_frame_size_poc.rs


To run the PoC

1

Start the single-sequencer devnet

2

Stop the compose batcher so the PoC logs stay isolated

3

Run the one-off batcher against the stable client

4

Confirm the batcher backfills from the stable client

5

If needed, send large normal L2 transactions while the one-off batcher is running

6

Expected sequence

  1. The sequencer accepts the large f(bytes) L2 transactions.

  2. The batcher continues to publish normal L1 blob txs.

  3. The next selected frame reaches blob_payload_size=130045.

  4. The batcher loops on the encode failure without creating a new L1 tx for the offending frame.

7

Restore the normal compose batcher after the run

8

Console output

This should be in the console output/log

Was this helpful?