@aztec/archiver 0.0.1-commit.1142ef1 → 0.0.1-commit.125b3452

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 (201) hide show
  1. package/README.md +156 -22
  2. package/dest/archiver.d.ts +139 -0
  3. package/dest/archiver.d.ts.map +1 -0
  4. package/dest/archiver.js +733 -0
  5. package/dest/{archiver/config.d.ts → config.d.ts} +9 -1
  6. package/dest/config.d.ts.map +1 -0
  7. package/dest/{archiver/config.js → config.js} +9 -0
  8. package/dest/errors.d.ts +53 -0
  9. package/dest/errors.d.ts.map +1 -0
  10. package/dest/errors.js +75 -0
  11. package/dest/factory.d.ts +8 -7
  12. package/dest/factory.d.ts.map +1 -1
  13. package/dest/factory.js +90 -8
  14. package/dest/index.d.ts +11 -4
  15. package/dest/index.d.ts.map +1 -1
  16. package/dest/index.js +9 -3
  17. package/dest/interfaces.d.ts +9 -0
  18. package/dest/interfaces.d.ts.map +1 -0
  19. package/dest/interfaces.js +3 -0
  20. package/dest/{archiver/l1 → l1}/bin/retrieve-calldata.d.ts +1 -1
  21. package/dest/l1/bin/retrieve-calldata.d.ts.map +1 -0
  22. package/dest/{archiver/l1 → l1}/bin/retrieve-calldata.js +35 -32
  23. package/dest/l1/calldata_retriever.d.ts +135 -0
  24. package/dest/l1/calldata_retriever.d.ts.map +1 -0
  25. package/dest/l1/calldata_retriever.js +402 -0
  26. package/dest/l1/data_retrieval.d.ts +88 -0
  27. package/dest/l1/data_retrieval.d.ts.map +1 -0
  28. package/dest/{archiver/l1 → l1}/data_retrieval.js +54 -71
  29. package/dest/{archiver/l1 → l1}/debug_tx.d.ts +1 -1
  30. package/dest/l1/debug_tx.d.ts.map +1 -0
  31. package/dest/{archiver/l1 → l1}/spire_proposer.d.ts +5 -5
  32. package/dest/l1/spire_proposer.d.ts.map +1 -0
  33. package/dest/{archiver/l1 → l1}/spire_proposer.js +9 -17
  34. package/dest/{archiver/l1 → l1}/trace_tx.d.ts +1 -1
  35. package/dest/l1/trace_tx.d.ts.map +1 -0
  36. package/dest/l1/types.d.ts +12 -0
  37. package/dest/l1/types.d.ts.map +1 -0
  38. package/dest/{archiver/l1 → l1}/validate_trace.d.ts +6 -3
  39. package/dest/l1/validate_trace.d.ts.map +1 -0
  40. package/dest/{archiver/l1 → l1}/validate_trace.js +13 -9
  41. package/dest/modules/data_source_base.d.ts +89 -0
  42. package/dest/modules/data_source_base.d.ts.map +1 -0
  43. package/dest/modules/data_source_base.js +216 -0
  44. package/dest/modules/data_store_updater.d.ts +88 -0
  45. package/dest/modules/data_store_updater.d.ts.map +1 -0
  46. package/dest/modules/data_store_updater.js +342 -0
  47. package/dest/modules/instrumentation.d.ts +50 -0
  48. package/dest/modules/instrumentation.d.ts.map +1 -0
  49. package/dest/{archiver → modules}/instrumentation.js +36 -12
  50. package/dest/modules/l1_synchronizer.d.ts +72 -0
  51. package/dest/modules/l1_synchronizer.d.ts.map +1 -0
  52. package/dest/modules/l1_synchronizer.js +1144 -0
  53. package/dest/{archiver → modules}/validation.d.ts +1 -1
  54. package/dest/modules/validation.d.ts.map +1 -0
  55. package/dest/{archiver → modules}/validation.js +6 -0
  56. package/dest/store/block_store.d.ts +195 -0
  57. package/dest/store/block_store.d.ts.map +1 -0
  58. package/dest/{archiver/kv_archiver_store → store}/block_store.js +248 -101
  59. package/dest/store/contract_class_store.d.ts +18 -0
  60. package/dest/store/contract_class_store.d.ts.map +1 -0
  61. package/dest/{archiver/kv_archiver_store → store}/contract_class_store.js +12 -8
  62. package/dest/store/contract_instance_store.d.ts +24 -0
  63. package/dest/store/contract_instance_store.d.ts.map +1 -0
  64. package/dest/{archiver/kv_archiver_store → store}/contract_instance_store.js +1 -1
  65. package/dest/store/kv_archiver_store.d.ts +367 -0
  66. package/dest/store/kv_archiver_store.d.ts.map +1 -0
  67. package/dest/store/kv_archiver_store.js +481 -0
  68. package/dest/store/l2_tips_cache.d.ts +19 -0
  69. package/dest/store/l2_tips_cache.d.ts.map +1 -0
  70. package/dest/store/l2_tips_cache.js +89 -0
  71. package/dest/store/log_store.d.ts +57 -0
  72. package/dest/store/log_store.d.ts.map +1 -0
  73. package/dest/{archiver/kv_archiver_store → store}/log_store.js +204 -92
  74. package/dest/store/message_store.d.ts +44 -0
  75. package/dest/store/message_store.d.ts.map +1 -0
  76. package/dest/{archiver/kv_archiver_store → store}/message_store.js +14 -1
  77. package/dest/{archiver/structs → structs}/data_retrieval.d.ts +1 -1
  78. package/dest/structs/data_retrieval.d.ts.map +1 -0
  79. package/dest/structs/inbox_message.d.ts +15 -0
  80. package/dest/structs/inbox_message.d.ts.map +1 -0
  81. package/dest/{archiver/structs → structs}/published.d.ts +1 -1
  82. package/dest/structs/published.d.ts.map +1 -0
  83. package/dest/test/fake_l1_state.d.ts +202 -0
  84. package/dest/test/fake_l1_state.d.ts.map +1 -0
  85. package/dest/test/fake_l1_state.js +455 -0
  86. package/dest/test/index.d.ts +2 -1
  87. package/dest/test/index.d.ts.map +1 -1
  88. package/dest/test/index.js +4 -1
  89. package/dest/test/mock_archiver.d.ts +2 -2
  90. package/dest/test/mock_archiver.d.ts.map +1 -1
  91. package/dest/test/mock_archiver.js +3 -3
  92. package/dest/test/mock_l2_block_source.d.ts +38 -19
  93. package/dest/test/mock_l2_block_source.d.ts.map +1 -1
  94. package/dest/test/mock_l2_block_source.js +183 -77
  95. package/dest/test/mock_structs.d.ts +81 -3
  96. package/dest/test/mock_structs.d.ts.map +1 -1
  97. package/dest/test/mock_structs.js +152 -7
  98. package/dest/test/noop_l1_archiver.d.ts +26 -0
  99. package/dest/test/noop_l1_archiver.d.ts.map +1 -0
  100. package/dest/test/noop_l1_archiver.js +72 -0
  101. package/package.json +16 -17
  102. package/src/archiver.ts +487 -0
  103. package/src/{archiver/config.ts → config.ts} +11 -0
  104. package/src/{archiver/errors.ts → errors.ts} +52 -24
  105. package/src/factory.ts +141 -10
  106. package/src/index.ts +11 -3
  107. package/src/interfaces.ts +9 -0
  108. package/src/l1/README.md +55 -0
  109. package/src/{archiver/l1 → l1}/bin/retrieve-calldata.ts +45 -33
  110. package/src/l1/calldata_retriever.ts +511 -0
  111. package/src/{archiver/l1 → l1}/data_retrieval.ts +75 -94
  112. package/src/{archiver/l1 → l1}/spire_proposer.ts +7 -15
  113. package/src/{archiver/l1 → l1}/validate_trace.ts +24 -6
  114. package/src/modules/data_source_base.ts +333 -0
  115. package/src/modules/data_store_updater.ts +464 -0
  116. package/src/{archiver → modules}/instrumentation.ts +46 -14
  117. package/src/modules/l1_synchronizer.ts +963 -0
  118. package/src/{archiver → modules}/validation.ts +5 -0
  119. package/src/{archiver/kv_archiver_store → store}/block_store.ts +309 -141
  120. package/src/{archiver/kv_archiver_store → store}/contract_class_store.ts +12 -8
  121. package/src/{archiver/kv_archiver_store → store}/contract_instance_store.ts +1 -1
  122. package/src/{archiver/kv_archiver_store → store}/kv_archiver_store.ts +294 -39
  123. package/src/store/l2_tips_cache.ts +89 -0
  124. package/src/{archiver/kv_archiver_store → store}/log_store.ts +308 -119
  125. package/src/{archiver/kv_archiver_store → store}/message_store.ts +20 -1
  126. package/src/test/fake_l1_state.ts +698 -0
  127. package/src/test/index.ts +4 -0
  128. package/src/test/mock_archiver.ts +4 -3
  129. package/src/test/mock_l2_block_source.ts +233 -93
  130. package/src/test/mock_structs.ts +283 -8
  131. package/src/test/noop_l1_archiver.ts +115 -0
  132. package/dest/archiver/archiver.d.ts +0 -307
  133. package/dest/archiver/archiver.d.ts.map +0 -1
  134. package/dest/archiver/archiver.js +0 -2102
  135. package/dest/archiver/archiver_store.d.ts +0 -315
  136. package/dest/archiver/archiver_store.d.ts.map +0 -1
  137. package/dest/archiver/archiver_store.js +0 -4
  138. package/dest/archiver/archiver_store_test_suite.d.ts +0 -8
  139. package/dest/archiver/archiver_store_test_suite.d.ts.map +0 -1
  140. package/dest/archiver/archiver_store_test_suite.js +0 -2770
  141. package/dest/archiver/config.d.ts.map +0 -1
  142. package/dest/archiver/errors.d.ts +0 -36
  143. package/dest/archiver/errors.d.ts.map +0 -1
  144. package/dest/archiver/errors.js +0 -54
  145. package/dest/archiver/index.d.ts +0 -7
  146. package/dest/archiver/index.d.ts.map +0 -1
  147. package/dest/archiver/index.js +0 -4
  148. package/dest/archiver/instrumentation.d.ts +0 -37
  149. package/dest/archiver/instrumentation.d.ts.map +0 -1
  150. package/dest/archiver/kv_archiver_store/block_store.d.ts +0 -164
  151. package/dest/archiver/kv_archiver_store/block_store.d.ts.map +0 -1
  152. package/dest/archiver/kv_archiver_store/contract_class_store.d.ts +0 -18
  153. package/dest/archiver/kv_archiver_store/contract_class_store.d.ts.map +0 -1
  154. package/dest/archiver/kv_archiver_store/contract_instance_store.d.ts +0 -24
  155. package/dest/archiver/kv_archiver_store/contract_instance_store.d.ts.map +0 -1
  156. package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts +0 -159
  157. package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts.map +0 -1
  158. package/dest/archiver/kv_archiver_store/kv_archiver_store.js +0 -316
  159. package/dest/archiver/kv_archiver_store/log_store.d.ts +0 -45
  160. package/dest/archiver/kv_archiver_store/log_store.d.ts.map +0 -1
  161. package/dest/archiver/kv_archiver_store/message_store.d.ts +0 -40
  162. package/dest/archiver/kv_archiver_store/message_store.d.ts.map +0 -1
  163. package/dest/archiver/l1/bin/retrieve-calldata.d.ts.map +0 -1
  164. package/dest/archiver/l1/calldata_retriever.d.ts +0 -112
  165. package/dest/archiver/l1/calldata_retriever.d.ts.map +0 -1
  166. package/dest/archiver/l1/calldata_retriever.js +0 -471
  167. package/dest/archiver/l1/data_retrieval.d.ts +0 -90
  168. package/dest/archiver/l1/data_retrieval.d.ts.map +0 -1
  169. package/dest/archiver/l1/debug_tx.d.ts.map +0 -1
  170. package/dest/archiver/l1/spire_proposer.d.ts.map +0 -1
  171. package/dest/archiver/l1/trace_tx.d.ts.map +0 -1
  172. package/dest/archiver/l1/types.d.ts +0 -12
  173. package/dest/archiver/l1/types.d.ts.map +0 -1
  174. package/dest/archiver/l1/validate_trace.d.ts.map +0 -1
  175. package/dest/archiver/structs/data_retrieval.d.ts.map +0 -1
  176. package/dest/archiver/structs/inbox_message.d.ts +0 -15
  177. package/dest/archiver/structs/inbox_message.d.ts.map +0 -1
  178. package/dest/archiver/structs/published.d.ts.map +0 -1
  179. package/dest/archiver/validation.d.ts.map +0 -1
  180. package/dest/rpc/index.d.ts +0 -9
  181. package/dest/rpc/index.d.ts.map +0 -1
  182. package/dest/rpc/index.js +0 -15
  183. package/src/archiver/archiver.ts +0 -2265
  184. package/src/archiver/archiver_store.ts +0 -380
  185. package/src/archiver/archiver_store_test_suite.ts +0 -2842
  186. package/src/archiver/index.ts +0 -6
  187. package/src/archiver/l1/README.md +0 -98
  188. package/src/archiver/l1/calldata_retriever.ts +0 -641
  189. package/src/rpc/index.ts +0 -16
  190. /package/dest/{archiver/l1 → l1}/debug_tx.js +0 -0
  191. /package/dest/{archiver/l1 → l1}/trace_tx.js +0 -0
  192. /package/dest/{archiver/l1 → l1}/types.js +0 -0
  193. /package/dest/{archiver/structs → structs}/data_retrieval.js +0 -0
  194. /package/dest/{archiver/structs → structs}/inbox_message.js +0 -0
  195. /package/dest/{archiver/structs → structs}/published.js +0 -0
  196. /package/src/{archiver/l1 → l1}/debug_tx.ts +0 -0
  197. /package/src/{archiver/l1 → l1}/trace_tx.ts +0 -0
  198. /package/src/{archiver/l1 → l1}/types.ts +0 -0
  199. /package/src/{archiver/structs → structs}/data_retrieval.ts +0 -0
  200. /package/src/{archiver/structs → structs}/inbox_message.ts +0 -0
  201. /package/src/{archiver/structs → structs}/published.ts +0 -0
@@ -1,11 +1,13 @@
1
1
  import { INITIAL_L2_BLOCK_NUM } from '@aztec/constants';
2
2
  import { BlockNumber } from '@aztec/foundation/branded-types';
3
+ import { filterAsync } from '@aztec/foundation/collection';
3
4
  import { Fr } from '@aztec/foundation/curves/bn254';
4
5
  import { createLogger } from '@aztec/foundation/log';
5
6
  import { BufferReader, numToUInt32BE } from '@aztec/foundation/serialize';
6
7
  import type { AztecAsyncKVStore, AztecAsyncMap } from '@aztec/kv-store';
7
8
  import type { AztecAddress } from '@aztec/stdlib/aztec-address';
8
- import { L2BlockHash, L2BlockNew } from '@aztec/stdlib/block';
9
+ import { BlockHash, L2Block } from '@aztec/stdlib/block';
10
+ import { MAX_LOGS_PER_TAG } from '@aztec/stdlib/interfaces/api-limit';
9
11
  import type { GetContractClassLogsResponse, GetPublicLogsResponse } from '@aztec/stdlib/interfaces/client';
10
12
  import {
11
13
  ContractClassLog,
@@ -18,7 +20,9 @@ import {
18
20
  Tag,
19
21
  TxScopedL2Log,
20
22
  } from '@aztec/stdlib/logs';
23
+ import { TxHash } from '@aztec/stdlib/tx';
21
24
 
25
+ import { OutOfOrderLogInsertionError } from '../errors.js';
22
26
  import type { BlockStore } from './block_store.js';
23
27
 
24
28
  /**
@@ -57,7 +61,7 @@ export class LogStore {
57
61
  * @param block - The L2 block to extract logs from.
58
62
  * @returns An object containing the private and public tagged logs for the block.
59
63
  */
60
- #extractTaggedLogsFromBlock(block: L2BlockNew) {
64
+ #extractTaggedLogsFromBlock(block: L2Block) {
61
65
  // SiloedTag (as string) -> array of log buffers.
62
66
  const privateTaggedLogs = new Map<string, Buffer[]>();
63
67
  // "{contractAddress}_{tag}" (as string) -> array of log buffers.
@@ -118,7 +122,7 @@ export class LogStore {
118
122
  * @returns A map from tag (as string) to an array of serialized private logs belonging to that tag, and a map from
119
123
  * "{contractAddress}_{tag}" (as string) to an array of serialized public logs belonging to that key.
120
124
  */
121
- #extractTaggedLogs(blocks: L2BlockNew[]): {
125
+ #extractTaggedLogs(blocks: L2Block[]): {
122
126
  privateTaggedLogs: Map<string, Buffer[]>;
123
127
  publicTaggedLogs: Map<string, Buffer[]>;
124
128
  } {
@@ -144,111 +148,170 @@ export class LogStore {
144
148
  return { privateTaggedLogs, publicTaggedLogs };
145
149
  }
146
150
 
147
- /**
148
- * Append new logs to the store's list.
149
- * @param blocks - The blocks for which to add the logs.
150
- * @returns True if the operation is successful.
151
- */
152
- addLogs(blocks: L2BlockNew[]): Promise<boolean> {
153
- const { privateTaggedLogs, publicTaggedLogs } = this.#extractTaggedLogs(blocks);
151
+ async #addPrivateLogs(blocks: L2Block[]): Promise<void> {
152
+ const newBlocks = await filterAsync(
153
+ blocks,
154
+ async block => !(await this.#privateLogKeysByBlock.hasAsync(block.number)),
155
+ );
154
156
 
157
+ const { privateTaggedLogs } = this.#extractTaggedLogs(newBlocks);
155
158
  const keysOfPrivateLogsToUpdate = Array.from(privateTaggedLogs.keys());
156
- const keysOfPublicLogsToUpdate = Array.from(publicTaggedLogs.keys());
157
159
 
158
- return this.db.transactionAsync(async () => {
159
- const currentPrivateTaggedLogs = await Promise.all(
160
- keysOfPrivateLogsToUpdate.map(async key => ({
161
- tag: key,
162
- logBuffers: await this.#privateLogsByTag.getAsync(key),
163
- })),
164
- );
165
- currentPrivateTaggedLogs.forEach(taggedLogBuffer => {
166
- if (taggedLogBuffer.logBuffers && taggedLogBuffer.logBuffers.length > 0) {
167
- privateTaggedLogs.set(
160
+ const currentPrivateTaggedLogs = await Promise.all(
161
+ keysOfPrivateLogsToUpdate.map(async key => ({
162
+ tag: key,
163
+ logBuffers: await this.#privateLogsByTag.getAsync(key),
164
+ })),
165
+ );
166
+
167
+ for (const taggedLogBuffer of currentPrivateTaggedLogs) {
168
+ if (taggedLogBuffer.logBuffers && taggedLogBuffer.logBuffers.length > 0) {
169
+ const newLogs = privateTaggedLogs.get(taggedLogBuffer.tag)!;
170
+ if (newLogs.length === 0) {
171
+ continue;
172
+ }
173
+ const lastExisting = TxScopedL2Log.fromBuffer(taggedLogBuffer.logBuffers.at(-1)!);
174
+ const firstNew = TxScopedL2Log.fromBuffer(newLogs[0]);
175
+ if (lastExisting.blockNumber > firstNew.blockNumber) {
176
+ throw new OutOfOrderLogInsertionError(
177
+ 'private',
168
178
  taggedLogBuffer.tag,
169
- taggedLogBuffer.logBuffers!.concat(privateTaggedLogs.get(taggedLogBuffer.tag)!),
179
+ lastExisting.blockNumber,
180
+ firstNew.blockNumber,
170
181
  );
171
182
  }
172
- });
183
+ privateTaggedLogs.set(taggedLogBuffer.tag, taggedLogBuffer.logBuffers.concat(newLogs));
184
+ }
185
+ }
173
186
 
174
- const currentPublicTaggedLogs = await Promise.all(
175
- keysOfPublicLogsToUpdate.map(async key => ({
176
- key,
177
- logBuffers: await this.#publicLogsByContractAndTag.getAsync(key),
178
- })),
179
- );
180
- currentPublicTaggedLogs.forEach(taggedLogBuffer => {
181
- if (taggedLogBuffer.logBuffers && taggedLogBuffer.logBuffers.length > 0) {
182
- publicTaggedLogs.set(
183
- taggedLogBuffer.key,
184
- taggedLogBuffer.logBuffers!.concat(publicTaggedLogs.get(taggedLogBuffer.key)!),
185
- );
186
- }
187
- });
187
+ for (const block of newBlocks) {
188
+ const privateTagsInBlock: string[] = [];
189
+ for (const [tag, logs] of privateTaggedLogs.entries()) {
190
+ await this.#privateLogsByTag.set(tag, logs);
191
+ privateTagsInBlock.push(tag);
192
+ }
193
+ await this.#privateLogKeysByBlock.set(block.number, privateTagsInBlock);
194
+ }
195
+ }
188
196
 
189
- for (const block of blocks) {
190
- const blockHash = await block.hash();
197
+ async #addPublicLogs(blocks: L2Block[]): Promise<void> {
198
+ const newBlocks = await filterAsync(
199
+ blocks,
200
+ async block => !(await this.#publicLogKeysByBlock.hasAsync(block.number)),
201
+ );
191
202
 
192
- const privateTagsInBlock: string[] = [];
193
- for (const [tag, logs] of privateTaggedLogs.entries()) {
194
- await this.#privateLogsByTag.set(tag, logs);
195
- privateTagsInBlock.push(tag);
196
- }
197
- await this.#privateLogKeysByBlock.set(block.number, privateTagsInBlock);
203
+ const { publicTaggedLogs } = this.#extractTaggedLogs(newBlocks);
204
+ const keysOfPublicLogsToUpdate = Array.from(publicTaggedLogs.keys());
198
205
 
199
- const publicKeysInBlock: string[] = [];
200
- for (const [key, logs] of publicTaggedLogs.entries()) {
201
- await this.#publicLogsByContractAndTag.set(key, logs);
202
- publicKeysInBlock.push(key);
206
+ const currentPublicTaggedLogs = await Promise.all(
207
+ keysOfPublicLogsToUpdate.map(async key => ({
208
+ tag: key,
209
+ logBuffers: await this.#publicLogsByContractAndTag.getAsync(key),
210
+ })),
211
+ );
212
+
213
+ for (const taggedLogBuffer of currentPublicTaggedLogs) {
214
+ if (taggedLogBuffer.logBuffers && taggedLogBuffer.logBuffers.length > 0) {
215
+ const newLogs = publicTaggedLogs.get(taggedLogBuffer.tag)!;
216
+ if (newLogs.length === 0) {
217
+ continue;
203
218
  }
204
- await this.#publicLogKeysByBlock.set(block.number, publicKeysInBlock);
205
-
206
- const publicLogsInBlock = block.body.txEffects
207
- .map((txEffect, txIndex) =>
208
- [
209
- numToUInt32BE(txIndex),
210
- numToUInt32BE(txEffect.publicLogs.length),
211
- txEffect.publicLogs.map(log => log.toBuffer()),
212
- ].flat(),
213
- )
214
- .flat();
215
-
216
- const contractClassLogsInBlock = block.body.txEffects
217
- .map((txEffect, txIndex) =>
218
- [
219
- numToUInt32BE(txIndex),
220
- numToUInt32BE(txEffect.contractClassLogs.length),
221
- txEffect.contractClassLogs.map(log => log.toBuffer()),
222
- ].flat(),
223
- )
224
- .flat();
225
-
226
- await this.#publicLogsByBlock.set(block.number, this.#packWithBlockHash(blockHash, publicLogsInBlock));
227
- await this.#contractClassLogsByBlock.set(
228
- block.number,
229
- this.#packWithBlockHash(blockHash, contractClassLogsInBlock),
230
- );
219
+ const lastExisting = TxScopedL2Log.fromBuffer(taggedLogBuffer.logBuffers.at(-1)!);
220
+ const firstNew = TxScopedL2Log.fromBuffer(newLogs[0]);
221
+ if (lastExisting.blockNumber > firstNew.blockNumber) {
222
+ throw new OutOfOrderLogInsertionError(
223
+ 'public',
224
+ taggedLogBuffer.tag,
225
+ lastExisting.blockNumber,
226
+ firstNew.blockNumber,
227
+ );
228
+ }
229
+ publicTaggedLogs.set(taggedLogBuffer.tag, taggedLogBuffer.logBuffers.concat(newLogs));
231
230
  }
231
+ }
232
232
 
233
+ for (const block of newBlocks) {
234
+ const blockHash = await block.hash();
235
+ const publicTagsInBlock: string[] = [];
236
+ for (const [tag, logs] of publicTaggedLogs.entries()) {
237
+ await this.#publicLogsByContractAndTag.set(tag, logs);
238
+ publicTagsInBlock.push(tag);
239
+ }
240
+ await this.#publicLogKeysByBlock.set(block.number, publicTagsInBlock);
241
+
242
+ const publicLogsInBlock = block.body.txEffects
243
+ .map((txEffect, txIndex) =>
244
+ [
245
+ numToUInt32BE(txIndex),
246
+ txEffect.txHash.toBuffer(),
247
+ numToUInt32BE(txEffect.publicLogs.length),
248
+ txEffect.publicLogs.map(log => log.toBuffer()),
249
+ ].flat(),
250
+ )
251
+ .flat();
252
+
253
+ await this.#publicLogsByBlock.set(block.number, this.#packWithBlockHash(blockHash, publicLogsInBlock));
254
+ }
255
+ }
256
+
257
+ async #addContractClassLogs(blocks: L2Block[]): Promise<void> {
258
+ const newBlocks = await filterAsync(
259
+ blocks,
260
+ async block => !(await this.#contractClassLogsByBlock.hasAsync(block.number)),
261
+ );
262
+
263
+ for (const block of newBlocks) {
264
+ const blockHash = await block.hash();
265
+
266
+ const contractClassLogsInBlock = block.body.txEffects
267
+ .map((txEffect, txIndex) =>
268
+ [
269
+ numToUInt32BE(txIndex),
270
+ txEffect.txHash.toBuffer(),
271
+ numToUInt32BE(txEffect.contractClassLogs.length),
272
+ txEffect.contractClassLogs.map(log => log.toBuffer()),
273
+ ].flat(),
274
+ )
275
+ .flat();
276
+
277
+ await this.#contractClassLogsByBlock.set(
278
+ block.number,
279
+ this.#packWithBlockHash(blockHash, contractClassLogsInBlock),
280
+ );
281
+ }
282
+ }
283
+
284
+ /**
285
+ * Append new logs to the store's list.
286
+ * @param blocks - The blocks for which to add the logs.
287
+ * @returns True if the operation is successful.
288
+ */
289
+ addLogs(blocks: L2Block[]): Promise<boolean> {
290
+ return this.db.transactionAsync(async () => {
291
+ await Promise.all([
292
+ this.#addPrivateLogs(blocks),
293
+ this.#addPublicLogs(blocks),
294
+ this.#addContractClassLogs(blocks),
295
+ ]);
233
296
  return true;
234
297
  });
235
298
  }
236
299
 
237
- #packWithBlockHash(blockHash: Fr, data: Buffer<ArrayBufferLike>[]): Buffer<ArrayBufferLike> {
300
+ #packWithBlockHash(blockHash: BlockHash, data: Buffer<ArrayBufferLike>[]): Buffer<ArrayBufferLike> {
238
301
  return Buffer.concat([blockHash.toBuffer(), ...data]);
239
302
  }
240
303
 
241
- #unpackBlockHash(reader: BufferReader): L2BlockHash {
304
+ #unpackBlockHash(reader: BufferReader): BlockHash {
242
305
  const blockHash = reader.remainingBytes() > 0 ? reader.readObject(Fr) : undefined;
243
306
 
244
307
  if (!blockHash) {
245
308
  throw new Error('Failed to read block hash from log entry buffer');
246
309
  }
247
310
 
248
- return L2BlockHash.fromField(blockHash);
311
+ return new BlockHash(blockHash);
249
312
  }
250
313
 
251
- deleteLogs(blocks: L2BlockNew[]): Promise<boolean> {
314
+ deleteLogs(blocks: L2Block[]): Promise<boolean> {
252
315
  return this.db.transactionAsync(async () => {
253
316
  await Promise.all(
254
317
  blocks.map(async block => {
@@ -278,27 +341,71 @@ export class LogStore {
278
341
  }
279
342
 
280
343
  /**
281
- * Gets all private logs that match any of the `tags`. For each tag, an array of matching logs is returned. An empty
344
+ * Gets private logs that match any of the `tags`. For each tag, an array of matching logs is returned. An empty
282
345
  * array implies no logs match that tag.
346
+ * @param tags - The tags to search for.
347
+ * @param page - The page number (0-indexed) for pagination.
348
+ * @param upToBlockNumber - If set, only return logs from blocks up to and including this block number.
349
+ * @returns An array of log arrays, one per tag. Returns at most MAX_LOGS_PER_TAG logs per tag per page. If
350
+ * MAX_LOGS_PER_TAG logs are returned for a tag, the caller should fetch the next page to check for more logs.
283
351
  */
284
- async getPrivateLogsByTags(tags: SiloedTag[]): Promise<TxScopedL2Log[][]> {
352
+ async getPrivateLogsByTags(
353
+ tags: SiloedTag[],
354
+ page: number = 0,
355
+ upToBlockNumber?: BlockNumber,
356
+ ): Promise<TxScopedL2Log[][]> {
285
357
  const logs = await Promise.all(tags.map(tag => this.#privateLogsByTag.getAsync(tag.toString())));
286
358
 
287
- return logs.map(logBuffers => logBuffers?.map(logBuffer => TxScopedL2Log.fromBuffer(logBuffer)) ?? []);
359
+ const start = page * MAX_LOGS_PER_TAG;
360
+ const end = start + MAX_LOGS_PER_TAG;
361
+
362
+ return logs.map(logBuffers => {
363
+ const deserialized = logBuffers?.slice(start, end).map(buf => TxScopedL2Log.fromBuffer(buf)) ?? [];
364
+ if (upToBlockNumber !== undefined) {
365
+ const cutoff = deserialized.findIndex(log => log.blockNumber > upToBlockNumber);
366
+ if (cutoff !== -1) {
367
+ return deserialized.slice(0, cutoff);
368
+ }
369
+ }
370
+ return deserialized;
371
+ });
288
372
  }
289
373
 
290
374
  /**
291
- * Gets all public logs that match any of the `tags` from the specified contract. For each tag, an array of matching
375
+ * Gets public logs that match any of the `tags` from the specified contract. For each tag, an array of matching
292
376
  * logs is returned. An empty array implies no logs match that tag.
377
+ * @param contractAddress - The contract address to search logs for.
378
+ * @param tags - The tags to search for.
379
+ * @param page - The page number (0-indexed) for pagination.
380
+ * @param upToBlockNumber - If set, only return logs from blocks up to and including this block number.
381
+ * @returns An array of log arrays, one per tag. Returns at most MAX_LOGS_PER_TAG logs per tag per page. If
382
+ * MAX_LOGS_PER_TAG logs are returned for a tag, the caller should fetch the next page to check for more logs.
293
383
  */
294
- async getPublicLogsByTagsFromContract(contractAddress: AztecAddress, tags: Tag[]): Promise<TxScopedL2Log[][]> {
384
+ async getPublicLogsByTagsFromContract(
385
+ contractAddress: AztecAddress,
386
+ tags: Tag[],
387
+ page: number = 0,
388
+ upToBlockNumber?: BlockNumber,
389
+ ): Promise<TxScopedL2Log[][]> {
295
390
  const logs = await Promise.all(
296
391
  tags.map(tag => {
297
392
  const key = `${contractAddress.toString()}_${tag.value.toString()}`;
298
393
  return this.#publicLogsByContractAndTag.getAsync(key);
299
394
  }),
300
395
  );
301
- return logs.map(logBuffers => logBuffers?.map(logBuffer => TxScopedL2Log.fromBuffer(logBuffer)) ?? []);
396
+ const start = page * MAX_LOGS_PER_TAG;
397
+ const end = start + MAX_LOGS_PER_TAG;
398
+
399
+ return logs.map(logBuffers => {
400
+ const deserialized = logBuffers?.slice(start, end).map(buf => TxScopedL2Log.fromBuffer(buf)) ?? [];
401
+ if (upToBlockNumber !== undefined) {
402
+ const cutoff = deserialized.findIndex(log => log.blockNumber > upToBlockNumber);
403
+ if (cutoff !== -1) {
404
+ return deserialized.slice(0, cutoff);
405
+ }
406
+ }
407
+ return deserialized;
408
+ });
302
409
  }
303
410
 
304
411
  /**
@@ -327,24 +434,33 @@ export class LogStore {
327
434
  }
328
435
 
329
436
  const buffer = (await this.#publicLogsByBlock.getAsync(blockNumber)) ?? Buffer.alloc(0);
330
- const publicLogsInBlock: [PublicLog[]] = [[]];
437
+ const publicLogsInBlock: { txHash: TxHash; logs: PublicLog[] }[] = [];
331
438
  const reader = new BufferReader(buffer);
332
439
 
333
440
  const blockHash = this.#unpackBlockHash(reader);
334
441
 
335
442
  while (reader.remainingBytes() > 0) {
336
443
  const indexOfTx = reader.readNumber();
444
+ const txHash = reader.readObject(TxHash);
337
445
  const numLogsInTx = reader.readNumber();
338
- publicLogsInBlock[indexOfTx] = [];
446
+ publicLogsInBlock[indexOfTx] = { txHash, logs: [] };
339
447
  for (let i = 0; i < numLogsInTx; i++) {
340
- publicLogsInBlock[indexOfTx].push(reader.readObject(PublicLog));
448
+ publicLogsInBlock[indexOfTx].logs.push(reader.readObject(PublicLog));
341
449
  }
342
450
  }
343
451
 
344
- const txLogs = publicLogsInBlock[txIndex];
452
+ const txData = publicLogsInBlock[txIndex];
345
453
 
346
454
  const logs: ExtendedPublicLog[] = [];
347
- const maxLogsHit = this.#accumulateLogs(logs, blockNumber, blockHash, txIndex, txLogs, filter);
455
+ const maxLogsHit = this.#accumulatePublicLogs(
456
+ logs,
457
+ blockNumber,
458
+ blockHash,
459
+ txIndex,
460
+ txData.txHash,
461
+ txData.logs,
462
+ filter,
463
+ );
348
464
 
349
465
  return { logs, maxLogsHit };
350
466
  }
@@ -365,22 +481,31 @@ export class LogStore {
365
481
 
366
482
  let maxLogsHit = false;
367
483
  loopOverBlocks: for await (const [blockNumber, logBuffer] of this.#publicLogsByBlock.entriesAsync({ start, end })) {
368
- const publicLogsInBlock: [PublicLog[]] = [[]];
484
+ const publicLogsInBlock: { txHash: TxHash; logs: PublicLog[] }[] = [];
369
485
  const reader = new BufferReader(logBuffer);
370
486
 
371
487
  const blockHash = this.#unpackBlockHash(reader);
372
488
 
373
489
  while (reader.remainingBytes() > 0) {
374
490
  const indexOfTx = reader.readNumber();
491
+ const txHash = reader.readObject(TxHash);
375
492
  const numLogsInTx = reader.readNumber();
376
- publicLogsInBlock[indexOfTx] = [];
493
+ publicLogsInBlock[indexOfTx] = { txHash, logs: [] };
377
494
  for (let i = 0; i < numLogsInTx; i++) {
378
- publicLogsInBlock[indexOfTx].push(reader.readObject(PublicLog));
495
+ publicLogsInBlock[indexOfTx].logs.push(reader.readObject(PublicLog));
379
496
  }
380
497
  }
381
498
  for (let txIndex = filter.afterLog?.txIndex ?? 0; txIndex < publicLogsInBlock.length; txIndex++) {
382
- const txLogs = publicLogsInBlock[txIndex];
383
- maxLogsHit = this.#accumulateLogs(logs, blockNumber, blockHash, txIndex, txLogs, filter);
499
+ const txData = publicLogsInBlock[txIndex];
500
+ maxLogsHit = this.#accumulatePublicLogs(
501
+ logs,
502
+ blockNumber,
503
+ blockHash,
504
+ txIndex,
505
+ txData.txHash,
506
+ txData.logs,
507
+ filter,
508
+ );
384
509
  if (maxLogsHit) {
385
510
  this.#log.debug(`Max logs hit at block ${blockNumber}`);
386
511
  break loopOverBlocks;
@@ -416,24 +541,33 @@ export class LogStore {
416
541
  return { logs: [], maxLogsHit: false };
417
542
  }
418
543
  const contractClassLogsBuffer = (await this.#contractClassLogsByBlock.getAsync(blockNumber)) ?? Buffer.alloc(0);
419
- const contractClassLogsInBlock: [ContractClassLog[]] = [[]];
544
+ const contractClassLogsInBlock: { txHash: TxHash; logs: ContractClassLog[] }[] = [];
420
545
 
421
546
  const reader = new BufferReader(contractClassLogsBuffer);
422
547
  const blockHash = this.#unpackBlockHash(reader);
423
548
 
424
549
  while (reader.remainingBytes() > 0) {
425
550
  const indexOfTx = reader.readNumber();
551
+ const txHash = reader.readObject(TxHash);
426
552
  const numLogsInTx = reader.readNumber();
427
- contractClassLogsInBlock[indexOfTx] = [];
553
+ contractClassLogsInBlock[indexOfTx] = { txHash, logs: [] };
428
554
  for (let i = 0; i < numLogsInTx; i++) {
429
- contractClassLogsInBlock[indexOfTx].push(reader.readObject(ContractClassLog));
555
+ contractClassLogsInBlock[indexOfTx].logs.push(reader.readObject(ContractClassLog));
430
556
  }
431
557
  }
432
558
 
433
- const txLogs = contractClassLogsInBlock[txIndex];
559
+ const txData = contractClassLogsInBlock[txIndex];
434
560
 
435
561
  const logs: ExtendedContractClassLog[] = [];
436
- const maxLogsHit = this.#accumulateLogs(logs, blockNumber, blockHash, txIndex, txLogs, filter);
562
+ const maxLogsHit = this.#accumulateContractClassLogs(
563
+ logs,
564
+ blockNumber,
565
+ blockHash,
566
+ txIndex,
567
+ txData.txHash,
568
+ txData.logs,
569
+ filter,
570
+ );
437
571
 
438
572
  return { logs, maxLogsHit };
439
573
  }
@@ -457,20 +591,29 @@ export class LogStore {
457
591
  start,
458
592
  end,
459
593
  })) {
460
- const contractClassLogsInBlock: [ContractClassLog[]] = [[]];
594
+ const contractClassLogsInBlock: { txHash: TxHash; logs: ContractClassLog[] }[] = [];
461
595
  const reader = new BufferReader(logBuffer);
462
596
  const blockHash = this.#unpackBlockHash(reader);
463
597
  while (reader.remainingBytes() > 0) {
464
598
  const indexOfTx = reader.readNumber();
599
+ const txHash = reader.readObject(TxHash);
465
600
  const numLogsInTx = reader.readNumber();
466
- contractClassLogsInBlock[indexOfTx] = [];
601
+ contractClassLogsInBlock[indexOfTx] = { txHash, logs: [] };
467
602
  for (let i = 0; i < numLogsInTx; i++) {
468
- contractClassLogsInBlock[indexOfTx].push(reader.readObject(ContractClassLog));
603
+ contractClassLogsInBlock[indexOfTx].logs.push(reader.readObject(ContractClassLog));
469
604
  }
470
605
  }
471
606
  for (let txIndex = filter.afterLog?.txIndex ?? 0; txIndex < contractClassLogsInBlock.length; txIndex++) {
472
- const txLogs = contractClassLogsInBlock[txIndex];
473
- maxLogsHit = this.#accumulateLogs(logs, blockNumber, blockHash, txIndex, txLogs, filter);
607
+ const txData = contractClassLogsInBlock[txIndex];
608
+ maxLogsHit = this.#accumulateContractClassLogs(
609
+ logs,
610
+ blockNumber,
611
+ blockHash,
612
+ txIndex,
613
+ txData.txHash,
614
+ txData.logs,
615
+ filter,
616
+ );
474
617
  if (maxLogsHit) {
475
618
  this.#log.debug(`Max logs hit at block ${blockNumber}`);
476
619
  break loopOverBlocks;
@@ -481,28 +624,74 @@ export class LogStore {
481
624
  return { logs, maxLogsHit };
482
625
  }
483
626
 
484
- #accumulateLogs(
485
- results: (ExtendedContractClassLog | ExtendedPublicLog)[],
627
+ #accumulatePublicLogs(
628
+ results: ExtendedPublicLog[],
486
629
  blockNumber: number,
487
- blockHash: L2BlockHash,
630
+ blockHash: BlockHash,
488
631
  txIndex: number,
489
- txLogs: (ContractClassLog | PublicLog)[],
632
+ txHash: TxHash,
633
+ txLogs: PublicLog[],
490
634
  filter: LogFilter = {},
491
635
  ): boolean {
636
+ if (filter.fromBlock && blockNumber < filter.fromBlock) {
637
+ return false;
638
+ }
639
+ if (filter.toBlock && blockNumber >= filter.toBlock) {
640
+ return false;
641
+ }
642
+ if (filter.txHash && !txHash.equals(filter.txHash)) {
643
+ return false;
644
+ }
645
+
492
646
  let maxLogsHit = false;
493
647
  let logIndex = typeof filter.afterLog?.logIndex === 'number' ? filter.afterLog.logIndex + 1 : 0;
494
648
  for (; logIndex < txLogs.length; logIndex++) {
495
649
  const log = txLogs[logIndex];
496
- if (!filter.contractAddress || log.contractAddress.equals(filter.contractAddress)) {
497
- if (log instanceof ContractClassLog) {
498
- results.push(
499
- new ExtendedContractClassLog(new LogId(BlockNumber(blockNumber), blockHash, txIndex, logIndex), log),
500
- );
501
- } else if (log instanceof PublicLog) {
502
- results.push(new ExtendedPublicLog(new LogId(BlockNumber(blockNumber), blockHash, txIndex, logIndex), log));
503
- } else {
504
- throw new Error('Unknown log type');
650
+ if (
651
+ (!filter.contractAddress || log.contractAddress.equals(filter.contractAddress)) &&
652
+ (!filter.tag || log.fields[0]?.equals(filter.tag))
653
+ ) {
654
+ results.push(
655
+ new ExtendedPublicLog(new LogId(BlockNumber(blockNumber), blockHash, txHash, txIndex, logIndex), log),
656
+ );
657
+
658
+ if (results.length >= this.#logsMaxPageSize) {
659
+ maxLogsHit = true;
660
+ break;
505
661
  }
662
+ }
663
+ }
664
+
665
+ return maxLogsHit;
666
+ }
667
+
668
+ #accumulateContractClassLogs(
669
+ results: ExtendedContractClassLog[],
670
+ blockNumber: number,
671
+ blockHash: BlockHash,
672
+ txIndex: number,
673
+ txHash: TxHash,
674
+ txLogs: ContractClassLog[],
675
+ filter: LogFilter = {},
676
+ ): boolean {
677
+ if (filter.fromBlock && blockNumber < filter.fromBlock) {
678
+ return false;
679
+ }
680
+ if (filter.toBlock && blockNumber >= filter.toBlock) {
681
+ return false;
682
+ }
683
+ if (filter.txHash && !txHash.equals(filter.txHash)) {
684
+ return false;
685
+ }
686
+
687
+ let maxLogsHit = false;
688
+ let logIndex = typeof filter.afterLog?.logIndex === 'number' ? filter.afterLog.logIndex + 1 : 0;
689
+ for (; logIndex < txLogs.length; logIndex++) {
690
+ const log = txLogs[logIndex];
691
+ if (!filter.contractAddress || log.contractAddress.equals(filter.contractAddress)) {
692
+ results.push(
693
+ new ExtendedContractClassLog(new LogId(BlockNumber(blockNumber), blockHash, txHash, txIndex, logIndex), log),
694
+ );
506
695
 
507
696
  if (results.length >= this.#logsMaxPageSize) {
508
697
  maxLogsHit = true;
@@ -14,6 +14,7 @@ import {
14
14
  } from '@aztec/kv-store';
15
15
  import { InboxLeaf } from '@aztec/stdlib/messaging';
16
16
 
17
+ import { L1ToL2MessagesNotReadyError } from '../errors.js';
17
18
  import {
18
19
  type InboxMessage,
19
20
  deserializeInboxMessage,
@@ -40,6 +41,8 @@ export class MessageStore {
40
41
  #lastSynchedL1Block: AztecAsyncSingleton<Buffer>;
41
42
  /** Stores total messages stored */
42
43
  #totalMessageCount: AztecAsyncSingleton<bigint>;
44
+ /** Stores the checkpoint number whose message tree is currently being filled on L1. */
45
+ #inboxTreeInProgress: AztecAsyncSingleton<bigint>;
43
46
 
44
47
  #log = createLogger('archiver:message_store');
45
48
 
@@ -48,6 +51,7 @@ export class MessageStore {
48
51
  this.#l1ToL2MessageIndices = db.openMap('archiver_l1_to_l2_message_indices');
49
52
  this.#lastSynchedL1Block = db.openSingleton('archiver_last_l1_block_id');
50
53
  this.#totalMessageCount = db.openSingleton('archiver_l1_to_l2_message_count');
54
+ this.#inboxTreeInProgress = db.openSingleton('archiver_inbox_tree_in_progress');
51
55
  }
52
56
 
53
57
  public async getTotalL1ToL2MessageCount(): Promise<bigint> {
@@ -137,7 +141,7 @@ export class MessageStore {
137
141
  );
138
142
  }
139
143
 
140
- // Check the first message in a block has the correct index.
144
+ // Check the first message in a checkpoint has the correct index.
141
145
  if (
142
146
  (!lastMessage || message.checkpointNumber > lastMessage.checkpointNumber) &&
143
147
  message.index !== expectedStart
@@ -185,7 +189,22 @@ export class MessageStore {
185
189
  return msg ? deserializeInboxMessage(msg) : undefined;
186
190
  }
187
191
 
192
+ /** Returns the inbox tree-in-progress checkpoint number from L1, or undefined if not yet set. */
193
+ public getInboxTreeInProgress(): Promise<bigint | undefined> {
194
+ return this.#inboxTreeInProgress.getAsync();
195
+ }
196
+
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);
200
+ }
201
+
188
202
  public async getL1ToL2Messages(checkpointNumber: CheckpointNumber): Promise<Fr[]> {
203
+ const treeInProgress = await this.#inboxTreeInProgress.getAsync();
204
+ if (treeInProgress !== undefined && BigInt(checkpointNumber) >= treeInProgress) {
205
+ throw new L1ToL2MessagesNotReadyError(checkpointNumber, treeInProgress);
206
+ }
207
+
189
208
  const messages: Fr[] = [];
190
209
 
191
210
  const [startIndex, endIndex] = InboxLeaf.indexRangeForCheckpoint(checkpointNumber);