+ Visual breakdown of how the L2 slot is divided into phases. With pipelining, attestation collection and L1 submission extend into the next slot (shown as overflow).
+
+
+
+
Transaction Lifecycle
This tool models the user-perceived latency from sending a transaction to seeing its effects in the proposed
@@ -337,8 +367,9 @@
Transaction Lifecycle
L2 slot. The TX must wait until the next block building window starts, since the proposer snapshots the TX pool at
the beginning of each block.
4. Block execution. The proposer executes the transactions in the block. The actual execution
- time depends on block fill (how many and how complex the transactions are). Once done, the block proposal is
- broadcast to the network without waiting for the full block window to elapse.
+ time depends on block fill (how many and how complex the transactions are). For non-last blocks, the block
+ proposal is broadcast immediately. The last block is bundled with the checkpoint proposal, which is broadcast
+ after checkpoint assembly (adding a small delay).
5. P2P propagation back. The block proposal propagates back through the P2P network to the
user's node (another one-way propagation delay).
6. Node re-execution. The user's node re-executes the block to update its local world state
@@ -346,6 +377,11 @@
Transaction Lifecycle
proposed chain.
7. Slot wrap. If the TX arrives too late in the slot and misses all block building windows, it
must wait for the next slot's proposer to pick it up, adding up to one full slot duration of extra latency.
+
Pipelining. When proposer pipelining is enabled, checkpoint finalization (attestation collection and
+ L1 publishing) is deferred to the next slot. Only checkpoint assembly and one-way proposal broadcast need
+ to complete within the build slot. This frees up time for more blocks, significantly shrinking the dead zone and
+ reducing latency. Validators re-execute the last block and return attestations during a grace period that extends
+ into the target slot.
Note that this models "proposed chain" visibility -- the TX effects are visible locally before checkpoint
confirmation on L1 or epoch proving.
const checkpointFinalizationTime =
config.checkpointAssembleTime + 2 * config.p2pPropagationTime + config.l1PublishingTime;
- const timeReservedAtEnd = config.l2BlockDuration + checkpointFinalizationTime;
+ // When pipelining, checkpoint finalization (attestation collection + L1 publishing) is
+ // deferred to the next slot. Only assembly + one-way broadcast need to fit in the build slot.
+ // Without pipelining, we also reserve a full block duration for validator re-execution
+ // plus the entire checkpoint finalization time.
+ let timeReservedAtEnd;
+ if (config.pipelining) {
+ timeReservedAtEnd = config.checkpointAssembleTime + config.p2pPropagationTime;
+ } else {
+ timeReservedAtEnd = config.l2BlockDuration + checkpointFinalizationTime;
+ }
+
const timeAvailableForBlocks = config.l2SlotDuration - initializationOffset - timeReservedAtEnd;
const maxBlocks = Math.max(1, Math.floor(timeAvailableForBlocks / config.l2BlockDuration)) || 1;
@@ -440,18 +489,31 @@
Transaction Lifecycle
});
}
+ // When pipelining, attestation collection extends into the target slot.
+ // Grace period = validator re-execution (one block duration) + one-way attestation return.
+ const pipeliningAttestationGracePeriod = config.pipelining
+ ? config.l2BlockDuration + config.p2pPropagationTime
+ : 0;
+
return {
initializationOffset,
checkpointFinalizationTime,
timeAvailableForBlocks,
+ timeReservedAtEnd,
maxBlocks,
blockWindows,
+ pipelining: config.pipelining,
+ pipeliningAttestationGracePeriod,
};
}
/**
* Computes the user-perceived latency for a single transaction sent at a given
* time within an L2 slot.
+ *
+ * Non-last blocks are broadcast individually right after execution.
+ * The last block is bundled with the checkpoint proposal, so it incurs an extra
+ * checkpointAssembleTime delay before its proposal is broadcast.
*/
function computeLatency(config, timetable, txSendTime) {
const arrivalAtProposer = txSendTime + config.p2pPropagationTime;
@@ -466,17 +528,25 @@
Transaction Lifecycle
let wrapsToNextSlot = false;
let blockEnd;
+ let isLastBlock = false;
if (blockIndex >= timetable.maxBlocks) {
wrapsToNextSlot = true;
blockIndex = 0;
blockEnd = config.l2SlotDuration + timetable.initializationOffset + executionTime;
+ // When wrapping to next slot, block 0 of next slot is only "last" if maxBlocks is 1
+ isLastBlock = timetable.maxBlocks === 1;
} else {
const blockStart = timetable.initializationOffset + blockIndex * config.l2BlockDuration;
blockEnd = blockStart + executionTime;
+ isLastBlock = blockIndex === timetable.maxBlocks - 1;
}
- const proposalArrival = blockEnd + config.p2pPropagationTime;
+ // The last block is bundled with the checkpoint proposal, so its broadcast is
+ // delayed by checkpoint assembly time. Non-last blocks are broadcast immediately.
+ const assemblyDelay = isLastBlock ? config.checkpointAssembleTime : 0;
+ const proposalBroadcast = blockEnd + assemblyDelay;
+ const proposalArrival = proposalBroadcast + config.p2pPropagationTime;
const effectsVisible = proposalArrival + executionTime;
const latency = effectsVisible - txSendTime;
@@ -505,8 +575,21 @@