Skip to content

Latest commit

 

History

History
56 lines (31 loc) · 3.42 KB

091.md

File metadata and controls

56 lines (31 loc) · 3.42 KB

Shambolic Banana Barbel

High

Rollup.sol cannot split batches across blobs, allowing inexpensive block stuffing

Summary

The maximum amount of calldata that can be passed to an L2 transaction is too large to fit in a single blob. Because Rollup.sol does not allow splitting batches across blobs, L2 blocks are capped by their calldata size. This is not properly accounted for in the L2 gas prices, and leads to an attack where blocks can be inexpensively stuffed, blocking all L2 transactions on the chain.

Root Cause

Batches are expected to commit to many blocks. We can expect up to 100 blocks per chunk (MaxBlocksPerChunk in node/types/chunk.go), and 45 chunks per batch (maxChunks in Gov.sol). This means that a batch can commit to 4500 blocks.

However, Rollup.sol has the surprising quirk that a full batch must fit into a single blob. For that reason, the batch is not just limited based on blocks, but also by calldata. We can see this logic in miner/pipeline.go#L260-266, where the block size is being tracked, and we skip all transactions that push it over the limit for the blob.

In the event that a user submits a single transaction with enough calldata to fill a whole blob, this transaction will end the block, and the entire batch will consist of the single block with one transaction.

This has two important implications:

First, the gas cost of stuffing an entire block is loosely the price of sending 128kb of calldata. For a transaction with no execution or value, we can calculate the L2 gas cost as intrinsic gas + calldata + l1DataFee.

If we assume an l1BaseFee of 30 gwei, an l1BlobBaseFee of 1, and the scalar and l2BaseFee values from the config file, we get:

  • intrinsic gas = 21_000 gas = 21 gwei
  • calldata = 1024 * 128 * (4 + 16) / 2 = 1,310,720 gas = 1,310 gwei (assumes half non-zero bytes to avoid compression)
  • l1DataFee = (1024 * 128 * 1 * 0.4) + (230 * 30)= 59,328 gwei
  • total cost = 21 + 1,310 + 59,328 = 60,659 gwei = $0.14

Second, and more importantly, block stuffing is usually protected by EIP1559 style gas pricing, where the base fee increases dramatically if sequential blocks are full. However, this block stuffing attack has the strange quirk that only 1.3mm gas (out of 30mm gas limit) will be used, which will actually lower the base fee over time.

Internal Preconditions

None

External Preconditions

None

Attack Path

  1. Submit a large number of transactions on L2 that use 128kb of calldata.
  2. Each time one is picked up (which should be each block), it costs only 1.3mm gas, but seals the block with no other L2 transactions.

Impact

L2 blocks will be stuffed and most activity on L2 will be blocked. This can cause major and unpredictable issues, such as oracles getting out of date, liquidations not occurring on time, and users being unable to take other important actions.

PoC

N/A

Mitigation

The architecture should be changed so that batches can be split across multiple blobs if needed.

Note that this issue has been reported directly to Scroll via their Immunefi bounty program. It has been deemed valid and rewarded with a bounty, and the Scroll team is currently working on a fix. It may be worthwhile to consult with them on the optimal solution.