Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
c9061f5
chore: Accumulated backports to v4 (#21651)
AztecBot Mar 17, 2026
21ef7e1
fix: HA deadlock for last block edge case (#21690)
spypsy Mar 17, 2026
be9b609
fix: process all contract classes in storeBroadcastedIndividualFuncti…
PhilWindle Mar 17, 2026
bb3ea6f
chore: Accumulated backports to v4 (#21694)
ludamad Mar 17, 2026
c48ee00
cherry-pick: fix: only delete logs from rolled-back blocks, not entir…
PhilWindle Mar 18, 2026
c2a952c
fix: resolve cherry-pick conflicts - adapt addProposedBlock to v4 add…
AztecBot Mar 18, 2026
445d6b8
fix: backport #21687 — only delete logs from rolled-back blocks, not …
ludamad Mar 18, 2026
129851b
fix: align testnet slashing penalties with L1 slash amounts (#21722)
AztecBot Mar 18, 2026
0e22b9d
Merge branch 'v4' into backport-to-v4-staging
ludamad Mar 18, 2026
408ff4d
chore: Backport to v4 staging (#21714)
aminsammara Mar 18, 2026
e47e4a3
feat(p2p): add tx validator for contract instance deployment addresse…
PhilWindle Mar 19, 2026
93ebc93
feat(archiver): validate contract instance addresses before storing (…
PhilWindle Mar 19, 2026
7d03c44
chore: Accumulated backports to v4 (#21783)
AztecBot Mar 19, 2026
daff36a
chore: Properly compute finalized block (#21156) (#21795)
spypsy Mar 20, 2026
9f1ff27
chore: Properly compute finalized block (#21156) (#21795) (#21850)
PhilWindle Mar 20, 2026
30efbfe
fix: verify accumulated pairing points in native ChonkVerifier
ludamad Mar 24, 2026
bef541f
fix: verify accumulated pairing points in native ChonkVerifier (v4 ba…
ludamad Mar 24, 2026
baea3af
merge: v4 into backport-to-v4-next-staging (with conflicts)
AztecBot Mar 25, 2026
34289b5
fix: resolve merge conflicts from v4 into backport-to-v4-next-staging
AztecBot Mar 25, 2026
53242ff
fix: run nargo fmt on noir-projects to fix formatter check
AztecBot Mar 25, 2026
7a09552
chore: retrigger CI
AztecBot Mar 25, 2026
eeed7bd
fix: resolve merge conflict in initialization_utils.nr
AztecBot Mar 26, 2026
3a5f88e
fix: remove duplicate resolveBlockNumber method from merge
AztecBot Mar 26, 2026
311b34a
fix: remove unused imports and fix formatting from merge
AztecBot Mar 26, 2026
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: 6 additions & 0 deletions barretenberg/cpp/src/barretenberg/chonk/chonk_verifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ template <> ChonkVerifier<false>::Output ChonkVerifier<false>::verify(const Proo
HidingKernelIO kernel_io;
kernel_io.reconstruct_from_public(verifier.get_public_inputs());

// Check accumulated pairing points from the IVC chain (inner recursive verifications)
if (!kernel_io.pairing_inputs.check()) {
info("ChonkVerifier: verification failed at PI pairing points check");
return false;
}

// Step 2: Perform databus consistency check
const Commitment calldata_commitment = verifier.get_calldata_commitment();
const Commitment return_data_commitment = kernel_io.kernel_return_data;
Expand Down
6 changes: 6 additions & 0 deletions barretenberg/rust/bootstrap.sh
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,12 @@ function release {
(cd ../ts && yarn generate)
fi

# Check if this version is already published on crates.io (idempotent re-runs).
if curl -sf -H "User-Agent: aztec-packages-ci (tech@aztec-labs.com)" "https://crates.io/api/v1/crates/barretenberg-rs/$version" | jq -e '.version.num' &>/dev/null; then
echo "barretenberg-rs@$version already published on crates.io. Skipping."
return 0
fi

# Publish to crates.io (--allow-dirty because version was just set and generated files are gitignored)
local extra_flags=""
if ! gh release view "v$version" --repo AztecProtocol/aztec-packages &>/dev/null; then
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,8 @@ pub fn assert_is_initialized_private(context: &mut PrivateContext) {
/// Asserts that the contract has been initialized, from a utility function's perspective.
///
/// Only checks the private initialization nullifier in the settled nullifier tree. Since both nullifiers are emitted
/// in
/// the same transaction, the private nullifier's presence in settled state guarantees the public one is also settled.
/// in the same transaction, the private nullifier's presence in settled state guarantees the public one is also
/// settled.
pub unconstrained fn assert_is_initialized_utility(context: UtilityContext) {
let address = context.this_address();
let instance = get_contract_instance(address);
Expand Down
26 changes: 13 additions & 13 deletions spartan/environments/network-defaults.yml
Original file line number Diff line number Diff line change
Expand Up @@ -280,19 +280,19 @@ networks:
# P2P
P2P_MAX_PENDING_TX_COUNT: 1000
P2P_TX_POOL_DELETE_TXS_AFTER_REORG: true
# Slasher penalties
SLASH_PRUNE_PENALTY: 10e18
SLASH_DATA_WITHHOLDING_PENALTY: 10e18
SLASH_INACTIVITY_TARGET_PERCENTAGE: 0.9
SLASH_INACTIVITY_CONSECUTIVE_EPOCH_THRESHOLD: 1
SLASH_INACTIVITY_PENALTY: 10e18
SLASH_PROPOSE_INVALID_ATTESTATIONS_PENALTY: 10e18
SLASH_DUPLICATE_PROPOSAL_PENALTY: 10e18
SLASH_DUPLICATE_ATTESTATION_PENALTY: 10e18
SLASH_ATTEST_DESCENDANT_OF_INVALID_PENALTY: 10e18
SLASH_UNKNOWN_PENALTY: 10e18
SLASH_INVALID_BLOCK_PENALTY: 10e18
SLASH_GRACE_PERIOD_L2_SLOTS: 64
# Slasher penalties — must be >= AZTEC_SLASH_AMOUNT_SMALL (100000e18) to trigger votes
SLASH_PRUNE_PENALTY: 0
SLASH_DATA_WITHHOLDING_PENALTY: 0
SLASH_INACTIVITY_TARGET_PERCENTAGE: 0.7
SLASH_INACTIVITY_CONSECUTIVE_EPOCH_THRESHOLD: 2
SLASH_INACTIVITY_PENALTY: 100000e18
SLASH_PROPOSE_INVALID_ATTESTATIONS_PENALTY: 100000e18
SLASH_DUPLICATE_PROPOSAL_PENALTY: 0
SLASH_DUPLICATE_ATTESTATION_PENALTY: 0
SLASH_ATTEST_DESCENDANT_OF_INVALID_PENALTY: 100000e18
SLASH_UNKNOWN_PENALTY: 100000e18
SLASH_INVALID_BLOCK_PENALTY: 100000e18
SLASH_GRACE_PERIOD_L2_SLOTS: 3600
ENABLE_VERSION_CHECK: true

mainnet:
Expand Down
4 changes: 1 addition & 3 deletions yarn-project/archiver/src/archiver-misc.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,7 @@ describe('Archiver misc', () => {

const tracer = getTelemetryClient().getTracer('');
const instrumentation = mock<ArchiverInstrumentation>({ isEnabled: () => true, tracer });
const archiverStore = new KVArchiverDataStore(await openTmpStore('archiver_misc_test'), 1000, {
epochDuration: EPOCH_DURATION,
});
const archiverStore = new KVArchiverDataStore(await openTmpStore('archiver_misc_test'), 1000);
const events = new EventEmitter() as ArchiverEmitter;
const l2TipsCache = new L2TipsCache(archiverStore.blockStore);

Expand Down
44 changes: 43 additions & 1 deletion yarn-project/archiver/src/archiver-store.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ describe('Archiver Store', () => {
const tracer = getTelemetryClient().getTracer('');
instrumentation = mock<ArchiverInstrumentation>({ isEnabled: () => true, tracer });

archiverStore = new KVArchiverDataStore(await openTmpStore('archiver_test'), 1000, { epochDuration: 4 });
archiverStore = new KVArchiverDataStore(await openTmpStore('archiver_test'), 1000);

l1Constants = {
l1GenesisTime: BigInt(now),
Expand Down Expand Up @@ -544,5 +544,47 @@ describe('Archiver Store', () => {
expect(await archiver.getSynchedCheckpointNumber()).toEqual(CheckpointNumber(2));
expect(await archiver.getProvenCheckpointNumber()).toEqual(CheckpointNumber(1));
});

it('rolls back finalized checkpoint number when target is before finalized block', async () => {
const genesisArchive = new AppendOnlyTreeSnapshot(new Fr(GENESIS_ARCHIVE_ROOT), 1);
// Checkpoint 1: blocks 1-2, Checkpoint 2: blocks 3-4, Checkpoint 3: blocks 5-6
const testCheckpoints = await makeChainedCheckpoints(3, {
previousArchive: genesisArchive,
blocksPerCheckpoint: 2,
});
await archiverStore.addCheckpoints(testCheckpoints);

// Mark checkpoints 1 and 2 as proven and finalized
await archiverStore.setProvenCheckpointNumber(CheckpointNumber(2));
await archiverStore.setFinalizedCheckpointNumber(CheckpointNumber(2));
expect(await archiver.getFinalizedL2BlockNumber()).toEqual(BlockNumber(4));

// Roll back to block 2 (end of checkpoint 1), which is before finalized block 4
await archiver.rollbackTo(BlockNumber(2));

expect(await archiver.getSynchedCheckpointNumber()).toEqual(CheckpointNumber(1));
expect(await archiver.getFinalizedL2BlockNumber()).toEqual(BlockNumber(2));
});

it('preserves finalized checkpoint number when target is after finalized block', async () => {
const genesisArchive = new AppendOnlyTreeSnapshot(new Fr(GENESIS_ARCHIVE_ROOT), 1);
// Checkpoint 1: blocks 1-2, Checkpoint 2: blocks 3-4, Checkpoint 3: blocks 5-6
const testCheckpoints = await makeChainedCheckpoints(3, {
previousArchive: genesisArchive,
blocksPerCheckpoint: 2,
});
await archiverStore.addCheckpoints(testCheckpoints);

// Mark checkpoint 1 as finalized, checkpoint 2 as proven
await archiverStore.setProvenCheckpointNumber(CheckpointNumber(2));
await archiverStore.setFinalizedCheckpointNumber(CheckpointNumber(1));
expect(await archiver.getFinalizedL2BlockNumber()).toEqual(BlockNumber(2));

// Roll back to block 4 (end of checkpoint 2), which is after finalized block 2
await archiver.rollbackTo(BlockNumber(4));

expect(await archiver.getSynchedCheckpointNumber()).toEqual(CheckpointNumber(2));
expect(await archiver.getFinalizedL2BlockNumber()).toEqual(BlockNumber(2));
});
});
});
67 changes: 66 additions & 1 deletion yarn-project/archiver/src/archiver-sync.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ describe('Archiver Sync', () => {
instrumentation = mock<ArchiverInstrumentation>({ isEnabled: () => true, tracer });

// Create archiver store
archiverStore = new KVArchiverDataStore(await openTmpStore('archiver_sync_test'), 1000, { epochDuration: 32 });
archiverStore = new KVArchiverDataStore(await openTmpStore('archiver_sync_test'), 1000);

const contractAddresses = {
registryAddress,
Expand Down Expand Up @@ -1225,6 +1225,71 @@ describe('Archiver Sync', () => {
}, 15_000);
});

describe('finalized checkpoint', () => {
it('reports no finalized blocks before any checkpoint is proven', async () => {
fake.setL1BlockNumber(100n);
fake.setFinalizedL1BlockNumber(100n);
await archiver.syncImmediate();

const tips = await archiver.getL2Tips();
expect(tips.finalized.checkpoint.number).toEqual(CheckpointNumber(0));
expect(tips.finalized.block.number).toEqual(BlockNumber(0));
});

it('updates finalized checkpoint when the L1 finalized block is at or past the proven checkpoint L1 block', async () => {
const { checkpoint: cp1 } = await fake.addCheckpoint(CheckpointNumber(1), {
l1BlockNumber: 70n,
messagesL1BlockNumber: 50n,
numL1ToL2Messages: 3,
});

// Sync all checkpoints
fake.setL1BlockNumber(100n);
await archiver.syncImmediate();

// Mark checkpoint 1 as proven and advance L1 so proven is registered
fake.markCheckpointAsProven(CheckpointNumber(1));
fake.setL1BlockNumber(101n);
await archiver.syncImmediate();
expect(await archiver.getProvenCheckpointNumber()).toEqual(CheckpointNumber(1));

// Finalized L1 block is at or past where checkpoint 1 was published (70)
fake.setFinalizedL1BlockNumber(70n);
fake.setL1BlockNumber(102n);
await archiver.syncImmediate();

const tips = await archiver.getL2Tips();
const lastBlockInCp1 = cp1.blocks.at(-1)!.number;
expect(tips.finalized.checkpoint.number).toEqual(CheckpointNumber(1));
expect(tips.finalized.block.number).toEqual(lastBlockInCp1);
});

it('does not advance finalized checkpoint when finalized L1 block is before the proven checkpoint', async () => {
await fake.addCheckpoint(CheckpointNumber(1), {
l1BlockNumber: 70n,
messagesL1BlockNumber: 50n,
numL1ToL2Messages: 3,
});

fake.setL1BlockNumber(100n);
await archiver.syncImmediate();

fake.markCheckpointAsProven(CheckpointNumber(1));
fake.setL1BlockNumber(101n);
await archiver.syncImmediate();
expect(await archiver.getProvenCheckpointNumber()).toEqual(CheckpointNumber(1));

// Finalized L1 block is before where checkpoint 1 was published (70)
fake.setFinalizedL1BlockNumber(50n);
fake.setL1BlockNumber(102n);
await archiver.syncImmediate();

const tips = await archiver.getL2Tips();
expect(tips.finalized.checkpoint.number).toEqual(CheckpointNumber(0));
expect(tips.finalized.block.number).toEqual(BlockNumber(0));
});
});

describe('checkpointing local proposed blocks', () => {
let pruneSpy: jest.Mock;

Expand Down
11 changes: 5 additions & 6 deletions yarn-project/archiver/src/archiver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -492,11 +492,10 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
this.log.info(`Rolling back proven L2 checkpoint to ${targetCheckpointNumber}`);
await this.updater.setProvenCheckpointNumber(targetCheckpointNumber);
}
// TODO(palla/reorg): Set the finalized block when we add support for it.
// const currentFinalizedBlock = currentBlocks.finalized.block.number;
// if (targetL2BlockNumber < currentFinalizedBlock) {
// this.log.info(`Rolling back finalized L2 checkpoint to ${targetCheckpointNumber}`);
// await this.updater.setFinalizedCheckpointNumber(targetCheckpointNumber);
// }
const currentFinalizedBlock = currentBlocks.finalized.block.number;
if (targetL2BlockNumber < currentFinalizedBlock) {
this.log.info(`Rolling back finalized L2 checkpoint to ${targetCheckpointNumber}`);
await this.updater.setFinalizedCheckpointNumber(targetCheckpointNumber);
}
}
}
6 changes: 2 additions & 4 deletions yarn-project/archiver/src/factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import { BundledProtocolContractsProvider } from '@aztec/protocol-contracts/prov
import { FunctionType, decodeFunctionSignature } from '@aztec/stdlib/abi';
import type { ArchiverEmitter } from '@aztec/stdlib/block';
import { type ContractClassPublicWithCommitment, computePublicBytecodeCommitment } from '@aztec/stdlib/contract';
import type { L1RollupConstants } from '@aztec/stdlib/epoch-helpers';
import { getTelemetryClient } from '@aztec/telemetry-client';

import { EventEmitter } from 'events';
Expand All @@ -33,14 +32,13 @@ export const ARCHIVER_STORE_NAME = 'archiver';
/** Creates an archiver store. */
export async function createArchiverStore(
userConfig: Pick<ArchiverConfig, 'archiverStoreMapSizeKb' | 'maxLogs'> & DataStoreConfig,
l1Constants: Pick<L1RollupConstants, 'epochDuration'>,
) {
const config = {
...userConfig,
dataStoreMapSizeKb: userConfig.archiverStoreMapSizeKb ?? userConfig.dataStoreMapSizeKb,
};
const store = await createStore(ARCHIVER_STORE_NAME, ARCHIVER_DB_VERSION, config);
return new KVArchiverDataStore(store, config.maxLogs, l1Constants);
return new KVArchiverDataStore(store, config.maxLogs);
}

/**
Expand All @@ -55,7 +53,7 @@ export async function createArchiver(
deps: ArchiverDeps,
opts: { blockUntilSync: boolean } = { blockUntilSync: true },
): Promise<Archiver> {
const archiverStore = await createArchiverStore(config, { epochDuration: config.aztecEpochDuration });
const archiverStore = await createArchiverStore(config);
await registerProtocolContracts(archiverStore);

// Create Ethereum clients
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ describe('ArchiverDataStoreUpdater', () => {
let instanceAddress: AztecAddress;

beforeEach(async () => {
store = new KVArchiverDataStore(await openTmpStore('data_store_updater_test'), 1000, { epochDuration: 32 });
store = new KVArchiverDataStore(await openTmpStore('data_store_updater_test'), 1000);
updater = new ArchiverDataStoreUpdater(store);

// Create contract class log from sample fixture data
Expand Down
32 changes: 31 additions & 1 deletion yarn-project/archiver/src/modules/data_store_updater.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { BlockNumber, CheckpointNumber } from '@aztec/foundation/branded-types';
import { filterAsync } from '@aztec/foundation/collection';
import { Fr } from '@aztec/foundation/curves/bn254';
import { createLogger } from '@aztec/foundation/log';
import {
Expand All @@ -16,6 +17,7 @@ import {
type ContractClassPublicWithCommitment,
type ExecutablePrivateFunctionWithMembershipProof,
type UtilityFunctionWithMembershipProof,
computeContractAddressFromInstance,
computeContractClassId,
isValidPrivateFunctionMembershipProof,
isValidUtilityFunctionMembershipProof,
Expand Down Expand Up @@ -282,6 +284,17 @@ export class ArchiverDataStoreUpdater {
});
}

/**
* Updates the finalized checkpoint number and refreshes the L2 tips cache.
* @param checkpointNumber - The checkpoint number to set as finalized.
*/
public async setFinalizedCheckpointNumber(checkpointNumber: CheckpointNumber): Promise<void> {
await this.store.transactionAsync(async () => {
await this.store.setFinalizedCheckpointNumber(checkpointNumber);
await this.l2TipsCache?.refresh();
});
}

/** Extracts and stores contract data from a single block. */
private addContractDataToDb(block: L2Block): Promise<boolean> {
return this.updateContractDataOnDb(block, Operation.Store);
Expand Down Expand Up @@ -365,10 +378,27 @@ export class ArchiverDataStoreUpdater {
blockNum: BlockNumber,
operation: Operation,
): Promise<boolean> {
const contractInstances = allLogs
const allInstances = allLogs
.filter(log => ContractInstancePublishedEvent.isContractInstancePublishedEvent(log))
.map(log => ContractInstancePublishedEvent.fromLog(log))
.map(e => e.toContractInstance());

// Verify that each instance's address matches the one derived from its fields if we're adding
const contractInstances =
operation === Operation.Delete
? allInstances
: await filterAsync(allInstances, async instance => {
const computedAddress = await computeContractAddressFromInstance(instance);
if (!computedAddress.equals(instance.address)) {
this.log.warn(
`Found contract instance with mismatched address at block ${blockNum}. Claimed ${instance.address} but computed ${computedAddress}.`,
{ instanceAddress: instance.address.toString(), computedAddress: computedAddress.toString(), blockNum },
);
return false;
}
return true;
});

if (contractInstances.length > 0) {
contractInstances.forEach(c =>
this.log.verbose(`${Operation[operation]} contract instance at ${c.address.toString()}`),
Expand Down
30 changes: 30 additions & 0 deletions yarn-project/archiver/src/modules/l1_synchronizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,9 @@ export class ArchiverL1Synchronizer implements Traceable {
this.instrumentation.updateL1BlockHeight(currentL1BlockNumber);
}

// Update the finalized L2 checkpoint based on L1 finality.
await this.updateFinalizedCheckpoint();

// After syncing has completed, update the current l1 block number and timestamp,
// otherwise we risk announcing to the world that we've synced to a given point,
// but the corresponding blocks have not been processed (see #12631).
Expand All @@ -231,6 +234,33 @@ export class ArchiverL1Synchronizer implements Traceable {
});
}

/** Query L1 for its finalized block and update the finalized checkpoint accordingly. */
private async updateFinalizedCheckpoint(): Promise<void> {
try {
const finalizedL1Block = await this.publicClient.getBlock({ blockTag: 'finalized', includeTransactions: false });
const finalizedL1BlockNumber = finalizedL1Block.number;
const finalizedCheckpointNumber = await this.rollup.getProvenCheckpointNumber({
blockNumber: finalizedL1BlockNumber,
});
const localFinalizedCheckpointNumber = await this.store.getFinalizedCheckpointNumber();
if (localFinalizedCheckpointNumber !== finalizedCheckpointNumber) {
await this.updater.setFinalizedCheckpointNumber(finalizedCheckpointNumber);
const finalizedL2BlockNumber = await this.store.getFinalizedL2BlockNumber();
this.log.info(
`Updated finalized chain to checkpoint ${finalizedCheckpointNumber} (L2 block ${finalizedL2BlockNumber})`,
{
finalizedCheckpointNumber,
previousFinalizedCheckpointNumber: localFinalizedCheckpointNumber,
finalizedL2BlockNumber,
finalizedL1BlockNumber,
},
);
}
} catch (err) {
this.log.warn(`Failed to update finalized checkpoint: ${err}`);
}
}

/** Prune all proposed local blocks that should have been checkpointed by now. */
private async pruneUncheckpointedBlocks(currentL1Timestamp: bigint) {
const [lastCheckpointedBlockNumber, lastProposedBlockNumber] = await Promise.all([
Expand Down
Loading
Loading