Skip to content
s1ns3nz0 | Known Unknowns
Go back

Lineth L2 Transaction Lifecycle (2) - From L2 Blocks to Execution Proofs: Trace and Conflation in the Coordinator

4 min read

Part of the Lineth L2 Transaction Lifecycle series. The component map is in part 0.

In the previous post, I followed an execution payload from Sequencer Besu to RPC Besu and confirmed the transaction through its receipt.

This post covers the next stage. The Coordinator groups L2 blocks into proof work, creates batches and blobs, and picks up the execution-proof results.

The example is L2 blocks 24,205 through 24,284, a different range from the block in the previous post. All timestamps are UTC.

Execution traces

A receipt says whether a transaction succeeded and how much gas it used. It says nothing about the individual steps of execution.

An execution trace does. It records the computation and state changes that happened while the transactions ran. A contract call might go:

Read input
    ↓
Do arithmetic
    ↓
Use the result
    ↓
Update contract state

The ZK tracer records what’s needed to prove each of those steps followed the EVM’s rules. The Prover takes that as input when it generates an execution proof.

Trace counters

The Coordinator logs counters like these:

ADD=327
BLOCK_HASH=1440
HUB=17679

Each is a quantity for one trace module. They aren’t transaction counts or durations, and they aren’t the trace itself. They summarize how much work the blocks represent, so the Coordinator can tell whether one more block still fits under the limits.

Conflation

Conflation groups consecutive L2 blocks into one unit of proof work. The Coordinator keeps adding blocks and checking their combined counters until something tells it to stop:

Add block 24205
    ↓
Add block 24206
    ↓
Keep adding
    ↓
Hit a closing condition
    ↓
Close the batch

That condition can be a trace limit, a block-count limit, or a deadline. Once the range is fixed, the traces for those blocks are prepared and combined for the Prover.

Nothing is merged on chain. The blocks stay separate; the grouping exists only for proving.

Execution trace   what's needed to prove execution
    ↓
Conflation        group blocks, combine their traces
    ↓
Batch             the unit one execution proof covers
    ↓
Prover            generates the proof

Batch and blob

The logs mention both a batch and a blob. They can cover the same blocks, but they’re for different things.

UnitPurpose
BatchGroups L2 blocks for execution proving
BlobGroups compressed block and transaction data for publication on L1
L2 blocks
    ├─ Execution proving → batch
    └─ Data publication  → compressed data in a blob

The proof shows execution was correct. The published data lets anyone rebuild the blocks and transactions. A blob isn’t a container for the proof file.

One blob can hold several batches. In the records below, each blob holds exactly one, so the ranges match.

The Coordinator’s side of the flow

L2 blocks available
    ↓
Coordinator collects block data and trace counters
    ↓
Conflation sets a batch boundary
    ↓
Traces prepared
    ↓
Coordinator writes an execution-proof request
    ↓
Prover works on it
    ↓
Coordinator picks up the result

Blob grouping runs alongside. A new blob record doesn’t mean the proof is done or that anything has gone to L1.

The batch closes on a trace limit

I searched the Coordinator logs for conflation decisions:

k -n lineth-l2 logs deploy/coordinator -c coordinator --since=1h \
  | grep -iE 'conflation|conflated' \
  | grep -iE 'trigger|limit|deadline|24205|24284'

The batch record, with the counter map cut short:

2026-10-14 04:30:32.787+0000 | vert.x-eventloop-thread-0 | INFO | ProofGeneratingConflationHandlerImpl | new batch: batch=[24205..24284]80 trigger=TRACES_LIMIT tracesCounters=TracesCountersV5(...)

Part of the full map:

ADD=327
BLOCK_DATA=2240
BLOCK_HASH=1440
HUB=17679
ROM=249232
BLOCK_TRANSACTIONS=114

The range is inclusive, so 24284 − 24205 + 1 = 80 blocks. BLOCK_TRANSACTIONS is 114; a block can hold more than one transaction, so the two numbers differ.

trigger=TRACES_LIMIT means a trace limit closed the batch. The log doesn’t say which module hit its limit.

Other batches in the same window look identical:

Batch rangeBlocksBLOCK_HASH
24,125–24,204801,440
24,205–24,284801,440
24,285–24,364801,440
24,365–24,444801,440

Every batch is 80 blocks with BLOCK_HASH=1440. That’s a pattern in this lab with these settings, not a rule that Lineth batches are 80 blocks.

The blob closes on a batch limit

One millisecond after the batch, the Coordinator logged:

2026-10-14 04:30:32.788+0000 | vert.x-eventloop-thread-0 | INFO | GlobalBlockConflationCalculator | new blob: blob=[24205..24284]80 trigger=BATCHES_LIMIT blobSizeBytes=5536 blobBatchesCount=1 blobBatchesLimit=1 blobBatchesList=[[24205..24284]80]1

The blob holds one batch, and the limit is one batch per blob. It’s 5,536 bytes. It closed on BATCHES_LIMIT, not on size, so this blob was nowhere near full.

The two boundaries are separate decisions:

Trace limit
    ↓
Batch 24205–24284 closes
    ↓
Batch goes into a blob
    ↓
Batch-count limit
    ↓
Blob for the same range closes

new blob is a local grouping decision. It doesn’t mean the blob was sent to L1.

The proof request

2026-10-14 04:30:33.164+0000 | vert.x-eventloop-thread-1 | INFO | ProofGeneratingConflationHandlerImpl | execution proof request generated: proofIndex=ExecutionProofIndex(startBlockNumber=24205, endBlockNumber=24284, startBlockTimestamp=2026-10-14T04:23:12Z)

startBlockNumber=24205 and endBlockNumber=24284 match the batch.

The three timestamps mean different things:

TimestampWhat it is
04:23:12First block in the batch
04:30:32.787Coordinator closed the batch
04:30:33.164Coordinator wrote the proof request

startBlockTimestamp is when the first block was made, not when the Prover started. The batch closed 7 minutes 20 seconds after that block.

Closing the batch to writing the request took 377ms.

In this lab, the Coordinator and Prover talk through files on shared storage. The Coordinator writes a request file and the Prover picks it up, so a request record means the file exists, not that the Prover is working on it yet.

The proof result

2026-10-14 04:33:57.425+0000 | vert.x-eventloop-thread-2 | INFO | ProofGeneratingConflationHandlerImpl | execution proof generated: batch=Batch(startBlockNumber=24205, endBlockNumber=24284)

Same range as the request. To pull both lines at once:

k -n lineth-l2 logs deploy/coordinator -c coordinator --since=1h \
  | grep -E 'execution proof request generated|execution proof generated' \
  | grep -E '24205|24284'
EventUTC
Batch closed (TRACES_LIMIT)04:30:32.787
Blob closed (BATCHES_LIMIT)04:30:32.788
Proof request written04:30:33.164
Proof result picked up04:33:57.425
04:33:57.425 − 04:30:33.164 = 204.261 s ≈ 3 min 24 s

That’s from the Coordinator writing the request to the Coordinator seeing the result. It includes the request waiting in line, the proving itself, and the Coordinator’s polling interval, so it’s an upper bound on proving time, not a measurement of it.

The lab runs a partial, dev-mode proving configuration, so execution proof generated here doesn’t carry the guarantees of a production proof.

Where this leaves block 24,205

Batch 24205–24284 closed on TRACES_LIMIT (80 blocks, 114 transactions)
    ↓
Blob for the same range closed on BATCHES_LIMIT (1 batch, 5,536 bytes)
    ↓
Execution-proof request written
    ↓
Result picked up 3 min 24 s later

The blocks now have an execution proof. The next post looks at what the Prover did during those 3 minutes 24 seconds.


Share this post:

Previous Post
Lineth L2 Transaction Lifecycle (3) - Inside the Prover: From a Coordinator Request to a Proof Response
Next Post
Lineth L2 Transaction Lifecycle (1) - Following an L2 Transaction Through Besu and Maru