Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 12 additions & 6 deletions yarn-project/archiver/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,18 @@ Two independent syncpoints track progress on L1:

### L1-to-L2 Messages

Messages are synced from the Inbox contract via `handleL1ToL2Messages()`:
Messages are synced from the Inbox contract. The sync compares local state (message count and rolling hash) against the Inbox contract state on L1, downloads any missing messages, and verifies consistency afterwards. On success, the syncpoint advances to the current L1 block. On failure (L1 reorg or inconsistency), the syncpoint rolls back to the last known-good message and the operation retries (up to 3 times within the same sync iteration).

1. Query Inbox state at the current L1 block (message count + rolling hash)
2. Compare local vs remote state
3. If they match, nothing to do
4. If mismatch, validate the local last message still exists on L1 with the same rolling hash
- If not found or hash differs, an L1 reorg occurred: find the last common message, delete everything after, and rollback the syncpoint
5. Fetch `MessageSent` events in batches and store
2. Compare local state against remote
3. If they match, advance syncpoint and return
4. If mismatch, fetch `MessageSent` events in batches and store them
- If storing fails due to a rolling hash mismatch (indicating an L1 reorg changed or removed messages), find the last common message with L1, delete everything after, reset the syncpoint, and retry
5. After storing, verify local state matches the remote state queried in step 1
- If still mismatched (e.g., messages missed due to a concurrent L1 reorg), rollback and retry
6. On success, advance the syncpoint

The syncpoint and the `inboxTreeInProgress` marker (which tracks which checkpoint's messages are currently being filled on L1) are updated atomically. The marker is only advanced after messages are stored, so concurrent reads don't see an unsealed checkpoint as readable before its messages are available.

### Checkpoints

Expand Down Expand Up @@ -81,6 +85,8 @@ The `blocksSynchedTo` syncpoint is updated:

Note that the `blocksSynchedTo` pointer is NOT updated during normal sync when there are no new checkpoints. This protects against small L1 reorgs that could add a checkpoint on an L1 block we have flagged as already synced.

The `messagesSynchedTo` pointer is always advanced to the current L1 block on success. If a rolling hash mismatch or post-download inconsistency is detected, the pointer rolls back to the last common message and the operation retries. The rolling hash chain and pre/post-sync consistency checks provide the primary reorg protection.

### Block Queue

The archiver implements `L2BlockSink`, allowing other subsystems to push blocks before they appear on L1:
Expand Down
5 changes: 4 additions & 1 deletion yarn-project/archiver/src/archiver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -490,7 +490,10 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
await this.store.rollbackL1ToL2MessagesToCheckpoint(targetCheckpointNumber);
this.log.info(`Setting L1 syncpoints to ${targetL1BlockNumber}`);
await this.store.setCheckpointSynchedL1BlockNumber(targetL1BlockNumber);
await this.store.setMessageSynchedL1Block({ l1BlockNumber: targetL1BlockNumber, l1BlockHash: targetL1BlockHash });
await this.store.setMessageSyncState(
{ l1BlockNumber: targetL1BlockNumber, l1BlockHash: targetL1BlockHash },
undefined,
);
if (targetL2BlockNumber < currentProvenBlock) {
this.log.info(`Rolling back proven L2 checkpoint to ${targetCheckpointNumber}`);
await this.updater.setProvenCheckpointNumber(targetCheckpointNumber);
Expand Down
18 changes: 7 additions & 11 deletions yarn-project/archiver/src/l1/data_retrieval.ts
Original file line number Diff line number Diff line change
Expand Up @@ -344,14 +344,10 @@ export async function getCheckpointBlobDataFromBlobs(
/** Given an L1 to L2 message, retrieves its corresponding event from the Inbox within a specific block range. */
export async function retrieveL1ToL2Message(
inbox: InboxContract,
leaf: Fr,
fromBlock: bigint,
toBlock: bigint,
message: InboxMessage,
): Promise<InboxMessage | undefined> {
const logs = await inbox.getMessageSentEventByHash(leaf.toString(), fromBlock, toBlock);

const messages = mapLogsInboxMessage(logs);
return messages.length > 0 ? messages[0] : undefined;
const log = await inbox.getMessageSentEventByHash(message.leaf.toString(), message.l1BlockHash.toString());
return log && mapLogInboxMessage(log);
}

/**
Expand All @@ -374,22 +370,22 @@ export async function retrieveL1ToL2Messages(
break;
}

retrievedL1ToL2Messages.push(...mapLogsInboxMessage(messageSentLogs));
retrievedL1ToL2Messages.push(...messageSentLogs.map(mapLogInboxMessage));
searchStartBlock = messageSentLogs.at(-1)!.l1BlockNumber + 1n;
}

return retrievedL1ToL2Messages;
}

function mapLogsInboxMessage(logs: MessageSentLog[]): InboxMessage[] {
return logs.map(log => ({
function mapLogInboxMessage(log: MessageSentLog): InboxMessage {
return {
index: log.args.index,
leaf: log.args.leaf,
l1BlockNumber: log.l1BlockNumber,
l1BlockHash: log.l1BlockHash,
checkpointNumber: log.args.checkpointNumber,
rollingHash: log.args.rollingHash,
}));
};
}

/** Retrieves L2ProofVerified events from the rollup contract. */
Expand Down
Loading
Loading