@aztec/archiver 0.0.1-commit.934299a21 → 0.0.1-commit.949a33fd8

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 (103) hide show
  1. package/README.md +12 -6
  2. package/dest/archiver.d.ts +18 -10
  3. package/dest/archiver.d.ts.map +1 -1
  4. package/dest/archiver.js +146 -57
  5. package/dest/config.d.ts +5 -3
  6. package/dest/config.d.ts.map +1 -1
  7. package/dest/config.js +16 -4
  8. package/dest/errors.d.ts +61 -10
  9. package/dest/errors.d.ts.map +1 -1
  10. package/dest/errors.js +88 -14
  11. package/dest/factory.d.ts +4 -5
  12. package/dest/factory.d.ts.map +1 -1
  13. package/dest/factory.js +26 -22
  14. package/dest/index.d.ts +3 -2
  15. package/dest/index.d.ts.map +1 -1
  16. package/dest/index.js +2 -1
  17. package/dest/l1/calldata_retriever.d.ts +2 -1
  18. package/dest/l1/calldata_retriever.d.ts.map +1 -1
  19. package/dest/l1/calldata_retriever.js +11 -5
  20. package/dest/l1/data_retrieval.d.ts +24 -12
  21. package/dest/l1/data_retrieval.d.ts.map +1 -1
  22. package/dest/l1/data_retrieval.js +36 -37
  23. package/dest/l1/validate_historical_logs.d.ts +23 -0
  24. package/dest/l1/validate_historical_logs.d.ts.map +1 -0
  25. package/dest/l1/validate_historical_logs.js +108 -0
  26. package/dest/modules/data_source_base.d.ts +12 -6
  27. package/dest/modules/data_source_base.d.ts.map +1 -1
  28. package/dest/modules/data_source_base.js +24 -6
  29. package/dest/modules/data_store_updater.d.ts +28 -15
  30. package/dest/modules/data_store_updater.d.ts.map +1 -1
  31. package/dest/modules/data_store_updater.js +102 -80
  32. package/dest/modules/instrumentation.d.ts +7 -2
  33. package/dest/modules/instrumentation.d.ts.map +1 -1
  34. package/dest/modules/instrumentation.js +22 -6
  35. package/dest/modules/l1_synchronizer.d.ts +8 -2
  36. package/dest/modules/l1_synchronizer.d.ts.map +1 -1
  37. package/dest/modules/l1_synchronizer.js +292 -144
  38. package/dest/modules/validation.d.ts +4 -3
  39. package/dest/modules/validation.d.ts.map +1 -1
  40. package/dest/modules/validation.js +6 -6
  41. package/dest/store/block_store.d.ts +83 -17
  42. package/dest/store/block_store.d.ts.map +1 -1
  43. package/dest/store/block_store.js +399 -124
  44. package/dest/store/contract_class_store.d.ts +2 -3
  45. package/dest/store/contract_class_store.d.ts.map +1 -1
  46. package/dest/store/contract_class_store.js +7 -67
  47. package/dest/store/contract_instance_store.d.ts +1 -1
  48. package/dest/store/contract_instance_store.d.ts.map +1 -1
  49. package/dest/store/contract_instance_store.js +6 -2
  50. package/dest/store/kv_archiver_store.d.ts +70 -23
  51. package/dest/store/kv_archiver_store.d.ts.map +1 -1
  52. package/dest/store/kv_archiver_store.js +88 -27
  53. package/dest/store/l2_tips_cache.d.ts +2 -1
  54. package/dest/store/l2_tips_cache.d.ts.map +1 -1
  55. package/dest/store/l2_tips_cache.js +27 -7
  56. package/dest/store/log_store.d.ts +6 -3
  57. package/dest/store/log_store.d.ts.map +1 -1
  58. package/dest/store/log_store.js +95 -20
  59. package/dest/store/message_store.d.ts +5 -1
  60. package/dest/store/message_store.d.ts.map +1 -1
  61. package/dest/store/message_store.js +21 -9
  62. package/dest/test/fake_l1_state.d.ts +20 -1
  63. package/dest/test/fake_l1_state.d.ts.map +1 -1
  64. package/dest/test/fake_l1_state.js +114 -18
  65. package/dest/test/mock_l1_to_l2_message_source.d.ts +1 -1
  66. package/dest/test/mock_l1_to_l2_message_source.d.ts.map +1 -1
  67. package/dest/test/mock_l1_to_l2_message_source.js +2 -1
  68. package/dest/test/mock_l2_block_source.d.ts +19 -4
  69. package/dest/test/mock_l2_block_source.d.ts.map +1 -1
  70. package/dest/test/mock_l2_block_source.js +57 -7
  71. package/dest/test/mock_structs.d.ts +4 -1
  72. package/dest/test/mock_structs.d.ts.map +1 -1
  73. package/dest/test/mock_structs.js +13 -1
  74. package/dest/test/noop_l1_archiver.d.ts +4 -1
  75. package/dest/test/noop_l1_archiver.d.ts.map +1 -1
  76. package/dest/test/noop_l1_archiver.js +9 -3
  77. package/package.json +13 -13
  78. package/src/archiver.ts +179 -56
  79. package/src/config.ts +23 -2
  80. package/src/errors.ts +133 -22
  81. package/src/factory.ts +24 -15
  82. package/src/index.ts +2 -1
  83. package/src/l1/calldata_retriever.ts +17 -5
  84. package/src/l1/data_retrieval.ts +52 -53
  85. package/src/l1/validate_historical_logs.ts +140 -0
  86. package/src/modules/data_source_base.ts +43 -7
  87. package/src/modules/data_store_updater.ts +126 -109
  88. package/src/modules/instrumentation.ts +27 -7
  89. package/src/modules/l1_synchronizer.ts +371 -178
  90. package/src/modules/validation.ts +10 -9
  91. package/src/store/block_store.ts +489 -143
  92. package/src/store/contract_class_store.ts +8 -106
  93. package/src/store/contract_instance_store.ts +8 -5
  94. package/src/store/kv_archiver_store.ts +133 -39
  95. package/src/store/l2_tips_cache.ts +58 -13
  96. package/src/store/log_store.ts +128 -32
  97. package/src/store/message_store.ts +27 -10
  98. package/src/structs/inbox_message.ts +1 -1
  99. package/src/test/fake_l1_state.ts +142 -29
  100. package/src/test/mock_l1_to_l2_message_source.ts +1 -0
  101. package/src/test/mock_l2_block_source.ts +73 -5
  102. package/src/test/mock_structs.ts +20 -6
  103. package/src/test/noop_l1_archiver.ts +10 -2
@@ -2,14 +2,7 @@ import { Fr } from '@aztec/foundation/curves/bn254';
2
2
  import { toArray } from '@aztec/foundation/iterable';
3
3
  import { BufferReader, numToUInt8, serializeToBuffer } from '@aztec/foundation/serialize';
4
4
  import type { AztecAsyncKVStore, AztecAsyncMap } from '@aztec/kv-store';
5
- import { FunctionSelector } from '@aztec/stdlib/abi';
6
- import type {
7
- ContractClassPublic,
8
- ContractClassPublicWithBlockNumber,
9
- ExecutablePrivateFunctionWithMembershipProof,
10
- UtilityFunctionWithMembershipProof,
11
- } from '@aztec/stdlib/contract';
12
- import { Vector } from '@aztec/stdlib/types';
5
+ import type { ContractClassPublic, ContractClassPublicWithBlockNumber } from '@aztec/stdlib/contract';
13
6
 
14
7
  /**
15
8
  * LMDB-based contract class storage for the archiver.
@@ -29,11 +22,15 @@ export class ContractClassStore {
29
22
  blockNumber: number,
30
23
  ): Promise<void> {
31
24
  await this.db.transactionAsync(async () => {
32
- await this.#contractClasses.setIfNotExists(
33
- contractClass.id.toString(),
25
+ const key = contractClass.id.toString();
26
+ if (await this.#contractClasses.hasAsync(key)) {
27
+ throw new Error(`Contract class ${key} already exists, cannot add again at block ${blockNumber}`);
28
+ }
29
+ await this.#contractClasses.set(
30
+ key,
34
31
  serializeContractClassPublic({ ...contractClass, l2BlockNumber: blockNumber }),
35
32
  );
36
- await this.#bytecodeCommitments.setIfNotExists(contractClass.id.toString(), bytecodeCommitment.toBuffer());
33
+ await this.#bytecodeCommitments.set(key, bytecodeCommitment.toBuffer());
37
34
  });
38
35
  }
39
36
 
@@ -60,37 +57,6 @@ export class ContractClassStore {
60
57
  async getContractClassIds(): Promise<Fr[]> {
61
58
  return (await toArray(this.#contractClasses.keysAsync())).map(key => Fr.fromHexString(key));
62
59
  }
63
-
64
- async addFunctions(
65
- contractClassId: Fr,
66
- newPrivateFunctions: ExecutablePrivateFunctionWithMembershipProof[],
67
- newUtilityFunctions: UtilityFunctionWithMembershipProof[],
68
- ): Promise<boolean> {
69
- await this.db.transactionAsync(async () => {
70
- const existingClassBuffer = await this.#contractClasses.getAsync(contractClassId.toString());
71
- if (!existingClassBuffer) {
72
- throw new Error(`Unknown contract class ${contractClassId} when adding private functions to store`);
73
- }
74
-
75
- const existingClass = deserializeContractClassPublic(existingClassBuffer);
76
- const { privateFunctions: existingPrivateFns, utilityFunctions: existingUtilityFns } = existingClass;
77
-
78
- const updatedClass: Omit<ContractClassPublicWithBlockNumber, 'id'> = {
79
- ...existingClass,
80
- privateFunctions: [
81
- ...existingPrivateFns,
82
- ...newPrivateFunctions.filter(newFn => !existingPrivateFns.some(f => f.selector.equals(newFn.selector))),
83
- ],
84
- utilityFunctions: [
85
- ...existingUtilityFns,
86
- ...newUtilityFunctions.filter(newFn => !existingUtilityFns.some(f => f.selector.equals(newFn.selector))),
87
- ],
88
- };
89
- await this.#contractClasses.set(contractClassId.toString(), serializeContractClassPublic(updatedClass));
90
- });
91
-
92
- return true;
93
- }
94
60
  }
95
61
 
96
62
  function serializeContractClassPublic(contractClass: Omit<ContractClassPublicWithBlockNumber, 'id'>): Buffer {
@@ -98,83 +64,19 @@ function serializeContractClassPublic(contractClass: Omit<ContractClassPublicWit
98
64
  contractClass.l2BlockNumber,
99
65
  numToUInt8(contractClass.version),
100
66
  contractClass.artifactHash,
101
- contractClass.privateFunctions.length,
102
- contractClass.privateFunctions.map(serializePrivateFunction),
103
- contractClass.utilityFunctions.length,
104
- contractClass.utilityFunctions.map(serializeUtilityFunction),
105
67
  contractClass.packedBytecode.length,
106
68
  contractClass.packedBytecode,
107
69
  contractClass.privateFunctionsRoot,
108
70
  );
109
71
  }
110
72
 
111
- function serializePrivateFunction(fn: ExecutablePrivateFunctionWithMembershipProof): Buffer {
112
- return serializeToBuffer(
113
- fn.selector,
114
- fn.vkHash,
115
- fn.bytecode.length,
116
- fn.bytecode,
117
- fn.functionMetadataHash,
118
- fn.artifactMetadataHash,
119
- fn.utilityFunctionsTreeRoot,
120
- new Vector(fn.privateFunctionTreeSiblingPath),
121
- fn.privateFunctionTreeLeafIndex,
122
- new Vector(fn.artifactTreeSiblingPath),
123
- fn.artifactTreeLeafIndex,
124
- );
125
- }
126
-
127
- function serializeUtilityFunction(fn: UtilityFunctionWithMembershipProof): Buffer {
128
- return serializeToBuffer(
129
- fn.selector,
130
- fn.bytecode.length,
131
- fn.bytecode,
132
- fn.functionMetadataHash,
133
- fn.artifactMetadataHash,
134
- fn.privateFunctionsArtifactTreeRoot,
135
- new Vector(fn.artifactTreeSiblingPath),
136
- fn.artifactTreeLeafIndex,
137
- );
138
- }
139
-
140
73
  function deserializeContractClassPublic(buffer: Buffer): Omit<ContractClassPublicWithBlockNumber, 'id'> {
141
74
  const reader = BufferReader.asReader(buffer);
142
75
  return {
143
76
  l2BlockNumber: reader.readNumber(),
144
77
  version: reader.readUInt8() as 1,
145
78
  artifactHash: reader.readObject(Fr),
146
- privateFunctions: reader.readVector({ fromBuffer: deserializePrivateFunction }),
147
- utilityFunctions: reader.readVector({ fromBuffer: deserializeUtilityFunction }),
148
79
  packedBytecode: reader.readBuffer(),
149
80
  privateFunctionsRoot: reader.readObject(Fr),
150
81
  };
151
82
  }
152
-
153
- function deserializePrivateFunction(buffer: Buffer | BufferReader): ExecutablePrivateFunctionWithMembershipProof {
154
- const reader = BufferReader.asReader(buffer);
155
- return {
156
- selector: reader.readObject(FunctionSelector),
157
- vkHash: reader.readObject(Fr),
158
- bytecode: reader.readBuffer(),
159
- functionMetadataHash: reader.readObject(Fr),
160
- artifactMetadataHash: reader.readObject(Fr),
161
- utilityFunctionsTreeRoot: reader.readObject(Fr),
162
- privateFunctionTreeSiblingPath: reader.readVector(Fr),
163
- privateFunctionTreeLeafIndex: reader.readNumber(),
164
- artifactTreeSiblingPath: reader.readVector(Fr),
165
- artifactTreeLeafIndex: reader.readNumber(),
166
- };
167
- }
168
-
169
- function deserializeUtilityFunction(buffer: Buffer | BufferReader): UtilityFunctionWithMembershipProof {
170
- const reader = BufferReader.asReader(buffer);
171
- return {
172
- selector: reader.readObject(FunctionSelector),
173
- bytecode: reader.readBuffer(),
174
- functionMetadataHash: reader.readObject(Fr),
175
- artifactMetadataHash: reader.readObject(Fr),
176
- privateFunctionsArtifactTreeRoot: reader.readObject(Fr),
177
- artifactTreeSiblingPath: reader.readVector(Fr),
178
- artifactTreeLeafIndex: reader.readNumber(),
179
- };
180
- }
@@ -27,11 +27,14 @@ export class ContractInstanceStore {
27
27
 
28
28
  addContractInstance(contractInstance: ContractInstanceWithAddress, blockNumber: number): Promise<void> {
29
29
  return this.db.transactionAsync(async () => {
30
- await this.#contractInstances.set(
31
- contractInstance.address.toString(),
32
- new SerializableContractInstance(contractInstance).toBuffer(),
33
- );
34
- await this.#contractInstancePublishedAt.set(contractInstance.address.toString(), blockNumber);
30
+ const key = contractInstance.address.toString();
31
+ if (await this.#contractInstances.hasAsync(key)) {
32
+ throw new Error(
33
+ `Contract instance at ${key} already exists (deployed at block ${await this.#contractInstancePublishedAt.getAsync(key)}), cannot add again at block ${blockNumber}`,
34
+ );
35
+ }
36
+ await this.#contractInstances.set(key, new SerializableContractInstance(contractInstance).toBuffer());
37
+ await this.#contractInstancePublishedAt.set(key, blockNumber);
35
38
  });
36
39
  }
37
40
 
@@ -10,17 +10,24 @@ import {
10
10
  type BlockData,
11
11
  BlockHash,
12
12
  CheckpointedL2Block,
13
+ type CommitteeAttestation,
13
14
  L2Block,
14
15
  type ValidateCheckpointResult,
15
16
  } from '@aztec/stdlib/block';
16
- import type { CheckpointData, PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
17
+ import type {
18
+ CheckpointData,
19
+ CommonCheckpointData,
20
+ L1PublishedData,
21
+ ProposedCheckpointData,
22
+ ProposedCheckpointInput,
23
+ PublishedCheckpoint,
24
+ } from '@aztec/stdlib/checkpoint';
17
25
  import type {
18
26
  ContractClassPublic,
27
+ ContractClassPublicWithCommitment,
19
28
  ContractDataSource,
20
29
  ContractInstanceUpdateWithAddress,
21
30
  ContractInstanceWithAddress,
22
- ExecutablePrivateFunctionWithMembershipProof,
23
- UtilityFunctionWithMembershipProof,
24
31
  } from '@aztec/stdlib/contract';
25
32
  import type { L1RollupConstants } from '@aztec/stdlib/epoch-helpers';
26
33
  import type { GetContractClassLogsResponse, GetPublicLogsResponse } from '@aztec/stdlib/interfaces/client';
@@ -37,7 +44,7 @@ import { ContractInstanceStore } from './contract_instance_store.js';
37
44
  import { LogStore } from './log_store.js';
38
45
  import { MessageStore } from './message_store.js';
39
46
 
40
- export const ARCHIVER_DB_VERSION = 5;
47
+ export const ARCHIVER_DB_VERSION = 6;
41
48
  export const MAX_FUNCTION_SIGNATURES = 1000;
42
49
  export const MAX_FUNCTION_NAME_LEN = 256;
43
50
 
@@ -71,9 +78,8 @@ export class KVArchiverDataStore implements ContractDataSource {
71
78
  constructor(
72
79
  private db: AztecAsyncKVStore,
73
80
  logsMaxPageSize: number = 1000,
74
- l1Constants: Pick<L1RollupConstants, 'epochDuration'>,
75
81
  ) {
76
- this.#blockStore = new BlockStore(db, l1Constants);
82
+ this.#blockStore = new BlockStore(db);
77
83
  this.#logStore = new LogStore(db, this.#blockStore, logsMaxPageSize);
78
84
  this.#messageStore = new MessageStore(db);
79
85
  this.#contractClassStore = new ContractClassStore(db);
@@ -168,19 +174,14 @@ export class KVArchiverDataStore implements ContractDataSource {
168
174
 
169
175
  /**
170
176
  * Add new contract classes from an L2 block to the store's list.
171
- * @param data - List of contract classes to be added.
172
- * @param bytecodeCommitments - Bytecode commitments for the contract classes.
177
+ * @param data - List of contract classes (with bytecode commitments) to be added.
173
178
  * @param blockNumber - Number of the L2 block the contracts were registered in.
174
179
  * @returns True if the operation is successful.
175
180
  */
176
- async addContractClasses(
177
- data: ContractClassPublic[],
178
- bytecodeCommitments: Fr[],
179
- blockNumber: BlockNumber,
180
- ): Promise<boolean> {
181
+ async addContractClasses(data: ContractClassPublicWithCommitment[], blockNumber: BlockNumber): Promise<boolean> {
181
182
  return (
182
183
  await Promise.all(
183
- data.map((c, i) => this.#contractClassStore.addContractClass(c, bytecodeCommitments[i], blockNumber)),
184
+ data.map(c => this.#contractClassStore.addContractClass(c, c.publicBytecodeCommitment, blockNumber)),
184
185
  )
185
186
  ).every(Boolean);
186
187
  }
@@ -195,15 +196,6 @@ export class KVArchiverDataStore implements ContractDataSource {
195
196
  return this.#contractClassStore.getBytecodeCommitment(contractClassId);
196
197
  }
197
198
 
198
- /** Adds private functions to a contract class. */
199
- addFunctions(
200
- contractClassId: Fr,
201
- privateFunctions: ExecutablePrivateFunctionWithMembershipProof[],
202
- utilityFunctions: UtilityFunctionWithMembershipProof[],
203
- ): Promise<boolean> {
204
- return this.#contractClassStore.addFunctions(contractClassId, privateFunctions, utilityFunctions);
205
- }
206
-
207
199
  /**
208
200
  * Add new contract instances from an L2 block to the store's list.
209
201
  * @param data - List of contract instances to be added.
@@ -246,14 +238,14 @@ export class KVArchiverDataStore implements ContractDataSource {
246
238
  }
247
239
 
248
240
  /**
249
- * Append new proposed blocks to the store's list.
250
- * These are uncheckpointed blocks that have been proposed by the sequencer but not yet included in a checkpoint on L1.
241
+ * Append a new proposed block to the store.
242
+ * This is an uncheckpointed block that has been proposed by the sequencer but not yet included in a checkpoint on L1.
251
243
  * For checkpointed blocks (already published to L1), use addCheckpoints() instead.
252
- * @param blocks - The proposed L2 blocks to be added to the store.
244
+ * @param block - The proposed L2 block to be added to the store.
253
245
  * @returns True if the operation is successful.
254
246
  */
255
- addProposedBlocks(blocks: L2Block[], opts: { force?: boolean; checkpointNumber?: number } = {}): Promise<boolean> {
256
- return this.#blockStore.addProposedBlocks(blocks, opts);
247
+ addProposedBlock(block: L2Block, opts: { force?: boolean } = {}): Promise<boolean> {
248
+ return this.#blockStore.addProposedBlock(block, opts);
257
249
  }
258
250
 
259
251
  /**
@@ -270,7 +262,7 @@ export class KVArchiverDataStore implements ContractDataSource {
270
262
  * @returns The number of the latest block
271
263
  */
272
264
  getLatestBlockNumber(): Promise<BlockNumber> {
273
- return this.#blockStore.getLatestBlockNumber();
265
+ return this.#blockStore.getLatestL2BlockNumber();
274
266
  }
275
267
 
276
268
  /**
@@ -410,8 +402,11 @@ export class KVArchiverDataStore implements ContractDataSource {
410
402
  * @param txHash - The hash of a tx we try to get the receipt for.
411
403
  * @returns The requested tx receipt (or undefined if not found).
412
404
  */
413
- getSettledTxReceipt(txHash: TxHash): Promise<TxReceipt | undefined> {
414
- return this.#blockStore.getSettledTxReceipt(txHash);
405
+ getSettledTxReceipt(
406
+ txHash: TxHash,
407
+ l1Constants?: Pick<L1RollupConstants, 'epochDuration'>,
408
+ ): Promise<TxReceipt | undefined> {
409
+ return this.#blockStore.getSettledTxReceipt(txHash, l1Constants);
415
410
  }
416
411
 
417
412
  /**
@@ -472,10 +467,11 @@ export class KVArchiverDataStore implements ContractDataSource {
472
467
  * array implies no logs match that tag.
473
468
  * @param tags - The tags to search for.
474
469
  * @param page - The page number (0-indexed) for pagination. Returns at most 10 logs per tag per page.
470
+ * @param upToBlockNumber - If set, only return logs from blocks up to and including this block number.
475
471
  */
476
- getPrivateLogsByTags(tags: SiloedTag[], page?: number): Promise<TxScopedL2Log[][]> {
472
+ getPrivateLogsByTags(tags: SiloedTag[], page?: number, upToBlockNumber?: BlockNumber): Promise<TxScopedL2Log[][]> {
477
473
  try {
478
- return this.#logStore.getPrivateLogsByTags(tags, page);
474
+ return this.#logStore.getPrivateLogsByTags(tags, page, upToBlockNumber);
479
475
  } catch (err) {
480
476
  return Promise.reject(err);
481
477
  }
@@ -487,14 +483,16 @@ export class KVArchiverDataStore implements ContractDataSource {
487
483
  * @param contractAddress - The contract address to search logs for.
488
484
  * @param tags - The tags to search for.
489
485
  * @param page - The page number (0-indexed) for pagination. Returns at most 10 logs per tag per page.
486
+ * @param upToBlockNumber - If set, only return logs from blocks up to and including this block number.
490
487
  */
491
488
  getPublicLogsByTagsFromContract(
492
489
  contractAddress: AztecAddress,
493
490
  tags: Tag[],
494
491
  page?: number,
492
+ upToBlockNumber?: BlockNumber,
495
493
  ): Promise<TxScopedL2Log[][]> {
496
494
  try {
497
- return this.#logStore.getPublicLogsByTagsFromContract(contractAddress, tags, page);
495
+ return this.#logStore.getPublicLogsByTagsFromContract(contractAddress, tags, page, upToBlockNumber);
498
496
  } catch (err) {
499
497
  return Promise.reject(err);
500
498
  }
@@ -542,15 +540,24 @@ export class KVArchiverDataStore implements ContractDataSource {
542
540
  await this.#blockStore.setProvenCheckpointNumber(checkpointNumber);
543
541
  }
544
542
 
545
- async setBlockSynchedL1BlockNumber(l1BlockNumber: bigint) {
546
- await this.#blockStore.setSynchedL1BlockNumber(l1BlockNumber);
543
+ /**
544
+ * Gets the number of the latest finalized checkpoint processed.
545
+ * @returns The number of the latest finalized checkpoint processed.
546
+ */
547
+ getFinalizedCheckpointNumber(): Promise<CheckpointNumber> {
548
+ return this.#blockStore.getFinalizedCheckpointNumber();
547
549
  }
548
550
 
549
551
  /**
550
- * Stores the l1 block that messages have been synched until
552
+ * Stores the number of the latest finalized checkpoint processed.
553
+ * @param checkpointNumber - The number of the latest finalized checkpoint processed.
551
554
  */
552
- async setMessageSynchedL1Block(l1Block: L1BlockId) {
553
- await this.#messageStore.setSynchedL1Block(l1Block);
555
+ async setFinalizedCheckpointNumber(checkpointNumber: CheckpointNumber) {
556
+ await this.#blockStore.setFinalizedCheckpointNumber(checkpointNumber);
557
+ }
558
+
559
+ async setBlockSynchedL1BlockNumber(l1BlockNumber: bigint) {
560
+ await this.#blockStore.setSynchedL1BlockNumber(l1BlockNumber);
554
561
  }
555
562
 
556
563
  /**
@@ -585,6 +592,11 @@ export class KVArchiverDataStore implements ContractDataSource {
585
592
  return this.#messageStore.rollbackL1ToL2MessagesToCheckpoint(targetCheckpointNumber);
586
593
  }
587
594
 
595
+ /** Atomically updates the message sync state: the L1 sync point and the inbox tree-in-progress marker. */
596
+ public setMessageSyncState(l1Block: L1BlockId, treeInProgress: bigint | undefined): Promise<void> {
597
+ return this.#messageStore.setMessageSyncState(l1Block, treeInProgress);
598
+ }
599
+
588
600
  /** Returns an async iterator to all L1 to L2 messages on the range. */
589
601
  public iterateL1ToL2Messages(range: CustomRange<bigint> = {}): AsyncIterableIterator<InboxMessage> {
590
602
  return this.#messageStore.iterateL1ToL2Messages(range);
@@ -605,6 +617,73 @@ export class KVArchiverDataStore implements ContractDataSource {
605
617
  return this.#blockStore.setPendingChainValidationStatus(status);
606
618
  }
607
619
 
620
+ /**
621
+ * Gets the L2 block number of the proposed checkpoint.
622
+ * @returns The block number of the proposed checkpoint, or the checkpointed block number if none.
623
+ */
624
+ public getProposedCheckpointL2BlockNumber(): Promise<BlockNumber> {
625
+ return this.#blockStore.getProposedCheckpointL2BlockNumber();
626
+ }
627
+
628
+ /** Returns the checkpoint data at the proposed tip */
629
+ public getLastCheckpoint(): Promise<CommonCheckpointData | undefined> {
630
+ return this.#blockStore.getLastCheckpoint();
631
+ }
632
+
633
+ /** Returns the proposed checkpoint data, or undefined if no proposed checkpoint exists. No fallback to confirmed. */
634
+ public getLastProposedCheckpoint(): Promise<ProposedCheckpointData | undefined> {
635
+ return this.#blockStore.getLastProposedCheckpoint();
636
+ }
637
+
638
+ /**
639
+ * Set proposed checkpoint
640
+ * @param proposedCheckpoint
641
+ * @returns
642
+ */
643
+ public addProposedCheckpoint(proposedCheckpoint: ProposedCheckpointInput): Promise<void> {
644
+ return this.#blockStore.addProposedCheckpoint(proposedCheckpoint);
645
+ }
646
+
647
+ /** Deletes all pending proposed checkpoints from storage. */
648
+ public deleteProposedCheckpoints(): Promise<void> {
649
+ return this.#blockStore.deleteProposedCheckpoints();
650
+ }
651
+
652
+ /** Returns the pending checkpoint for a specific checkpoint number, or undefined if not found. */
653
+ public getProposedCheckpointByNumber(n: CheckpointNumber): Promise<ProposedCheckpointData | undefined> {
654
+ return this.#blockStore.getProposedCheckpointByNumber(n);
655
+ }
656
+
657
+ /** Returns all pending checkpoints in ascending checkpoint-number order. */
658
+ public getProposedCheckpoints(): Promise<ProposedCheckpointData[]> {
659
+ return this.#blockStore.getProposedCheckpoints();
660
+ }
661
+
662
+ /**
663
+ * Evicts all pending checkpoints with checkpoint number >= fromNumber.
664
+ * Used for divergent-mined-checkpoint cleanup.
665
+ */
666
+ public evictProposedCheckpointsFrom(fromNumber: CheckpointNumber): Promise<void> {
667
+ return this.#blockStore.evictProposedCheckpointsFrom(fromNumber);
668
+ }
669
+
670
+ /**
671
+ * Promotes a specific pending checkpoint to a confirmed checkpoint entry.
672
+ * Should only be called after the checkpoint has been validated.
673
+ * @param checkpointNumber - The checkpoint number to promote.
674
+ * @param l1 - L1 published data for the checkpoint.
675
+ * @param attestations - Committee attestations.
676
+ * @param expectedArchiveRoot - The archive root to match against the proposed checkpoint, to guard against races.
677
+ */
678
+ public promoteProposedToCheckpointed(
679
+ checkpointNumber: CheckpointNumber,
680
+ l1: L1PublishedData,
681
+ attestations: CommitteeAttestation[],
682
+ expectedArchiveRoot: Fr,
683
+ ): Promise<void> {
684
+ return this.#blockStore.promoteProposedToCheckpointed(checkpointNumber, l1, attestations, expectedArchiveRoot);
685
+ }
686
+
608
687
  /**
609
688
  * Gets the number of the latest L2 block processed.
610
689
  * @returns The number of the latest L2 block processed.
@@ -650,6 +729,21 @@ export class KVArchiverDataStore implements ContractDataSource {
650
729
  return this.#blockStore.getCheckpointDataForSlotRange(startSlot, endSlot);
651
730
  }
652
731
 
732
+ /** Returns lightweight checkpoint metadata for a range of checkpoints. */
733
+ getCheckpointDataRange(from: CheckpointNumber, limit: number): Promise<CheckpointData[]> {
734
+ return this.#blockStore.getRangeOfCheckpoints(from, limit);
735
+ }
736
+
737
+ /** Returns the checkpoint number for a given slot, if one exists. */
738
+ getCheckpointNumberBySlot(slot: SlotNumber): Promise<CheckpointNumber | undefined> {
739
+ return this.#blockStore.getCheckpointNumberBySlot(slot);
740
+ }
741
+
742
+ /** Returns block metadata plus checkpoint-derived context (L1 publish info, attestations). */
743
+ getBlockDataWithCheckpointContext(blockNumber: BlockNumber) {
744
+ return this.#blockStore.getBlockDataWithCheckpointContext(blockNumber);
745
+ }
746
+
653
747
  /**
654
748
  * Gets all blocks that have the given slot number.
655
749
  * @param slotNumber - The slot number to search for.
@@ -1,6 +1,12 @@
1
- import { GENESIS_BLOCK_HEADER_HASH, INITIAL_L2_BLOCK_NUM } from '@aztec/constants';
1
+ import { INITIAL_L2_BLOCK_NUM } from '@aztec/constants';
2
2
  import { BlockNumber, CheckpointNumber } from '@aztec/foundation/branded-types';
3
- import { type BlockData, type CheckpointId, GENESIS_CHECKPOINT_HEADER_HASH, type L2Tips } from '@aztec/stdlib/block';
3
+ import {
4
+ type BlockData,
5
+ type CheckpointId,
6
+ GENESIS_BLOCK_HEADER_HASH,
7
+ GENESIS_CHECKPOINT_HEADER_HASH,
8
+ type L2Tips,
9
+ } from '@aztec/stdlib/block';
4
10
 
5
11
  import type { BlockStore } from './block_store.js';
6
12
 
@@ -26,9 +32,16 @@ export class L2TipsCache {
26
32
  }
27
33
 
28
34
  private async loadFromStore(): Promise<L2Tips> {
29
- const [latestBlockNumber, provenBlockNumber, checkpointedBlockNumber, finalizedBlockNumber] = await Promise.all([
30
- this.blockStore.getLatestBlockNumber(),
35
+ const [
36
+ latestBlockNumber,
37
+ provenBlockNumber,
38
+ proposedCheckpointBlockNumber,
39
+ checkpointedBlockNumber,
40
+ finalizedBlockNumber,
41
+ ] = await Promise.all([
42
+ this.blockStore.getLatestL2BlockNumber(),
31
43
  this.blockStore.getProvenBlockNumber(),
44
+ this.blockStore.getProposedCheckpointL2BlockNumber(),
32
45
  this.blockStore.getCheckpointedL2BlockNumber(),
33
46
  this.blockStore.getFinalizedL2BlockNumber(),
34
47
  ]);
@@ -42,19 +55,34 @@ export class L2TipsCache {
42
55
  const getBlockData = (blockNumber: BlockNumber) =>
43
56
  blockNumber > beforeInitialBlockNumber ? this.blockStore.getBlockData(blockNumber) : genesisBlockHeader;
44
57
 
45
- const [latestBlockData, provenBlockData, checkpointedBlockData, finalizedBlockData] = await Promise.all(
46
- [latestBlockNumber, provenBlockNumber, checkpointedBlockNumber, finalizedBlockNumber].map(getBlockData),
47
- );
58
+ const [latestBlockData, provenBlockData, proposedCheckpointBlockData, checkpointedBlockData, finalizedBlockData] =
59
+ await Promise.all(
60
+ [
61
+ latestBlockNumber,
62
+ provenBlockNumber,
63
+ proposedCheckpointBlockNumber,
64
+ checkpointedBlockNumber,
65
+ finalizedBlockNumber,
66
+ ].map(getBlockData),
67
+ );
48
68
 
49
- if (!latestBlockData || !provenBlockData || !finalizedBlockData || !checkpointedBlockData) {
69
+ if (
70
+ !latestBlockData ||
71
+ !provenBlockData ||
72
+ !finalizedBlockData ||
73
+ !checkpointedBlockData ||
74
+ !proposedCheckpointBlockData
75
+ ) {
50
76
  throw new Error('Failed to load block data for L2 tips');
51
77
  }
52
78
 
53
- const [provenCheckpointId, finalizedCheckpointId, checkpointedCheckpointId] = await Promise.all([
54
- this.getCheckpointIdForBlock(provenBlockData),
55
- this.getCheckpointIdForBlock(finalizedBlockData),
56
- this.getCheckpointIdForBlock(checkpointedBlockData),
57
- ]);
79
+ const [provenCheckpointId, finalizedCheckpointId, proposedCheckpointId, checkpointedCheckpointId] =
80
+ await Promise.all([
81
+ this.getCheckpointIdForBlock(provenBlockData),
82
+ this.getCheckpointIdForBlock(finalizedBlockData),
83
+ this.getCheckpointIdForProposedCheckpoint(checkpointedBlockData),
84
+ this.getCheckpointIdForBlock(checkpointedBlockData),
85
+ ]);
58
86
 
59
87
  return {
60
88
  proposed: { number: latestBlockNumber, hash: latestBlockData.blockHash.toString() },
@@ -62,6 +90,10 @@ export class L2TipsCache {
62
90
  block: { number: provenBlockNumber, hash: provenBlockData.blockHash.toString() },
63
91
  checkpoint: provenCheckpointId,
64
92
  },
93
+ proposedCheckpoint: {
94
+ block: { number: proposedCheckpointBlockNumber, hash: proposedCheckpointBlockData.blockHash.toString() },
95
+ checkpoint: proposedCheckpointId,
96
+ },
65
97
  finalized: {
66
98
  block: { number: finalizedBlockNumber, hash: finalizedBlockData.blockHash.toString() },
67
99
  checkpoint: finalizedCheckpointId,
@@ -73,6 +105,19 @@ export class L2TipsCache {
73
105
  };
74
106
  }
75
107
 
108
+ private async getCheckpointIdForProposedCheckpoint(
109
+ checkpointedBlockData: Pick<BlockData, 'checkpointNumber'>,
110
+ ): Promise<CheckpointId> {
111
+ const checkpointData = await this.blockStore.getLastProposedCheckpoint();
112
+ if (!checkpointData) {
113
+ return this.getCheckpointIdForBlock(checkpointedBlockData);
114
+ }
115
+ return {
116
+ number: checkpointData.checkpointNumber,
117
+ hash: checkpointData.header.hash().toString(),
118
+ };
119
+ }
120
+
76
121
  private async getCheckpointIdForBlock(blockData: Pick<BlockData, 'checkpointNumber'>): Promise<CheckpointId> {
77
122
  const checkpointData = await this.blockStore.getCheckpointData(blockData.checkpointNumber);
78
123
  if (!checkpointData) {