Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
b428973
refactor(stdlib)!: thin chain-checkpointed event, collapse sync (#24007)
spalladino Jun 12, 2026
45ea78d
refactor!: remove proposedCheckpoint tip (#24008)
spalladino Jun 12, 2026
49c510a
fix(sequencer): wait for previous L1 block before publishing (#24037)
spalladino Jun 12, 2026
15f769d
test(e2e_fees): bridge fee juice from a dedicated L1 account (#24054)
spalladino Jun 12, 2026
96d47a0
fix(aztec-node): pipelining-aware slot and fee simulation in simulate…
spalladino Jun 12, 2026
d979a26
refactor: move registerContractFunctionSignatures to the node debug A…
AztecBot Jun 12, 2026
4e1a165
chore: merge v5-next into merge-train/spartan-v5 (raw, conflict markers)
AztecBot Jun 14, 2026
f89fa28
fix: resolve v5-next → merge-train/spartan-v5 conflict (#24072)
AztecBot Jun 14, 2026
c38271a
chore: merge v5-next into merge-train/spartan-v5 (raw, conflict marke…
PhilWindle Jun 14, 2026
103dc95
test(ci): mark e2e_epochs/epochs_mbps_redistribution as flaky (#24098)
PhilWindle Jun 15, 2026
092c30f
fix: isolate cross-chain L1 writes from publisher nonce (#24104)
AztecBot Jun 15, 2026
b5830ee
test(world-state): make delayed-close fork queue-cleanup wait determi…
AztecBot Jun 15, 2026
fbfbca2
Merge branch 'v5-next' into merge-train/spartan-v5
Jun 16, 2026
93d3d0a
fix(kv-store): lazy-load skipped browser benchmark deps (#24108)
AztecBot Jun 16, 2026
18ee7ec
Merge branch 'v5-next' into merge-train/spartan-v5
Jun 16, 2026
d9463da
feat(p2p): slash proposers exceeding max blocks per checkpoint (A-116…
spalladino Jun 16, 2026
4d11f2c
fix(stdlib): fix race conditions in L2BlockStream (#24042)
spalladino Jun 16, 2026
566d37a
test(prover-node): make checkpoint store pruning test deterministic (…
AztecBot Jun 16, 2026
b7a5970
Merge branch 'v5-next' into merge-train/spartan-v5
Jun 16, 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
7 changes: 7 additions & 0 deletions .test_patterns.yml
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,13 @@ tests:
owners:
- *sean

# Asserts all late txs land in the checkpoint's last block, but the proposer seals the last block as
# soon as one tx is available and snapshots the mempool, so late txs still propagating over gossip
# spill into the next checkpoint. Flaky until the sequencer waits for the last block's tx window.
- regex: "src/e2e_epochs/epochs_mbps_redistribution.test.ts"
owners:
- *phil

# Blanket flake patterns for unstable p2p, epoch, and l1 tx utils tests
# This is a temporary measure while team bandwidth is constrained
# Replaced many specific patterns - see https://github.com/AztecProtocol/aztec-packages/pull/17962 for historical context
Expand Down
6 changes: 6 additions & 0 deletions docs/docs-developers/docs/resources/migration_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,12 @@ All Aztec node JSON-RPC method prefixes have changed:
If you call the node RPC directly (e.g. via `curl` or a custom client), update all method names accordingly.
Clients created via `createAztecNodeClient`, `createAztecNodeAdminClient`, and `createAztecNodeDebugClient` are updated automatically.

### [Node RPC] `registerContractFunctionSignatures` moved to the debug API

`registerContractFunctionSignatures` is no longer part of the main node JSON-RPC API (`aztec_` namespace). It is now a debug-only method exposed under the `aztecDebug_` namespace, which is only mounted when the node runs with debug endpoints enabled (`--node-debug`, always on in the in-process sandbox). This removes an unauthenticated write to node memory from prod-like nodes.

Clients that registered public function signatures over `aztec_registerContractFunctionSignatures` should call `aztecDebug_registerContractFunctionSignatures` against a debug-enabled node instead. In the PXE, this is now driven by an optional debug client: pass a `nodeDebug` client (e.g. `createAztecNodeDebugClient(nodeUrl)`) when creating the PXE to keep named public-execution traces; when it is absent, signature registration is skipped. Client-side error enrichment from the contract ABI is unaffected.

### [Aztec.nr] `get_pending_tagged_logs` oracle interface updated (oracle version 28)

The `aztec_utl_getPendingTaggedLogs` oracle now takes an additional `provided_secrets` parameter of type `EphemeralArray<ProvidedSecret>`. This lets apps pass tagging secrets that PXE cannot derive on its own (e.g. handshake-derived secrets) alongside the secrets PXE manages internally.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -606,7 +606,7 @@ const METHOD_GROUPS: { heading: string; namespace: string; methods: string[] }[]
{
heading: 'Debug operations',
namespace: 'aztec',
methods: ['registerContractFunctionSignatures', 'getAllowedPublicSetup'],
methods: ['getAllowedPublicSetup'],
},
{
heading: 'Admin API',
Expand Down
5 changes: 1 addition & 4 deletions yarn-project/archiver/src/archiver-misc.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -210,16 +210,13 @@ describe('Archiver misc', () => {
describe('isPruneDueAtSlot', () => {
/**
* Builds a fake L2Tips. `pending` is the L1-confirmed pending checkpoint (= `tips.checkpointed`
* in production). `proposedCheckpoint` is set to `pending + 1` to catch any implementation that
* accidentally reads the local-optimistic proposed checkpoint instead of the L1-confirmed one.
* in production).
*/
function makeTips(pending: CheckpointNumber, proven: CheckpointNumber): L2Tips {
const block = { number: BlockNumber(0), hash: '0x' };
const tip = (n: CheckpointNumber) => ({ block, checkpoint: { number: n, hash: '0x' } });
const proposedAhead = CheckpointNumber(Number(pending) + 1);
return {
proposed: block,
proposedCheckpoint: tip(proposedAhead),
checkpointed: tip(pending),
proven: tip(proven),
finalized: tip(proven),
Expand Down
16 changes: 9 additions & 7 deletions yarn-project/archiver/src/archiver-sync.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2075,11 +2075,15 @@ describe('Archiver Sync', () => {
// Proposed checkpoint should still be set
expect(await archiverStore.blocks.getLastProposedCheckpoint()).toBeDefined();

// Proposed tip should be ahead of the checkpointed tip
// Proposed checkpoint should lead the checkpointed tip
const tips = await archiver.getL2Tips();
expect(tips.proposedCheckpoint.checkpoint.number).toEqual(CheckpointNumber(2));
const proposedCheckpointResult = await archiver.getProposedCheckpointData();
expect(proposedCheckpointResult).toBeDefined();
expect(proposedCheckpointResult!.checkpointNumber).toEqual(CheckpointNumber(2));
expect(tips.checkpointed.checkpoint.number).toEqual(CheckpointNumber(1));
expect(tips.proposedCheckpoint.block.number).toBeGreaterThan(tips.checkpointed.block.number);
const proposedCheckpointLastBlock =
proposedCheckpointResult!.startBlock + proposedCheckpointResult!.blockCount - 1;
expect(proposedCheckpointLastBlock).toBeGreaterThan(tips.checkpointed.block.number);
}, 15_000);

it('prunes blocks and clears stale pending checkpoint when slot ends', async () => {
Expand Down Expand Up @@ -2142,11 +2146,9 @@ describe('Archiver Sync', () => {
expect(await archiver.getBlockNumber()).toEqual(lastBlockInCheckpoint1);
expect(await archiver.getCheckpointNumber()).toEqual(CheckpointNumber(1));

// Proposed checkpoint should be cleared, so proposed tip falls back to checkpointed tip
// Proposed checkpoint should be cleared, so no proposed checkpoint leads the checkpointed tip
expect(await archiverStore.blocks.getLastProposedCheckpoint()).toBeUndefined();
const tips = await archiver.getL2Tips();
expect(tips.proposedCheckpoint.checkpoint.number).toEqual(tips.checkpointed.checkpoint.number);
expect(tips.proposedCheckpoint.block.number).toEqual(tips.checkpointed.block.number);
expect(await archiver.getProposedCheckpointData()).toBeUndefined();
}, 15_000);
});

Expand Down
20 changes: 12 additions & 8 deletions yarn-project/archiver/src/archiver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type { L1ContractAddresses } from '@aztec/ethereum/l1-contract-addresses'
import type { ViemPublicClient, ViemPublicDebugClient } from '@aztec/ethereum/types';
import { BlockNumber, CheckpointNumber, EpochNumber, SlotNumber } from '@aztec/foundation/branded-types';
import { Buffer32 } from '@aztec/foundation/buffer';
import { merge, pick } from '@aztec/foundation/collection';
import { merge } from '@aztec/foundation/collection';
import { Fr } from '@aztec/foundation/curves/bn254';
import { EthAddress } from '@aztec/foundation/eth-address';
import { type Logger, createLogger } from '@aztec/foundation/log';
Expand Down Expand Up @@ -435,19 +435,23 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
const tips = await this.getL2Tips();
const now = BigInt(this.dateProvider.nowInSeconds());

// Frontier block covered by a proposed (or, falling back, confirmed) checkpoint. Blocks beyond it
// have no enclosing checkpoint proposal and are the orphan-pruning candidates.
const proposedCheckpointBlockNumber = await this.stores.blocks.getProposedCheckpointL2BlockNumber();

// The proposed tip is a proposed-checkpointed block, so there are no orphan proposed blocks to prune
if (tips.proposedCheckpoint.block.number === tips.proposed.number) {
this.log.trace(
`No orphan proposed blocks to prune: proposed tip ${tips.proposed.number} is checkpointed`,
pick(tips, 'proposed', 'proposedCheckpoint'),
);
if (proposedCheckpointBlockNumber === tips.proposed.number) {
this.log.trace(`No orphan proposed blocks to prune: proposed tip ${tips.proposed.number} is checkpointed`, {
proposed: tips.proposed,
proposedCheckpointBlockNumber,
});
return;
}

// Load the blocks that are candidates for pruning (ie blocks without a proposed checkpoint covering them)
const blocksWithoutProposedCheckpoint = await this.stores.blocks.getBlocksData({
from: BlockNumber(tips.proposedCheckpoint.block.number + 1),
limit: tips.proposed.number - tips.proposedCheckpoint.block.number,
from: BlockNumber(proposedCheckpointBlockNumber + 1),
limit: tips.proposed.number - proposedCheckpointBlockNumber,
});

// Iterate through them in order, the first one with a slot that should have received a proposed checkpoint
Expand Down
35 changes: 14 additions & 21 deletions yarn-project/archiver/src/store/block_store.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import {
BlockHash,
CommitteeAttestation,
EthAddress,
GENESIS_BLOCK_HEADER_HASH,
L2Block,
type ValidateCheckpointResult,
} from '@aztec/stdlib/block';
Expand All @@ -40,7 +39,6 @@ import {
makeStateForBlock,
} from '../test/mock_structs.js';
import { BlockStore } from './block_store.js';
import { L2TipsCache } from './l2_tips_cache.js';

async function addProposedBlocks(
blockStore: BlockStore,
Expand Down Expand Up @@ -2627,26 +2625,19 @@ describe('BlockStore', () => {
});
});

describe('L2TipsCache proposedCheckpoint', () => {
it('returns proposedCheckpoint equal to checkpointed when no pending exists', async () => {
// Add checkpoint 1 with blocks 1-3
describe('getLastProposedCheckpoint', () => {
it('returns undefined when there is no pending checkpoint', async () => {
// Add checkpoint 1 with blocks 1-3; confirmation deletes any matching proposed entry.
const checkpoint1 = makePublishedCheckpoint(
await Checkpoint.random(CheckpointNumber(1), { numBlocks: 3, startBlockNumber: 1 }),
10,
);
await blockStore.addCheckpoints([checkpoint1]);

const l2TipsCache = new L2TipsCache(blockStore, GENESIS_BLOCK_HEADER_HASH);
const tips = await l2TipsCache.getL2Tips();

// proposedCheckpoint should always be defined
expect(tips.proposedCheckpoint).toBeDefined();
// With no proposed checkpoint, it should equal the checkpointed tip
expect(tips.proposedCheckpoint!.block.number).toBe(tips.checkpointed.block.number);
expect(tips.proposedCheckpoint!.checkpoint.number).toBe(tips.checkpointed.checkpoint.number);
expect(await blockStore.getLastProposedCheckpoint()).toBeUndefined();
});

it('returns proposedCheckpoint ahead of checkpointed when pending is set', async () => {
it('returns the leading proposed checkpoint payload', async () => {
// Add checkpoint 1
const checkpoint1 = makePublishedCheckpoint(
await Checkpoint.random(CheckpointNumber(1), { numBlocks: 1, startBlockNumber: 1 }),
Expand All @@ -2663,21 +2654,23 @@ describe('BlockStore', () => {
await blockStore.addProposedBlock(block2, { force: true });

// Set proposed checkpoint
const header = CheckpointHeader.empty();
await blockStore.addProposedCheckpoint({
checkpointNumber: CheckpointNumber(2),
header: CheckpointHeader.empty(),
header,
startBlock: BlockNumber(2),
blockCount: 1,
totalManaUsed: 100n,
feeAssetPriceModifier: 50n,
});

const l2TipsCache = new L2TipsCache(blockStore, GENESIS_BLOCK_HEADER_HASH);
const tips = await l2TipsCache.getL2Tips();

expect(tips.proposedCheckpoint).toBeDefined();
expect(tips.proposedCheckpoint!.block.number).toBeGreaterThan(tips.checkpointed.block.number);
expect(tips.proposedCheckpoint!.checkpoint.number).toBeGreaterThan(tips.checkpointed.checkpoint.number);
const proposedCheckpoint = await blockStore.getLastProposedCheckpoint();
expect(proposedCheckpoint).toBeDefined();
// Callers derive the tip from the payload: last block = startBlock + blockCount - 1.
expect(proposedCheckpoint!.checkpointNumber).toBe(CheckpointNumber(2));
expect(proposedCheckpoint!.startBlock).toBe(BlockNumber(2));
expect(proposedCheckpoint!.blockCount).toBe(1);
expect(proposedCheckpoint!.totalManaUsed).toBe(100n);
});
});

Expand Down
37 changes: 11 additions & 26 deletions yarn-project/archiver/src/store/block_store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1167,14 +1167,13 @@ export class BlockStore {
}

/**
* Resolves all five L2 chain tips (proposed, proposedCheckpoint, checkpointed, proven, finalized)
* in a single read-only transaction so the snapshot is internally consistent. Each underlying
* record is read at most once: latest block, latest confirmed checkpoint, and latest pending
* checkpoint are each loaded directly (no separate "find the number, then look up data" hop),
* the proven/finalized checkpoint singletons are read once and their storage entries are
* reused if they coincide with the latest checkpoint, and per-tip block hashes are deduped
* when two tips land on the same block (e.g. finalized == proven, or proposedCheckpoint falls
* back to checkpointed when no pending checkpoint exists).
* Resolves all four L2 chain tips (proposed, checkpointed, proven, finalized) in a single
* read-only transaction so the snapshot is internally consistent. Each underlying record is
* read at most once: latest block and latest confirmed checkpoint are loaded directly (no
* separate "find the number, then look up data" hop), the proven/finalized checkpoint
* singletons are read once and their storage entries are reused if they coincide with the
* latest checkpoint, and per-tip block hashes are deduped when two tips land on the same block
* (e.g. finalized == proven).
*
* The result is guaranteed to satisfy `finalized <= proven <= checkpointed <= proposed` (by
* block number). Genesis is represented by `(INITIAL_L2_BLOCK_NUM - 1)` and the supplied
Expand All @@ -1197,9 +1196,6 @@ export class BlockStore {

// Load latest block and checkpoint entries
const [latestBlockEntry] = await toArray(this.#blocks.entriesAsync({ reverse: true, limit: 1 }));
const [proposedCheckpointEntry] = await toArray(
this.#proposedCheckpoints.entriesAsync({ reverse: true, limit: 1 }),
);
const [latestCheckpointEntry] = await toArray(this.#checkpoints.entriesAsync({ reverse: true, limit: 1 }));
const latestCheckpointNumber = latestCheckpointEntry
? CheckpointNumber(latestCheckpointEntry[0])
Expand Down Expand Up @@ -1285,14 +1281,6 @@ export class BlockStore {
const provenTip = await buildTipFromCheckpoint(provenCheckpoint);
const finalizedTip = await buildTipFromCheckpoint(finalizedCheckpoint);

// Proposed checkpoint falls back to the checkpoint tip if it's not set. And if local storage is
// inconsistent and the proposed checkpoint is behind the checkpointed tip, we patch that and
// report the checkpointed tip as the proposed checkpoint to maintain the invariant.
const proposedCheckpointTip =
proposedCheckpointEntry === undefined || proposedCheckpointEntry[0] <= latestCheckpointNumber
? checkpointedTip
: await buildTipFromCheckpoint(proposedCheckpointEntry[1]);

// A checkpointed block past the latest stored block would mean a checkpoint
// references blocks that aren't in blocks.
if (proposedBlockId.number < checkpointedTip.block.number) {
Expand All @@ -1304,29 +1292,26 @@ export class BlockStore {
// Assert that checkpoint numbers are increasing
if (
finalizedTip.checkpoint.number > provenTip.checkpoint.number ||
provenTip.checkpoint.number > checkpointedTip.checkpoint.number ||
checkpointedTip.checkpoint.number > proposedCheckpointTip.checkpoint.number
provenTip.checkpoint.number > checkpointedTip.checkpoint.number
) {
throw new Error(
`Inconsistent checkpoint numbers in chain tips: finalized=${finalizedTip.checkpoint.number} proven=${provenTip.checkpoint.number} checkpointed=${checkpointedTip.checkpoint.number} proposed=${proposedCheckpointTip.checkpoint.number}`,
`Inconsistent checkpoint numbers in chain tips: finalized=${finalizedTip.checkpoint.number} proven=${provenTip.checkpoint.number} checkpointed=${checkpointedTip.checkpoint.number}`,
);
}

// Assert block numbers are increasing
if (
finalizedTip.block.number > provenTip.block.number ||
provenTip.block.number > checkpointedTip.block.number ||
checkpointedTip.block.number > proposedCheckpointTip.block.number ||
proposedCheckpointTip.block.number > proposedBlockId.number
checkpointedTip.block.number > proposedBlockId.number
) {
throw new Error(
`Inconsistent block numbers in chain tips: finalized=${finalizedTip.block.number} proven=${provenTip.block.number} checkpointed=${checkpointedTip.block.number} proposedCheckpoint=${proposedCheckpointTip.block.number} proposed=${proposedBlockId.number}`,
`Inconsistent block numbers in chain tips: finalized=${finalizedTip.block.number} proven=${provenTip.block.number} checkpointed=${checkpointedTip.block.number} proposed=${proposedBlockId.number}`,
);
}

return {
proposed: proposedBlockId,
proposedCheckpoint: proposedCheckpointTip,
checkpointed: checkpointedTip,
proven: provenTip,
finalized: finalizedTip,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ export class MockL1ToL2MessageSource implements L1ToL2MessageSource {
checkpointed: tip,
proven: tip,
finalized: tip,
proposedCheckpoint: tip,
});
}
}
Loading
Loading