@aztec/archiver 0.0.1-commit.5de5ca79e → 0.0.1-commit.6201a7b05

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 (99) hide show
  1. package/README.md +12 -6
  2. package/dest/archiver.d.ts +16 -7
  3. package/dest/archiver.d.ts.map +1 -1
  4. package/dest/archiver.js +104 -48
  5. package/dest/config.d.ts +3 -1
  6. package/dest/config.d.ts.map +1 -1
  7. package/dest/config.js +14 -3
  8. package/dest/errors.d.ts +41 -2
  9. package/dest/errors.d.ts.map +1 -1
  10. package/dest/errors.js +62 -1
  11. package/dest/factory.d.ts +2 -2
  12. package/dest/factory.d.ts.map +1 -1
  13. package/dest/factory.js +9 -7
  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 +19 -10
  21. package/dest/l1/data_retrieval.d.ts.map +1 -1
  22. package/dest/l1/data_retrieval.js +25 -32
  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 +8 -2
  27. package/dest/modules/data_source_base.d.ts.map +1 -1
  28. package/dest/modules/data_source_base.js +19 -1
  29. package/dest/modules/data_store_updater.d.ts +15 -10
  30. package/dest/modules/data_store_updater.d.ts.map +1 -1
  31. package/dest/modules/data_store_updater.js +71 -68
  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 +7 -2
  36. package/dest/modules/l1_synchronizer.d.ts.map +1 -1
  37. package/dest/modules/l1_synchronizer.js +261 -143
  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 +72 -5
  42. package/dest/store/block_store.d.ts.map +1 -1
  43. package/dest/store/block_store.js +341 -66
  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 +51 -17
  51. package/dest/store/kv_archiver_store.d.ts.map +1 -1
  52. package/dest/store/kv_archiver_store.js +60 -16
  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 +1 -1
  57. package/dest/store/log_store.d.ts.map +1 -1
  58. package/dest/store/log_store.js +2 -4
  59. package/dest/store/message_store.d.ts +3 -3
  60. package/dest/store/message_store.d.ts.map +1 -1
  61. package/dest/store/message_store.js +9 -10
  62. package/dest/test/fake_l1_state.d.ts +15 -3
  63. package/dest/test/fake_l1_state.d.ts.map +1 -1
  64. package/dest/test/fake_l1_state.js +80 -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 +16 -2
  69. package/dest/test/mock_l2_block_source.d.ts.map +1 -1
  70. package/dest/test/mock_l2_block_source.js +50 -3
  71. package/dest/test/noop_l1_archiver.d.ts +1 -1
  72. package/dest/test/noop_l1_archiver.d.ts.map +1 -1
  73. package/dest/test/noop_l1_archiver.js +4 -2
  74. package/package.json +13 -13
  75. package/src/archiver.ts +126 -46
  76. package/src/config.ts +15 -1
  77. package/src/errors.ts +97 -2
  78. package/src/factory.ts +13 -7
  79. package/src/index.ts +2 -1
  80. package/src/l1/calldata_retriever.ts +17 -5
  81. package/src/l1/data_retrieval.ts +36 -45
  82. package/src/l1/validate_historical_logs.ts +140 -0
  83. package/src/modules/data_source_base.ts +32 -1
  84. package/src/modules/data_store_updater.ts +98 -97
  85. package/src/modules/instrumentation.ts +27 -7
  86. package/src/modules/l1_synchronizer.ts +328 -169
  87. package/src/modules/validation.ts +10 -9
  88. package/src/store/block_store.ts +419 -76
  89. package/src/store/contract_class_store.ts +8 -106
  90. package/src/store/contract_instance_store.ts +8 -5
  91. package/src/store/kv_archiver_store.ts +100 -32
  92. package/src/store/l2_tips_cache.ts +58 -13
  93. package/src/store/log_store.ts +2 -5
  94. package/src/store/message_store.ts +10 -12
  95. package/src/structs/inbox_message.ts +1 -1
  96. package/src/test/fake_l1_state.ts +99 -27
  97. package/src/test/mock_l1_to_l2_message_source.ts +1 -0
  98. package/src/test/mock_l2_block_source.ts +58 -2
  99. package/src/test/noop_l1_archiver.ts +3 -1
@@ -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
 
@@ -167,19 +174,14 @@ export class KVArchiverDataStore implements ContractDataSource {
167
174
 
168
175
  /**
169
176
  * Add new contract classes from an L2 block to the store's list.
170
- * @param data - List of contract classes to be added.
171
- * @param bytecodeCommitments - Bytecode commitments for the contract classes.
177
+ * @param data - List of contract classes (with bytecode commitments) to be added.
172
178
  * @param blockNumber - Number of the L2 block the contracts were registered in.
173
179
  * @returns True if the operation is successful.
174
180
  */
175
- async addContractClasses(
176
- data: ContractClassPublic[],
177
- bytecodeCommitments: Fr[],
178
- blockNumber: BlockNumber,
179
- ): Promise<boolean> {
181
+ async addContractClasses(data: ContractClassPublicWithCommitment[], blockNumber: BlockNumber): Promise<boolean> {
180
182
  return (
181
183
  await Promise.all(
182
- data.map((c, i) => this.#contractClassStore.addContractClass(c, bytecodeCommitments[i], blockNumber)),
184
+ data.map(c => this.#contractClassStore.addContractClass(c, c.publicBytecodeCommitment, blockNumber)),
183
185
  )
184
186
  ).every(Boolean);
185
187
  }
@@ -194,15 +196,6 @@ export class KVArchiverDataStore implements ContractDataSource {
194
196
  return this.#contractClassStore.getBytecodeCommitment(contractClassId);
195
197
  }
196
198
 
197
- /** Adds private functions to a contract class. */
198
- addFunctions(
199
- contractClassId: Fr,
200
- privateFunctions: ExecutablePrivateFunctionWithMembershipProof[],
201
- utilityFunctions: UtilityFunctionWithMembershipProof[],
202
- ): Promise<boolean> {
203
- return this.#contractClassStore.addFunctions(contractClassId, privateFunctions, utilityFunctions);
204
- }
205
-
206
199
  /**
207
200
  * Add new contract instances from an L2 block to the store's list.
208
201
  * @param data - List of contract instances to be added.
@@ -269,7 +262,7 @@ export class KVArchiverDataStore implements ContractDataSource {
269
262
  * @returns The number of the latest block
270
263
  */
271
264
  getLatestBlockNumber(): Promise<BlockNumber> {
272
- return this.#blockStore.getLatestBlockNumber();
265
+ return this.#blockStore.getLatestL2BlockNumber();
273
266
  }
274
267
 
275
268
  /**
@@ -567,13 +560,6 @@ export class KVArchiverDataStore implements ContractDataSource {
567
560
  await this.#blockStore.setSynchedL1BlockNumber(l1BlockNumber);
568
561
  }
569
562
 
570
- /**
571
- * Stores the l1 block that messages have been synched until
572
- */
573
- async setMessageSynchedL1Block(l1Block: L1BlockId) {
574
- await this.#messageStore.setSynchedL1Block(l1Block);
575
- }
576
-
577
563
  /**
578
564
  * Returns the number of the most recent proven block
579
565
  * @returns The number of the most recent proven block
@@ -606,9 +592,9 @@ export class KVArchiverDataStore implements ContractDataSource {
606
592
  return this.#messageStore.rollbackL1ToL2MessagesToCheckpoint(targetCheckpointNumber);
607
593
  }
608
594
 
609
- /** Persists the inbox tree-in-progress checkpoint number from L1 state. */
610
- public setInboxTreeInProgress(value: bigint): Promise<void> {
611
- return this.#messageStore.setInboxTreeInProgress(value);
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);
612
598
  }
613
599
 
614
600
  /** Returns an async iterator to all L1 to L2 messages on the range. */
@@ -631,6 +617,73 @@ export class KVArchiverDataStore implements ContractDataSource {
631
617
  return this.#blockStore.setPendingChainValidationStatus(status);
632
618
  }
633
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
+
634
687
  /**
635
688
  * Gets the number of the latest L2 block processed.
636
689
  * @returns The number of the latest L2 block processed.
@@ -676,6 +729,21 @@ export class KVArchiverDataStore implements ContractDataSource {
676
729
  return this.#blockStore.getCheckpointDataForSlotRange(startSlot, endSlot);
677
730
  }
678
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
+
679
747
  /**
680
748
  * Gets all blocks that have the given slot number.
681
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) {
@@ -1,7 +1,6 @@
1
1
  import { INITIAL_L2_BLOCK_NUM } from '@aztec/constants';
2
2
  import { BlockNumber } from '@aztec/foundation/branded-types';
3
3
  import { compactArray, filterAsync } from '@aztec/foundation/collection';
4
- import { Fr } from '@aztec/foundation/curves/bn254';
5
4
  import { createLogger } from '@aztec/foundation/log';
6
5
  import { BufferReader, numToUInt32BE } from '@aztec/foundation/serialize';
7
6
  import type { AztecAsyncKVStore, AztecAsyncMap } from '@aztec/kv-store';
@@ -302,13 +301,11 @@ export class LogStore {
302
301
  }
303
302
 
304
303
  #unpackBlockHash(reader: BufferReader): BlockHash {
305
- const blockHash = reader.remainingBytes() > 0 ? reader.readObject(Fr) : undefined;
306
-
307
- if (!blockHash) {
304
+ if (reader.remainingBytes() === 0) {
308
305
  throw new Error('Failed to read block hash from log entry buffer');
309
306
  }
310
307
 
311
- return new BlockHash(blockHash);
308
+ return BlockHash.fromBuffer(reader);
312
309
  }
313
310
 
314
311
  deleteLogs(blocks: L2Block[]): Promise<boolean> {
@@ -161,15 +161,6 @@ export class MessageStore {
161
161
  lastMessage = message;
162
162
  }
163
163
 
164
- // Update the L1 sync point to that of the last message added.
165
- const currentSyncPoint = await this.getSynchedL1Block();
166
- if (!currentSyncPoint || currentSyncPoint.l1BlockNumber < lastMessage!.l1BlockNumber) {
167
- await this.setSynchedL1Block({
168
- l1BlockNumber: lastMessage!.l1BlockNumber,
169
- l1BlockHash: lastMessage!.l1BlockHash,
170
- });
171
- }
172
-
173
164
  // Update total message count with the number of inserted messages.
174
165
  await this.increaseTotalMessageCount(messageCount);
175
166
  });
@@ -194,9 +185,16 @@ export class MessageStore {
194
185
  return this.#inboxTreeInProgress.getAsync();
195
186
  }
196
187
 
197
- /** Persists the inbox tree-in-progress checkpoint number from L1 state. */
198
- public async setInboxTreeInProgress(value: bigint): Promise<void> {
199
- await this.#inboxTreeInProgress.set(value);
188
+ /** Atomically updates the message sync state: the L1 sync point and the inbox tree-in-progress marker. */
189
+ public setMessageSyncState(l1Block: L1BlockId, treeInProgress: bigint | undefined): Promise<void> {
190
+ return this.db.transactionAsync(async () => {
191
+ await this.setSynchedL1Block(l1Block);
192
+ if (treeInProgress !== undefined) {
193
+ await this.#inboxTreeInProgress.set(treeInProgress);
194
+ } else {
195
+ await this.#inboxTreeInProgress.delete();
196
+ }
197
+ });
200
198
  }
201
199
 
202
200
  public async getL1ToL2Messages(checkpointNumber: CheckpointNumber): Promise<Fr[]> {
@@ -8,7 +8,7 @@ export type InboxMessage = {
8
8
  index: bigint;
9
9
  leaf: Fr;
10
10
  checkpointNumber: CheckpointNumber;
11
- l1BlockNumber: bigint; // L1 block number - NOT Aztec L2
11
+ l1BlockNumber: bigint;
12
12
  l1BlockHash: Buffer32;
13
13
  rollingHash: Buffer16;
14
14
  };