Skip to content

Commit 5fd0d02

Browse files
authored
feat: merge-train/spartan-v5 (#24053)
BEGIN_COMMIT_OVERRIDE refactor(stdlib)!: thin chain-checkpointed event, collapse sync (#24007) refactor!: remove proposedCheckpoint tip (#24008) fix(sequencer): wait for previous L1 block before publishing (#24037) test(e2e_fees): bridge fee juice from a dedicated L1 account (#24054) fix(aztec-node): pipelining-aware slot and fee simulation in simulatePublicCalls (#24031) refactor: move registerContractFunctionSignatures to the node debug API (#24066) fix: resolve v5-next → merge-train/spartan-v5 conflict (#24072) chore: merge v5-next into merge-train/spartan-v5 (raw, conflict markers) (#24071) test(ci): mark e2e_epochs/epochs_mbps_redistribution as flaky (#24098) fix: isolate cross-chain L1 writes from publisher nonce (#24104) test(world-state): make delayed-close fork queue-cleanup wait deterministic (#24106) fix(kv-store): lazy-load skipped browser benchmark deps (#24108) feat(p2p): slash proposers exceeding max blocks per checkpoint (A-1166) (#24041) fix(stdlib): fix race conditions in L2BlockStream (#24042) test(prover-node): make checkpoint store pruning test deterministic (#24130) END_COMMIT_OVERRIDE
2 parents d074c17 + b7a5970 commit 5fd0d02

102 files changed

Lines changed: 4311 additions & 3088 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.test_patterns.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,13 @@ tests:
275275
owners:
276276
- *sean
277277

278+
# Asserts all late txs land in the checkpoint's last block, but the proposer seals the last block as
279+
# soon as one tx is available and snapshots the mempool, so late txs still propagating over gossip
280+
# spill into the next checkpoint. Flaky until the sequencer waits for the last block's tx window.
281+
- regex: "src/e2e_epochs/epochs_mbps_redistribution.test.ts"
282+
owners:
283+
- *phil
284+
278285
# Blanket flake patterns for unstable p2p, epoch, and l1 tx utils tests
279286
# This is a temporary measure while team bandwidth is constrained
280287
# Replaced many specific patterns - see https://github.com/AztecProtocol/aztec-packages/pull/17962 for historical context

docs/docs-developers/docs/resources/migration_notes.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,12 @@ All Aztec node JSON-RPC method prefixes have changed:
152152
If you call the node RPC directly (e.g. via `curl` or a custom client), update all method names accordingly.
153153
Clients created via `createAztecNodeClient`, `createAztecNodeAdminClient`, and `createAztecNodeDebugClient` are updated automatically.
154154

155+
### [Node RPC] `registerContractFunctionSignatures` moved to the debug API
156+
157+
`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.
158+
159+
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.
160+
155161
### [Aztec.nr] `get_pending_tagged_logs` oracle interface updated (oracle version 28)
156162

157163
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.

docs/scripts/node_api_reference_generation/generate_node_api_reference.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -606,7 +606,7 @@ const METHOD_GROUPS: { heading: string; namespace: string; methods: string[] }[]
606606
{
607607
heading: 'Debug operations',
608608
namespace: 'aztec',
609-
methods: ['registerContractFunctionSignatures', 'getAllowedPublicSetup'],
609+
methods: ['getAllowedPublicSetup'],
610610
},
611611
{
612612
heading: 'Admin API',

yarn-project/archiver/src/archiver-misc.test.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -210,16 +210,13 @@ describe('Archiver misc', () => {
210210
describe('isPruneDueAtSlot', () => {
211211
/**
212212
* Builds a fake L2Tips. `pending` is the L1-confirmed pending checkpoint (= `tips.checkpointed`
213-
* in production). `proposedCheckpoint` is set to `pending + 1` to catch any implementation that
214-
* accidentally reads the local-optimistic proposed checkpoint instead of the L1-confirmed one.
213+
* in production).
215214
*/
216215
function makeTips(pending: CheckpointNumber, proven: CheckpointNumber): L2Tips {
217216
const block = { number: BlockNumber(0), hash: '0x' };
218217
const tip = (n: CheckpointNumber) => ({ block, checkpoint: { number: n, hash: '0x' } });
219-
const proposedAhead = CheckpointNumber(Number(pending) + 1);
220218
return {
221219
proposed: block,
222-
proposedCheckpoint: tip(proposedAhead),
223220
checkpointed: tip(pending),
224221
proven: tip(proven),
225222
finalized: tip(proven),

yarn-project/archiver/src/archiver-sync.test.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2075,11 +2075,15 @@ describe('Archiver Sync', () => {
20752075
// Proposed checkpoint should still be set
20762076
expect(await archiverStore.blocks.getLastProposedCheckpoint()).toBeDefined();
20772077

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

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

2145-
// Proposed checkpoint should be cleared, so proposed tip falls back to checkpointed tip
2149+
// Proposed checkpoint should be cleared, so no proposed checkpoint leads the checkpointed tip
21462150
expect(await archiverStore.blocks.getLastProposedCheckpoint()).toBeUndefined();
2147-
const tips = await archiver.getL2Tips();
2148-
expect(tips.proposedCheckpoint.checkpoint.number).toEqual(tips.checkpointed.checkpoint.number);
2149-
expect(tips.proposedCheckpoint.block.number).toEqual(tips.checkpointed.block.number);
2151+
expect(await archiver.getProposedCheckpointData()).toBeUndefined();
21502152
}, 15_000);
21512153
});
21522154

yarn-project/archiver/src/archiver.ts

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import type { L1ContractAddresses } from '@aztec/ethereum/l1-contract-addresses'
55
import type { ViemPublicClient, ViemPublicDebugClient } from '@aztec/ethereum/types';
66
import { BlockNumber, CheckpointNumber, EpochNumber, SlotNumber } from '@aztec/foundation/branded-types';
77
import { Buffer32 } from '@aztec/foundation/buffer';
8-
import { merge, pick } from '@aztec/foundation/collection';
8+
import { merge } from '@aztec/foundation/collection';
99
import { Fr } from '@aztec/foundation/curves/bn254';
1010
import { EthAddress } from '@aztec/foundation/eth-address';
1111
import { type Logger, createLogger } from '@aztec/foundation/log';
@@ -435,19 +435,23 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
435435
const tips = await this.getL2Tips();
436436
const now = BigInt(this.dateProvider.nowInSeconds());
437437

438+
// Frontier block covered by a proposed (or, falling back, confirmed) checkpoint. Blocks beyond it
439+
// have no enclosing checkpoint proposal and are the orphan-pruning candidates.
440+
const proposedCheckpointBlockNumber = await this.stores.blocks.getProposedCheckpointL2BlockNumber();
441+
438442
// The proposed tip is a proposed-checkpointed block, so there are no orphan proposed blocks to prune
439-
if (tips.proposedCheckpoint.block.number === tips.proposed.number) {
440-
this.log.trace(
441-
`No orphan proposed blocks to prune: proposed tip ${tips.proposed.number} is checkpointed`,
442-
pick(tips, 'proposed', 'proposedCheckpoint'),
443-
);
443+
if (proposedCheckpointBlockNumber === tips.proposed.number) {
444+
this.log.trace(`No orphan proposed blocks to prune: proposed tip ${tips.proposed.number} is checkpointed`, {
445+
proposed: tips.proposed,
446+
proposedCheckpointBlockNumber,
447+
});
444448
return;
445449
}
446450

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

453457
// Iterate through them in order, the first one with a slot that should have received a proposed checkpoint

yarn-project/archiver/src/store/block_store.test.ts

Lines changed: 14 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import {
1313
BlockHash,
1414
CommitteeAttestation,
1515
EthAddress,
16-
GENESIS_BLOCK_HEADER_HASH,
1716
L2Block,
1817
type ValidateCheckpointResult,
1918
} from '@aztec/stdlib/block';
@@ -40,7 +39,6 @@ import {
4039
makeStateForBlock,
4140
} from '../test/mock_structs.js';
4241
import { BlockStore } from './block_store.js';
43-
import { L2TipsCache } from './l2_tips_cache.js';
4442

4543
async function addProposedBlocks(
4644
blockStore: BlockStore,
@@ -2627,26 +2625,19 @@ describe('BlockStore', () => {
26272625
});
26282626
});
26292627

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

2639-
const l2TipsCache = new L2TipsCache(blockStore, GENESIS_BLOCK_HEADER_HASH);
2640-
const tips = await l2TipsCache.getL2Tips();
2641-
2642-
// proposedCheckpoint should always be defined
2643-
expect(tips.proposedCheckpoint).toBeDefined();
2644-
// With no proposed checkpoint, it should equal the checkpointed tip
2645-
expect(tips.proposedCheckpoint!.block.number).toBe(tips.checkpointed.block.number);
2646-
expect(tips.proposedCheckpoint!.checkpoint.number).toBe(tips.checkpointed.checkpoint.number);
2637+
expect(await blockStore.getLastProposedCheckpoint()).toBeUndefined();
26472638
});
26482639

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

26652656
// Set proposed checkpoint
2657+
const header = CheckpointHeader.empty();
26662658
await blockStore.addProposedCheckpoint({
26672659
checkpointNumber: CheckpointNumber(2),
2668-
header: CheckpointHeader.empty(),
2660+
header,
26692661
startBlock: BlockNumber(2),
26702662
blockCount: 1,
26712663
totalManaUsed: 100n,
26722664
feeAssetPriceModifier: 50n,
26732665
});
26742666

2675-
const l2TipsCache = new L2TipsCache(blockStore, GENESIS_BLOCK_HEADER_HASH);
2676-
const tips = await l2TipsCache.getL2Tips();
2677-
2678-
expect(tips.proposedCheckpoint).toBeDefined();
2679-
expect(tips.proposedCheckpoint!.block.number).toBeGreaterThan(tips.checkpointed.block.number);
2680-
expect(tips.proposedCheckpoint!.checkpoint.number).toBeGreaterThan(tips.checkpointed.checkpoint.number);
2667+
const proposedCheckpoint = await blockStore.getLastProposedCheckpoint();
2668+
expect(proposedCheckpoint).toBeDefined();
2669+
// Callers derive the tip from the payload: last block = startBlock + blockCount - 1.
2670+
expect(proposedCheckpoint!.checkpointNumber).toBe(CheckpointNumber(2));
2671+
expect(proposedCheckpoint!.startBlock).toBe(BlockNumber(2));
2672+
expect(proposedCheckpoint!.blockCount).toBe(1);
2673+
expect(proposedCheckpoint!.totalManaUsed).toBe(100n);
26812674
});
26822675
});
26832676

yarn-project/archiver/src/store/block_store.ts

Lines changed: 11 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1167,14 +1167,13 @@ export class BlockStore {
11671167
}
11681168

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

11981197
// Load latest block and checkpoint entries
11991198
const [latestBlockEntry] = await toArray(this.#blocks.entriesAsync({ reverse: true, limit: 1 }));
1200-
const [proposedCheckpointEntry] = await toArray(
1201-
this.#proposedCheckpoints.entriesAsync({ reverse: true, limit: 1 }),
1202-
);
12031199
const [latestCheckpointEntry] = await toArray(this.#checkpoints.entriesAsync({ reverse: true, limit: 1 }));
12041200
const latestCheckpointNumber = latestCheckpointEntry
12051201
? CheckpointNumber(latestCheckpointEntry[0])
@@ -1285,14 +1281,6 @@ export class BlockStore {
12851281
const provenTip = await buildTipFromCheckpoint(provenCheckpoint);
12861282
const finalizedTip = await buildTipFromCheckpoint(finalizedCheckpoint);
12871283

1288-
// Proposed checkpoint falls back to the checkpoint tip if it's not set. And if local storage is
1289-
// inconsistent and the proposed checkpoint is behind the checkpointed tip, we patch that and
1290-
// report the checkpointed tip as the proposed checkpoint to maintain the invariant.
1291-
const proposedCheckpointTip =
1292-
proposedCheckpointEntry === undefined || proposedCheckpointEntry[0] <= latestCheckpointNumber
1293-
? checkpointedTip
1294-
: await buildTipFromCheckpoint(proposedCheckpointEntry[1]);
1295-
12961284
// A checkpointed block past the latest stored block would mean a checkpoint
12971285
// references blocks that aren't in blocks.
12981286
if (proposedBlockId.number < checkpointedTip.block.number) {
@@ -1304,29 +1292,26 @@ export class BlockStore {
13041292
// Assert that checkpoint numbers are increasing
13051293
if (
13061294
finalizedTip.checkpoint.number > provenTip.checkpoint.number ||
1307-
provenTip.checkpoint.number > checkpointedTip.checkpoint.number ||
1308-
checkpointedTip.checkpoint.number > proposedCheckpointTip.checkpoint.number
1295+
provenTip.checkpoint.number > checkpointedTip.checkpoint.number
13091296
) {
13101297
throw new Error(
1311-
`Inconsistent checkpoint numbers in chain tips: finalized=${finalizedTip.checkpoint.number} proven=${provenTip.checkpoint.number} checkpointed=${checkpointedTip.checkpoint.number} proposed=${proposedCheckpointTip.checkpoint.number}`,
1298+
`Inconsistent checkpoint numbers in chain tips: finalized=${finalizedTip.checkpoint.number} proven=${provenTip.checkpoint.number} checkpointed=${checkpointedTip.checkpoint.number}`,
13121299
);
13131300
}
13141301

13151302
// Assert block numbers are increasing
13161303
if (
13171304
finalizedTip.block.number > provenTip.block.number ||
13181305
provenTip.block.number > checkpointedTip.block.number ||
1319-
checkpointedTip.block.number > proposedCheckpointTip.block.number ||
1320-
proposedCheckpointTip.block.number > proposedBlockId.number
1306+
checkpointedTip.block.number > proposedBlockId.number
13211307
) {
13221308
throw new Error(
1323-
`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}`,
1309+
`Inconsistent block numbers in chain tips: finalized=${finalizedTip.block.number} proven=${provenTip.block.number} checkpointed=${checkpointedTip.block.number} proposed=${proposedBlockId.number}`,
13241310
);
13251311
}
13261312

13271313
return {
13281314
proposed: proposedBlockId,
1329-
proposedCheckpoint: proposedCheckpointTip,
13301315
checkpointed: checkpointedTip,
13311316
proven: provenTip,
13321317
finalized: finalizedTip,

yarn-project/archiver/src/test/mock_l1_to_l2_message_source.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ export class MockL1ToL2MessageSource implements L1ToL2MessageSource {
4444
checkpointed: tip,
4545
proven: tip,
4646
finalized: tip,
47-
proposedCheckpoint: tip,
4847
});
4948
}
5049
}

0 commit comments

Comments
 (0)