@aztec/pxe 0.0.1-commit.3100065 → 0.0.1-commit.330febf

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 (207) hide show
  1. package/dest/bin/check_oracle_version.js +5 -46
  2. package/dest/bin/index.d.ts +2 -2
  3. package/dest/bin/index.d.ts.map +1 -1
  4. package/dest/bin/index.js +1 -1
  5. package/dest/bin/oracle_version_helpers.d.ts +8 -39
  6. package/dest/bin/oracle_version_helpers.d.ts.map +1 -1
  7. package/dest/bin/oracle_version_helpers.js +10 -239
  8. package/dest/block_synchronizer/block_synchronizer.d.ts +3 -5
  9. package/dest/block_synchronizer/block_synchronizer.d.ts.map +1 -1
  10. package/dest/block_synchronizer/block_synchronizer.js +5 -18
  11. package/dest/config/package_info.js +1 -1
  12. package/dest/contract/contract_class_service.d.ts +1 -8
  13. package/dest/contract/contract_class_service.d.ts.map +1 -1
  14. package/dest/contract/contract_class_service.js +8 -40
  15. package/dest/contract/helpers.d.ts +1 -1
  16. package/dest/contract/helpers.d.ts.map +1 -1
  17. package/dest/contract/helpers.js +3 -3
  18. package/dest/contract/skip_sync_contracts.d.ts +11 -0
  19. package/dest/contract/skip_sync_contracts.d.ts.map +1 -0
  20. package/dest/contract/skip_sync_contracts.js +18 -0
  21. package/dest/contract_function_simulator/contract_function_simulator.d.ts +1 -8
  22. package/dest/contract_function_simulator/contract_function_simulator.d.ts.map +1 -1
  23. package/dest/contract_function_simulator/contract_function_simulator.js +5 -20
  24. package/dest/contract_function_simulator/index.d.ts +3 -3
  25. package/dest/contract_function_simulator/index.d.ts.map +1 -1
  26. package/dest/contract_function_simulator/index.js +1 -2
  27. package/dest/contract_function_simulator/noir-structs/resolved_tx.d.ts +7 -9
  28. package/dest/contract_function_simulator/noir-structs/resolved_tx.d.ts.map +1 -1
  29. package/dest/contract_function_simulator/noir-structs/resolved_tx.js +3 -44
  30. package/dest/contract_function_simulator/noir-structs/tx_effect_data.d.ts +5 -4
  31. package/dest/contract_function_simulator/noir-structs/tx_effect_data.d.ts.map +1 -1
  32. package/dest/contract_function_simulator/noir-structs/tx_effect_data.js +2 -2
  33. package/dest/contract_function_simulator/oracle/oracle_registry.d.ts +4 -3
  34. package/dest/contract_function_simulator/oracle/oracle_registry.d.ts.map +1 -1
  35. package/dest/contract_function_simulator/oracle/oracle_registry.js +15 -6
  36. package/dest/contract_function_simulator/oracle/oracle_type_mappings.d.ts +87 -35
  37. package/dest/contract_function_simulator/oracle/oracle_type_mappings.d.ts.map +1 -1
  38. package/dest/contract_function_simulator/oracle/oracle_type_mappings.js +297 -287
  39. package/dest/contract_function_simulator/oracle/private_execution_oracle.d.ts +1 -1
  40. package/dest/contract_function_simulator/oracle/private_execution_oracle.d.ts.map +1 -1
  41. package/dest/contract_function_simulator/oracle/private_execution_oracle.js +11 -8
  42. package/dest/contract_function_simulator/oracle/utility_execution_oracle.d.ts +15 -4
  43. package/dest/contract_function_simulator/oracle/utility_execution_oracle.d.ts.map +1 -1
  44. package/dest/contract_function_simulator/oracle/utility_execution_oracle.js +168 -53
  45. package/dest/entrypoints/client/bundle/index.d.ts +2 -1
  46. package/dest/entrypoints/client/bundle/index.d.ts.map +1 -1
  47. package/dest/entrypoints/client/bundle/index.js +1 -0
  48. package/dest/entrypoints/client/bundle/utils.d.ts +1 -1
  49. package/dest/entrypoints/client/bundle/utils.d.ts.map +1 -1
  50. package/dest/entrypoints/client/bundle/utils.js +10 -4
  51. package/dest/entrypoints/client/lazy/index.d.ts +2 -1
  52. package/dest/entrypoints/client/lazy/index.d.ts.map +1 -1
  53. package/dest/entrypoints/client/lazy/index.js +1 -0
  54. package/dest/entrypoints/client/lazy/utils.d.ts +1 -1
  55. package/dest/entrypoints/client/lazy/utils.d.ts.map +1 -1
  56. package/dest/entrypoints/client/lazy/utils.js +10 -4
  57. package/dest/entrypoints/client/store.d.ts +14 -0
  58. package/dest/entrypoints/client/store.d.ts.map +1 -0
  59. package/dest/entrypoints/client/store.js +42 -0
  60. package/dest/entrypoints/server/index.d.ts +2 -1
  61. package/dest/entrypoints/server/index.d.ts.map +1 -1
  62. package/dest/entrypoints/server/index.js +1 -0
  63. package/dest/entrypoints/server/store.d.ts +18 -0
  64. package/dest/entrypoints/server/store.d.ts.map +1 -0
  65. package/dest/entrypoints/server/store.js +28 -0
  66. package/dest/entrypoints/server/utils.d.ts +1 -1
  67. package/dest/entrypoints/server/utils.d.ts.map +1 -1
  68. package/dest/entrypoints/server/utils.js +8 -2
  69. package/dest/events/event_service.d.ts +13 -6
  70. package/dest/events/event_service.d.ts.map +1 -1
  71. package/dest/events/event_service.js +13 -13
  72. package/dest/logs/log_service.d.ts +15 -6
  73. package/dest/logs/log_service.d.ts.map +1 -1
  74. package/dest/logs/log_service.js +25 -32
  75. package/dest/messages/tx_resolver_service.d.ts +18 -7
  76. package/dest/messages/tx_resolver_service.d.ts.map +1 -1
  77. package/dest/messages/tx_resolver_service.js +13 -7
  78. package/dest/node/benchmarked_node.d.ts +20 -0
  79. package/dest/node/benchmarked_node.d.ts.map +1 -0
  80. package/dest/node/benchmarked_node.js +94 -0
  81. package/dest/node/caching_aztec_node.d.ts +24 -0
  82. package/dest/node/caching_aztec_node.d.ts.map +1 -0
  83. package/dest/node/caching_aztec_node.js +267 -0
  84. package/dest/notes/note_service.d.ts +13 -7
  85. package/dest/notes/note_service.d.ts.map +1 -1
  86. package/dest/notes/note_service.js +10 -10
  87. package/dest/oracle_version.d.ts +2 -2
  88. package/dest/oracle_version.js +5 -5
  89. package/dest/private_kernel/batch_planner.d.ts +1 -1
  90. package/dest/private_kernel/batch_planner.d.ts.map +1 -1
  91. package/dest/private_kernel/batch_planner.js +1 -1
  92. package/dest/private_kernel/private_kernel_execution_prover.d.ts +1 -1
  93. package/dest/private_kernel/private_kernel_execution_prover.d.ts.map +1 -1
  94. package/dest/private_kernel/private_kernel_execution_prover.js +45 -5
  95. package/dest/pxe.d.ts +1 -1
  96. package/dest/pxe.d.ts.map +1 -1
  97. package/dest/pxe.js +39 -19
  98. package/dest/storage/contract_store/contract_store.d.ts +1 -1
  99. package/dest/storage/contract_store/contract_store.d.ts.map +1 -1
  100. package/dest/storage/contract_store/contract_store.js +2 -2
  101. package/dest/storage/fact_store/fact_store.d.ts +1 -1
  102. package/dest/storage/fact_store/fact_store.js +1 -1
  103. package/dest/storage/fact_store/index.d.ts +2 -2
  104. package/dest/storage/fact_store/index.d.ts.map +1 -1
  105. package/dest/storage/fact_store/index.js +1 -1
  106. package/dest/storage/fact_store/origin_state.d.ts +4 -2
  107. package/dest/storage/fact_store/origin_state.d.ts.map +1 -1
  108. package/dest/storage/fact_store/origin_state.js +8 -1
  109. package/dest/storage/store_identity.d.ts +35 -0
  110. package/dest/storage/store_identity.d.ts.map +1 -0
  111. package/dest/storage/store_identity.js +44 -0
  112. package/dest/storage/tagging_store/sender_tagging_store.d.ts +41 -10
  113. package/dest/storage/tagging_store/sender_tagging_store.d.ts.map +1 -1
  114. package/dest/storage/tagging_store/sender_tagging_store.js +81 -29
  115. package/dest/tagging/constants.d.ts +10 -1
  116. package/dest/tagging/constants.d.ts.map +1 -1
  117. package/dest/tagging/constants.js +19 -0
  118. package/dest/tagging/get_all_logs_by_tags.d.ts +29 -8
  119. package/dest/tagging/get_all_logs_by_tags.d.ts.map +1 -1
  120. package/dest/tagging/get_all_logs_by_tags.js +29 -10
  121. package/dest/tagging/index.d.ts +3 -3
  122. package/dest/tagging/index.d.ts.map +1 -1
  123. package/dest/tagging/index.js +2 -2
  124. package/dest/tagging/recipient_sync/sync_tagged_private_logs.d.ts +10 -5
  125. package/dest/tagging/recipient_sync/sync_tagged_private_logs.d.ts.map +1 -1
  126. package/dest/tagging/recipient_sync/sync_tagged_private_logs.js +69 -38
  127. package/dest/tagging/sender_sync/sync_sender_tagging_indexes.d.ts +6 -3
  128. package/dest/tagging/sender_sync/sync_sender_tagging_indexes.d.ts.map +1 -1
  129. package/dest/tagging/sender_sync/sync_sender_tagging_indexes.js +23 -38
  130. package/dest/tagging/sender_sync/utils/classify_pending_txs.d.ts +22 -0
  131. package/dest/tagging/sender_sync/utils/classify_pending_txs.d.ts.map +1 -0
  132. package/dest/tagging/sender_sync/utils/classify_pending_txs.js +61 -0
  133. package/dest/tagging/sender_sync/utils/load_and_store_new_tagging_indexes.d.ts +15 -5
  134. package/dest/tagging/sender_sync/utils/load_and_store_new_tagging_indexes.d.ts.map +1 -1
  135. package/dest/tagging/sender_sync/utils/load_and_store_new_tagging_indexes.js +36 -35
  136. package/dest/tagging/sender_sync/utils/resolve_pending_txs.d.ts +29 -0
  137. package/dest/tagging/sender_sync/utils/resolve_pending_txs.d.ts.map +1 -0
  138. package/dest/tagging/sender_sync/utils/resolve_pending_txs.js +41 -0
  139. package/dest/tagging/sender_sync/utils/test_utils.d.ts +16 -0
  140. package/dest/tagging/sender_sync/utils/test_utils.d.ts.map +1 -0
  141. package/dest/tagging/sender_sync/utils/test_utils.js +24 -0
  142. package/dest/tagging/testing/tag_query_test_utils.d.ts +6 -0
  143. package/dest/tagging/testing/tag_query_test_utils.d.ts.map +1 -0
  144. package/dest/tagging/testing/tag_query_test_utils.js +10 -0
  145. package/package.json +17 -17
  146. package/src/bin/check_oracle_version.ts +5 -56
  147. package/src/bin/index.ts +1 -5
  148. package/src/bin/oracle_version_helpers.ts +11 -292
  149. package/src/block_synchronizer/block_synchronizer.ts +6 -23
  150. package/src/config/package_info.ts +1 -1
  151. package/src/contract/contract_class_service.ts +8 -46
  152. package/src/contract/helpers.ts +3 -3
  153. package/src/contract/skip_sync_contracts.ts +23 -0
  154. package/src/contract_function_simulator/contract_function_simulator.ts +5 -20
  155. package/src/contract_function_simulator/index.ts +23 -3
  156. package/src/contract_function_simulator/noir-structs/resolved_tx.ts +11 -45
  157. package/src/contract_function_simulator/noir-structs/tx_effect_data.ts +4 -3
  158. package/src/contract_function_simulator/oracle/oracle_registry.ts +12 -8
  159. package/src/contract_function_simulator/oracle/oracle_type_mappings.ts +301 -198
  160. package/src/contract_function_simulator/oracle/private_execution_oracle.ts +19 -9
  161. package/src/contract_function_simulator/oracle/utility_execution_oracle.ts +184 -56
  162. package/src/entrypoints/client/bundle/index.ts +1 -0
  163. package/src/entrypoints/client/bundle/utils.ts +15 -3
  164. package/src/entrypoints/client/lazy/index.ts +1 -0
  165. package/src/entrypoints/client/lazy/utils.ts +15 -3
  166. package/src/entrypoints/client/store.ts +54 -0
  167. package/src/entrypoints/server/index.ts +1 -0
  168. package/src/entrypoints/server/store.ts +47 -0
  169. package/src/entrypoints/server/utils.ts +21 -7
  170. package/src/events/event_service.ts +24 -14
  171. package/src/logs/log_service.ts +41 -39
  172. package/src/messages/tx_resolver_service.ts +26 -15
  173. package/src/node/benchmarked_node.ts +140 -0
  174. package/src/node/caching_aztec_node.ts +406 -0
  175. package/src/notes/note_service.ts +20 -14
  176. package/src/oracle_version.ts +5 -5
  177. package/src/private_kernel/batch_planner.ts +0 -1
  178. package/src/private_kernel/private_kernel_execution_prover.ts +90 -4
  179. package/src/pxe.ts +40 -21
  180. package/src/storage/contract_store/contract_store.ts +2 -1
  181. package/src/storage/fact_store/fact_store.ts +1 -1
  182. package/src/storage/fact_store/index.ts +1 -0
  183. package/src/storage/fact_store/origin_state.ts +10 -1
  184. package/src/storage/store_identity.ts +72 -0
  185. package/src/storage/tagging_store/sender_tagging_store.ts +113 -41
  186. package/src/tagging/constants.ts +22 -0
  187. package/src/tagging/get_all_logs_by_tags.ts +47 -11
  188. package/src/tagging/index.ts +7 -2
  189. package/src/tagging/recipient_sync/sync_tagged_private_logs.ts +116 -62
  190. package/src/tagging/sender_sync/sync_sender_tagging_indexes.ts +27 -57
  191. package/src/tagging/sender_sync/utils/classify_pending_txs.ts +91 -0
  192. package/src/tagging/sender_sync/utils/load_and_store_new_tagging_indexes.ts +46 -40
  193. package/src/tagging/sender_sync/utils/resolve_pending_txs.ts +64 -0
  194. package/src/tagging/sender_sync/utils/test_utils.ts +46 -0
  195. package/src/tagging/testing/tag_query_test_utils.ts +11 -0
  196. package/dest/contract_function_simulator/aztec_node_read_cache.d.ts +0 -39
  197. package/dest/contract_function_simulator/aztec_node_read_cache.d.ts.map +0 -1
  198. package/dest/contract_function_simulator/aztec_node_read_cache.js +0 -60
  199. package/dest/contract_function_simulator/benchmarked_node.d.ts +0 -9
  200. package/dest/contract_function_simulator/benchmarked_node.d.ts.map +0 -1
  201. package/dest/contract_function_simulator/benchmarked_node.js +0 -77
  202. package/dest/tagging/sender_sync/utils/get_status_change_of_pending.d.ts +0 -17
  203. package/dest/tagging/sender_sync/utils/get_status_change_of_pending.d.ts.map +0 -1
  204. package/dest/tagging/sender_sync/utils/get_status_change_of_pending.js +0 -60
  205. package/src/contract_function_simulator/aztec_node_read_cache.ts +0 -89
  206. package/src/contract_function_simulator/benchmarked_node.ts +0 -103
  207. package/src/tagging/sender_sync/utils/get_status_change_of_pending.ts +0 -66
@@ -3,10 +3,13 @@ import { AppTaggingSecret, SiloedTag, type TaggingIndexRange } from '@aztec/stdl
3
3
  import { TxEffect, TxHash } from '@aztec/stdlib/tx';
4
4
 
5
5
  import type { StagedStore } from '../../job_coordinator/job_coordinator.js';
6
- import { UNFINALIZED_TAGGING_INDEXES_WINDOW_LEN } from '../../tagging/constants.js';
6
+ import { UNFINALIZED_TAGGING_INDEXES_WINDOW_LEN, unfinalizedTaggingIndexesWindowEnd } from '../../tagging/constants.js';
7
+
8
+ /** A tx still awaiting finalization, and the highest tagging index it used for one secret. */
9
+ export type PendingTx = { txHash: string; highestIndex: number };
7
10
 
8
11
  /** Internal representation of a pending index range entry. */
9
- type PendingIndexesEntry = { lowestIndex: number; highestIndex: number; txHash: string };
12
+ type PendingIndexesEntry = PendingTx & { lowestIndex: number };
10
13
 
11
14
  /**
12
15
  * Data provider of tagging data used when syncing the sender tagging indexes. The recipient counterpart of this class
@@ -128,10 +131,11 @@ export class SenderTaggingStore implements StagedStore {
128
131
  }
129
132
 
130
133
  /**
131
- * Stores pending index ranges.
134
+ * Stores pending index ranges, rejecting any range that disagrees with an already-stored one.
132
135
  * @remarks If the same (secret, txHash) pair already exists in the db with an equal range, it's a no-op. This is
133
136
  * expected to happen because whenever we start sync we start from the last finalized index and we can have pending
134
- * ranges already stored from previous syncs. If the ranges differ, it throws an error as that indicates a bug.
137
+ * ranges already stored from previous syncs. If the ranges differ, it throws an error as that indicates a bug in
138
+ * callers that record indexes at prove time. Discovery from onchain logs must use `mergePendingIndexes` instead.
135
139
  * @param ranges - The tagging index ranges containing the directional app tagging secrets and the index ranges that are
136
140
  * to be stored in the db.
137
141
  * @param txHash - The tx in which the tagging indexes were used in private logs.
@@ -141,6 +145,33 @@ export class SenderTaggingStore implements StagedStore {
141
145
  * @throws If a different range already exists for the same (secret, txHash) pair.
142
146
  */
143
147
  storePendingIndexes(ranges: TaggingIndexRange[], txHash: TxHash, jobId: string): Promise<void> {
148
+ return this.#storePendingIndexes(ranges, txHash, jobId, false);
149
+ }
150
+
151
+ /**
152
+ * Stores pending index ranges, widening an existing entry for the same (secret, txHash) pair to the union of the
153
+ * stored and incoming ranges instead of throwing on a mismatch. Discovery from onchain logs needs this: it may see
154
+ * only the surviving (non-revertible phase) sub-range of a partially reverted tx recorded at prove time (the
155
+ * finalized receipt step of the sync resolves that difference), or indexes beyond a partially discovered entry
156
+ * when a tx from another PXE straddles a sync window boundary. Callers that record indexes at prove time must use
157
+ * `storePendingIndexes` instead, so that a range disagreement surfaces as a bug rather than being absorbed.
158
+ * @param ranges - The tagging index ranges containing the directional app tagging secrets and the index ranges that are
159
+ * to be stored in the db.
160
+ * @param txHash - The tx in which the tagging indexes were used in private logs.
161
+ * @param jobId - job context for staged writes to this store. See `JobCoordinator` for more details.
162
+ * @throws If the highestIndex is further than window length from the highest finalized index for the same secret.
163
+ * @throws If the lowestIndex is lower than or equal to the last finalized index for the same secret.
164
+ */
165
+ mergePendingIndexes(ranges: TaggingIndexRange[], txHash: TxHash, jobId: string): Promise<void> {
166
+ return this.#storePendingIndexes(ranges, txHash, jobId, true);
167
+ }
168
+
169
+ #storePendingIndexes(
170
+ ranges: TaggingIndexRange[],
171
+ txHash: TxHash,
172
+ jobId: string,
173
+ mergeExisting: boolean,
174
+ ): Promise<void> {
144
175
  if (ranges.length === 0) {
145
176
  return Promise.resolve();
146
177
  }
@@ -167,13 +198,9 @@ export class SenderTaggingStore implements StagedStore {
167
198
 
168
199
  // Process in memory and validate
169
200
  for (const { range, secretStr, pendingData, finalizedIndex } of rangeData) {
170
- // Check that the highest index is not further than window length from the highest finalized index.
171
- if (range.highestIndex > (finalizedIndex ?? 0) + UNFINALIZED_TAGGING_INDEXES_WINDOW_LEN) {
172
- throw new Error(
173
- `Highest used index ${range.highestIndex} is further than window length from the highest finalized index ${finalizedIndex ?? 0}.
174
- Tagging window length ${UNFINALIZED_TAGGING_INDEXES_WINDOW_LEN} is configured too low. Contact the Aztec team
175
- to increase it!`,
176
- );
201
+ const windowEnd = unfinalizedTaggingIndexesWindowEnd(finalizedIndex);
202
+ if (range.highestIndex >= windowEnd) {
203
+ throw windowExceededError(range.highestIndex, windowEnd, finalizedIndex);
177
204
  }
178
205
 
179
206
  // Throw if the lowest index is lower than or equal to the last finalized index
@@ -187,48 +214,60 @@ export class SenderTaggingStore implements StagedStore {
187
214
  // Check if an entry with the same txHash already exists
188
215
  const existingEntry = pendingData.find(entry => entry.txHash === txHashStr);
189
216
 
190
- if (existingEntry) {
191
- // Assert that the ranges are equal — different ranges for the same (secret, txHash) indicates a bug
192
- if (existingEntry.lowestIndex !== range.lowestIndex || existingEntry.highestIndex !== range.highestIndex) {
193
- throw new Error(
194
- `Conflicting range for secret ${secretStr} and txHash ${txHashStr}: ` +
195
- `existing [${existingEntry.lowestIndex}, ${existingEntry.highestIndex}] vs ` +
196
- `new [${range.lowestIndex}, ${range.highestIndex}]`,
197
- );
198
- }
199
- // Exact duplicate — skip
200
- } else {
201
- this.#writePendingIndexes(jobId, secretStr, [
217
+ let updatedPending: PendingIndexesEntry[] | undefined;
218
+ if (!existingEntry) {
219
+ updatedPending = [
202
220
  ...pendingData,
203
221
  { lowestIndex: range.lowestIndex, highestIndex: range.highestIndex, txHash: txHashStr },
204
- ]);
222
+ ];
223
+ } else if (mergeExisting) {
224
+ // Widen the entry to the union of both ranges, never shrink it: replacing would drop prove-time
225
+ // indexes the chain doesn't show (partially reverted tx), and skipping would drop onchain indexes
226
+ // discovered in a later sync window (tx straddling the window boundary).
227
+ const lowestIndex = Math.min(existingEntry.lowestIndex, range.lowestIndex);
228
+ const highestIndex = Math.max(existingEntry.highestIndex, range.highestIndex);
229
+ if (lowestIndex !== existingEntry.lowestIndex || highestIndex !== existingEntry.highestIndex) {
230
+ updatedPending = pendingData.map(entry =>
231
+ entry === existingEntry ? { lowestIndex, highestIndex, txHash: entry.txHash } : entry,
232
+ );
233
+ }
234
+ } else if (
235
+ existingEntry.lowestIndex !== range.lowestIndex ||
236
+ existingEntry.highestIndex !== range.highestIndex
237
+ ) {
238
+ // Different ranges for the same (secret, txHash) indicate a bug in callers that record indexes at prove
239
+ // time.
240
+ throw new Error(
241
+ `Conflicting range for secret ${secretStr} and txHash ${txHashStr}: ` +
242
+ `existing [${existingEntry.lowestIndex}, ${existingEntry.highestIndex}] vs ` +
243
+ `new [${range.lowestIndex}, ${range.highestIndex}]`,
244
+ );
245
+ }
246
+ // Remaining cases (a merge whose union equals the stored range, or an identical range without
247
+ // mergeExisting): duplicate evidence, nothing to write.
248
+
249
+ if (updatedPending) {
250
+ this.#writePendingIndexes(jobId, secretStr, updatedPending);
205
251
  }
206
252
  }
207
253
  });
208
254
  }
209
255
 
210
256
  /**
211
- * Returns the transaction hashes of all pending transactions that contain highest indexes within a specified range
212
- * for a given directional app tagging secret. We check based on the highest indexes only as that is the relevant
213
- * information for the caller of this function.
257
+ * Returns the pending txs whose highest index falls within [startIndex, endIndex) for a given directional app
258
+ * tagging secret. The highest index is what decides whether a tx belongs to the window, so it alone is matched
259
+ * against the bounds, and it is also the only index a caller needs: a tx whose highest index is onchain has every
260
+ * lower one onchain too. A secret holds at most one entry per tx hash, so no tx hash appears twice in the result.
214
261
  * @param secret - The directional app tagging secret to query pending indexes for.
215
262
  * @param startIndex - The lower bound of the index range (inclusive).
216
263
  * @param endIndex - The upper bound of the index range (exclusive).
217
- * @returns An array of unique transaction hashes for pending transactions that contain indexes in the range
218
- * [startIndex, endIndex). Returns an empty array if no pending indexes exist in the range.
219
264
  */
220
- getTxHashesOfPendingIndexes(
221
- secret: AppTaggingSecret,
222
- startIndex: number,
223
- endIndex: number,
224
- jobId: string,
225
- ): Promise<TxHash[]> {
265
+ getPendingTxs(secret: AppTaggingSecret, startIndex: number, endIndex: number, jobId: string): Promise<PendingTx[]> {
226
266
  return this.#store.transactionAsync(async () => {
227
267
  const existing = await this.#readPendingIndexes(jobId, secret.toString());
228
- const txHashes = existing
268
+ return existing
229
269
  .filter(entry => entry.highestIndex >= startIndex && entry.highestIndex < endIndex)
230
- .map(entry => entry.txHash);
231
- return Array.from(new Set(txHashes)).map(TxHash.fromString);
270
+ .map(entry => ({ txHash: entry.txHash, highestIndex: entry.highestIndex }));
232
271
  });
233
272
  }
234
273
 
@@ -358,15 +397,33 @@ export class SenderTaggingStore implements StagedStore {
358
397
 
359
398
  /**
360
399
  * Updates pending indexes corresponding to the given transaction hashes to be finalized and prunes any lower pending
361
- * indexes.
400
+ * indexes. Applies to every secret the txs used, so the caller must hold tx-level evidence that the whole tx
401
+ * finalized. Callers holding evidence about a single secret must use {@link finalizePendingIndexesOfSecret} instead.
402
+ */
403
+ finalizePendingIndexes(txHashes: TxHash[], jobId: string): Promise<void> {
404
+ return this.#finalizePendingIndexes(txHashes, jobId);
405
+ }
406
+
407
+ /**
408
+ * Same as {@link finalizePendingIndexes}, but restricted to the pending indexes of a single secret.
409
+ *
410
+ * Finalizing every secret off single-secret evidence would be unsound: a tx whose execution partially reverted can
411
+ * have all of one secret's tags onchain and none of another's, and the second secret's indexes must not be recorded
412
+ * as finalized when they never reached the chain.
362
413
  */
363
- async finalizePendingIndexes(txHashes: TxHash[], jobId: string): Promise<void> {
414
+ finalizePendingIndexesOfSecret(secret: AppTaggingSecret, txHashes: TxHash[], jobId: string): Promise<void> {
415
+ return this.#finalizePendingIndexes(txHashes, jobId, secret.toString());
416
+ }
417
+
418
+ async #finalizePendingIndexes(txHashes: TxHash[], jobId: string, onlySecret?: string): Promise<void> {
364
419
  if (txHashes.length === 0) {
365
420
  return;
366
421
  }
367
422
 
368
423
  const txHashStrings = new Set(txHashes.map(tx => tx.toString()));
369
- const secretsWithData = await this.#getSecretsWithPendingData(jobId);
424
+ const secretsWithData = (await this.#getSecretsWithPendingData(jobId)).filter(
425
+ ({ secret }) => onlySecret === undefined || secret === onlySecret,
426
+ );
370
427
 
371
428
  for (const { secret, pendingData, lastFinalized } of secretsWithData) {
372
429
  let currentPending = pendingData;
@@ -474,3 +531,18 @@ export class SenderTaggingStore implements StagedStore {
474
531
  }
475
532
  }
476
533
  }
534
+
535
+ /** Builds the error thrown when a pending tag index is at or past the unfinalized tagging window end. */
536
+ export function windowExceededError(
537
+ highestIndex: number,
538
+ windowEnd: number,
539
+ finalizedIndex: number | undefined,
540
+ ): Error {
541
+ const finalizedDescription =
542
+ finalizedIndex === undefined ? 'no index finalized yet' : `highest finalized index ${finalizedIndex}`;
543
+ return new Error(
544
+ `Highest used index ${highestIndex} is at or past the window end ${windowEnd} (${finalizedDescription}). ` +
545
+ `Tagging window length ${UNFINALIZED_TAGGING_INDEXES_WINDOW_LEN} is configured too low. ` +
546
+ `Contact the Aztec team to increase it!`,
547
+ );
548
+ }
@@ -18,3 +18,25 @@ import { MAX_PRIVATE_LOGS_PER_TX } from '@aztec/constants';
18
18
  // reserved at log emission time, before squashing is decided, and the kernel's reset/squash loop is not bounded by
19
19
  // MAX_PRIVATE_LOGS_PER_TX. No fixed window value closes that gap.
20
20
  export const UNFINALIZED_TAGGING_INDEXES_WINDOW_LEN = MAX_PRIVATE_LOGS_PER_TX + 20;
21
+
22
+ /**
23
+ * Exclusive upper bound of the tag indexes that can exist for a secret whose highest finalized index is
24
+ * `finalizedIndex` (undefined when nothing is finalized yet). The sender store refuses pending indexes at or past it,
25
+ * and both sender and recipient sync scan exactly up to it. Every absolute window bound must come from this helper:
26
+ * a site with a wider or narrower bound lets a tx land at an index the syncs never scan, so two stores sharing the
27
+ * secret could later pick a colliding index.
28
+ */
29
+ export function unfinalizedTaggingIndexesWindowEnd(finalizedIndex: number | undefined): number {
30
+ const windowStart = finalizedIndex === undefined ? 0 : finalizedIndex + 1;
31
+ return windowStart + UNFINALIZED_TAGGING_INDEXES_WINDOW_LEN;
32
+ }
33
+
34
+ // The number of tags probed per constrained secret in the first round.
35
+ //
36
+ // The probe doubles each round (2, 4, 8, ..., capped at UNFINALIZED_TAGGING_INDEXES_WINDOW_LEN) while every probed
37
+ // index is a hit, stopping at the first missing tag. Constrained delivery is gapless, so a single missing tag proves
38
+ // the stream has ended: at steady state this turns a full WINDOW_LEN probe into two tags. A secret K logs behind
39
+ // catches up in ~log2(K) round-trips while the probe is still doubling (2, 4, 8, 16, 32), but once it saturates the cap
40
+ // and advances WINDOW_LEN tags per round, deeper catch-up is linear at ~K/WINDOW_LEN rounds. Either way it beats both
41
+ // the full window every round and one round per log.
42
+ export const INITIAL_CONSTRAINED_PROBE_LEN = 2;
@@ -1,4 +1,4 @@
1
- import type { BlockNumber } from '@aztec/foundation/branded-types';
1
+ import { BlockNumber } from '@aztec/foundation/branded-types';
2
2
  import type { AztecAddress } from '@aztec/stdlib/aztec-address';
3
3
  import type { BlockHash } from '@aztec/stdlib/block';
4
4
  import { MAX_RPC_LEN } from '@aztec/stdlib/interfaces/api-limit';
@@ -11,6 +11,31 @@ import {
11
11
  queryAllPrivateLogsByTags,
12
12
  queryAllPublicLogsByTags,
13
13
  } from '@aztec/stdlib/logs';
14
+ import type { BlockHeader } from '@aztec/stdlib/tx';
15
+
16
+ /**
17
+ * The block a tag query is anchored to.
18
+ *
19
+ * Both fields come from one header (see {@link logQueryAnchorOf}), which is what keeps the bound and the reorg check
20
+ * talking about the same block.
21
+ */
22
+ export type LogQueryAnchor = {
23
+ /**
24
+ * Hash of the anchor block, naming the chain the query must be answered on: the node throws if that block is gone,
25
+ * which is how a reorg surfaces.
26
+ */
27
+ hash: BlockHash;
28
+ /** Height of the anchor block, bounding the query to blocks at or below it. */
29
+ number: BlockNumber;
30
+ };
31
+
32
+ /**
33
+ * The {@link LogQueryAnchor} naming `anchorBlockHeader`: its hash and its height. Callers build it once and reuse
34
+ * it across every query anchored to that block, so the header is hashed only once.
35
+ */
36
+ export async function logQueryAnchorOf(anchorBlockHeader: BlockHeader): Promise<LogQueryAnchor> {
37
+ return { hash: await anchorBlockHeader.hash(), number: anchorBlockHeader.getBlockNumber() };
38
+ }
14
39
 
15
40
  /** Optional block-range, effects opt-in, and pagination cap shared by both wrappers. */
16
41
  export type GetAllLogsByTagsOptions = {
@@ -61,15 +86,15 @@ async function getAllPagesInBatches<T extends Tag | SiloedTag, Opts extends LogI
61
86
  *
62
87
  * @param aztecNode - The Aztec node to query.
63
88
  * @param tags - The siloed tags to search for.
64
- * @param anchorBlockHash - Reference block for the Aztec node query, throws if block is not found there (typically
65
- * because of reorgs).
89
+ * @param anchor - Block the query is anchored to: results stop there, and the call throws if that block is not found
90
+ * (typically because of reorgs).
66
91
  * @param options - Optional `fromBlock`/`toBlock` range and `includeEffects` opt-in.
67
92
  * @returns An array of log arrays, one per tag, containing all logs across all pages.
68
93
  */
69
94
  export function getAllPrivateLogsByTags<Opts extends GetAllLogsByTagsOptions = GetAllLogsByTagsOptions>(
70
95
  aztecNode: AztecNode,
71
96
  tags: SiloedTag[],
72
- anchorBlockHash: BlockHash,
97
+ anchor: LogQueryAnchor,
73
98
  options: Opts = {} as Opts,
74
99
  ): Promise<LogResult<Opts>[][]> {
75
100
  return getAllPagesInBatches<SiloedTag, Opts>(
@@ -77,9 +102,9 @@ export function getAllPrivateLogsByTags<Opts extends GetAllLogsByTagsOptions = G
77
102
  batch =>
78
103
  queryAllPrivateLogsByTags(aztecNode, {
79
104
  tags: batch,
80
- referenceBlock: anchorBlockHash,
105
+ referenceBlock: anchor.hash,
81
106
  fromBlock: options.fromBlock,
82
- toBlock: options.toBlock,
107
+ toBlock: exclusiveUpperBound(anchor, options.toBlock),
83
108
  includeEffects: options.includeEffects ?? false,
84
109
  limitPerTag: options.limitPerTag,
85
110
  }) as Promise<LogResult<Opts>[][]>,
@@ -92,8 +117,8 @@ export function getAllPrivateLogsByTags<Opts extends GetAllLogsByTagsOptions = G
92
117
  * @param aztecNode - The Aztec node to query.
93
118
  * @param contractAddress - The contract address to search logs for.
94
119
  * @param tags - The tags to search for.
95
- * @param anchorBlockHash - Reference block for the Aztec node query, throws if block is not found there (typically
96
- * because of reorgs).
120
+ * @param anchor - Block the query is anchored to: results stop there, and the call throws if that block is not found
121
+ * (typically because of reorgs).
97
122
  * @param options - Optional `fromBlock`/`toBlock` range and `includeEffects` opt-in.
98
123
  * @returns An array of log arrays, one per tag, containing all logs across all pages.
99
124
  */
@@ -101,7 +126,7 @@ export function getAllPublicLogsByTagsFromContract<Opts extends GetAllLogsByTags
101
126
  aztecNode: AztecNode,
102
127
  contractAddress: AztecAddress,
103
128
  tags: Tag[],
104
- anchorBlockHash: BlockHash,
129
+ anchor: LogQueryAnchor,
105
130
  options: Opts = {} as Opts,
106
131
  ): Promise<LogResult<Opts>[][]> {
107
132
  return getAllPagesInBatches<Tag, Opts>(
@@ -110,11 +135,22 @@ export function getAllPublicLogsByTagsFromContract<Opts extends GetAllLogsByTags
110
135
  queryAllPublicLogsByTags(aztecNode, {
111
136
  contractAddress,
112
137
  tags: batch,
113
- referenceBlock: anchorBlockHash,
138
+ referenceBlock: anchor.hash,
114
139
  fromBlock: options.fromBlock,
115
- toBlock: options.toBlock,
140
+ toBlock: exclusiveUpperBound(anchor, options.toBlock),
116
141
  includeEffects: options.includeEffects ?? false,
117
142
  limitPerTag: options.limitPerTag,
118
143
  }) as Promise<LogResult<Opts>[][]>,
119
144
  );
120
145
  }
146
+
147
+ /**
148
+ * Exclusive upper block bound for a query anchored at `anchor`: one past the anchor block, or the caller's own
149
+ * `toBlock` when that is tighter.
150
+ *
151
+ * The bound is spelled out in the request rather than left implicit in `referenceBlock`, so the request itself
152
+ * names a fixed block range that no later block can widen.
153
+ */
154
+ function exclusiveUpperBound(anchor: LogQueryAnchor, toBlock: BlockNumber | undefined): BlockNumber {
155
+ return BlockNumber(Math.min(toBlock ?? Infinity, anchor.number + 1));
156
+ }
@@ -12,8 +12,13 @@
12
12
  export { syncTaggedPrivateLogs } from './recipient_sync/sync_tagged_private_logs.js';
13
13
  export { syncSenderTaggingIndexes } from './sender_sync/sync_sender_tagging_indexes.js';
14
14
  export { persistSenderTaggingIndexRangesForTx } from './persist_sender_tagging_index_ranges.js';
15
- export { UNFINALIZED_TAGGING_INDEXES_WINDOW_LEN } from './constants.js';
16
- export { getAllPrivateLogsByTags, getAllPublicLogsByTagsFromContract } from './get_all_logs_by_tags.js';
15
+ export { UNFINALIZED_TAGGING_INDEXES_WINDOW_LEN, INITIAL_CONSTRAINED_PROBE_LEN } from './constants.js';
16
+ export {
17
+ type LogQueryAnchor,
18
+ getAllPrivateLogsByTags,
19
+ getAllPublicLogsByTagsFromContract,
20
+ logQueryAnchorOf,
21
+ } from './get_all_logs_by_tags.js';
17
22
 
18
23
  // Re-export tagging-related types from stdlib
19
24
  export { AppTaggingSecret, Tag, SiloedTag } from '@aztec/stdlib/logs';