@aztec/archiver 0.0.1-commit.bf2612ae → 0.0.1-commit.c0b82b2

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.
Files changed (84) hide show
  1. package/dest/archiver.d.ts +5 -2
  2. package/dest/archiver.d.ts.map +1 -1
  3. package/dest/archiver.js +24 -93
  4. package/dest/factory.d.ts +1 -1
  5. package/dest/factory.d.ts.map +1 -1
  6. package/dest/factory.js +10 -9
  7. package/dest/index.d.ts +2 -1
  8. package/dest/index.d.ts.map +1 -1
  9. package/dest/index.js +1 -0
  10. package/dest/l1/bin/retrieve-calldata.js +36 -33
  11. package/dest/l1/calldata_retriever.d.ts +73 -50
  12. package/dest/l1/calldata_retriever.d.ts.map +1 -1
  13. package/dest/l1/calldata_retriever.js +190 -259
  14. package/dest/l1/data_retrieval.d.ts +9 -9
  15. package/dest/l1/data_retrieval.d.ts.map +1 -1
  16. package/dest/l1/data_retrieval.js +22 -20
  17. package/dest/l1/spire_proposer.d.ts +5 -5
  18. package/dest/l1/spire_proposer.d.ts.map +1 -1
  19. package/dest/l1/spire_proposer.js +9 -17
  20. package/dest/l1/validate_trace.d.ts +6 -3
  21. package/dest/l1/validate_trace.d.ts.map +1 -1
  22. package/dest/l1/validate_trace.js +13 -9
  23. package/dest/modules/data_source_base.d.ts +11 -6
  24. package/dest/modules/data_source_base.d.ts.map +1 -1
  25. package/dest/modules/data_source_base.js +28 -72
  26. package/dest/modules/data_store_updater.d.ts +9 -2
  27. package/dest/modules/data_store_updater.d.ts.map +1 -1
  28. package/dest/modules/data_store_updater.js +40 -19
  29. package/dest/modules/instrumentation.d.ts +15 -2
  30. package/dest/modules/instrumentation.d.ts.map +1 -1
  31. package/dest/modules/instrumentation.js +36 -12
  32. package/dest/modules/l1_synchronizer.d.ts +4 -8
  33. package/dest/modules/l1_synchronizer.d.ts.map +1 -1
  34. package/dest/modules/l1_synchronizer.js +16 -12
  35. package/dest/store/block_store.d.ts +19 -15
  36. package/dest/store/block_store.d.ts.map +1 -1
  37. package/dest/store/block_store.js +71 -19
  38. package/dest/store/contract_class_store.d.ts +1 -1
  39. package/dest/store/contract_class_store.d.ts.map +1 -1
  40. package/dest/store/contract_class_store.js +11 -7
  41. package/dest/store/kv_archiver_store.d.ts +21 -7
  42. package/dest/store/kv_archiver_store.d.ts.map +1 -1
  43. package/dest/store/kv_archiver_store.js +20 -3
  44. package/dest/store/l2_tips_cache.d.ts +19 -0
  45. package/dest/store/l2_tips_cache.d.ts.map +1 -0
  46. package/dest/store/l2_tips_cache.js +89 -0
  47. package/dest/store/log_store.d.ts +1 -1
  48. package/dest/store/log_store.d.ts.map +1 -1
  49. package/dest/store/log_store.js +56 -36
  50. package/dest/test/fake_l1_state.d.ts +6 -1
  51. package/dest/test/fake_l1_state.d.ts.map +1 -1
  52. package/dest/test/fake_l1_state.js +56 -18
  53. package/dest/test/index.d.ts +1 -2
  54. package/dest/test/index.d.ts.map +1 -1
  55. package/dest/test/index.js +3 -2
  56. package/dest/test/mock_archiver.d.ts +1 -1
  57. package/dest/test/mock_archiver.d.ts.map +1 -1
  58. package/dest/test/mock_archiver.js +3 -2
  59. package/dest/test/mock_l2_block_source.d.ts +21 -6
  60. package/dest/test/mock_l2_block_source.d.ts.map +1 -1
  61. package/dest/test/mock_l2_block_source.js +127 -84
  62. package/package.json +14 -13
  63. package/src/archiver.ts +31 -111
  64. package/src/factory.ts +24 -11
  65. package/src/index.ts +1 -0
  66. package/src/l1/README.md +25 -68
  67. package/src/l1/bin/retrieve-calldata.ts +46 -39
  68. package/src/l1/calldata_retriever.ts +249 -379
  69. package/src/l1/data_retrieval.ts +24 -26
  70. package/src/l1/spire_proposer.ts +7 -15
  71. package/src/l1/validate_trace.ts +24 -6
  72. package/src/modules/data_source_base.ts +56 -95
  73. package/src/modules/data_store_updater.ts +43 -18
  74. package/src/modules/instrumentation.ts +44 -12
  75. package/src/modules/l1_synchronizer.ts +17 -15
  76. package/src/store/block_store.ts +87 -38
  77. package/src/store/contract_class_store.ts +11 -7
  78. package/src/store/kv_archiver_store.ts +40 -8
  79. package/src/store/l2_tips_cache.ts +89 -0
  80. package/src/store/log_store.ts +95 -33
  81. package/src/test/fake_l1_state.ts +75 -17
  82. package/src/test/index.ts +3 -1
  83. package/src/test/mock_archiver.ts +3 -2
  84. package/src/test/mock_l2_block_source.ts +163 -83
@@ -5,27 +5,39 @@ import { Buffer32 } from '@aztec/foundation/buffer';
5
5
  import { Fr } from '@aztec/foundation/curves/bn254';
6
6
  import { EthAddress } from '@aztec/foundation/eth-address';
7
7
  import { createLogger } from '@aztec/foundation/log';
8
- import { BlockHash, CheckpointedL2Block, L2Block } from '@aztec/stdlib/block';
8
+ import { CheckpointedL2Block } from '@aztec/stdlib/block';
9
9
  import { Checkpoint, L1PublishedData, PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
10
10
  import { EmptyL1RollupConstants, getSlotRangeForEpoch } from '@aztec/stdlib/epoch-helpers';
11
+ import { computeCheckpointOutHash } from '@aztec/stdlib/messaging';
12
+ import { CheckpointHeader } from '@aztec/stdlib/rollup';
11
13
  import { TxExecutionResult, TxReceipt, TxStatus } from '@aztec/stdlib/tx';
12
14
  /**
13
15
  * A mocked implementation of L2BlockSource to be used in tests.
14
16
  */ export class MockL2BlockSource {
15
17
  l2Blocks = [];
18
+ checkpointList = [];
16
19
  provenBlockNumber = 0;
17
20
  finalizedBlockNumber = 0;
18
21
  checkpointedBlockNumber = 0;
19
22
  log = createLogger('archiver:mock_l2_block_source');
20
- async createBlocks(numBlocks) {
21
- for(let i = 0; i < numBlocks; i++){
22
- const blockNum = this.l2Blocks.length + 1;
23
- const block = await L2Block.random(BlockNumber(blockNum), {
24
- slotNumber: SlotNumber(blockNum)
23
+ /** Creates blocks grouped into single-block checkpoints. */ async createBlocks(numBlocks) {
24
+ await this.createCheckpoints(numBlocks, 1);
25
+ }
26
+ /** Creates checkpoints, each containing `blocksPerCheckpoint` blocks. */ async createCheckpoints(numCheckpoints, blocksPerCheckpoint = 1) {
27
+ for(let c = 0; c < numCheckpoints; c++){
28
+ const checkpointNum = CheckpointNumber(this.checkpointList.length + 1);
29
+ const startBlockNum = this.l2Blocks.length + 1;
30
+ const slotNumber = SlotNumber(Number(checkpointNum));
31
+ const checkpoint = await Checkpoint.random(checkpointNum, {
32
+ numBlocks: blocksPerCheckpoint,
33
+ startBlockNumber: startBlockNum,
34
+ slotNumber,
35
+ checkpointNumber: checkpointNum
25
36
  });
26
- this.l2Blocks.push(block);
37
+ this.checkpointList.push(checkpoint);
38
+ this.l2Blocks.push(...checkpoint.blocks);
27
39
  }
28
- this.log.verbose(`Created ${numBlocks} blocks in the mock L2 block source`);
40
+ this.log.verbose(`Created ${numCheckpoints} checkpoints with ${blocksPerCheckpoint} blocks each in the mock L2 block source`);
29
41
  }
30
42
  addProposedBlocks(blocks) {
31
43
  this.l2Blocks.push(...blocks);
@@ -33,6 +45,16 @@ import { TxExecutionResult, TxReceipt, TxStatus } from '@aztec/stdlib/tx';
33
45
  }
34
46
  removeBlocks(numBlocks) {
35
47
  this.l2Blocks = this.l2Blocks.slice(0, -numBlocks);
48
+ const maxBlockNum = this.l2Blocks.length;
49
+ // Remove any checkpoint whose last block is beyond the remaining blocks.
50
+ this.checkpointList = this.checkpointList.filter((c)=>{
51
+ const lastBlockNum = c.blocks[0].number + c.blocks.length - 1;
52
+ return lastBlockNum <= maxBlockNum;
53
+ });
54
+ // Keep tip numbers consistent with remaining blocks.
55
+ this.checkpointedBlockNumber = Math.min(this.checkpointedBlockNumber, maxBlockNum);
56
+ this.provenBlockNumber = Math.min(this.provenBlockNumber, maxBlockNum);
57
+ this.finalizedBlockNumber = Math.min(this.finalizedBlockNumber, maxBlockNum);
36
58
  this.log.verbose(`Removed ${numBlocks} blocks from the mock L2 block source`);
37
59
  }
38
60
  setProvenBlockNumber(provenBlockNumber) {
@@ -45,7 +67,32 @@ import { TxExecutionResult, TxReceipt, TxStatus } from '@aztec/stdlib/tx';
45
67
  this.finalizedBlockNumber = finalizedBlockNumber;
46
68
  }
47
69
  setCheckpointedBlockNumber(checkpointedBlockNumber) {
70
+ const prevCheckpointed = this.checkpointedBlockNumber;
48
71
  this.checkpointedBlockNumber = checkpointedBlockNumber;
72
+ // Auto-create single-block checkpoints for newly checkpointed blocks that don't have one yet.
73
+ // This handles blocks added via addProposedBlocks that are now being marked as checkpointed.
74
+ const newCheckpoints = [];
75
+ for(let blockNum = prevCheckpointed + 1; blockNum <= checkpointedBlockNumber; blockNum++){
76
+ const block = this.l2Blocks[blockNum - 1];
77
+ if (!block) {
78
+ continue;
79
+ }
80
+ if (this.checkpointList.some((c)=>c.blocks.some((b)=>b.number === block.number))) {
81
+ continue;
82
+ }
83
+ const checkpointNum = CheckpointNumber(this.checkpointList.length + newCheckpoints.length + 1);
84
+ const checkpoint = new Checkpoint(block.archive, CheckpointHeader.random({
85
+ slotNumber: block.header.globalVariables.slotNumber
86
+ }), [
87
+ block
88
+ ], checkpointNum);
89
+ newCheckpoints.push(checkpoint);
90
+ }
91
+ // Insert new checkpoints in order by number.
92
+ if (newCheckpoints.length > 0) {
93
+ this.checkpointList.push(...newCheckpoints);
94
+ this.checkpointList.sort((a, b)=>a.number - b.number);
95
+ }
49
96
  }
50
97
  /**
51
98
  * Method to fetch the rollup contract address at the base-layer.
@@ -82,8 +129,7 @@ import { TxExecutionResult, TxReceipt, TxStatus } from '@aztec/stdlib/tx';
82
129
  if (!block) {
83
130
  return Promise.resolve(undefined);
84
131
  }
85
- const checkpointedBlock = new CheckpointedL2Block(CheckpointNumber.fromBlockNumber(number), block, new L1PublishedData(BigInt(number), BigInt(number), `0x${number.toString(16).padStart(64, '0')}`), []);
86
- return Promise.resolve(checkpointedBlock);
132
+ return Promise.resolve(this.toCheckpointedBlock(block));
87
133
  }
88
134
  async getCheckpointedBlocks(from, limit) {
89
135
  const result = [];
@@ -124,44 +170,18 @@ import { TxExecutionResult, TxReceipt, TxStatus } from '@aztec/stdlib/tx';
124
170
  return Promise.resolve(this.l2Blocks.slice(from - 1, from - 1 + limit));
125
171
  }
126
172
  getCheckpoints(from, limit) {
127
- // TODO(mbps): Implement this properly. This only works when we have one block per checkpoint.
128
- const blocks = this.l2Blocks.slice(from - 1, from - 1 + limit);
129
- return Promise.all(blocks.map(async (block)=>{
130
- // Create a checkpoint from the block - manually construct since L2Block doesn't have toCheckpoint()
131
- const checkpoint = await Checkpoint.random(block.checkpointNumber, {
132
- numBlocks: 1
133
- });
134
- checkpoint.blocks = [
135
- block
136
- ];
137
- return new PublishedCheckpoint(checkpoint, new L1PublishedData(BigInt(block.number), BigInt(block.number), Buffer32.random().toString()), []);
138
- }));
173
+ const checkpoints = this.checkpointList.slice(from - 1, from - 1 + limit);
174
+ return Promise.resolve(checkpoints.map((checkpoint)=>new PublishedCheckpoint(checkpoint, this.mockL1DataForCheckpoint(checkpoint), [])));
139
175
  }
140
- async getCheckpointByArchive(archive) {
141
- // TODO(mbps): Implement this properly. This only works when we have one block per checkpoint.
142
- const block = this.l2Blocks.find((b)=>b.archive.root.equals(archive));
143
- if (!block) {
144
- return undefined;
145
- }
146
- // Create a checkpoint from the block - manually construct since L2Block doesn't have toCheckpoint()
147
- const checkpoint = await Checkpoint.random(block.checkpointNumber, {
148
- numBlocks: 1
149
- });
150
- checkpoint.blocks = [
151
- block
152
- ];
153
- return checkpoint;
176
+ getCheckpointByArchive(archive) {
177
+ const checkpoint = this.checkpointList.find((c)=>c.archive.root.equals(archive));
178
+ return Promise.resolve(checkpoint);
154
179
  }
155
180
  async getCheckpointedBlockByHash(blockHash) {
156
181
  for (const block of this.l2Blocks){
157
182
  const hash = await block.hash();
158
183
  if (hash.equals(blockHash)) {
159
- return CheckpointedL2Block.fromFields({
160
- checkpointNumber: CheckpointNumber.fromBlockNumber(block.number),
161
- block,
162
- l1: new L1PublishedData(BigInt(block.number), BigInt(block.number), Buffer32.random().toString()),
163
- attestations: []
164
- });
184
+ return this.toCheckpointedBlock(block);
165
185
  }
166
186
  }
167
187
  return undefined;
@@ -171,12 +191,7 @@ import { TxExecutionResult, TxReceipt, TxStatus } from '@aztec/stdlib/tx';
171
191
  if (!block) {
172
192
  return Promise.resolve(undefined);
173
193
  }
174
- return Promise.resolve(CheckpointedL2Block.fromFields({
175
- checkpointNumber: CheckpointNumber.fromBlockNumber(block.number),
176
- block,
177
- l1: new L1PublishedData(BigInt(block.number), BigInt(block.number), Buffer32.random().toString()),
178
- attestations: []
179
- }));
194
+ return Promise.resolve(this.toCheckpointedBlock(block));
180
195
  }
181
196
  async getL2BlockByHash(blockHash) {
182
197
  for (const block of this.l2Blocks){
@@ -204,45 +219,54 @@ import { TxExecutionResult, TxReceipt, TxStatus } from '@aztec/stdlib/tx';
204
219
  const block = this.l2Blocks.find((b)=>b.archive.root.equals(archive));
205
220
  return Promise.resolve(block?.header);
206
221
  }
222
+ async getBlockData(number) {
223
+ const block = this.l2Blocks[number - 1];
224
+ if (!block) {
225
+ return undefined;
226
+ }
227
+ return {
228
+ header: block.header,
229
+ archive: block.archive,
230
+ blockHash: await block.hash(),
231
+ checkpointNumber: block.checkpointNumber,
232
+ indexWithinCheckpoint: block.indexWithinCheckpoint
233
+ };
234
+ }
235
+ async getBlockDataByArchive(archive) {
236
+ const block = this.l2Blocks.find((b)=>b.archive.root.equals(archive));
237
+ if (!block) {
238
+ return undefined;
239
+ }
240
+ return {
241
+ header: block.header,
242
+ archive: block.archive,
243
+ blockHash: await block.hash(),
244
+ checkpointNumber: block.checkpointNumber,
245
+ indexWithinCheckpoint: block.indexWithinCheckpoint
246
+ };
247
+ }
207
248
  getBlockHeader(number) {
208
249
  return Promise.resolve(this.l2Blocks.at(typeof number === 'number' ? number - 1 : -1)?.header);
209
250
  }
210
251
  getCheckpointsForEpoch(epochNumber) {
211
- // TODO(mbps): Implement this properly. This only works when we have one block per checkpoint.
212
- const epochDuration = DefaultL1ContractsConfig.aztecEpochDuration;
213
- const [start, end] = getSlotRangeForEpoch(epochNumber, {
214
- epochDuration
215
- });
216
- const blocks = this.l2Blocks.filter((b)=>{
217
- const slot = b.header.globalVariables.slotNumber;
218
- return slot >= start && slot <= end;
219
- });
220
- // Create checkpoints from blocks - manually construct since L2Block doesn't have toCheckpoint()
221
- return Promise.all(blocks.map(async (block)=>{
222
- const checkpoint = await Checkpoint.random(block.checkpointNumber, {
223
- numBlocks: 1
224
- });
225
- checkpoint.blocks = [
226
- block
227
- ];
228
- return checkpoint;
229
- }));
252
+ return Promise.resolve(this.getCheckpointsInEpoch(epochNumber));
253
+ }
254
+ getCheckpointsDataForEpoch(epochNumber) {
255
+ const checkpoints = this.getCheckpointsInEpoch(epochNumber);
256
+ return Promise.resolve(checkpoints.map((checkpoint)=>({
257
+ checkpointNumber: checkpoint.number,
258
+ header: checkpoint.header,
259
+ archive: checkpoint.archive,
260
+ checkpointOutHash: computeCheckpointOutHash(checkpoint.blocks.map((b)=>b.body.txEffects.map((tx)=>tx.l2ToL1Msgs))),
261
+ startBlock: checkpoint.blocks[0].number,
262
+ blockCount: checkpoint.blocks.length,
263
+ attestations: [],
264
+ l1: this.mockL1DataForCheckpoint(checkpoint)
265
+ })));
230
266
  }
231
267
  getCheckpointedBlocksForEpoch(epochNumber) {
232
- const epochDuration = DefaultL1ContractsConfig.aztecEpochDuration;
233
- const [start, end] = getSlotRangeForEpoch(epochNumber, {
234
- epochDuration
235
- });
236
- const blocks = this.l2Blocks.filter((b)=>{
237
- const slot = b.header.globalVariables.slotNumber;
238
- return slot >= start && slot <= end;
239
- });
240
- return Promise.resolve(blocks.map((block)=>CheckpointedL2Block.fromFields({
241
- checkpointNumber: CheckpointNumber.fromBlockNumber(block.number),
242
- block,
243
- l1: new L1PublishedData(BigInt(block.number), BigInt(block.number), Buffer32.random().toString()),
244
- attestations: []
245
- })));
268
+ const checkpoints = this.getCheckpointsInEpoch(epochNumber);
269
+ return Promise.resolve(checkpoints.flatMap((checkpoint)=>checkpoint.blocks.map((block)=>this.toCheckpointedBlock(block))));
246
270
  }
247
271
  getBlocksForSlot(slotNumber) {
248
272
  const blocks = this.l2Blocks.filter((b)=>b.header.globalVariables.slotNumber === slotNumber);
@@ -268,7 +292,7 @@ import { TxExecutionResult, TxReceipt, TxStatus } from '@aztec/stdlib/tx';
268
292
  return {
269
293
  data: txEffect,
270
294
  l2BlockNumber: block.number,
271
- l2BlockHash: BlockHash.fromField(await block.hash()),
295
+ l2BlockHash: await block.hash(),
272
296
  txIndexInBlock: block.body.txEffects.indexOf(txEffect)
273
297
  };
274
298
  }
@@ -281,7 +305,7 @@ import { TxExecutionResult, TxReceipt, TxStatus } from '@aztec/stdlib/tx';
281
305
  for (const txEffect of block.body.txEffects){
282
306
  if (txEffect.txHash.equals(txHash)) {
283
307
  // In mock, assume all txs are checkpointed with successful execution
284
- return new TxReceipt(txHash, TxStatus.CHECKPOINTED, TxExecutionResult.SUCCESS, undefined, txEffect.transactionFee.toBigInt(), BlockHash.fromField(await block.hash()), block.number);
308
+ return new TxReceipt(txHash, TxStatus.CHECKPOINTED, TxExecutionResult.SUCCESS, undefined, txEffect.transactionFee.toBigInt(), await block.hash(), block.number);
285
309
  }
286
310
  }
287
311
  }
@@ -317,7 +341,7 @@ import { TxExecutionResult, TxReceipt, TxStatus } from '@aztec/stdlib/tx';
317
341
  const makeTipId = (blockId)=>({
318
342
  block: blockId,
319
343
  checkpoint: {
320
- number: CheckpointNumber.fromBlockNumber(blockId.number),
344
+ number: this.findCheckpointNumberForBlock(blockId.number) ?? CheckpointNumber(0),
321
345
  hash: blockId.hash
322
346
  }
323
347
  });
@@ -391,4 +415,23 @@ import { TxExecutionResult, TxReceipt, TxStatus } from '@aztec/stdlib/tx';
391
415
  valid: true
392
416
  });
393
417
  }
418
+ /** Returns checkpoints whose slot falls within the given epoch. */ getCheckpointsInEpoch(epochNumber) {
419
+ const epochDuration = DefaultL1ContractsConfig.aztecEpochDuration;
420
+ const [start, end] = getSlotRangeForEpoch(epochNumber, {
421
+ epochDuration
422
+ });
423
+ return this.checkpointList.filter((c)=>c.header.slotNumber >= start && c.header.slotNumber <= end);
424
+ }
425
+ /** Creates a mock L1PublishedData for a checkpoint. */ mockL1DataForCheckpoint(checkpoint) {
426
+ return new L1PublishedData(BigInt(checkpoint.number), BigInt(checkpoint.number), Buffer32.random().toString());
427
+ }
428
+ /** Creates a CheckpointedL2Block from a block using stored checkpoint info. */ toCheckpointedBlock(block) {
429
+ const checkpoint = this.checkpointList.find((c)=>c.blocks.some((b)=>b.number === block.number));
430
+ const checkpointNumber = checkpoint?.number ?? block.checkpointNumber;
431
+ return new CheckpointedL2Block(checkpointNumber, block, new L1PublishedData(BigInt(block.number), BigInt(block.number), `0x${block.number.toString(16).padStart(64, '0')}`), []);
432
+ }
433
+ /** Finds the checkpoint number for a block, or undefined if the block is not in any checkpoint. */ findCheckpointNumberForBlock(blockNumber) {
434
+ const checkpoint = this.checkpointList.find((c)=>c.blocks.some((b)=>b.number === blockNumber));
435
+ return checkpoint?.number;
436
+ }
394
437
  }
package/package.json CHANGED
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "@aztec/archiver",
3
- "version": "0.0.1-commit.bf2612ae",
3
+ "version": "0.0.1-commit.c0b82b2",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./dest/index.js",
7
7
  "./test": "./dest/test/index.js",
8
+ "./test/noop-l1": "./dest/test/noop_l1_archiver.js",
8
9
  "./config": "./dest/config.js"
9
10
  },
10
11
  "typedocOptions": {
@@ -64,18 +65,18 @@
64
65
  ]
65
66
  },
66
67
  "dependencies": {
67
- "@aztec/blob-client": "0.0.1-commit.bf2612ae",
68
- "@aztec/blob-lib": "0.0.1-commit.bf2612ae",
69
- "@aztec/constants": "0.0.1-commit.bf2612ae",
70
- "@aztec/epoch-cache": "0.0.1-commit.bf2612ae",
71
- "@aztec/ethereum": "0.0.1-commit.bf2612ae",
72
- "@aztec/foundation": "0.0.1-commit.bf2612ae",
73
- "@aztec/kv-store": "0.0.1-commit.bf2612ae",
74
- "@aztec/l1-artifacts": "0.0.1-commit.bf2612ae",
75
- "@aztec/noir-protocol-circuits-types": "0.0.1-commit.bf2612ae",
76
- "@aztec/protocol-contracts": "0.0.1-commit.bf2612ae",
77
- "@aztec/stdlib": "0.0.1-commit.bf2612ae",
78
- "@aztec/telemetry-client": "0.0.1-commit.bf2612ae",
68
+ "@aztec/blob-client": "0.0.1-commit.c0b82b2",
69
+ "@aztec/blob-lib": "0.0.1-commit.c0b82b2",
70
+ "@aztec/constants": "0.0.1-commit.c0b82b2",
71
+ "@aztec/epoch-cache": "0.0.1-commit.c0b82b2",
72
+ "@aztec/ethereum": "0.0.1-commit.c0b82b2",
73
+ "@aztec/foundation": "0.0.1-commit.c0b82b2",
74
+ "@aztec/kv-store": "0.0.1-commit.c0b82b2",
75
+ "@aztec/l1-artifacts": "0.0.1-commit.c0b82b2",
76
+ "@aztec/noir-protocol-circuits-types": "0.0.1-commit.c0b82b2",
77
+ "@aztec/protocol-contracts": "0.0.1-commit.c0b82b2",
78
+ "@aztec/stdlib": "0.0.1-commit.c0b82b2",
79
+ "@aztec/telemetry-client": "0.0.1-commit.c0b82b2",
79
80
  "lodash.groupby": "^4.6.0",
80
81
  "lodash.omit": "^4.5.0",
81
82
  "tslib": "^2.5.0",
package/src/archiver.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  import type { BlobClientInterface } from '@aztec/blob-client/client';
2
- import { GENESIS_BLOCK_HEADER_HASH, INITIAL_L2_BLOCK_NUM } from '@aztec/constants';
3
2
  import { EpochCache } from '@aztec/epoch-cache';
4
3
  import { BlockTagTooOldError, RollupContract } from '@aztec/ethereum/contracts';
5
4
  import type { L1ContractAddresses } from '@aztec/ethereum/l1-contract-addresses';
@@ -15,8 +14,6 @@ import { RunningPromise, makeLoggingErrorHandler } from '@aztec/foundation/runni
15
14
  import { DateProvider } from '@aztec/foundation/timer';
16
15
  import {
17
16
  type ArchiverEmitter,
18
- type CheckpointId,
19
- GENESIS_CHECKPOINT_HEADER_HASH,
20
17
  L2Block,
21
18
  type L2BlockSink,
22
19
  type L2Tips,
@@ -26,6 +23,7 @@ import { PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
26
23
  import {
27
24
  type L1RollupConstants,
28
25
  getEpochNumberAtTimestamp,
26
+ getSlotAtNextL1Block,
29
27
  getSlotAtTimestamp,
30
28
  getSlotRangeForEpoch,
31
29
  getTimestampRangeForEpoch,
@@ -40,6 +38,7 @@ import { ArchiverDataStoreUpdater } from './modules/data_store_updater.js';
40
38
  import type { ArchiverInstrumentation } from './modules/instrumentation.js';
41
39
  import type { ArchiverL1Synchronizer } from './modules/l1_synchronizer.js';
42
40
  import type { KVArchiverDataStore } from './store/kv_archiver_store.js';
41
+ import { L2TipsCache } from './store/l2_tips_cache.js';
43
42
 
44
43
  /** Export ArchiverEmitter for use in factory and tests. */
45
44
  export type { ArchiverEmitter };
@@ -82,6 +81,9 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
82
81
  /** Helper to handle updates to the store */
83
82
  private readonly updater: ArchiverDataStoreUpdater;
84
83
 
84
+ /** In-memory cache for L2 chain tips. */
85
+ private readonly l2TipsCache: L2TipsCache;
86
+
85
87
  public readonly tracer: Tracer;
86
88
 
87
89
  /**
@@ -121,6 +123,7 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
121
123
  protected override readonly l1Constants: L1RollupConstants & { l1StartBlockHash: Buffer32; genesisArchiveRoot: Fr },
122
124
  synchronizer: ArchiverL1Synchronizer,
123
125
  events: ArchiverEmitter,
126
+ l2TipsCache?: L2TipsCache,
124
127
  private readonly log: Logger = createLogger('archiver'),
125
128
  ) {
126
129
  super(dataStore, l1Constants);
@@ -129,7 +132,8 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
129
132
  this.initialSyncPromise = promiseWithResolvers();
130
133
  this.synchronizer = synchronizer;
131
134
  this.events = events;
132
- this.updater = new ArchiverDataStoreUpdater(this.dataStore);
135
+ this.l2TipsCache = l2TipsCache ?? new L2TipsCache(this.dataStore.blockStore);
136
+ this.updater = new ArchiverDataStoreUpdater(this.dataStore, this.l2TipsCache);
133
137
 
134
138
  // Running promise starts with a small interval inbetween runs, so all iterations needed for the initial sync
135
139
  // are done as fast as possible. This then gets updated once the initial sync completes.
@@ -158,7 +162,11 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
158
162
 
159
163
  await this.blobClient.testSources();
160
164
  await this.synchronizer.testEthereumNodeSynced();
161
- await validateAndLogTraceAvailability(this.debugClient, this.config.ethereumAllowNoDebugHosts ?? false);
165
+ await validateAndLogTraceAvailability(
166
+ this.debugClient,
167
+ this.config.ethereumAllowNoDebugHosts ?? false,
168
+ this.log.getBindings(),
169
+ );
162
170
 
163
171
  // Log initial state for the archiver
164
172
  const { l1StartBlock } = this.l1Constants;
@@ -212,8 +220,23 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
212
220
  const queuedItems = this.blockQueue.splice(0, this.blockQueue.length);
213
221
  this.log.debug(`Processing ${queuedItems.length} queued block(s)`);
214
222
 
223
+ // Calculate slot threshold for validation
224
+ const l1Timestamp = this.synchronizer.getL1Timestamp();
225
+ const slotAtNextL1Block =
226
+ l1Timestamp === undefined ? undefined : getSlotAtNextL1Block(l1Timestamp, this.l1Constants);
227
+
215
228
  // Process each block individually to properly resolve/reject each promise
216
229
  for (const { block, resolve, reject } of queuedItems) {
230
+ const blockSlot = block.header.globalVariables.slotNumber;
231
+ if (slotAtNextL1Block !== undefined && blockSlot < slotAtNextL1Block) {
232
+ this.log.warn(
233
+ `Rejecting proposed block ${block.number} for past slot ${blockSlot} (current is ${slotAtNextL1Block})`,
234
+ { block: block.toBlockInfo(), l1Timestamp, slotAtNextL1Block },
235
+ );
236
+ reject(new Error(`Block ${block.number} is for past slot ${blockSlot} (current is ${slotAtNextL1Block})`));
237
+ continue;
238
+ }
239
+
217
240
  try {
218
241
  await this.updater.addProposedBlocks([block]);
219
242
  this.log.debug(`Added block ${block.number} to store`);
@@ -371,111 +394,8 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
371
394
  return true;
372
395
  }
373
396
 
374
- public async getL2Tips(): Promise<L2Tips> {
375
- const [latestBlockNumber, provenBlockNumber, checkpointedBlockNumber, finalizedBlockNumber] = await Promise.all([
376
- this.getBlockNumber(),
377
- this.getProvenBlockNumber(),
378
- this.getCheckpointedL2BlockNumber(),
379
- this.getFinalizedL2BlockNumber(),
380
- ] as const);
381
-
382
- const beforeInitialblockNumber = BlockNumber(INITIAL_L2_BLOCK_NUM - 1);
383
-
384
- // Get the latest block header and checkpointed blocks for proven, finalised and checkpointed blocks
385
- const [latestBlockHeader, provenCheckpointedBlock, finalizedCheckpointedBlock, checkpointedBlock] =
386
- await Promise.all([
387
- latestBlockNumber > beforeInitialblockNumber ? this.getBlockHeader(latestBlockNumber) : undefined,
388
- provenBlockNumber > beforeInitialblockNumber ? this.getCheckpointedBlock(provenBlockNumber) : undefined,
389
- finalizedBlockNumber > beforeInitialblockNumber ? this.getCheckpointedBlock(finalizedBlockNumber) : undefined,
390
- checkpointedBlockNumber > beforeInitialblockNumber
391
- ? this.getCheckpointedBlock(checkpointedBlockNumber)
392
- : undefined,
393
- ] as const);
394
-
395
- if (latestBlockNumber > beforeInitialblockNumber && !latestBlockHeader) {
396
- throw new Error(`Failed to retrieve latest block header for block ${latestBlockNumber}`);
397
- }
398
-
399
- // Checkpointed blocks must exist for proven, finalized and checkpointed tips if they are beyond the initial block number.
400
- if (checkpointedBlockNumber > beforeInitialblockNumber && !checkpointedBlock?.block.header) {
401
- throw new Error(
402
- `Failed to retrieve checkpointed block header for block ${checkpointedBlockNumber} (latest block is ${latestBlockNumber})`,
403
- );
404
- }
405
-
406
- if (provenBlockNumber > beforeInitialblockNumber && !provenCheckpointedBlock?.block.header) {
407
- throw new Error(
408
- `Failed to retrieve proven checkpointed for block ${provenBlockNumber} (latest block is ${latestBlockNumber})`,
409
- );
410
- }
411
-
412
- if (finalizedBlockNumber > beforeInitialblockNumber && !finalizedCheckpointedBlock?.block.header) {
413
- throw new Error(
414
- `Failed to retrieve finalized block header for block ${finalizedBlockNumber} (latest block is ${latestBlockNumber})`,
415
- );
416
- }
417
-
418
- const latestBlockHeaderHash = (await latestBlockHeader?.hash()) ?? GENESIS_BLOCK_HEADER_HASH;
419
- const provenBlockHeaderHash = (await provenCheckpointedBlock?.block.header?.hash()) ?? GENESIS_BLOCK_HEADER_HASH;
420
- const finalizedBlockHeaderHash =
421
- (await finalizedCheckpointedBlock?.block.header?.hash()) ?? GENESIS_BLOCK_HEADER_HASH;
422
- const checkpointedBlockHeaderHash = (await checkpointedBlock?.block.header?.hash()) ?? GENESIS_BLOCK_HEADER_HASH;
423
-
424
- // Now attempt to retrieve checkpoints for proven, finalised and checkpointed blocks
425
- const [[provenBlockCheckpoint], [finalizedBlockCheckpoint], [checkpointedBlockCheckpoint]] = await Promise.all([
426
- provenCheckpointedBlock !== undefined
427
- ? await this.getCheckpoints(provenCheckpointedBlock?.checkpointNumber, 1)
428
- : [undefined],
429
- finalizedCheckpointedBlock !== undefined
430
- ? await this.getCheckpoints(finalizedCheckpointedBlock?.checkpointNumber, 1)
431
- : [undefined],
432
- checkpointedBlock !== undefined ? await this.getCheckpoints(checkpointedBlock?.checkpointNumber, 1) : [undefined],
433
- ]);
434
-
435
- const initialcheckpointId: CheckpointId = {
436
- number: CheckpointNumber.ZERO,
437
- hash: GENESIS_CHECKPOINT_HEADER_HASH.toString(),
438
- };
439
-
440
- const makeCheckpointId = (checkpoint: PublishedCheckpoint | undefined) => {
441
- if (checkpoint === undefined) {
442
- return initialcheckpointId;
443
- }
444
- return {
445
- number: checkpoint.checkpoint.number,
446
- hash: checkpoint.checkpoint.hash().toString(),
447
- };
448
- };
449
-
450
- const l2Tips: L2Tips = {
451
- proposed: {
452
- number: latestBlockNumber,
453
- hash: latestBlockHeaderHash.toString(),
454
- },
455
- proven: {
456
- block: {
457
- number: provenBlockNumber,
458
- hash: provenBlockHeaderHash.toString(),
459
- },
460
- checkpoint: makeCheckpointId(provenBlockCheckpoint),
461
- },
462
- finalized: {
463
- block: {
464
- number: finalizedBlockNumber,
465
- hash: finalizedBlockHeaderHash.toString(),
466
- },
467
- checkpoint: makeCheckpointId(finalizedBlockCheckpoint),
468
- },
469
- checkpointed: {
470
- block: {
471
- number: checkpointedBlockNumber,
472
- hash: checkpointedBlockHeaderHash.toString(),
473
- },
474
- checkpoint: makeCheckpointId(checkpointedBlockCheckpoint),
475
- },
476
- };
477
-
478
- return l2Tips;
397
+ public getL2Tips(): Promise<L2Tips> {
398
+ return this.l2TipsCache.getL2Tips();
479
399
  }
480
400
 
481
401
  public async rollbackTo(targetL2BlockNumber: BlockNumber): Promise<void> {
@@ -512,7 +432,7 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
512
432
  await this.store.setMessageSynchedL1Block({ l1BlockNumber: targetL1BlockNumber, l1BlockHash: targetL1BlockHash });
513
433
  if (targetL2BlockNumber < currentProvenBlock) {
514
434
  this.log.info(`Clearing proven L2 block number`);
515
- await this.store.setProvenCheckpointNumber(CheckpointNumber.ZERO);
435
+ await this.updater.setProvenCheckpointNumber(CheckpointNumber.ZERO);
516
436
  }
517
437
  // TODO(palla/reorg): Set the finalized block when we add support for it.
518
438
  // if (targetL2BlockNumber < currentFinalizedBlock) {
package/src/factory.ts CHANGED
@@ -6,7 +6,6 @@ import { BlockNumber } from '@aztec/foundation/branded-types';
6
6
  import { Buffer32 } from '@aztec/foundation/buffer';
7
7
  import { merge } from '@aztec/foundation/collection';
8
8
  import { Fr } from '@aztec/foundation/curves/bn254';
9
- import { createLogger } from '@aztec/foundation/log';
10
9
  import { DateProvider } from '@aztec/foundation/timer';
11
10
  import type { DataStoreConfig } from '@aztec/kv-store/config';
12
11
  import { createStore } from '@aztec/kv-store/lmdb-v2';
@@ -26,6 +25,7 @@ import { type ArchiverConfig, mapArchiverConfig } from './config.js';
26
25
  import { ArchiverInstrumentation } from './modules/instrumentation.js';
27
26
  import { ArchiverL1Synchronizer } from './modules/l1_synchronizer.js';
28
27
  import { ARCHIVER_DB_VERSION, KVArchiverDataStore } from './store/kv_archiver_store.js';
28
+ import { L2TipsCache } from './store/l2_tips_cache.js';
29
29
 
30
30
  export const ARCHIVER_STORE_NAME = 'archiver';
31
31
 
@@ -38,7 +38,7 @@ export async function createArchiverStore(
38
38
  ...userConfig,
39
39
  dataStoreMapSizeKb: userConfig.archiverStoreMapSizeKb ?? userConfig.dataStoreMapSizeKb,
40
40
  };
41
- const store = await createStore(ARCHIVER_STORE_NAME, ARCHIVER_DB_VERSION, config, createLogger('archiver:lmdb'));
41
+ const store = await createStore(ARCHIVER_STORE_NAME, ARCHIVER_DB_VERSION, config);
42
42
  return new KVArchiverDataStore(store, config.maxLogs, l1Constants);
43
43
  }
44
44
 
@@ -78,14 +78,21 @@ export async function createArchiver(
78
78
  const inbox = new InboxContract(publicClient, config.l1Contracts.inboxAddress);
79
79
 
80
80
  // Fetch L1 constants from rollup contract
81
- const [l1StartBlock, l1GenesisTime, proofSubmissionEpochs, genesisArchiveRoot, slashingProposerAddress] =
82
- await Promise.all([
83
- rollup.getL1StartBlock(),
84
- rollup.getL1GenesisTime(),
85
- rollup.getProofSubmissionEpochs(),
86
- rollup.getGenesisArchiveTreeRoot(),
87
- rollup.getSlashingProposerAddress(),
88
- ] as const);
81
+ const [
82
+ l1StartBlock,
83
+ l1GenesisTime,
84
+ proofSubmissionEpochs,
85
+ genesisArchiveRoot,
86
+ slashingProposerAddress,
87
+ targetCommitteeSize,
88
+ ] = await Promise.all([
89
+ rollup.getL1StartBlock(),
90
+ rollup.getL1GenesisTime(),
91
+ rollup.getProofSubmissionEpochs(),
92
+ rollup.getGenesisArchiveTreeRoot(),
93
+ rollup.getSlashingProposerAddress(),
94
+ rollup.getTargetCommitteeSize(),
95
+ ] as const);
89
96
 
90
97
  const l1StartBlockHash = await publicClient
91
98
  .getBlock({ blockNumber: l1StartBlock, includeTransactions: false })
@@ -101,6 +108,7 @@ export async function createArchiver(
101
108
  slotDuration,
102
109
  ethereumSlotDuration,
103
110
  proofSubmissionEpochs: Number(proofSubmissionEpochs),
111
+ targetCommitteeSize,
104
112
  genesisArchiveRoot: Fr.fromString(genesisArchiveRoot.toString()),
105
113
  };
106
114
 
@@ -121,13 +129,15 @@ export async function createArchiver(
121
129
  // Create the event emitter that will be shared by archiver and synchronizer
122
130
  const events = new EventEmitter() as ArchiverEmitter;
123
131
 
132
+ // Create L2 tips cache shared by archiver and synchronizer
133
+ const l2TipsCache = new L2TipsCache(archiverStore.blockStore);
134
+
124
135
  // Create the L1 synchronizer
125
136
  const synchronizer = new ArchiverL1Synchronizer(
126
137
  publicClient,
127
138
  debugClient,
128
139
  rollup,
129
140
  inbox,
130
- { ...config.l1Contracts, slashingProposerAddress },
131
141
  archiverStore,
132
142
  archiverConfig,
133
143
  deps.blobClient,
@@ -137,6 +147,8 @@ export async function createArchiver(
137
147
  l1Constants,
138
148
  events,
139
149
  instrumentation.tracer,
150
+ l2TipsCache,
151
+ undefined, // log (use default)
140
152
  );
141
153
 
142
154
  const archiver = new Archiver(
@@ -151,6 +163,7 @@ export async function createArchiver(
151
163
  l1Constants,
152
164
  synchronizer,
153
165
  events,
166
+ l2TipsCache,
154
167
  );
155
168
 
156
169
  await archiver.start(opts.blockUntilSync);