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
6 changes: 5 additions & 1 deletion yarn-project/archiver/src/archiver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
type L2Tips,
type ValidateCheckpointResult,
} from '@aztec/stdlib/block';
import { PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
import { type ProposedCheckpointInput, PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
import {
type L1RollupConstants,
getEpochAtSlot,
Expand Down Expand Up @@ -209,6 +209,10 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
});
}

public async setProposedCheckpoint(pending: ProposedCheckpointInput): Promise<void> {
await this.updater.setProposedCheckpoint(pending);
}

/**
* Processes all queued blocks, adding them to the store.
* Called at the beginning of each sync iteration.
Expand Down
32 changes: 30 additions & 2 deletions yarn-project/archiver/src/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,13 @@ export class InitialCheckpointNumberNotSequentialError extends Error {
}

export class CheckpointNumberNotSequentialError extends Error {
constructor(newCheckpointNumber: number, previous: number | undefined) {
constructor(
newCheckpointNumber: number,
previous: number | undefined,
source: 'confirmed' | 'proposed' = 'confirmed',
) {
super(
`Cannot insert new checkpoint ${newCheckpointNumber} given previous checkpoint number in batch is ${previous ?? 'undefined'}`,
`Cannot insert new checkpoint ${newCheckpointNumber} given previous ${source} checkpoint number is ${previous ?? 'undefined'}`,
);
}
}
Expand Down Expand Up @@ -104,6 +108,30 @@ export class L1ToL2MessagesNotReadyError extends Error {
}
}

/** Thrown when a proposed checkpoint number is stale (already processed). */
export class ProposedCheckpointStaleError extends Error {
constructor(
public readonly proposedCheckpointNumber: number,
public readonly currentProposedNumber: number,
) {
super(`Stale proposed checkpoint ${proposedCheckpointNumber}: current proposed is ${currentProposedNumber}`);
this.name = 'ProposedCheckpointStaleError';
}
}

/** Thrown when a proposed checkpoint number is not the expected confirmed + 1. */
export class ProposedCheckpointNotSequentialError extends Error {
constructor(
public readonly proposedCheckpointNumber: number,
public readonly confirmedCheckpointNumber: number,
) {
super(
`Proposed checkpoint ${proposedCheckpointNumber} is not sequential: expected ${confirmedCheckpointNumber + 1} (confirmed + 1)`,
);
this.name = 'ProposedCheckpointNotSequentialError';
}
}

/** Thrown when a proposed block conflicts with an already checkpointed block (different content). */
export class CannotOverwriteCheckpointedBlockError extends Error {
constructor(
Expand Down
16 changes: 15 additions & 1 deletion yarn-project/archiver/src/modules/data_source_base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,13 @@ import { isDefined } from '@aztec/foundation/types';
import type { FunctionSelector } from '@aztec/stdlib/abi';
import type { AztecAddress } from '@aztec/stdlib/aztec-address';
import { type BlockData, type BlockHash, CheckpointedL2Block, L2Block, type L2Tips } from '@aztec/stdlib/block';
import { Checkpoint, type CheckpointData, PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
import {
Checkpoint,
type CheckpointData,
type CommonCheckpointData,
type ProposedCheckpointData,
PublishedCheckpoint,
} from '@aztec/stdlib/checkpoint';
import type { ContractClassPublic, ContractDataSource, ContractInstanceWithAddress } from '@aztec/stdlib/contract';
import { type L1RollupConstants, getSlotRangeForEpoch } from '@aztec/stdlib/epoch-helpers';
import type { GetContractClassLogsResponse, GetPublicLogsResponse } from '@aztec/stdlib/interfaces/client';
Expand Down Expand Up @@ -157,6 +163,14 @@ export abstract class ArchiverDataSourceBase
return this.store.getSettledTxReceipt(txHash, this.l1Constants);
}

public getProposedCheckpoint(): Promise<CommonCheckpointData | undefined> {
return this.store.getProposedCheckpoint();
}

public getProposedCheckpointOnly(): Promise<ProposedCheckpointData | undefined> {
return this.store.getProposedCheckpointOnly();
}

public isPendingChainInvalid(): Promise<boolean> {
return this.getPendingChainValidationStatus().then(status => !status.valid);
}
Expand Down
15 changes: 14 additions & 1 deletion yarn-project/archiver/src/modules/data_store_updater.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
ContractInstanceUpdatedEvent,
} from '@aztec/protocol-contracts/instance-registry';
import type { L2Block, ValidateCheckpointResult } from '@aztec/stdlib/block';
import { type PublishedCheckpoint, validateCheckpoint } from '@aztec/stdlib/checkpoint';
import { type ProposedCheckpointInput, type PublishedCheckpoint, validateCheckpoint } from '@aztec/stdlib/checkpoint';
import {
type ContractClassPublicWithCommitment,
computeContractAddressFromInstance,
Expand Down Expand Up @@ -118,6 +118,15 @@ export class ArchiverDataStoreUpdater {
return result;
}

public async setProposedCheckpoint(proposedCheckpoint: ProposedCheckpointInput) {
const result = await this.store.transactionAsync(async () => {
await this.store.setProposedCheckpoint(proposedCheckpoint);
await this.l2TipsCache?.refresh();
});

return result;
}

/**
* Checks for local proposed blocks that do not match the ones to be checkpointed and prunes them.
* This method handles multiple checkpoints but returns after pruning the first conflict found.
Expand Down Expand Up @@ -211,6 +220,10 @@ export class ArchiverDataStoreUpdater {
}

const result = await this.removeBlocksAfter(blockNumber);

// Clear the proposed checkpoint if it exists, since its blocks have been pruned
await this.store.deleteProposedCheckpoint();

await this.l2TipsCache?.refresh();
return result;
});
Expand Down
37 changes: 20 additions & 17 deletions yarn-project/archiver/src/modules/l1_synchronizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -269,29 +269,32 @@ export class ArchiverL1Synchronizer implements Traceable {
return;
}

// What's the slot of the first uncheckpointed block?
// What's the slot at the next L1 block? All blocks for slots strictly before this one should've been checkpointed by now.
const slotAtNextL1Block = getSlotAtNextL1Block(currentL1Timestamp, this.l1Constants);
const firstUncheckpointedBlockNumber = BlockNumber(lastCheckpointedBlockNumber + 1);

// What's the slot of the first uncheckpointed block?
const [firstUncheckpointedBlockHeader] = await this.store.getBlockHeaders(firstUncheckpointedBlockNumber, 1);
const firstUncheckpointedBlockSlot = firstUncheckpointedBlockHeader?.getSlot();

// What's the slot at the next L1 block? All blocks for slots strictly before this one should've been checkpointed by now.
const slotAtNextL1Block = getSlotAtNextL1Block(currentL1Timestamp, this.l1Constants);
if (firstUncheckpointedBlockSlot === undefined || firstUncheckpointedBlockSlot >= slotAtNextL1Block) {
return;
}

// Prune provisional blocks from slots that have ended without being checkpointed
if (firstUncheckpointedBlockSlot !== undefined && firstUncheckpointedBlockSlot < slotAtNextL1Block) {
this.log.warn(
`Pruning blocks after block ${lastCheckpointedBlockNumber} due to slot ${firstUncheckpointedBlockSlot} not being checkpointed`,
{ firstUncheckpointedBlockHeader: firstUncheckpointedBlockHeader.toInspect(), slotAtNextL1Block },
);
const prunedBlocks = await this.updater.removeUncheckpointedBlocksAfter(lastCheckpointedBlockNumber);
// Prune provisional blocks from slots that have ended without being checkpointed.
// This also clears any proposed checkpoint whose blocks are being pruned.
this.log.warn(
`Pruning blocks after block ${lastCheckpointedBlockNumber} due to slot ${firstUncheckpointedBlockSlot} not being checkpointed`,
{ firstUncheckpointedBlockHeader: firstUncheckpointedBlockHeader.toInspect(), slotAtNextL1Block },
);
const prunedBlocks = await this.updater.removeUncheckpointedBlocksAfter(lastCheckpointedBlockNumber);

if (prunedBlocks.length > 0) {
this.events.emit(L2BlockSourceEvents.L2PruneUncheckpointed, {
type: L2BlockSourceEvents.L2PruneUncheckpointed,
slotNumber: firstUncheckpointedBlockSlot,
blocks: prunedBlocks,
});
}
if (prunedBlocks.length > 0) {
this.events.emit(L2BlockSourceEvents.L2PruneUncheckpointed, {
type: L2BlockSourceEvents.L2PruneUncheckpointed,
slotNumber: firstUncheckpointedBlockSlot,
blocks: prunedBlocks,
});
}
}

Expand Down
4 changes: 2 additions & 2 deletions yarn-project/archiver/src/modules/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
getAttestationInfoFromPayload,
} from '@aztec/stdlib/block';
import type { PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
import { type L1RollupConstants, getEpochAtSlot } from '@aztec/stdlib/epoch-helpers';
import { type L1RollupConstants, computeQuorum, getEpochAtSlot } from '@aztec/stdlib/epoch-helpers';
import { ConsensusPayload } from '@aztec/stdlib/p2p';

export type { ValidateCheckpointResult };
Expand Down Expand Up @@ -66,7 +66,7 @@ export async function validateCheckpointAttestations(
return { valid: true };
}

const requiredAttestationCount = Math.floor((committee.length * 2) / 3) + 1;
const requiredAttestationCount = computeQuorum(committee.length);

const failedValidationResult = <TReason extends ValidateCheckpointNegativeResult['reason']>(reason: TReason) => ({
valid: false as const,
Expand Down
Loading
Loading