@aztec/stdlib 5.0.0-nightly.20260611 → 5.0.0-nightly.20260613
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dest/block/l2_block_source.d.ts +7 -1
- package/dest/block/l2_block_source.d.ts.map +1 -1
- package/dest/block/l2_block_stream/interfaces.d.ts +44 -8
- package/dest/block/l2_block_stream/interfaces.d.ts.map +1 -1
- package/dest/block/l2_block_stream/l2_block_stream.d.ts +1 -1
- package/dest/block/l2_block_stream/l2_block_stream.d.ts.map +1 -1
- package/dest/block/l2_block_stream/l2_block_stream.js +13 -4
- package/dest/block/l2_block_stream/l2_tips_memory_store.d.ts +6 -12
- package/dest/block/l2_block_stream/l2_tips_memory_store.d.ts.map +1 -1
- package/dest/block/l2_block_stream/l2_tips_memory_store.js +8 -32
- package/dest/block/l2_block_stream/l2_tips_store_base.d.ts +9 -18
- package/dest/block/l2_block_stream/l2_tips_store_base.d.ts.map +1 -1
- package/dest/block/l2_block_stream/l2_tips_store_base.js +52 -58
- package/dest/block/test/l2_tips_store_test_suite.d.ts +1 -1
- package/dest/block/test/l2_tips_store_test_suite.d.ts.map +1 -1
- package/dest/block/test/l2_tips_store_test_suite.js +202 -34
- package/dest/config/index.d.ts +2 -1
- package/dest/config/index.d.ts.map +1 -1
- package/dest/config/index.js +1 -0
- package/dest/config/network-consensus-config.d.ts +72 -0
- package/dest/config/network-consensus-config.d.ts.map +1 -0
- package/dest/config/network-consensus-config.js +231 -0
- package/dest/config/sequencer-config.d.ts +3 -1
- package/dest/config/sequencer-config.d.ts.map +1 -1
- package/dest/config/sequencer-config.js +5 -4
- package/dest/contract/interfaces/node-info.d.ts +11 -1
- package/dest/contract/interfaces/node-info.d.ts.map +1 -1
- package/dest/contract/interfaces/node-info.js +7 -1
- package/dest/gas/gas_settings.d.ts +7 -13
- package/dest/gas/gas_settings.d.ts.map +1 -1
- package/dest/gas/gas_settings.js +9 -16
- package/dest/gas/index.d.ts +2 -1
- package/dest/gas/index.d.ts.map +1 -1
- package/dest/gas/index.js +1 -0
- package/dest/gas/tx_gas_limits.d.ts +72 -0
- package/dest/gas/tx_gas_limits.d.ts.map +1 -0
- package/dest/gas/tx_gas_limits.js +85 -0
- package/dest/interfaces/aztec-node-admin.d.ts +18 -17
- package/dest/interfaces/aztec-node-admin.d.ts.map +1 -1
- package/dest/interfaces/aztec-node-admin.js +1 -1
- package/dest/interfaces/aztec-node-debug.d.ts +15 -2
- package/dest/interfaces/aztec-node-debug.d.ts.map +1 -1
- package/dest/interfaces/aztec-node-debug.js +9 -1
- package/dest/interfaces/aztec-node.d.ts +40 -11
- package/dest/interfaces/aztec-node.d.ts.map +1 -1
- package/dest/interfaces/aztec-node.js +42 -5
- package/dest/interfaces/block-builder.d.ts +3 -1
- package/dest/interfaces/block-builder.d.ts.map +1 -1
- package/dest/interfaces/client.d.ts +2 -1
- package/dest/interfaces/client.d.ts.map +1 -1
- package/dest/interfaces/configs.d.ts +12 -6
- package/dest/interfaces/configs.d.ts.map +1 -1
- package/dest/interfaces/configs.js +2 -1
- package/dest/interfaces/get_tx_by_hash_options.d.ts +9 -0
- package/dest/interfaces/get_tx_by_hash_options.d.ts.map +1 -0
- package/dest/interfaces/get_tx_by_hash_options.js +4 -0
- package/dest/interfaces/p2p.d.ts +32 -8
- package/dest/interfaces/p2p.d.ts.map +1 -1
- package/dest/interfaces/p2p.js +12 -2
- package/dest/interfaces/proving-job.d.ts +70 -70
- package/dest/interfaces/validator.d.ts +3 -3
- package/dest/interfaces/validator.d.ts.map +1 -1
- package/dest/interfaces/validator.js +1 -1
- package/dest/tests/factories.d.ts +1 -1
- package/dest/tests/factories.d.ts.map +1 -1
- package/dest/tests/factories.js +4 -1
- package/dest/tests/mocks.d.ts +1 -1
- package/dest/tests/mocks.d.ts.map +1 -1
- package/dest/tests/mocks.js +3 -2
- package/dest/timetable/budgets.d.ts +4 -2
- package/dest/timetable/budgets.d.ts.map +1 -1
- package/dest/timetable/budgets.js +2 -1
- package/dest/timetable/build_proposer_timetable.d.ts +21 -0
- package/dest/timetable/build_proposer_timetable.d.ts.map +1 -0
- package/dest/timetable/build_proposer_timetable.js +17 -0
- package/dest/timetable/consensus_timetable.d.ts +5 -7
- package/dest/timetable/consensus_timetable.d.ts.map +1 -1
- package/dest/timetable/consensus_timetable.js +6 -8
- package/dest/timetable/index.d.ts +2 -1
- package/dest/timetable/index.d.ts.map +1 -1
- package/dest/timetable/index.js +1 -0
- package/dest/timetable/proposer_timetable.d.ts +18 -24
- package/dest/timetable/proposer_timetable.d.ts.map +1 -1
- package/dest/timetable/proposer_timetable.js +26 -55
- package/dest/tx/fee_provider.d.ts +2 -2
- package/dest/tx/fee_provider.d.ts.map +1 -1
- package/dest/tx/validator/error_texts.d.ts +2 -2
- package/dest/tx/validator/error_texts.d.ts.map +1 -1
- package/dest/tx/validator/error_texts.js +1 -1
- package/package.json +8 -8
- package/src/block/l2_block_source.ts +7 -0
- package/src/block/l2_block_stream/interfaces.ts +39 -7
- package/src/block/l2_block_stream/l2_block_stream.ts +21 -3
- package/src/block/l2_block_stream/l2_tips_memory_store.ts +12 -41
- package/src/block/l2_block_stream/l2_tips_store_base.ts +63 -93
- package/src/block/test/l2_tips_store_test_suite.ts +197 -24
- package/src/config/index.ts +1 -0
- package/src/config/network-consensus-config.ts +302 -0
- package/src/config/sequencer-config.ts +7 -5
- package/src/contract/interfaces/node-info.ts +11 -0
- package/src/gas/README.md +92 -0
- package/src/gas/gas_settings.ts +11 -21
- package/src/gas/index.ts +1 -0
- package/src/gas/tx_gas_limits.ts +123 -0
- package/src/interfaces/aztec-node-admin.ts +1 -1
- package/src/interfaces/aztec-node-debug.ts +17 -2
- package/src/interfaces/aztec-node.ts +74 -13
- package/src/interfaces/block-builder.ts +2 -0
- package/src/interfaces/client.ts +1 -0
- package/src/interfaces/configs.ts +10 -6
- package/src/interfaces/get_tx_by_hash_options.ts +14 -0
- package/src/interfaces/p2p.ts +21 -8
- package/src/interfaces/validator.ts +5 -5
- package/src/tests/factories.ts +7 -1
- package/src/tests/mocks.ts +7 -2
- package/src/timetable/README.md +10 -2
- package/src/timetable/budgets.ts +5 -2
- package/src/timetable/build_proposer_timetable.ts +42 -0
- package/src/timetable/consensus_timetable.ts +8 -14
- package/src/timetable/index.ts +1 -0
- package/src/timetable/proposer_timetable.ts +37 -61
- package/src/tx/fee_provider.ts +1 -1
- package/src/tx/validator/error_texts.ts +2 -1
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { BlockNumber, CheckpointNumber } from '@aztec/foundation/branded-types';
|
|
2
2
|
|
|
3
|
-
import type { PublishedCheckpoint } from '../../checkpoint/published_checkpoint.js';
|
|
4
3
|
import type { BlockHash } from '../block_hash.js';
|
|
5
4
|
import type { L2Block } from '../l2_block.js';
|
|
6
5
|
import {
|
|
@@ -8,7 +7,7 @@ import {
|
|
|
8
7
|
GENESIS_CHECKPOINT_HEADER_HASH,
|
|
9
8
|
type L2BlockId,
|
|
10
9
|
type L2BlockTag,
|
|
11
|
-
type
|
|
10
|
+
type LocalL2Tips,
|
|
12
11
|
} from '../l2_block_source.js';
|
|
13
12
|
import type { L2BlockStreamEvent, L2BlockStreamEventHandler, L2BlockStreamLocalDataProvider } from './interfaces.js';
|
|
14
13
|
|
|
@@ -26,6 +25,12 @@ export abstract class L2TipsStoreBase implements L2BlockStreamEventHandler, L2Bl
|
|
|
26
25
|
/** Sets the block number for a given tag. */
|
|
27
26
|
protected abstract setTip(tag: L2BlockTag, blockNumber: BlockNumber): Promise<void>;
|
|
28
27
|
|
|
28
|
+
/** Gets the checkpoint id recorded for a given tag, if any. */
|
|
29
|
+
protected abstract getTipCheckpoint(tag: L2BlockTag): Promise<CheckpointId | undefined>;
|
|
30
|
+
|
|
31
|
+
/** Records the checkpoint id for a given tag. */
|
|
32
|
+
protected abstract setTipCheckpoint(tag: L2BlockTag, checkpoint: CheckpointId): Promise<void>;
|
|
33
|
+
|
|
29
34
|
/** Gets the block hash for a given block number. */
|
|
30
35
|
protected abstract getStoredBlockHash(blockNumber: BlockNumber): Promise<string | undefined>;
|
|
31
36
|
|
|
@@ -35,27 +40,6 @@ export abstract class L2TipsStoreBase implements L2BlockStreamEventHandler, L2Bl
|
|
|
35
40
|
/** Deletes all block hashes for blocks before the given block number. */
|
|
36
41
|
protected abstract deleteBlockHashesBefore(blockNumber: BlockNumber): Promise<void>;
|
|
37
42
|
|
|
38
|
-
/** Gets the checkpoint number for a given block number. */
|
|
39
|
-
protected abstract getCheckpointNumberForBlock(blockNumber: BlockNumber): Promise<CheckpointNumber | undefined>;
|
|
40
|
-
|
|
41
|
-
/** Sets the checkpoint number for a given block number. */
|
|
42
|
-
protected abstract setCheckpointNumberForBlock(
|
|
43
|
-
blockNumber: BlockNumber,
|
|
44
|
-
checkpointNumber: CheckpointNumber,
|
|
45
|
-
): Promise<void>;
|
|
46
|
-
|
|
47
|
-
/** Deletes all block-to-checkpoint mappings for blocks before the given block number. */
|
|
48
|
-
protected abstract deleteBlockToCheckpointBefore(blockNumber: BlockNumber): Promise<void>;
|
|
49
|
-
|
|
50
|
-
/** Gets a checkpoint by its number. */
|
|
51
|
-
protected abstract getCheckpoint(checkpointNumber: CheckpointNumber): Promise<PublishedCheckpoint | undefined>;
|
|
52
|
-
|
|
53
|
-
/** Saves a checkpoint. */
|
|
54
|
-
protected abstract saveCheckpointData(checkpoint: PublishedCheckpoint): Promise<void>;
|
|
55
|
-
|
|
56
|
-
/** Deletes all checkpoints before the given checkpoint number. */
|
|
57
|
-
protected abstract deleteCheckpointsBefore(checkpointNumber: CheckpointNumber): Promise<void>;
|
|
58
|
-
|
|
59
43
|
/** Runs the given function in a transaction. Memory stores can just execute immediately. */
|
|
60
44
|
protected abstract runInTransaction<T>(fn: () => Promise<T>): Promise<T>;
|
|
61
45
|
|
|
@@ -68,31 +52,26 @@ export abstract class L2TipsStoreBase implements L2BlockStreamEventHandler, L2Bl
|
|
|
68
52
|
return this.getStoredBlockHash(number);
|
|
69
53
|
}
|
|
70
54
|
|
|
71
|
-
public getL2Tips(): Promise<
|
|
55
|
+
public getL2Tips(): Promise<LocalL2Tips> {
|
|
72
56
|
return this.runInTransaction(async () => {
|
|
73
|
-
const [proposedBlockId, finalizedBlockId, provenBlockId, checkpointedBlockId
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
this.getBlockId('proposedCheckpoint'),
|
|
80
|
-
]);
|
|
57
|
+
const [proposedBlockId, finalizedBlockId, provenBlockId, checkpointedBlockId] = await Promise.all([
|
|
58
|
+
this.getBlockId('proposed'),
|
|
59
|
+
this.getBlockId('finalized'),
|
|
60
|
+
this.getBlockId('proven'),
|
|
61
|
+
this.getBlockId('checkpointed'),
|
|
62
|
+
]);
|
|
81
63
|
|
|
82
|
-
const [finalizedCheckpointId, provenCheckpointId, checkpointedCheckpointId
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
this.getCheckpointId('proposedCheckpoint'),
|
|
88
|
-
]);
|
|
64
|
+
const [finalizedCheckpointId, provenCheckpointId, checkpointedCheckpointId] = await Promise.all([
|
|
65
|
+
this.getCheckpointId('finalized'),
|
|
66
|
+
this.getCheckpointId('proven'),
|
|
67
|
+
this.getCheckpointId('checkpointed'),
|
|
68
|
+
]);
|
|
89
69
|
|
|
90
70
|
return {
|
|
91
71
|
proposed: proposedBlockId,
|
|
92
72
|
finalized: { block: finalizedBlockId, checkpoint: finalizedCheckpointId },
|
|
93
73
|
proven: { block: provenBlockId, checkpoint: provenCheckpointId },
|
|
94
74
|
checkpointed: { block: checkpointedBlockId, checkpoint: checkpointedCheckpointId },
|
|
95
|
-
proposedCheckpoint: { block: proposedCheckpointBlockId, checkpoint: proposedCheckpointId },
|
|
96
75
|
};
|
|
97
76
|
});
|
|
98
77
|
}
|
|
@@ -139,17 +118,21 @@ export abstract class L2TipsStoreBase implements L2BlockStreamEventHandler, L2Bl
|
|
|
139
118
|
private async getCheckpointId(tag: L2BlockTag): Promise<CheckpointId> {
|
|
140
119
|
const blockNumber = await this.getTip(tag);
|
|
141
120
|
if (blockNumber === undefined || blockNumber === 0) {
|
|
142
|
-
return
|
|
143
|
-
}
|
|
144
|
-
const checkpointNumber = await this.getCheckpointNumberForBlock(blockNumber);
|
|
145
|
-
if (checkpointNumber === undefined) {
|
|
146
|
-
return { number: CheckpointNumber.ZERO, hash: GENESIS_CHECKPOINT_HEADER_HASH.toString() };
|
|
121
|
+
return this.genesisCheckpointId();
|
|
147
122
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
123
|
+
// The checkpoint id recorded for this cursor when it was last advanced is the single source of truth.
|
|
124
|
+
// The writers (handleChainCheckpointed/Proven/Finalized/Pruned) always record an id alongside any
|
|
125
|
+
// non-genesis cursor advance, so a missing id on a real block is genuine store corruption. Fail loudly
|
|
126
|
+
// rather than silently reporting checkpoint zero, which would drive a checkpoint-replay storm.
|
|
127
|
+
const storedCheckpoint = await this.getTipCheckpoint(tag);
|
|
128
|
+
if (storedCheckpoint !== undefined) {
|
|
129
|
+
return storedCheckpoint;
|
|
151
130
|
}
|
|
152
|
-
|
|
131
|
+
throw new Error(`No checkpoint id recorded for ${tag} tip at block ${blockNumber}; the L2 tips store is corrupted`);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
private genesisCheckpointId(): CheckpointId {
|
|
135
|
+
return { number: CheckpointNumber.ZERO, hash: GENESIS_CHECKPOINT_HEADER_HASH.toString() };
|
|
153
136
|
}
|
|
154
137
|
|
|
155
138
|
private async handleBlocksAdded(event: L2BlockStreamEvent): Promise<void> {
|
|
@@ -170,14 +153,12 @@ export abstract class L2TipsStoreBase implements L2BlockStreamEventHandler, L2Bl
|
|
|
170
153
|
return;
|
|
171
154
|
}
|
|
172
155
|
await this.runInTransaction(async () => {
|
|
156
|
+
const checkpointId: CheckpointId = {
|
|
157
|
+
number: event.checkpoint.checkpoint.number,
|
|
158
|
+
hash: event.checkpoint.checkpoint.hash().toString(),
|
|
159
|
+
};
|
|
173
160
|
await this.saveTag('checkpointed', event.block);
|
|
174
|
-
await this.
|
|
175
|
-
// proposedCheckpoint is always >= checkpointed. If checkpointed has caught up
|
|
176
|
-
// or surpassed it, advance proposedCheckpoint to match.
|
|
177
|
-
const proposedCheckpointBlock = await this.getBlockId('proposedCheckpoint');
|
|
178
|
-
if (event.block.number > proposedCheckpointBlock.number) {
|
|
179
|
-
await this.saveTag('proposedCheckpoint', event.block);
|
|
180
|
-
}
|
|
161
|
+
await this.setTipCheckpoint('checkpointed', checkpointId);
|
|
181
162
|
});
|
|
182
163
|
}
|
|
183
164
|
|
|
@@ -186,12 +167,27 @@ export abstract class L2TipsStoreBase implements L2BlockStreamEventHandler, L2Bl
|
|
|
186
167
|
return;
|
|
187
168
|
}
|
|
188
169
|
await this.runInTransaction(async () => {
|
|
170
|
+
// A prune is a rollback: the proposed tip moves to the prune target unconditionally, but
|
|
171
|
+
// checkpoint-bearing cursors may only move backward. Forward-advancing them onto an
|
|
172
|
+
// uncheckpointed block leaves them on a block with no recorded checkpoint id, which getCheckpointId
|
|
173
|
+
// would then throw on.
|
|
189
174
|
await this.saveTag('proposed', event.block);
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
175
|
+
|
|
176
|
+
// Clamp each checkpoint-bearing cursor down to its OWN source tip when it leads it. Clamping the proven
|
|
177
|
+
// cursor onto the checkpointed tip would transiently report unproven blocks as proven (the source's proven
|
|
178
|
+
// tip can sit below its checkpointed tip after a proof-tx reorg), until the corrective chain-proven event
|
|
179
|
+
// lands at the end of the same sync iteration. The event carries a valid (block, id) pair for each
|
|
180
|
+
// boundary, so the clamped cursor always resolves to a recorded id. The source guarantees proven <=
|
|
181
|
+
// checkpointed, so clamping each cursor to its own tip preserves the local proven <= checkpointed invariant.
|
|
182
|
+
for (const { tag, sourceTip } of [
|
|
183
|
+
{ tag: 'checkpointed', sourceTip: event.checkpointed },
|
|
184
|
+
{ tag: 'proven', sourceTip: event.proven },
|
|
185
|
+
] as const) {
|
|
186
|
+
const current = await this.getTip(tag);
|
|
187
|
+
if (current !== undefined && current > sourceTip.block.number) {
|
|
188
|
+
await this.saveTag(tag, sourceTip.block);
|
|
189
|
+
await this.setTipCheckpoint(tag, sourceTip.checkpoint);
|
|
190
|
+
}
|
|
195
191
|
}
|
|
196
192
|
});
|
|
197
193
|
}
|
|
@@ -202,6 +198,7 @@ export abstract class L2TipsStoreBase implements L2BlockStreamEventHandler, L2Bl
|
|
|
202
198
|
}
|
|
203
199
|
await this.runInTransaction(async () => {
|
|
204
200
|
await this.saveTag('proven', event.block);
|
|
201
|
+
await this.setTipCheckpoint('proven', event.checkpoint);
|
|
205
202
|
});
|
|
206
203
|
}
|
|
207
204
|
|
|
@@ -211,33 +208,16 @@ export abstract class L2TipsStoreBase implements L2BlockStreamEventHandler, L2Bl
|
|
|
211
208
|
}
|
|
212
209
|
await this.runInTransaction(async () => {
|
|
213
210
|
await this.saveTag('finalized', event.block);
|
|
214
|
-
|
|
211
|
+
await this.setTipCheckpoint('finalized', event.checkpoint);
|
|
215
212
|
|
|
216
|
-
//
|
|
217
|
-
//
|
|
218
|
-
//
|
|
213
|
+
// Prune block hashes below the lowest live tip. Cap the deletion bound at the lowest live tip rather
|
|
214
|
+
// than the finalized tip alone: this should always be the finalized tip, but we have hit bugs where
|
|
215
|
+
// this is not the case. Deleting the block hash for a live tip would dangle subsequent `getBlockId`
|
|
219
216
|
// lookups and lock the block stream into an error loop.
|
|
220
|
-
const tips = await Promise.all([
|
|
221
|
-
this.getTip('proposed'),
|
|
222
|
-
this.getTip('proposedCheckpoint'),
|
|
223
|
-
this.getTip('checkpointed'),
|
|
224
|
-
this.getTip('proven'),
|
|
225
|
-
]);
|
|
217
|
+
const tips = await Promise.all([this.getTip('proposed'), this.getTip('checkpointed'), this.getTip('proven')]);
|
|
226
218
|
const liveTipBlocks = tips.filter((t): t is BlockNumber => t !== undefined && t > 0);
|
|
227
219
|
const safeBlockBound = BlockNumber(Math.min(event.block.number, ...liveTipBlocks));
|
|
228
220
|
await this.deleteBlockHashesBefore(safeBlockBound);
|
|
229
|
-
await this.deleteBlockToCheckpointBefore(safeBlockBound);
|
|
230
|
-
|
|
231
|
-
if (finalizedCheckpointNumber !== undefined) {
|
|
232
|
-
const tipCheckpoints = await Promise.all(liveTipBlocks.map(b => this.getCheckpointNumberForBlock(b)));
|
|
233
|
-
const safeCheckpointBound = CheckpointNumber(
|
|
234
|
-
Math.min(
|
|
235
|
-
finalizedCheckpointNumber,
|
|
236
|
-
...tipCheckpoints.filter((c): c is CheckpointNumber => c !== undefined && c > 0),
|
|
237
|
-
),
|
|
238
|
-
);
|
|
239
|
-
await this.deleteCheckpointsBefore(safeCheckpointBound);
|
|
240
|
-
}
|
|
241
221
|
});
|
|
242
222
|
}
|
|
243
223
|
|
|
@@ -247,14 +227,4 @@ export abstract class L2TipsStoreBase implements L2BlockStreamEventHandler, L2Bl
|
|
|
247
227
|
await this.setBlockHash(block.number, block.hash);
|
|
248
228
|
}
|
|
249
229
|
}
|
|
250
|
-
|
|
251
|
-
private async saveCheckpoint(publishedCheckpoint: PublishedCheckpoint): Promise<void> {
|
|
252
|
-
const checkpoint = publishedCheckpoint.checkpoint;
|
|
253
|
-
const lastBlock = checkpoint.blocks.at(-1)!;
|
|
254
|
-
// Only store the mapping for the last block since tips only point to checkpoint boundaries
|
|
255
|
-
await Promise.all([
|
|
256
|
-
this.setCheckpointNumberForBlock(lastBlock.number, checkpoint.number),
|
|
257
|
-
this.saveCheckpointData(publishedCheckpoint),
|
|
258
|
-
]);
|
|
259
|
-
}
|
|
260
230
|
}
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
GENESIS_CHECKPOINT_HEADER_HASH,
|
|
7
7
|
L2Block,
|
|
8
8
|
type L2BlockId,
|
|
9
|
+
type L2BlockTag,
|
|
9
10
|
type L2TipId,
|
|
10
11
|
} from '@aztec/stdlib/block';
|
|
11
12
|
import { Checkpoint, L1PublishedData, PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
|
|
@@ -67,18 +68,11 @@ export function testL2TipsStore(makeTipsStore: () => Promise<L2TipsStore>) {
|
|
|
67
68
|
checkpoint: makeCheckpointIdForBlock(blockNumber),
|
|
68
69
|
});
|
|
69
70
|
|
|
70
|
-
const makeTips = (
|
|
71
|
-
proposed: number,
|
|
72
|
-
proven: number,
|
|
73
|
-
finalized: number,
|
|
74
|
-
checkpointed: number = 0,
|
|
75
|
-
proposedCheckpoint: number = 0,
|
|
76
|
-
) => ({
|
|
71
|
+
const makeTips = (proposed: number, proven: number, finalized: number, checkpointed: number = 0) => ({
|
|
77
72
|
proposed: makeTip(proposed),
|
|
78
73
|
proven: makeTipId(proven),
|
|
79
74
|
finalized: makeTipId(finalized),
|
|
80
75
|
checkpointed: makeTipId(checkpointed),
|
|
81
|
-
proposedCheckpoint: makeTipId(proposedCheckpoint),
|
|
82
76
|
});
|
|
83
77
|
|
|
84
78
|
const makeCheckpoint = async (checkpointNumber: number, blocks: L2Block[]): Promise<PublishedCheckpoint> => {
|
|
@@ -153,7 +147,11 @@ export function testL2TipsStore(makeTipsStore: () => Promise<L2TipsStore>) {
|
|
|
153
147
|
await tipsStore.handleBlockStreamEvent(await makeCheckpointedEvent(checkpoint1));
|
|
154
148
|
|
|
155
149
|
// Prove up to block 5
|
|
156
|
-
await tipsStore.handleBlockStreamEvent({
|
|
150
|
+
await tipsStore.handleBlockStreamEvent({
|
|
151
|
+
type: 'chain-proven',
|
|
152
|
+
block: makeBlockId(5),
|
|
153
|
+
checkpoint: makeCheckpointIdForBlock(5),
|
|
154
|
+
});
|
|
157
155
|
|
|
158
156
|
const tips = await tipsStore.getL2Tips();
|
|
159
157
|
expect(tips.proposed).toEqual(makeTip(5));
|
|
@@ -173,8 +171,16 @@ export function testL2TipsStore(makeTipsStore: () => Promise<L2TipsStore>) {
|
|
|
173
171
|
await tipsStore.handleBlockStreamEvent(await makeCheckpointedEvent(checkpoint1));
|
|
174
172
|
|
|
175
173
|
// Prove and finalize
|
|
176
|
-
await tipsStore.handleBlockStreamEvent({
|
|
177
|
-
|
|
174
|
+
await tipsStore.handleBlockStreamEvent({
|
|
175
|
+
type: 'chain-proven',
|
|
176
|
+
block: makeBlockId(5),
|
|
177
|
+
checkpoint: makeCheckpointIdForBlock(5),
|
|
178
|
+
});
|
|
179
|
+
await tipsStore.handleBlockStreamEvent({
|
|
180
|
+
type: 'chain-finalized',
|
|
181
|
+
block: makeBlockId(5),
|
|
182
|
+
checkpoint: makeCheckpointIdForBlock(5),
|
|
183
|
+
});
|
|
178
184
|
|
|
179
185
|
const tips = await tipsStore.getL2Tips();
|
|
180
186
|
expect(tips.proposed).toEqual(makeTip(5));
|
|
@@ -229,8 +235,16 @@ export function testL2TipsStore(makeTipsStore: () => Promise<L2TipsStore>) {
|
|
|
229
235
|
await tipsStore.handleBlockStreamEvent(await makeCheckpointedEvent(checkpoint2));
|
|
230
236
|
|
|
231
237
|
// Prove and finalize up to block 3 (checkpoint 1)
|
|
232
|
-
await tipsStore.handleBlockStreamEvent({
|
|
233
|
-
|
|
238
|
+
await tipsStore.handleBlockStreamEvent({
|
|
239
|
+
type: 'chain-proven',
|
|
240
|
+
block: makeBlockId(3),
|
|
241
|
+
checkpoint: makeCheckpointIdForBlock(3),
|
|
242
|
+
});
|
|
243
|
+
await tipsStore.handleBlockStreamEvent({
|
|
244
|
+
type: 'chain-finalized',
|
|
245
|
+
block: makeBlockId(3),
|
|
246
|
+
checkpoint: makeCheckpointIdForBlock(3),
|
|
247
|
+
});
|
|
234
248
|
|
|
235
249
|
// Blocks before finalized should be cleared
|
|
236
250
|
expect(await tipsStore.getL2BlockHash(1)).toBeUndefined();
|
|
@@ -250,7 +264,8 @@ export function testL2TipsStore(makeTipsStore: () => Promise<L2TipsStore>) {
|
|
|
250
264
|
await tipsStore.handleBlockStreamEvent({
|
|
251
265
|
type: 'chain-pruned',
|
|
252
266
|
block: makeBlockId(5),
|
|
253
|
-
|
|
267
|
+
checkpointed: makeTipId(0),
|
|
268
|
+
proven: makeTipId(0),
|
|
254
269
|
});
|
|
255
270
|
|
|
256
271
|
const tips = await tipsStore.getL2Tips();
|
|
@@ -274,7 +289,8 @@ export function testL2TipsStore(makeTipsStore: () => Promise<L2TipsStore>) {
|
|
|
274
289
|
await tipsStore.handleBlockStreamEvent({
|
|
275
290
|
type: 'chain-pruned',
|
|
276
291
|
block: makeTip(0),
|
|
277
|
-
|
|
292
|
+
checkpointed: makeTipId(0),
|
|
293
|
+
proven: makeTipId(0),
|
|
278
294
|
});
|
|
279
295
|
|
|
280
296
|
tips = await tipsStore.getL2Tips();
|
|
@@ -339,7 +355,8 @@ export function testL2TipsStore(makeTipsStore: () => Promise<L2TipsStore>) {
|
|
|
339
355
|
await tipsStore.handleBlockStreamEvent({
|
|
340
356
|
type: 'chain-pruned',
|
|
341
357
|
block: makeBlockId(5),
|
|
342
|
-
|
|
358
|
+
checkpointed: makeTipId(5),
|
|
359
|
+
proven: makeTipId(0),
|
|
343
360
|
});
|
|
344
361
|
|
|
345
362
|
tips = await tipsStore.getL2Tips();
|
|
@@ -403,7 +420,8 @@ export function testL2TipsStore(makeTipsStore: () => Promise<L2TipsStore>) {
|
|
|
403
420
|
await tipsStore.handleBlockStreamEvent({
|
|
404
421
|
type: 'chain-pruned',
|
|
405
422
|
block: makeBlockId(3),
|
|
406
|
-
|
|
423
|
+
checkpointed: makeTipId(3),
|
|
424
|
+
proven: makeTipId(0),
|
|
407
425
|
});
|
|
408
426
|
|
|
409
427
|
tips = await tipsStore.getL2Tips();
|
|
@@ -440,7 +458,11 @@ export function testL2TipsStore(makeTipsStore: () => Promise<L2TipsStore>) {
|
|
|
440
458
|
await tipsStore.handleBlockStreamEvent(await makeCheckpointedEvent(checkpoint1));
|
|
441
459
|
|
|
442
460
|
// Prove up to block 3
|
|
443
|
-
await tipsStore.handleBlockStreamEvent({
|
|
461
|
+
await tipsStore.handleBlockStreamEvent({
|
|
462
|
+
type: 'chain-proven',
|
|
463
|
+
block: makeBlockId(3),
|
|
464
|
+
checkpoint: makeCheckpointIdForBlock(3),
|
|
465
|
+
});
|
|
444
466
|
|
|
445
467
|
let tips = await tipsStore.getL2Tips();
|
|
446
468
|
expect(tips.proposed).toEqual(makeTip(3));
|
|
@@ -481,7 +503,8 @@ export function testL2TipsStore(makeTipsStore: () => Promise<L2TipsStore>) {
|
|
|
481
503
|
await tipsStore.handleBlockStreamEvent({
|
|
482
504
|
type: 'chain-pruned',
|
|
483
505
|
block: makeBlockId(3),
|
|
484
|
-
|
|
506
|
+
checkpointed: makeTipId(3),
|
|
507
|
+
proven: makeTipId(3),
|
|
485
508
|
});
|
|
486
509
|
|
|
487
510
|
tips = await tipsStore.getL2Tips();
|
|
@@ -530,15 +553,165 @@ export function testL2TipsStore(makeTipsStore: () => Promise<L2TipsStore>) {
|
|
|
530
553
|
expect(await tipsStore.getL2BlockHash(7)).not.toEqual(originalHash7);
|
|
531
554
|
});
|
|
532
555
|
|
|
533
|
-
// Regression test for #13142
|
|
534
|
-
|
|
556
|
+
// Regression test for #13142: proving an unseen block number (one with no local block->checkpoint
|
|
557
|
+
// mapping) must not blow up. With per-cursor checkpoint ids, the proven tip resolves to the
|
|
558
|
+
// checkpoint id carried by the event rather than falling back to checkpoint zero.
|
|
559
|
+
it('resolves the proven checkpoint from the carried id when setting proven on an unseen block number', async () => {
|
|
535
560
|
await tipsStore.handleBlockStreamEvent({ type: 'blocks-added', blocks: [await makeBlock(5)] });
|
|
536
|
-
|
|
561
|
+
// Block 3 has no local block->checkpoint mapping, but the event carries a real checkpoint id.
|
|
562
|
+
const carriedCheckpoint: CheckpointId = { number: CheckpointNumber(1), hash: new Fr(42).toString() };
|
|
563
|
+
await tipsStore.handleBlockStreamEvent({
|
|
564
|
+
type: 'chain-proven',
|
|
565
|
+
block: makeBlockId(3),
|
|
566
|
+
checkpoint: carriedCheckpoint,
|
|
567
|
+
});
|
|
537
568
|
|
|
538
569
|
const tips = await tipsStore.getL2Tips();
|
|
539
570
|
expect(tips.proposed).toEqual(makeTip(5));
|
|
540
571
|
expect(tips.proven.block).toEqual(makeTip(3));
|
|
541
|
-
//
|
|
542
|
-
expect(tips.proven.checkpoint
|
|
572
|
+
// Resolved from the carried id, not the (missing) local mapping.
|
|
573
|
+
expect(tips.proven.checkpoint).toEqual(carriedCheckpoint);
|
|
574
|
+
});
|
|
575
|
+
|
|
576
|
+
// proven/finalized resolve to the carried checkpoint id even when the block has no local
|
|
577
|
+
// block->checkpoint mapping (the cursor legitimately leads the locally-checkpointed frontier).
|
|
578
|
+
it('resolves proven and finalized checkpoints from carried ids without a local mapping', async () => {
|
|
579
|
+
await tipsStore.handleBlockStreamEvent({ type: 'blocks-added', blocks: [await makeBlock(7)] });
|
|
580
|
+
const provenCheckpoint: CheckpointId = { number: CheckpointNumber(2), hash: new Fr(101).toString() };
|
|
581
|
+
const finalizedCheckpoint: CheckpointId = { number: CheckpointNumber(1), hash: new Fr(100).toString() };
|
|
582
|
+
await tipsStore.handleBlockStreamEvent({
|
|
583
|
+
type: 'chain-proven',
|
|
584
|
+
block: makeBlockId(5),
|
|
585
|
+
checkpoint: provenCheckpoint,
|
|
586
|
+
});
|
|
587
|
+
await tipsStore.handleBlockStreamEvent({
|
|
588
|
+
type: 'chain-finalized',
|
|
589
|
+
block: makeBlockId(3),
|
|
590
|
+
checkpoint: finalizedCheckpoint,
|
|
591
|
+
});
|
|
592
|
+
|
|
593
|
+
const tips = await tipsStore.getL2Tips();
|
|
594
|
+
expect(tips.proven.checkpoint).toEqual(provenCheckpoint);
|
|
595
|
+
expect(tips.finalized.checkpoint).toEqual(finalizedCheckpoint);
|
|
596
|
+
});
|
|
597
|
+
|
|
598
|
+
// Genuine corruption: a cursor points at a real (non-genesis) block that has a block hash but
|
|
599
|
+
// neither a stored per-cursor checkpoint id nor a block->checkpoint mapping. getL2Tips must throw
|
|
600
|
+
// loudly rather than silently report checkpoint zero. We reach this state by reaching past the
|
|
601
|
+
// event API to place the tip directly, since the normal writers always record an id.
|
|
602
|
+
it('throws when a cursor points at a real block with neither a stored id nor a mapping', async () => {
|
|
603
|
+
// blocks-added records the block hash for block 5 (so getBlockId succeeds) but no checkpoint id.
|
|
604
|
+
await tipsStore.handleBlockStreamEvent({ type: 'blocks-added', blocks: [await makeBlock(5)] });
|
|
605
|
+
// Corrupt the proven cursor to point at block 5 without any id or mapping.
|
|
606
|
+
const internal = tipsStore as unknown as { setTip(tag: L2BlockTag, blockNumber: BlockNumber): Promise<void> };
|
|
607
|
+
await internal.setTip('proven', BlockNumber(5));
|
|
608
|
+
|
|
609
|
+
await expect(tipsStore.getL2Tips()).rejects.toThrow(/checkpoint/i);
|
|
610
|
+
});
|
|
611
|
+
|
|
612
|
+
// Backward prune of a leading cursor: a checkpoint-bearing cursor that legitimately leads the source's
|
|
613
|
+
// confirmed checkpointed tip is clamped down to that tip, resolving to the id the prune event carries
|
|
614
|
+
// (never genesis, never a throw). This is the skipped-history shape where the cursor sits on a block
|
|
615
|
+
// ahead of the checkpointed frontier.
|
|
616
|
+
it('clamps a leading checkpoint cursor down to the source checkpointed tip carried by the prune event', async () => {
|
|
617
|
+
// Checkpoint blocks 1-5 (checkpointed = block 5 / ckpt 1), then add uncheckpointed blocks 6-10.
|
|
618
|
+
const blocks1to5 = await Promise.all(times(5, i => makeBlock(i + 1)));
|
|
619
|
+
await tipsStore.handleBlockStreamEvent({ type: 'blocks-added', blocks: blocks1to5 });
|
|
620
|
+
const checkpoint1 = await makeCheckpoint(1, blocks1to5);
|
|
621
|
+
await tipsStore.handleBlockStreamEvent(await makeCheckpointedEvent(checkpoint1));
|
|
622
|
+
const blocks6to10 = await Promise.all(times(5, i => makeBlock(i + 6)));
|
|
623
|
+
await tipsStore.handleBlockStreamEvent({ type: 'blocks-added', blocks: blocks6to10 });
|
|
624
|
+
|
|
625
|
+
// Advance the proven cursor onto an uncheckpointed block (8) via a carried id, so it LEADS the
|
|
626
|
+
// source checkpointed tip (block 5) and will be clamped down to it on prune.
|
|
627
|
+
await tipsStore.handleBlockStreamEvent({
|
|
628
|
+
type: 'chain-proven',
|
|
629
|
+
block: makeBlockId(8),
|
|
630
|
+
checkpoint: { number: CheckpointNumber(2), hash: new Fr(202).toString() },
|
|
631
|
+
});
|
|
632
|
+
|
|
633
|
+
// Prune to block 7, carrying the source's confirmed checkpointed and proven tips (both block 5 / ckpt 1
|
|
634
|
+
// here, the source's proven tip having rolled back together with its checkpointed tip).
|
|
635
|
+
await tipsStore.handleBlockStreamEvent({
|
|
636
|
+
type: 'chain-pruned',
|
|
637
|
+
block: makeBlockId(7),
|
|
638
|
+
checkpointed: makeTipId(5),
|
|
639
|
+
proven: makeTipId(5),
|
|
640
|
+
});
|
|
641
|
+
|
|
642
|
+
// Must not throw; the proven cursor is clamped to the carried proven tip, resolving to ckpt 1.
|
|
643
|
+
const tips = await tipsStore.getL2Tips();
|
|
644
|
+
expect(tips.proposed).toEqual(makeTip(7));
|
|
645
|
+
expect(tips.proven.block).toEqual(makeTip(5));
|
|
646
|
+
expect(tips.proven.checkpoint.number).toEqual(CheckpointNumber(1));
|
|
647
|
+
});
|
|
648
|
+
|
|
649
|
+
it('keeps the checkpointed tip when pruning to an uncheckpointed block ahead of it', async () => {
|
|
650
|
+
const blocks1to5 = await Promise.all(times(5, i => makeBlock(i + 1)));
|
|
651
|
+
await tipsStore.handleBlockStreamEvent({ type: 'blocks-added', blocks: blocks1to5 });
|
|
652
|
+
const checkpoint1 = await makeCheckpoint(1, blocks1to5);
|
|
653
|
+
await tipsStore.handleBlockStreamEvent(await makeCheckpointedEvent(checkpoint1)); // checkpointed = block 5 / ckpt 1
|
|
654
|
+
|
|
655
|
+
const blocks6to7 = await Promise.all(times(2, i => makeBlock(i + 6)));
|
|
656
|
+
await tipsStore.handleBlockStreamEvent({ type: 'blocks-added', blocks: blocks6to7 }); // proposed = 7
|
|
657
|
+
|
|
658
|
+
// Prune to block 6: an uncheckpointed block AHEAD of the checkpointed tip (block 5). The source
|
|
659
|
+
// checkpointed tip is still block 5 / ckpt 1, so the checkpointed cursor must not move.
|
|
660
|
+
await tipsStore.handleBlockStreamEvent({
|
|
661
|
+
type: 'chain-pruned',
|
|
662
|
+
block: makeBlockId(6),
|
|
663
|
+
checkpointed: makeTipId(5),
|
|
664
|
+
proven: makeTipId(0),
|
|
665
|
+
});
|
|
666
|
+
|
|
667
|
+
const tips = await tipsStore.getL2Tips();
|
|
668
|
+
expect(tips.proposed).toEqual(makeTip(6));
|
|
669
|
+
expect(tips.checkpointed.block).toEqual(makeTip(5));
|
|
670
|
+
expect(tips.checkpointed.checkpoint.number).toEqual(CheckpointNumber(1)); // must NOT be zero
|
|
671
|
+
});
|
|
672
|
+
|
|
673
|
+
// Per-cursor clamping on prune: when the source rolls back its proven tip below its checkpointed tip
|
|
674
|
+
// (e.g. a proof tx dropped by an L1 reorg), the prune event carries both source tips and each local
|
|
675
|
+
// cursor must clamp to its OWN source tip. Clamping the proven cursor onto the (higher) checkpointed
|
|
676
|
+
// tip would transiently report unproven blocks as proven until the corrective chain-proven event lands.
|
|
677
|
+
it('clamps the proven cursor to the source proven tip, separately from the checkpointed cursor, on prune', async () => {
|
|
678
|
+
// Checkpoint blocks 1-15 across three checkpoints (ckpt 1 = 1-5, ckpt 2 = 6-10, ckpt 3 = 11-15).
|
|
679
|
+
const blocks1to5 = await Promise.all(times(5, i => makeBlock(i + 1)));
|
|
680
|
+
await tipsStore.handleBlockStreamEvent({ type: 'blocks-added', blocks: blocks1to5 });
|
|
681
|
+
await tipsStore.handleBlockStreamEvent(await makeCheckpointedEvent(await makeCheckpoint(1, blocks1to5)));
|
|
682
|
+
const blocks6to10 = await Promise.all(times(5, i => makeBlock(i + 6)));
|
|
683
|
+
await tipsStore.handleBlockStreamEvent({ type: 'blocks-added', blocks: blocks6to10 });
|
|
684
|
+
await tipsStore.handleBlockStreamEvent(await makeCheckpointedEvent(await makeCheckpoint(2, blocks6to10)));
|
|
685
|
+
const blocks11to15 = await Promise.all(times(5, i => makeBlock(i + 11)));
|
|
686
|
+
await tipsStore.handleBlockStreamEvent({ type: 'blocks-added', blocks: blocks11to15 });
|
|
687
|
+
await tipsStore.handleBlockStreamEvent(await makeCheckpointedEvent(await makeCheckpoint(3, blocks11to15)));
|
|
688
|
+
|
|
689
|
+
// Prove the whole chain up to block 15 (ckpt 3), so the local proven cursor leads both source tips below.
|
|
690
|
+
await tipsStore.handleBlockStreamEvent({
|
|
691
|
+
type: 'chain-proven',
|
|
692
|
+
block: makeBlockId(15),
|
|
693
|
+
checkpoint: makeCheckpointIdForBlock(15),
|
|
694
|
+
});
|
|
695
|
+
|
|
696
|
+
let tips = await tipsStore.getL2Tips();
|
|
697
|
+
expect(tips.checkpointed.block).toEqual(makeTip(15));
|
|
698
|
+
expect(tips.proven.block).toEqual(makeTip(15));
|
|
699
|
+
|
|
700
|
+
// Prune arrives with the source's proven tip (block 5 / ckpt 1) BELOW its checkpointed tip (block 10 /
|
|
701
|
+
// ckpt 2) and below the local proven cursor (block 15). Each cursor must clamp to its own source tip.
|
|
702
|
+
await tipsStore.handleBlockStreamEvent({
|
|
703
|
+
type: 'chain-pruned',
|
|
704
|
+
block: makeBlockId(10),
|
|
705
|
+
checkpointed: makeTipId(10),
|
|
706
|
+
proven: makeTipId(5),
|
|
707
|
+
});
|
|
708
|
+
|
|
709
|
+
tips = await tipsStore.getL2Tips();
|
|
710
|
+
// The proven cursor lands exactly on the source proven tip, NOT on the (higher) checkpointed tip.
|
|
711
|
+
expect(tips.proven.block).toEqual(makeTip(5));
|
|
712
|
+
expect(tips.proven.checkpoint.number).toEqual(CheckpointNumber(1));
|
|
713
|
+
// The checkpointed cursor lands on the source checkpointed tip.
|
|
714
|
+
expect(tips.checkpointed.block).toEqual(makeTip(10));
|
|
715
|
+
expect(tips.checkpointed.checkpoint.number).toEqual(CheckpointNumber(2));
|
|
543
716
|
});
|
|
544
717
|
}
|
package/src/config/index.ts
CHANGED