@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
@@ -36,8 +36,9 @@ import {
36
36
  type TaggingSecretStrategy,
37
37
  } from '../../hooks/resolve_tagging_secret_strategy.js';
38
38
  import { NoteService } from '../../notes/note_service.js';
39
+ import { assertAllowedScope } from '../../storage/allowed_scopes.js';
39
40
  import type { SenderTaggingStore } from '../../storage/tagging_store/sender_tagging_store.js';
40
- import { syncSenderTaggingIndexes } from '../../tagging/index.js';
41
+ import { logQueryAnchorOf, syncSenderTaggingIndexes } from '../../tagging/index.js';
41
42
  import type { ExecutionNoteCache } from '../execution_note_cache.js';
42
43
  import { ExecutionTaggingIndexCache } from '../execution_tagging_index_cache.js';
43
44
  import type { HashedValuesCache } from '../hashed_values_cache.js';
@@ -321,7 +322,17 @@ export class PrivateExecutionOracle extends UtilityExecutionOracle implements IP
321
322
  * @returns The app tagging secret, or `None` if the recipient is invalid.
322
323
  */
323
324
  public async getAppTaggingSecret(sender: AztecAddress, recipient: AztecAddress): Promise<Option<Fr>> {
324
- const extendedSecret = await this.#calculateAppTaggingSecret(this.contractAddress, sender, recipient);
325
+ assertAllowedScope(sender, this.scopes);
326
+
327
+ const senderCompleteAddress = await this.getCompleteAddressOrFail(sender);
328
+ const senderIvsk = await this.keyStore.getMasterIncomingViewingSecretKey(sender);
329
+ const extendedSecret = await AppTaggingSecret.computeViaEcdh(
330
+ senderCompleteAddress,
331
+ senderIvsk,
332
+ recipient,
333
+ this.contractAddress,
334
+ recipient,
335
+ );
325
336
 
326
337
  if (!extendedSecret) {
327
338
  this.logger.warn(`Computing a tagging secret for invalid recipient ${recipient} - returning no secret`, {
@@ -354,12 +365,6 @@ export class PrivateExecutionOracle extends UtilityExecutionOracle implements IP
354
365
  return index;
355
366
  }
356
367
 
357
- async #calculateAppTaggingSecret(contractAddress: AztecAddress, sender: AztecAddress, recipient: AztecAddress) {
358
- const senderCompleteAddress = await this.getCompleteAddressOrFail(sender);
359
- const senderIvsk = await this.keyStore.getMasterIncomingViewingSecretKey(sender);
360
- return AppTaggingSecret.computeViaEcdh(senderCompleteAddress, senderIvsk, recipient, contractAddress, recipient);
361
- }
362
-
363
368
  async #getIndexToUseForSecret(secret: AppTaggingSecret): Promise<number> {
364
369
  // If we have the tagging index in the cache, we use it. If not we obtain it from the execution data provider.
365
370
  const lastUsedIndexInTx = this.taggingIndexCache.getLastUsedIndex(secret);
@@ -370,11 +375,16 @@ export class PrivateExecutionOracle extends UtilityExecutionOracle implements IP
370
375
  // This is a tagging secret we've not yet used in this tx, so first sync our store to make sure its indices
371
376
  // are up to date. We do this here because this store is not synced as part of the global sync because
372
377
  // that'd be wasteful as most tagging secrets are not used in each tx.
378
+ const [{ finalized }, anchor] = await Promise.all([
379
+ this.l2TipsStore.getL2Tips(),
380
+ logQueryAnchorOf(this.anchorBlockHeader),
381
+ ]);
373
382
  await syncSenderTaggingIndexes(
374
383
  secret,
375
384
  this.aztecNode,
376
385
  this.senderTaggingStore,
377
- await this.anchorBlockHeader.hash(),
386
+ finalized.block.number,
387
+ anchor,
378
388
  this.jobId,
379
389
  );
380
390
 
@@ -1,4 +1,4 @@
1
- import { ARCHIVE_HEIGHT, type NOTE_HASH_TREE_HEIGHT } from '@aztec/constants';
1
+ import { ARCHIVE_HEIGHT, type NOTE_HASH_TREE_HEIGHT, PRIVATE_LOG_CIPHERTEXT_LEN } from '@aztec/constants';
2
2
  import type { BlockNumber } from '@aztec/foundation/branded-types';
3
3
  import { uniqueBy } from '@aztec/foundation/collection';
4
4
  import { Aes128 } from '@aztec/foundation/crypto/aes128';
@@ -33,21 +33,23 @@ import {
33
33
  type BlockHeader,
34
34
  CallContext,
35
35
  type Capsule,
36
- type IndexedTxEffect,
37
36
  type OffchainEffect,
37
+ type TxEffect,
38
38
  type TxHash,
39
+ type TxReceipt,
39
40
  } from '@aztec/stdlib/tx';
40
41
 
41
42
  import type { ContractSyncService } from '../../contract/contract_sync_service.js';
42
43
  import { createContractLogger, logContractMessage, stripAztecnrLogPrefix } from '../../contract_logging.js';
43
- import { EventService } from '../../events/event_service.js';
44
+ import { EventService, type EventValidationTxData } from '../../events/event_service.js';
44
45
  import type { UtilityCallAuthorizationRequest } from '../../hooks/authorize_utility_call.js';
45
46
  import type { ExecutionHooks } from '../../hooks/index.js';
46
- import { LogService } from '../../logs/log_service.js';
47
- import { TxResolverService } from '../../messages/tx_resolver_service.js';
48
- import { NoteService } from '../../notes/note_service.js';
47
+ import { LogService, type RetrievedTaggedLog } from '../../logs/log_service.js';
48
+ import { type TxOnchainContext, TxResolverService } from '../../messages/tx_resolver_service.js';
49
+ import { NoteService, type NoteValidationTxData } from '../../notes/note_service.js';
49
50
  import { ORACLE_VERSION_MAJOR } from '../../oracle_version.js';
50
51
  import type { AddressStore } from '../../storage/address_store/address_store.js';
52
+ import { assertAllowedScope } from '../../storage/allowed_scopes.js';
51
53
  import type { CapsuleService } from '../../storage/capsule_store/capsule_service.js';
52
54
  import { FactCollectionKey, FactCollectionTypeKey, anchoredTipBlockNumbers } from '../../storage/fact_store/index.js';
53
55
  import type { FactService, OriginBlock } from '../../storage/fact_store/index.js';
@@ -56,7 +58,6 @@ import type { PrivateEventStore } from '../../storage/private_event_store/privat
56
58
  import type { RecipientTaggingStore } from '../../storage/tagging_store/recipient_tagging_store.js';
57
59
  import type { TaggingSecretSourcesStore } from '../../storage/tagging_store/tagging_secret_sources_store.js';
58
60
  import type { AnchoredContractData } from '../anchored_contract_data.js';
59
- import { AztecNodeReadCache } from '../aztec_node_read_cache.js';
60
61
  import { EphemeralArrayService } from '../ephemeral_array_service.js';
61
62
  import { BoundedVec } from '../noir-structs/bounded_vec.js';
62
63
  import type { EmbeddedCurvePoint } from '../noir-structs/embedded_curve_point.js';
@@ -120,7 +121,15 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
120
121
  private offchainEffects: OffchainEffect[] = [];
121
122
  private readonly ephemeralArrayService = new EphemeralArrayService();
122
123
  protected readonly transientArrayService: TransientArrayService;
123
- private readonly aztecNodeReadCache: AztecNodeReadCache;
124
+ /** Keyed by tx hash string. */
125
+ private readonly txReceiptsCache = new Map<string, Promise<TxReceipt<{ includeTxEffect: true }>>>();
126
+ /**
127
+ * Information that can be used to validate the existence of a note or an event, keyed by tx hash string. It is
128
+ * populated by the node queries that precede validation (tagged log retrieval, tx resolution), which already return
129
+ * everything validation needs, so validating a note or event created in one of those txs costs no node roundtrip.
130
+ * Notes and events reached through other paths (e.g. offchain inbox messages) still need a receipt.
131
+ */
132
+ private readonly validationTxDataCache = new Map<string, ValidationTxData>();
124
133
 
125
134
  // We store oracle version to be able to show a nice error message when an oracle handler is missing.
126
135
  private contractOracleVersion: { major: number; minor: number } | undefined;
@@ -174,7 +183,6 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
174
183
  this.hooks = args.hooks;
175
184
  this.utilityExecutor = args.utilityExecutor;
176
185
  this.transientArrayService = args.transientArrayService;
177
- this.aztecNodeReadCache = new AztecNodeReadCache(args.aztecNode);
178
186
  }
179
187
 
180
188
  public assertCompatibleOracleVersion(major: number, minor: number): void {
@@ -268,7 +276,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
268
276
  // hash at all. If the block hash did not exist by the reference block hash, then the node will not return the
269
277
  // membership witness as there is none.
270
278
  const witness = await this.#queryWithBlockHashNotAfterAnchor(referenceBlockHash, () =>
271
- this.aztecNodeReadCache.getBlockHashMembershipWitness(referenceBlockHash, blockHash),
279
+ this.aztecNode.getBlockHashMembershipWitness(referenceBlockHash, blockHash),
272
280
  );
273
281
  return witness ? Option.some(witness) : Option.none();
274
282
  }
@@ -282,7 +290,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
282
290
  const memberships = await this.#queryWithBlockHashNotAfterAnchor(referenceBlockHash, () =>
283
291
  Promise.all(
284
292
  hashes.map(blockHash =>
285
- this.aztecNodeReadCache.getBlockHashMembershipWitness(referenceBlockHash, blockHash).then(Boolean),
293
+ this.aztecNode.getBlockHashMembershipWitness(referenceBlockHash, blockHash).then(Boolean),
286
294
  ),
287
295
  ),
288
296
  );
@@ -337,7 +345,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
337
345
  */
338
346
  public async getPublicDataWitness(blockHash: BlockHash, leafSlot: Fr): Promise<PublicDataWitness> {
339
347
  const witness = await this.#queryWithBlockHashNotAfterAnchor(blockHash, () =>
340
- this.aztecNodeReadCache.getPublicDataWitness(blockHash, leafSlot),
348
+ this.aztecNode.getPublicDataWitness(blockHash, leafSlot),
341
349
  );
342
350
  if (!witness) {
343
351
  throw new Error(`Public data witness not found for slot ${leafSlot} at block hash ${blockHash.toString()}.`);
@@ -528,11 +536,11 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
528
536
  numberOfElements: number,
529
537
  ) {
530
538
  return this.#queryWithBlockHashNotAfterAnchor(blockHash, async () => {
531
- const values = await this.aztecNodeReadCache.getPublicStorageRange(
532
- blockHash,
533
- contractAddress,
534
- startStorageSlot,
535
- numberOfElements,
539
+ const slots = Array(numberOfElements)
540
+ .fill(0)
541
+ .map((_, i) => new Fr(startStorageSlot.toBigInt() + BigInt(i)));
542
+ const values = await Promise.all(
543
+ slots.map(storageSlot => this.aztecNode.getPublicStorageAt(blockHash, contractAddress, storageSlot)),
536
544
  );
537
545
 
538
546
  this.logger.debug(
@@ -599,8 +607,11 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
599
607
  .map(ps => new AppTaggingSecret(ps.secret, this.contractAddress, ps.mode));
600
608
 
601
609
  const logService = this.#createLogService();
602
- const logs = await logService.fetchTaggedLogs(this.contractAddress, scope, secrets);
603
- return EphemeralArray.fromValues(this.ephemeralArrayService, logs);
610
+ const retrievedLogs = await logService.fetchTaggedLogs(this.contractAddress, scope, secrets);
611
+
612
+ this.#cacheValidationTxData(retrievedLogs);
613
+
614
+ return EphemeralArray.fromValues(this.ephemeralArrayService, retrievedLogs.map(toPendingTaggedLog));
604
615
  }
605
616
 
606
617
  #createLogService(): LogService {
@@ -612,6 +623,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
612
623
  this.recipientTaggingStore,
613
624
  this.taggingSecretSourcesStore,
614
625
  this.addressStore,
626
+ this.scopes,
615
627
  this.jobId,
616
628
  this.logger.getBindings(),
617
629
  );
@@ -634,7 +646,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
634
646
  eventValidationRequests: EventValidationRequest[],
635
647
  scope: AztecAddress,
636
648
  ) {
637
- const txEffects = await this.#fetchTxEffects([
649
+ const validationTxData = await this.#getValidationTxData([
638
650
  ...noteValidationRequests.map(r => r.txHash),
639
651
  ...eventValidationRequests.map(r => r.txHash),
640
652
  ]);
@@ -643,8 +655,8 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
643
655
  const eventService = new EventService(this.anchorBlockHeader, this.aztecNode, this.privateEventStore, this.jobId);
644
656
 
645
657
  await Promise.all([
646
- noteService.validateAndStoreNotes(noteValidationRequests, scope, txEffects),
647
- eventService.validateAndStoreEvents(eventValidationRequests, scope, txEffects),
658
+ noteService.validateAndStoreNotes(noteValidationRequests, scope, validationTxData),
659
+ eventService.validateAndStoreEvents(eventValidationRequests, scope, validationTxData),
648
660
  ]);
649
661
  }
650
662
 
@@ -654,11 +666,13 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
654
666
  const logRetrievalRequests = requests.readAll(this.ephemeralArrayService);
655
667
  const logService = this.#createLogService();
656
668
 
657
- const logRetrievalResponses = await logService.fetchLogsByTag(this.contractAddress, logRetrievalRequests);
669
+ const retrievedLogsPerRequest = await logService.fetchLogsByTag(this.contractAddress, logRetrievalRequests);
670
+
671
+ this.#cacheValidationTxData(retrievedLogsPerRequest.flat());
658
672
 
659
673
  // Create an inner ephemeral array for each request's matching logs, then wrap all slots in an outer array.
660
- const innerArrays = logRetrievalResponses.map(responses =>
661
- EphemeralArray.fromValues(this.ephemeralArrayService, responses),
674
+ const innerArrays = retrievedLogsPerRequest.map(retrievedLogs =>
675
+ EphemeralArray.fromValues(this.ephemeralArrayService, retrievedLogs.map(toLogRetrievalResponse)),
662
676
  );
663
677
 
664
678
  return EphemeralArray.fromValues(this.ephemeralArrayService, innerArrays);
@@ -670,7 +684,9 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
670
684
 
671
685
  const resolved = await this.txResolver.resolveTxs(txHashes, this.anchorBlockHeader.getBlockNumber());
672
686
 
673
- const options = resolved.map(r => (r ? Option.some(r) : Option.none<ResolvedTx>()));
687
+ this.#cacheValidationTxData(resolved.filter(tx => tx !== null));
688
+
689
+ const options = resolved.map(tx => (tx ? Option.some(toResolvedTx(tx)) : Option.none<ResolvedTx>()));
674
690
  return EphemeralArray.fromValues(this.ephemeralArrayService, options);
675
691
  }
676
692
 
@@ -683,21 +699,25 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
683
699
  throw new Error('Invalid tx hash passed into aztec_utl_getTxEffect oracle handler');
684
700
  }
685
701
 
686
- const receipt = await this.aztecNodeReadCache.getTxReceiptWithEffect(txHash);
687
- if (!receipt.isMined() || !receipt.txEffect || receipt.blockNumber > this.anchorBlockHeader.getBlockNumber()) {
688
- return Option.none();
702
+ return await this.#getTxEffectOption(txHash);
703
+ }
704
+
705
+ /** Fetches transaction effects for all hashes, preserving request order. */
706
+ public async getTxEffects(txHashes: EphemeralArray<TxHash>): Promise<EphemeralArray<Option<TxEffectData>>> {
707
+ const hashes = txHashes.readAll(this.ephemeralArrayService);
708
+ const invalidHash = hashes.find(txHash => txHash.hash.isZero());
709
+ if (invalidHash) {
710
+ throw new Error('Invalid tx hash passed into aztec_utl_getTxEffects oracle handler');
689
711
  }
690
712
 
691
- const txEffect = receipt.txEffect;
692
- return Option.some({
693
- ...txEffect,
694
- publicLogs: FlatPublicLogs.fromLogs(txEffect.publicLogs),
695
- contractClassLogs: txEffect.contractClassLogs.map(log => ({
696
- contractAddress: log.contractAddress,
697
- fields: log.fields.toFields(),
698
- emittedLength: log.emittedLength,
699
- })),
700
- });
713
+ const uniqueTxHashes = uniqueBy(hashes, h => h.toString());
714
+ const options = await Promise.all(uniqueTxHashes.map(txHash => this.#getTxEffectOption(txHash)));
715
+ const optionsByHash = new Map(uniqueTxHashes.map((txHash, i) => [txHash.toString(), options[i]]));
716
+
717
+ return EphemeralArray.fromValues(
718
+ this.ephemeralArrayService,
719
+ hashes.map(txHash => optionsByHash.get(txHash.toString())!),
720
+ );
701
721
  }
702
722
 
703
723
  public setCapsule(contractAddress: AztecAddress, slot: Fr, capsule: Fr[], scope: AztecAddress): void {
@@ -852,13 +872,17 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
852
872
  // TODO(#11849): consider replacing this oracle with a pure Noir implementation of aes decryption.
853
873
  public async decryptAes128(
854
874
  ciphertext: BoundedVec<number>,
855
- iv: Buffer,
856
- symKey: Buffer,
875
+ iv: number[],
876
+ symKey: number[],
857
877
  ): Promise<Option<BoundedVec<number>>> {
858
878
  const capacity = ciphertext.maxLength;
859
879
  try {
860
880
  const aes128 = new Aes128();
861
- const plaintext = await aes128.decryptBufferCBC(Buffer.from(ciphertext.data), iv, symKey);
881
+ const plaintext = await aes128.decryptBufferCBC(
882
+ Buffer.from(ciphertext.data),
883
+ Buffer.from(iv),
884
+ Buffer.from(symKey),
885
+ );
862
886
  return Option.some(BoundedVec.from<number>({ data: [...plaintext], maxLength: capacity }));
863
887
  } catch {
864
888
  return Option.none({ maxLength: capacity });
@@ -871,7 +895,8 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
871
895
  * @param ephPks - Ephemeral array containing the serialized Points.
872
896
  * @param contractAddress - The contract address for app-siloing (validated against execution context).
873
897
  * @returns A new ephemeral array containing the computed shared secrets, or an empty array when the PXE does not
874
- * hold the keys for `address`, signaling that no secrets can be derived for it.
898
+ * hold the keys for `address`.
899
+ * @throws If `address` is not in the execution's allowed scopes.
875
900
  */
876
901
  public async getSharedSecrets(
877
902
  address: AztecAddress,
@@ -883,6 +908,11 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
883
908
  `getSharedSecrets called with contract address ${contractAddress}, expected ${this.contractAddress}`,
884
909
  );
885
910
  }
911
+
912
+ assertAllowedScope(address, this.scopes);
913
+
914
+ // An address can be in scope without the PXE holding its keys (e.g. syncing a registered but non-owned account),
915
+ // in which case no secrets can be derived and we return an empty array rather than failing.
886
916
  const recipientCompleteAddress = await this.addressStore.getCompleteAddress(address);
887
917
  if (!recipientCompleteAddress) {
888
918
  this.logger.warn(
@@ -1090,16 +1120,34 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
1090
1120
  return this.offchainEffects;
1091
1121
  }
1092
1122
 
1123
+ /** Stores the onchain context of the given txs, so that validating the notes and events they created is free. */
1124
+ #cacheValidationTxData(txs: TxOnchainContext[]) {
1125
+ txs.forEach(tx => this.validationTxDataCache.set(tx.txHash.toString(), toValidationTxData(tx)));
1126
+ }
1127
+
1093
1128
  /**
1094
- * Fetches tx effects for the given hashes in parallel, deduplicating repeated hashes so each tx is only requested
1095
- * once. Returns a map keyed by `TxHash.toString()`; hashes for which the node has no tx effect are omitted.
1129
+ * Returns the information needed to validate the notes and events created in the given txs, keyed by
1130
+ * `TxHash.toString()`. Txs already in {@link validationTxDataCache} cost no node request, and the rest are read from
1131
+ * the node. Txs with no tx effect are absent from the returned map.
1096
1132
  */
1097
- async #fetchTxEffects(txHashes: TxHash[]): Promise<Map<string, IndexedTxEffect>> {
1098
- const uniqueTxHashes = uniqueBy(txHashes, h => h.toString());
1099
- const fetched = await Promise.all(uniqueTxHashes.map(h => this.aztecNodeReadCache.getTxReceiptWithEffect(h)));
1100
- return new Map(
1101
- uniqueTxHashes
1102
- .map((h, i): [string, IndexedTxEffect | undefined] => {
1133
+ async #getValidationTxData(txHashes: TxHash[]): Promise<Map<string, ValidationTxData>> {
1134
+ const known: [string, ValidationTxData][] = [];
1135
+ const misses: TxHash[] = [];
1136
+ for (const txHash of uniqueBy(txHashes, h => h.toString())) {
1137
+ const key = txHash.toString();
1138
+ const cached = this.validationTxDataCache.get(key);
1139
+ if (cached) {
1140
+ known.push([key, cached]);
1141
+ } else {
1142
+ misses.push(txHash);
1143
+ }
1144
+ }
1145
+
1146
+ const fetched = await Promise.all(misses.map(h => this.#getTxReceiptWithEffect(h)));
1147
+ return new Map([
1148
+ ...known,
1149
+ ...misses
1150
+ .map((h, i): [string, ValidationTxData | undefined] => {
1103
1151
  const receipt = fetched[i];
1104
1152
  if (!receipt.isMined() || !receipt.txEffect) {
1105
1153
  return [h.toString(), undefined];
@@ -1107,16 +1155,58 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
1107
1155
  return [
1108
1156
  h.toString(),
1109
1157
  {
1110
- data: receipt.txEffect,
1158
+ noteHashes: receipt.txEffect.noteHashes,
1159
+ nullifiers: receipt.txEffect.nullifiers,
1111
1160
  l2BlockNumber: receipt.blockNumber,
1112
1161
  l2BlockHash: receipt.blockHash,
1113
1162
  txIndexInBlock: receipt.txIndexInBlock,
1114
- slotNumber: receipt.slotNumber,
1115
1163
  },
1116
1164
  ];
1117
1165
  })
1118
- .filter((entry): entry is [string, IndexedTxEffect] => entry[1] !== undefined),
1119
- );
1166
+ .filter((entry): entry is [string, ValidationTxData] => entry[1] !== undefined),
1167
+ ]);
1168
+ }
1169
+
1170
+ /**
1171
+ * Reads a receipt with its effect, at most once per tx for the lifetime of this execution.
1172
+ *
1173
+ * A receipt is not cacheable in general, since pending, mined and dropped are all correct answers to the same call
1174
+ * over time. Within one execution it is: the execution is anchored at a fixed block, and validation runs in several
1175
+ * batches that name overlapping tx hashes, so re-reading would both cost extra requests and let one execution see a
1176
+ * tx as included in one batch and absent in the next.
1177
+ */
1178
+ #getTxReceiptWithEffect(txHash: TxHash) {
1179
+ const key = txHash.toString();
1180
+ let receipt = this.txReceiptsCache.get(key);
1181
+ if (!receipt) {
1182
+ receipt = this.aztecNode.getTxReceipt(txHash, { includeTxEffect: true }).catch(err => {
1183
+ this.txReceiptsCache.delete(key);
1184
+ throw err;
1185
+ });
1186
+ this.txReceiptsCache.set(key, receipt);
1187
+ }
1188
+ return receipt;
1189
+ }
1190
+
1191
+ async #getTxEffectOption(txHash: TxHash): Promise<Option<TxEffectData>> {
1192
+ const receipt = await this.#getTxReceiptWithEffect(txHash);
1193
+ if (!receipt.isMined() || !receipt.txEffect || receipt.blockNumber > this.anchorBlockHeader.getBlockNumber()) {
1194
+ return Option.none();
1195
+ }
1196
+ return Option.some(this.#toTxEffectData(receipt.txEffect));
1197
+ }
1198
+
1199
+ #toTxEffectData(txEffect: TxEffect): TxEffectData {
1200
+ return {
1201
+ ...txEffect,
1202
+ revertCode: txEffect.revertCode.getCode(),
1203
+ publicLogs: FlatPublicLogs.fromLogs(txEffect.publicLogs),
1204
+ contractClassLogs: txEffect.contractClassLogs.map(log => ({
1205
+ contractAddress: log.contractAddress,
1206
+ fields: log.fields.toFields(),
1207
+ emittedLength: log.emittedLength,
1208
+ })),
1209
+ };
1120
1210
  }
1121
1211
 
1122
1212
  /** Runs a query concurrently with a validation that the block hash is not ahead of the anchor block. */
@@ -1130,7 +1220,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
1130
1220
  const [response] = await Promise.all([
1131
1221
  query(),
1132
1222
  (async () => {
1133
- const block = await this.aztecNodeReadCache.getBlock(blockHash);
1223
+ const block = await this.aztecNode.getBlock(blockHash);
1134
1224
  const header = block?.header;
1135
1225
  if (!header) {
1136
1226
  throw new Error(`Could not find block header for block hash ${blockHash}`);
@@ -1191,3 +1281,41 @@ async function isStandardHandshakeRegistryUtilityRead(
1191
1281
  );
1192
1282
  return matches.some(Boolean);
1193
1283
  }
1284
+
1285
+ function toPendingTaggedLog(retrievedLog: RetrievedTaggedLog): PendingTaggedLog {
1286
+ return { log: retrievedLog.logData, context: toResolvedTx(retrievedLog) };
1287
+ }
1288
+
1289
+ function toResolvedTx(tx: TxOnchainContext): ResolvedTx {
1290
+ const { txHash, blockNumber, blockHash, noteHashes, nullifiers } = tx;
1291
+ return {
1292
+ txHash,
1293
+ uniqueNoteHashesInTx: noteHashes,
1294
+ firstNullifierInTx: nullifiers[0],
1295
+ blockNumber,
1296
+ blockHash: blockHash.toFr(),
1297
+ };
1298
+ }
1299
+
1300
+ function toLogRetrievalResponse(retrievedLog: RetrievedTaggedLog): LogRetrievalResponse {
1301
+ const { logData, txHash, blockNumber, blockHash, blockTimestamp, noteHashes, nullifiers } = retrievedLog;
1302
+ return {
1303
+ // Skip the tag, and clip to the wire cap: public logs can exceed PRIVATE_LOG_CIPHERTEXT_LEN, which is the fixed
1304
+ // size of the oracle's BoundedVec slot. A no-op for private logs, which are already within the cap.
1305
+ logPayload: logData.slice(1, 1 + PRIVATE_LOG_CIPHERTEXT_LEN),
1306
+ txHash,
1307
+ uniqueNoteHashesInTx: noteHashes,
1308
+ firstNullifierInTx: nullifiers[0],
1309
+ blockNumber,
1310
+ blockTimestamp,
1311
+ blockHash,
1312
+ };
1313
+ }
1314
+
1315
+ function toValidationTxData(tx: TxOnchainContext): ValidationTxData {
1316
+ const { blockNumber, blockHash, txIndexInBlock, noteHashes, nullifiers } = tx;
1317
+ return { noteHashes, nullifiers, l2BlockNumber: blockNumber, l2BlockHash: blockHash, txIndexInBlock };
1318
+ }
1319
+
1320
+ /** The onchain context of a tx served to note and event validation: the union of what the two services read. */
1321
+ type ValidationTxData = NoteValidationTxData & EventValidationTxData;
@@ -6,3 +6,4 @@ export * from '../../../contract_logging.js';
6
6
  export * from '../../../storage/index.js';
7
7
  export * from './utils.js';
8
8
  export type { PXECreationOptions } from '../../pxe_creation_options.js';
9
+ export { openBrowserStore } from '../store.js';
@@ -1,6 +1,5 @@
1
1
  import { BBBundlePrivateKernelProver } from '@aztec/bb-prover/client/bundle';
2
2
  import { createLogger } from '@aztec/foundation/log';
3
- import { createStore } from '@aztec/kv-store/sqlite-opfs';
4
3
  import { BundledProtocolContractsProvider } from '@aztec/protocol-contracts/providers/bundle';
5
4
  import { WASMSimulator } from '@aztec/simulator/client';
6
5
  import { getStandardAuthRegistry } from '@aztec/standard-contracts/auth-registry';
@@ -12,6 +11,7 @@ import type { PXEConfig } from '../../../config/index.js';
12
11
  import { PXE } from '../../../pxe.js';
13
12
  import { PXE_DATA_SCHEMA_VERSION } from '../../../storage/metadata.js';
14
13
  import { type PXECreationOptions, isPrivateKernelProver } from '../../pxe_creation_options.js';
14
+ import { openBrowserStore } from '../store.js';
15
15
 
16
16
  /**
17
17
  * Create and start an PXE instance with the given AztecNode.
@@ -31,16 +31,28 @@ export async function createPXE(
31
31
  const actor = options.loggerActorLabel;
32
32
  const loggers = options.loggers ?? {};
33
33
 
34
- const l1ContractAddresses = await aztecNode.getL1ContractAddresses();
34
+ const { l1ChainId, l1ContractAddresses, rollupVersion } = await aztecNode.getNodeInfo();
35
35
  const configWithContracts = {
36
36
  ...config,
37
37
  ...l1ContractAddresses,
38
+ l1ChainId,
39
+ rollupVersion,
38
40
  } as PXEConfig;
39
41
 
40
42
  const storeLogger = loggers.store ?? createLogger('pxe:data', { actor });
41
43
 
42
44
  const store =
43
- options.store ?? (await createStore('pxe_data', configWithContracts, PXE_DATA_SCHEMA_VERSION, storeLogger));
45
+ options.store ??
46
+ (await openBrowserStore(
47
+ 'pxe_data',
48
+ PXE_DATA_SCHEMA_VERSION,
49
+ {
50
+ l1ChainId,
51
+ rollupAddress: l1ContractAddresses.rollupAddress,
52
+ dataStoreMapSizeKb: configWithContracts.dataStoreMapSizeKb,
53
+ },
54
+ storeLogger,
55
+ ));
44
56
 
45
57
  const simulator = options.simulator ?? new WASMSimulator();
46
58
  const proverLogger = loggers.prover ?? createLogger('pxe:bb:wasm:bundle', { actor });
@@ -6,3 +6,4 @@ export * from '../../../error_enriching.js';
6
6
  export * from '../../../contract_logging.js';
7
7
  export * from './utils.js';
8
8
  export { type PXECreationOptions } from '../../pxe_creation_options.js';
9
+ export { openBrowserStore } from '../store.js';
@@ -1,6 +1,5 @@
1
1
  import { BBLazyPrivateKernelProver } from '@aztec/bb-prover/client/lazy';
2
2
  import { createLogger } from '@aztec/foundation/log';
3
- import { createStore } from '@aztec/kv-store/sqlite-opfs';
4
3
  import { LazyProtocolContractsProvider } from '@aztec/protocol-contracts/providers/lazy';
5
4
  import { WASMSimulator } from '@aztec/simulator/client';
6
5
  import { getStandardAuthRegistry } from '@aztec/standard-contracts/auth-registry/lazy';
@@ -12,6 +11,7 @@ import type { PXEConfig } from '../../../config/index.js';
12
11
  import { PXE } from '../../../pxe.js';
13
12
  import { PXE_DATA_SCHEMA_VERSION } from '../../../storage/metadata.js';
14
13
  import { type PXECreationOptions, isPrivateKernelProver } from '../../pxe_creation_options.js';
14
+ import { openBrowserStore } from '../store.js';
15
15
 
16
16
  /**
17
17
  * Create and start an PXE instance with the given AztecNode.
@@ -29,10 +29,12 @@ export async function createPXE(
29
29
  ) {
30
30
  const actor = options.loggerActorLabel;
31
31
 
32
- const l1ContractAddresses = await aztecNode.getL1ContractAddresses();
32
+ const { l1ChainId, l1ContractAddresses, rollupVersion } = await aztecNode.getNodeInfo();
33
33
  const configWithContracts = {
34
34
  ...config,
35
35
  ...l1ContractAddresses,
36
+ l1ChainId,
37
+ rollupVersion,
36
38
  } as PXEConfig;
37
39
 
38
40
  const loggers = options.loggers ?? {};
@@ -40,7 +42,17 @@ export async function createPXE(
40
42
  const storeLogger = loggers.store ?? createLogger('pxe:data', { actor });
41
43
 
42
44
  const store =
43
- options.store ?? (await createStore('pxe_data', configWithContracts, PXE_DATA_SCHEMA_VERSION, storeLogger));
45
+ options.store ??
46
+ (await openBrowserStore(
47
+ 'pxe_data',
48
+ PXE_DATA_SCHEMA_VERSION,
49
+ {
50
+ l1ChainId,
51
+ rollupAddress: l1ContractAddresses.rollupAddress,
52
+ dataStoreMapSizeKb: configWithContracts.dataStoreMapSizeKb,
53
+ },
54
+ storeLogger,
55
+ ));
44
56
 
45
57
  const simulator = options.simulator ?? new WASMSimulator();
46
58
  const proverLogger = loggers.prover ?? createLogger('pxe:bb:wasm:bundle', { actor });
@@ -0,0 +1,54 @@
1
+ import type { EthAddress } from '@aztec/foundation/eth-address';
2
+ import { type Logger, createLogger } from '@aztec/foundation/log';
3
+ import {
4
+ AztecSQLiteOPFSStore,
5
+ SqliteCorruptionError,
6
+ deleteStore,
7
+ storePoolDirectory,
8
+ } from '@aztec/kv-store/sqlite-opfs';
9
+
10
+ import { assertStoreIdentity, effectiveStoreName } from '../../storage/store_identity.js';
11
+
12
+ /**
13
+ * Opens the persistent browser (sqlite-opfs) store selected by `name` and identity `(config.l1ChainId,
14
+ * config.rollupAddress, schemaVersion)` triple. A store exists per identity: reopening with the same identity returns
15
+ * the same data, a different identity selects a different (possibly fresh) store.
16
+ */
17
+ export async function openBrowserStore(
18
+ name: string,
19
+ schemaVersion: number,
20
+ config: { l1ChainId: number; rollupAddress: EthAddress; dataStoreMapSizeKb?: number },
21
+ log: Logger = createLogger('pxe:data'),
22
+ ): Promise<AztecSQLiteOPFSStore> {
23
+ const identity = { l1ChainId: config.l1ChainId, rollupAddress: config.rollupAddress, schemaVersion };
24
+ const storeName = effectiveStoreName(name, identity);
25
+ log.info(`Creating ${storeName} SQLite-OPFS data store`, {
26
+ storeName,
27
+ dataStoreMapSizeKb: config.dataStoreMapSizeKb,
28
+ });
29
+ const openStore = () =>
30
+ AztecSQLiteOPFSStore.open(createLogger('kv-store:sqlite-opfs'), storeName, false, storePoolDirectory(storeName));
31
+
32
+ let store: AztecSQLiteOPFSStore;
33
+ try {
34
+ store = await openStore();
35
+ } catch (err) {
36
+ if (!(err instanceof SqliteCorruptionError)) {
37
+ throw err;
38
+ }
39
+ // A corrupt image is unrecoverable — no retry against the same bytes helps. Wipe the store's OPFS directory
40
+ // (safe: the failed open left no SAH-pool lock behind) and reopen a fresh, empty one, so the browser self-heals
41
+ // into a normal first-run rather than dead-ending on "database disk image is malformed" every load.
42
+ log.warn(`${storeName} store is corrupt (${err.message}); wiping and reopening fresh`);
43
+ await deleteStore(storeName);
44
+ store = await openStore();
45
+ }
46
+ try {
47
+ await assertStoreIdentity(store, storeName, identity);
48
+ } catch (err) {
49
+ // The store handle owns a worker and OPFS locks; release them before surfacing the refusal.
50
+ await store.close().catch(() => {});
51
+ throw err;
52
+ }
53
+ return store;
54
+ }
@@ -6,6 +6,7 @@ export * from '../../config/index.js';
6
6
  export * from '../../error_enriching.js';
7
7
  export * from '../../storage/index.js';
8
8
  export * from './utils.js';
9
+ export * from './store.js';
9
10
  export { NoteService } from '../../notes/note_service.js';
10
11
  export { ORACLE_VERSION_MAJOR, ORACLE_VERSION_MINOR } from '../../oracle_version.js';
11
12
  export { type PXECreationOptions } from '../pxe_creation_options.js';