@aztec/pxe 0.0.1-commit.54489865 → 0.0.1-commit.5914bae

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 (249) hide show
  1. package/dest/bin/check_oracle_version.js +4 -4
  2. package/dest/block_synchronizer/block_synchronizer.d.ts +4 -2
  3. package/dest/block_synchronizer/block_synchronizer.d.ts.map +1 -1
  4. package/dest/block_synchronizer/block_synchronizer.js +13 -1
  5. package/dest/config/package_info.js +1 -1
  6. package/dest/contract_function_simulator/contract_function_simulator.d.ts +63 -31
  7. package/dest/contract_function_simulator/contract_function_simulator.d.ts.map +1 -1
  8. package/dest/contract_function_simulator/contract_function_simulator.js +203 -72
  9. package/dest/contract_function_simulator/ephemeral_array_service.d.ts +28 -0
  10. package/dest/contract_function_simulator/ephemeral_array_service.d.ts.map +1 -0
  11. package/dest/contract_function_simulator/ephemeral_array_service.js +78 -0
  12. package/dest/contract_function_simulator/execution_tagging_index_cache.d.ts +7 -7
  13. package/dest/contract_function_simulator/execution_tagging_index_cache.d.ts.map +1 -1
  14. package/dest/contract_function_simulator/execution_tagging_index_cache.js +19 -11
  15. package/dest/contract_function_simulator/index.d.ts +2 -1
  16. package/dest/contract_function_simulator/index.d.ts.map +1 -1
  17. package/dest/contract_function_simulator/index.js +1 -0
  18. package/dest/contract_function_simulator/noir-structs/event_validation_request.d.ts +4 -6
  19. package/dest/contract_function_simulator/noir-structs/event_validation_request.d.ts.map +1 -1
  20. package/dest/contract_function_simulator/noir-structs/event_validation_request.js +8 -10
  21. package/dest/contract_function_simulator/noir-structs/log_retrieval_request.d.ts +1 -1
  22. package/dest/contract_function_simulator/noir-structs/log_retrieval_request.js +1 -1
  23. package/dest/contract_function_simulator/noir-structs/log_retrieval_response.d.ts +2 -2
  24. package/dest/contract_function_simulator/noir-structs/log_retrieval_response.d.ts.map +1 -1
  25. package/dest/contract_function_simulator/noir-structs/log_retrieval_response.js +2 -4
  26. package/dest/contract_function_simulator/noir-structs/note_validation_request.d.ts +4 -7
  27. package/dest/contract_function_simulator/noir-structs/note_validation_request.d.ts.map +1 -1
  28. package/dest/contract_function_simulator/noir-structs/note_validation_request.js +6 -11
  29. package/dest/contract_function_simulator/oracle/interfaces.d.ts +61 -45
  30. package/dest/contract_function_simulator/oracle/interfaces.d.ts.map +1 -1
  31. package/dest/contract_function_simulator/oracle/legacy_oracle_mappings.d.ts +9 -0
  32. package/dest/contract_function_simulator/oracle/legacy_oracle_mappings.d.ts.map +1 -0
  33. package/dest/contract_function_simulator/oracle/legacy_oracle_mappings.js +47 -0
  34. package/dest/contract_function_simulator/oracle/oracle.d.ts +75 -44
  35. package/dest/contract_function_simulator/oracle/oracle.d.ts.map +1 -1
  36. package/dest/contract_function_simulator/oracle/oracle.js +305 -108
  37. package/dest/contract_function_simulator/oracle/private_execution.js +5 -3
  38. package/dest/contract_function_simulator/oracle/private_execution_oracle.d.ts +53 -79
  39. package/dest/contract_function_simulator/oracle/private_execution_oracle.d.ts.map +1 -1
  40. package/dest/contract_function_simulator/oracle/private_execution_oracle.js +110 -87
  41. package/dest/contract_function_simulator/oracle/utility_execution_oracle.d.ts +126 -67
  42. package/dest/contract_function_simulator/oracle/utility_execution_oracle.d.ts.map +1 -1
  43. package/dest/contract_function_simulator/oracle/utility_execution_oracle.js +328 -140
  44. package/dest/contract_function_simulator/pick_notes.d.ts +1 -1
  45. package/dest/contract_function_simulator/pick_notes.d.ts.map +1 -1
  46. package/dest/contract_function_simulator/pick_notes.js +14 -3
  47. package/dest/contract_function_simulator/proxied_contract_data_source.d.ts +1 -1
  48. package/dest/contract_function_simulator/proxied_contract_data_source.d.ts.map +1 -1
  49. package/dest/contract_function_simulator/proxied_contract_data_source.js +3 -0
  50. package/dest/contract_logging.d.ts +27 -0
  51. package/dest/contract_logging.d.ts.map +1 -0
  52. package/dest/contract_logging.js +38 -0
  53. package/dest/contract_sync/contract_sync_service.d.ts +44 -0
  54. package/dest/contract_sync/contract_sync_service.d.ts.map +1 -0
  55. package/dest/contract_sync/contract_sync_service.js +116 -0
  56. package/dest/contract_sync/helpers.d.ts +28 -0
  57. package/dest/contract_sync/helpers.d.ts.map +1 -0
  58. package/dest/contract_sync/{index.js → helpers.js} +19 -13
  59. package/dest/debug/pxe_debug_utils.d.ts +14 -10
  60. package/dest/debug/pxe_debug_utils.d.ts.map +1 -1
  61. package/dest/debug/pxe_debug_utils.js +16 -15
  62. package/dest/entrypoints/client/bundle/index.d.ts +3 -1
  63. package/dest/entrypoints/client/bundle/index.d.ts.map +1 -1
  64. package/dest/entrypoints/client/bundle/index.js +2 -0
  65. package/dest/entrypoints/client/bundle/utils.d.ts +2 -2
  66. package/dest/entrypoints/client/bundle/utils.d.ts.map +1 -1
  67. package/dest/entrypoints/client/bundle/utils.js +13 -4
  68. package/dest/entrypoints/client/lazy/index.d.ts +3 -1
  69. package/dest/entrypoints/client/lazy/index.d.ts.map +1 -1
  70. package/dest/entrypoints/client/lazy/index.js +2 -0
  71. package/dest/entrypoints/client/lazy/utils.d.ts +2 -2
  72. package/dest/entrypoints/client/lazy/utils.d.ts.map +1 -1
  73. package/dest/entrypoints/client/lazy/utils.js +13 -4
  74. package/dest/entrypoints/pxe_creation_options.d.ts +3 -1
  75. package/dest/entrypoints/pxe_creation_options.d.ts.map +1 -1
  76. package/dest/entrypoints/pxe_creation_options.js +3 -1
  77. package/dest/entrypoints/server/index.d.ts +4 -3
  78. package/dest/entrypoints/server/index.d.ts.map +1 -1
  79. package/dest/entrypoints/server/index.js +3 -2
  80. package/dest/entrypoints/server/utils.d.ts +2 -2
  81. package/dest/entrypoints/server/utils.d.ts.map +1 -1
  82. package/dest/entrypoints/server/utils.js +11 -3
  83. package/dest/events/event_service.d.ts +5 -5
  84. package/dest/events/event_service.d.ts.map +1 -1
  85. package/dest/events/event_service.js +20 -9
  86. package/dest/events/private_event_filter_validator.d.ts +3 -2
  87. package/dest/events/private_event_filter_validator.d.ts.map +1 -1
  88. package/dest/events/private_event_filter_validator.js +15 -0
  89. package/dest/logs/log_service.d.ts +9 -9
  90. package/dest/logs/log_service.d.ts.map +1 -1
  91. package/dest/logs/log_service.js +40 -60
  92. package/dest/messages/message_context_service.d.ts +17 -0
  93. package/dest/messages/message_context_service.d.ts.map +1 -0
  94. package/dest/messages/message_context_service.js +36 -0
  95. package/dest/notes/note_service.d.ts +7 -8
  96. package/dest/notes/note_service.d.ts.map +1 -1
  97. package/dest/notes/note_service.js +23 -14
  98. package/dest/notes_filter.d.ts +24 -0
  99. package/dest/notes_filter.d.ts.map +1 -0
  100. package/dest/notes_filter.js +4 -0
  101. package/dest/oracle_version.d.ts +4 -3
  102. package/dest/oracle_version.d.ts.map +1 -1
  103. package/dest/oracle_version.js +20 -10
  104. package/dest/private_kernel/hints/compute_tx_expiration_timestamp.d.ts +4 -0
  105. package/dest/private_kernel/hints/compute_tx_expiration_timestamp.d.ts.map +1 -0
  106. package/dest/private_kernel/hints/{compute_tx_include_by_timestamp.js → compute_tx_expiration_timestamp.js} +12 -12
  107. package/dest/private_kernel/hints/index.d.ts +1 -1
  108. package/dest/private_kernel/hints/index.js +1 -1
  109. package/dest/private_kernel/hints/private_kernel_reset_private_inputs_builder.d.ts +4 -3
  110. package/dest/private_kernel/hints/private_kernel_reset_private_inputs_builder.d.ts.map +1 -1
  111. package/dest/private_kernel/hints/private_kernel_reset_private_inputs_builder.js +129 -68
  112. package/dest/private_kernel/hints/test_utils.d.ts +122 -0
  113. package/dest/private_kernel/hints/test_utils.d.ts.map +1 -0
  114. package/dest/private_kernel/hints/test_utils.js +203 -0
  115. package/dest/private_kernel/private_kernel_execution_prover.d.ts +1 -1
  116. package/dest/private_kernel/private_kernel_execution_prover.d.ts.map +1 -1
  117. package/dest/private_kernel/private_kernel_execution_prover.js +18 -13
  118. package/dest/private_kernel/private_kernel_oracle.d.ts +6 -2
  119. package/dest/private_kernel/private_kernel_oracle.d.ts.map +1 -1
  120. package/dest/private_kernel/private_kernel_oracle.js +7 -3
  121. package/dest/pxe.d.ts +72 -23
  122. package/dest/pxe.d.ts.map +1 -1
  123. package/dest/pxe.js +126 -71
  124. package/dest/storage/address_store/address_store.d.ts +1 -1
  125. package/dest/storage/address_store/address_store.d.ts.map +1 -1
  126. package/dest/storage/address_store/address_store.js +12 -11
  127. package/dest/storage/anchor_block_store/anchor_block_store.d.ts +9 -1
  128. package/dest/storage/anchor_block_store/anchor_block_store.d.ts.map +1 -1
  129. package/dest/storage/anchor_block_store/anchor_block_store.js +9 -2
  130. package/dest/storage/capsule_store/capsule_service.d.ts +21 -0
  131. package/dest/storage/capsule_store/capsule_service.d.ts.map +1 -0
  132. package/dest/storage/capsule_store/capsule_service.js +50 -0
  133. package/dest/storage/capsule_store/capsule_store.d.ts +9 -9
  134. package/dest/storage/capsule_store/capsule_store.d.ts.map +1 -1
  135. package/dest/storage/capsule_store/capsule_store.js +42 -36
  136. package/dest/storage/capsule_store/index.d.ts +2 -1
  137. package/dest/storage/capsule_store/index.d.ts.map +1 -1
  138. package/dest/storage/capsule_store/index.js +1 -0
  139. package/dest/storage/contract_store/contract_store.d.ts +42 -15
  140. package/dest/storage/contract_store/contract_store.d.ts.map +1 -1
  141. package/dest/storage/contract_store/contract_store.js +160 -73
  142. package/dest/storage/metadata.d.ts +1 -1
  143. package/dest/storage/metadata.js +1 -1
  144. package/dest/storage/note_store/note_store.d.ts +13 -3
  145. package/dest/storage/note_store/note_store.d.ts.map +1 -1
  146. package/dest/storage/note_store/note_store.js +173 -131
  147. package/dest/storage/private_event_store/private_event_store.d.ts +1 -1
  148. package/dest/storage/private_event_store/private_event_store.d.ts.map +1 -1
  149. package/dest/storage/private_event_store/private_event_store.js +126 -101
  150. package/dest/storage/tagging_store/recipient_tagging_store.d.ts +6 -6
  151. package/dest/storage/tagging_store/recipient_tagging_store.d.ts.map +1 -1
  152. package/dest/storage/tagging_store/recipient_tagging_store.js +31 -19
  153. package/dest/storage/tagging_store/sender_address_book_store.d.ts +1 -1
  154. package/dest/storage/tagging_store/sender_address_book_store.d.ts.map +1 -1
  155. package/dest/storage/tagging_store/sender_address_book_store.js +20 -14
  156. package/dest/storage/tagging_store/sender_tagging_store.d.ts +29 -28
  157. package/dest/storage/tagging_store/sender_tagging_store.d.ts.map +1 -1
  158. package/dest/storage/tagging_store/sender_tagging_store.js +233 -137
  159. package/dest/tagging/get_all_logs_by_tags.d.ts +1 -1
  160. package/dest/tagging/get_all_logs_by_tags.d.ts.map +1 -1
  161. package/dest/tagging/get_all_logs_by_tags.js +17 -3
  162. package/dest/tagging/index.d.ts +3 -3
  163. package/dest/tagging/index.d.ts.map +1 -1
  164. package/dest/tagging/index.js +1 -1
  165. package/dest/tagging/recipient_sync/load_private_logs_for_sender_recipient_pair.d.ts +4 -5
  166. package/dest/tagging/recipient_sync/load_private_logs_for_sender_recipient_pair.d.ts.map +1 -1
  167. package/dest/tagging/recipient_sync/load_private_logs_for_sender_recipient_pair.js +7 -21
  168. package/dest/tagging/recipient_sync/utils/find_highest_indexes.js +2 -2
  169. package/dest/tagging/recipient_sync/utils/load_logs_for_range.d.ts +6 -7
  170. package/dest/tagging/recipient_sync/utils/load_logs_for_range.d.ts.map +1 -1
  171. package/dest/tagging/recipient_sync/utils/load_logs_for_range.js +12 -11
  172. package/dest/tagging/sender_sync/sync_sender_tagging_indexes.d.ts +4 -8
  173. package/dest/tagging/sender_sync/sync_sender_tagging_indexes.d.ts.map +1 -1
  174. package/dest/tagging/sender_sync/sync_sender_tagging_indexes.js +13 -7
  175. package/dest/tagging/sender_sync/utils/get_status_change_of_pending.d.ts +4 -3
  176. package/dest/tagging/sender_sync/utils/get_status_change_of_pending.d.ts.map +1 -1
  177. package/dest/tagging/sender_sync/utils/get_status_change_of_pending.js +20 -10
  178. package/dest/tagging/sender_sync/utils/load_and_store_new_tagging_indexes.d.ts +5 -7
  179. package/dest/tagging/sender_sync/utils/load_and_store_new_tagging_indexes.d.ts.map +1 -1
  180. package/dest/tagging/sender_sync/utils/load_and_store_new_tagging_indexes.js +36 -24
  181. package/package.json +25 -16
  182. package/src/bin/check_oracle_version.ts +4 -4
  183. package/src/block_synchronizer/block_synchronizer.ts +12 -0
  184. package/src/config/package_info.ts +1 -1
  185. package/src/contract_function_simulator/contract_function_simulator.ts +363 -135
  186. package/src/contract_function_simulator/ephemeral_array_service.ts +110 -0
  187. package/src/contract_function_simulator/execution_tagging_index_cache.ts +19 -14
  188. package/src/contract_function_simulator/index.ts +1 -0
  189. package/src/contract_function_simulator/noir-structs/event_validation_request.ts +9 -9
  190. package/src/contract_function_simulator/noir-structs/log_retrieval_request.ts +1 -1
  191. package/src/contract_function_simulator/noir-structs/log_retrieval_response.ts +2 -5
  192. package/src/contract_function_simulator/noir-structs/note_validation_request.ts +4 -10
  193. package/src/contract_function_simulator/oracle/interfaces.ts +88 -60
  194. package/src/contract_function_simulator/oracle/legacy_oracle_mappings.ts +104 -0
  195. package/src/contract_function_simulator/oracle/oracle.ts +385 -144
  196. package/src/contract_function_simulator/oracle/private_execution.ts +4 -4
  197. package/src/contract_function_simulator/oracle/private_execution_oracle.ts +136 -184
  198. package/src/contract_function_simulator/oracle/utility_execution_oracle.ts +543 -171
  199. package/src/contract_function_simulator/pick_notes.ts +14 -3
  200. package/src/contract_function_simulator/proxied_contract_data_source.ts +8 -1
  201. package/src/contract_logging.ts +52 -0
  202. package/src/contract_sync/contract_sync_service.ts +176 -0
  203. package/src/contract_sync/{index.ts → helpers.ts} +22 -22
  204. package/src/debug/pxe_debug_utils.ts +48 -18
  205. package/src/entrypoints/client/bundle/index.ts +2 -0
  206. package/src/entrypoints/client/bundle/utils.ts +14 -5
  207. package/src/entrypoints/client/lazy/index.ts +2 -0
  208. package/src/entrypoints/client/lazy/utils.ts +14 -5
  209. package/src/entrypoints/pxe_creation_options.ts +7 -0
  210. package/src/entrypoints/server/index.ts +3 -2
  211. package/src/entrypoints/server/utils.ts +9 -10
  212. package/src/events/event_service.ts +21 -10
  213. package/src/events/private_event_filter_validator.ts +21 -1
  214. package/src/logs/log_service.ts +71 -102
  215. package/src/messages/message_context_service.ts +44 -0
  216. package/src/notes/note_service.ts +24 -15
  217. package/src/notes_filter.ts +24 -0
  218. package/src/oracle_version.ts +20 -10
  219. package/src/private_kernel/hints/{compute_tx_include_by_timestamp.ts → compute_tx_expiration_timestamp.ts} +13 -13
  220. package/src/private_kernel/hints/index.ts +1 -1
  221. package/src/private_kernel/hints/private_kernel_reset_private_inputs_builder.ts +164 -117
  222. package/src/private_kernel/hints/test_utils.ts +325 -0
  223. package/src/private_kernel/private_kernel_execution_prover.ts +18 -16
  224. package/src/private_kernel/private_kernel_oracle.ts +7 -7
  225. package/src/pxe.ts +233 -122
  226. package/src/storage/address_store/address_store.ts +15 -15
  227. package/src/storage/anchor_block_store/anchor_block_store.ts +9 -1
  228. package/src/storage/capsule_store/capsule_service.ts +90 -0
  229. package/src/storage/capsule_store/capsule_store.ts +52 -34
  230. package/src/storage/capsule_store/index.ts +1 -0
  231. package/src/storage/contract_store/contract_store.ts +193 -81
  232. package/src/storage/metadata.ts +1 -1
  233. package/src/storage/note_store/note_store.ts +192 -153
  234. package/src/storage/private_event_store/private_event_store.ts +151 -128
  235. package/src/storage/tagging_store/recipient_tagging_store.ts +38 -24
  236. package/src/storage/tagging_store/sender_address_book_store.ts +20 -14
  237. package/src/storage/tagging_store/sender_tagging_store.ts +287 -156
  238. package/src/tagging/get_all_logs_by_tags.ts +28 -4
  239. package/src/tagging/index.ts +2 -2
  240. package/src/tagging/recipient_sync/load_private_logs_for_sender_recipient_pair.ts +9 -24
  241. package/src/tagging/recipient_sync/utils/find_highest_indexes.ts +2 -2
  242. package/src/tagging/recipient_sync/utils/load_logs_for_range.ts +10 -15
  243. package/src/tagging/sender_sync/sync_sender_tagging_indexes.ts +23 -10
  244. package/src/tagging/sender_sync/utils/get_status_change_of_pending.ts +26 -11
  245. package/src/tagging/sender_sync/utils/load_and_store_new_tagging_indexes.ts +27 -26
  246. package/dest/contract_sync/index.d.ts +0 -23
  247. package/dest/contract_sync/index.d.ts.map +0 -1
  248. package/dest/private_kernel/hints/compute_tx_include_by_timestamp.d.ts +0 -4
  249. package/dest/private_kernel/hints/compute_tx_include_by_timestamp.d.ts.map +0 -1
@@ -1,12 +1,12 @@
1
- import { toArray } from '@aztec/foundation/iterable';
2
1
  import { Semaphore } from '@aztec/foundation/queue';
3
2
  import type { Fr } from '@aztec/foundation/schemas';
4
3
  import type { AztecAsyncKVStore, AztecAsyncMap, AztecAsyncMultiMap } from '@aztec/kv-store';
5
4
  import type { AztecAddress } from '@aztec/stdlib/aztec-address';
6
5
  import type { DataInBlock } from '@aztec/stdlib/block';
7
- import { NoteDao, NoteStatus, type NotesFilter } from '@aztec/stdlib/note';
6
+ import { NoteDao, NoteStatus } from '@aztec/stdlib/note';
8
7
 
9
8
  import type { StagedStore } from '../../job_coordinator/job_coordinator.js';
9
+ import type { NotesFilter } from '../../notes_filter.js';
10
10
  import { StoredNote } from './stored_note.js';
11
11
 
12
12
  /**
@@ -18,6 +18,8 @@ import { StoredNote } from './stored_note.js';
18
18
  export class NoteStore implements StagedStore {
19
19
  readonly storeName: string = 'note';
20
20
 
21
+ #store: AztecAsyncKVStore;
22
+
21
23
  // Note that we use the siloedNullifier as the note id in the store as it's guaranteed to be unique.
22
24
 
23
25
  // Main storage for notes. Avoid performing full scans on it as it contains all notes PXE knows, use
@@ -46,6 +48,7 @@ export class NoteStore implements StagedStore {
46
48
  #jobLocks: Map<string, Semaphore>;
47
49
 
48
50
  constructor(store: AztecAsyncKVStore) {
51
+ this.#store = store;
49
52
  this.#notes = store.openMap('notes');
50
53
  this.#nullifiersByContractAddress = store.openMultiMap('note_nullifiers_by_contract');
51
54
  this.#nullifiersByNullificationBlockNumber = store.openMultiMap('note_block_number_to_nullifier');
@@ -65,32 +68,26 @@ export class NoteStore implements StagedStore {
65
68
  * @param jobId - The job context for staged writes
66
69
  */
67
70
  public addNotes(notes: NoteDao[], scope: AztecAddress, jobId: string): Promise<void[]> {
68
- return this.#withJobLock(jobId, () => Promise.all(notes.map(noteDao => this.#addNote(noteDao, scope, jobId))));
69
- }
70
-
71
- async #addNote(note: NoteDao, scope: AztecAddress, jobId: string) {
72
- const noteForJob =
73
- (await this.#readNote(note.siloedNullifier.toString(), jobId)) ?? new StoredNote(note, new Set());
74
-
75
- // Make sure the note is linked to the scope and staged for this job
76
- noteForJob.addScope(scope.toString());
77
- this.#writeNote(noteForJob, jobId);
71
+ return this.#withJobLock(jobId, () =>
72
+ this.#store.transactionAsync(() =>
73
+ Promise.all(
74
+ notes.map(async note => {
75
+ const noteForJob =
76
+ (await this.#readNote(note.siloedNullifier.toString(), jobId)) ?? new StoredNote(note, new Set());
77
+ noteForJob.addScope(scope.toString());
78
+ this.#writeNote(noteForJob, jobId);
79
+ }),
80
+ ),
81
+ ),
82
+ );
78
83
  }
79
84
 
80
85
  async #readNote(nullifier: string, jobId: string): Promise<StoredNote | undefined> {
81
- // First check staged notes for this job
82
- const noteForJob = this.#getNotesForJob(jobId).get(nullifier);
83
- if (noteForJob) {
84
- return noteForJob;
85
- }
86
-
87
- // Then check persistent storage
86
+ // Always issue DB read to keep IndexedDB transaction alive (they auto-commit when a new micro-task starts and there
87
+ // are no pending read requests). The staged value still takes precedence if it exists.
88
88
  const noteBuffer = await this.#notes.getAsync(nullifier);
89
- if (noteBuffer) {
90
- return StoredNote.fromBuffer(noteBuffer);
91
- }
92
-
93
- return undefined;
89
+ const noteForJob = this.#getNotesForJob(jobId).get(nullifier);
90
+ return noteForJob ?? (noteBuffer ? StoredNote.fromBuffer(noteBuffer) : undefined);
94
91
  }
95
92
 
96
93
  #writeNote(note: StoredNote, jobId: string) {
@@ -107,59 +104,99 @@ export class NoteStore implements StagedStore {
107
104
  * @params jobId - the job context to read from.
108
105
  * @returns Filtered and deduplicated notes (a note might be present in multiple scopes - we ensure it is only
109
106
  * returned once if this is the case)
110
- * @throws If filtering by an empty scopes array. Scopes have to be set to undefined or to a non-empty array.
111
107
  */
112
- async getNotes(filter: NotesFilter, jobId: string): Promise<NoteDao[]> {
113
- if (filter.scopes !== undefined && filter.scopes.length === 0) {
114
- throw new Error('Trying to get notes with an empty scopes array');
108
+ getNotes(filter: NotesFilter, jobId: string): Promise<NoteDao[]> {
109
+ if (filter.scopes.length === 0) {
110
+ return Promise.resolve([]);
115
111
  }
116
112
 
117
- const targetStatus = filter.status ?? NoteStatus.ACTIVE;
113
+ return this.#store.transactionAsync(async () => {
114
+ const targetStatus = filter.status ?? NoteStatus.ACTIVE;
115
+
116
+ // The code below might read a bit unnatural, the reason is that we need to be careful in how we use `await` inside
117
+ // `transactionAsync`, otherwise browsers might choose to auto-commit the IndexedDB transaction forcing us to
118
+ // explicitly handle that condition. The rule we need to honor is: do not await unless you generate a database
119
+ // read or write or you're done using the DB for the remainder of the transaction. The following sequence is
120
+ // unsafe in IndexedDB:
121
+ //
122
+ // 1. start transactionAsync()
123
+ // 2. await readDb() <-- OK, transaction alive because we issued DB ops
124
+ // 3. run a bunch of computations (no await involved) <-- OK, tx alive because we are in the same microtask
125
+ // 4. await doSthNotInDb() <-- no DB ops issued in this task, browser's free to decide to commit the tx
126
+ // 5. await readDb() <-- BOOM, TransactionInactiveError
127
+ //
128
+ // Note that the real issue is in step number 5: we try to continue using a transaction that the browser might
129
+ // have already committed.
130
+ //
131
+ // We need to read candidate notes which are either indexed by contract address in the DB (in
132
+ // #nullifiersByContractAddress), or lie in memory for the not yet committed `jobId`.
133
+ // So we collect promises based on both sources without awaiting for them.
134
+ const noteReadPromises: Map<string, Promise<StoredNote | undefined>> = new Map();
135
+
136
+ // Awaiting the getValuesAsync iterator is fine because it's reading from the DB
137
+ for await (const nullifier of this.#nullifiersByContractAddress.getValuesAsync(
138
+ filter.contractAddress.toString(),
139
+ )) {
140
+ // Each #readNote will perform a DB read
141
+ noteReadPromises.set(nullifier, this.#readNote(nullifier, jobId));
142
+ }
118
143
 
119
- const foundNotes: Map<string, NoteDao> = new Map();
144
+ // Add staged nullifiers from job, no awaits involved, so we are fine
145
+ for (const storedNote of this.#getNotesForJob(jobId).values()) {
146
+ if (storedNote.noteDao.contractAddress.equals(filter.contractAddress)) {
147
+ const nullifier = storedNote.noteDao.siloedNullifier.toString();
148
+ if (!noteReadPromises.has(nullifier)) {
149
+ noteReadPromises.set(nullifier, Promise.resolve(storedNote));
150
+ }
151
+ }
152
+ }
120
153
 
121
- const nullifiersOfContract = await this.#nullifiersOfContract(filter.contractAddress, jobId);
122
- for (const nullifier of nullifiersOfContract) {
123
- const note = await this.#readNote(nullifier, jobId);
154
+ // By now we have pending DB requests from all the #readNote calls. Await them all together.
155
+ const notes = await Promise.all(noteReadPromises.values());
124
156
 
125
- // Defensive: hitting this case means we're mishandling contract indices or in-memory job data
126
- if (!note) {
127
- throw new Error('PXE note database is corrupted.');
128
- }
157
+ // The rest of the function is await-free, and just deals with filtering and sorting our findings.
158
+ const foundNotes: Map<string, NoteDao> = new Map();
129
159
 
130
- // Apply filters
131
- if (targetStatus === NoteStatus.ACTIVE && note.isNullified()) {
132
- continue;
133
- }
160
+ for (const note of notes) {
161
+ // Defensive: hitting this case means we're mishandling contract indices or in-memory job data
162
+ if (!note) {
163
+ throw new Error('PXE note database is corrupted.');
164
+ }
134
165
 
135
- if (filter.owner && !note.noteDao.owner.equals(filter.owner)) {
136
- continue;
137
- }
166
+ // Apply filters
167
+ if (targetStatus === NoteStatus.ACTIVE && note.isNullified()) {
168
+ continue;
169
+ }
138
170
 
139
- if (filter.storageSlot && !note.noteDao.storageSlot.equals(filter.storageSlot)) {
140
- continue;
141
- }
171
+ if (filter.owner && !note.noteDao.owner.equals(filter.owner)) {
172
+ continue;
173
+ }
142
174
 
143
- if (filter.siloedNullifier && !note.noteDao.siloedNullifier.equals(filter.siloedNullifier)) {
144
- continue;
145
- }
175
+ if (filter.storageSlot && !note.noteDao.storageSlot.equals(filter.storageSlot)) {
176
+ continue;
177
+ }
146
178
 
147
- if (filter.scopes && note.scopes.intersection(new Set(filter.scopes.map(s => s.toString()))).size === 0) {
148
- continue;
149
- }
179
+ if (filter.siloedNullifier && !note.noteDao.siloedNullifier.equals(filter.siloedNullifier)) {
180
+ continue;
181
+ }
150
182
 
151
- foundNotes.set(note.noteDao.siloedNullifier.toString(), note.noteDao);
152
- }
183
+ if (note.scopes.intersection(new Set(filter.scopes.map(s => s.toString()))).size === 0) {
184
+ continue;
185
+ }
153
186
 
154
- // Sort by block number, then by tx index within block, then by note index within tx
155
- return [...foundNotes.values()].sort((a, b) => {
156
- if (a.l2BlockNumber !== b.l2BlockNumber) {
157
- return a.l2BlockNumber - b.l2BlockNumber;
187
+ foundNotes.set(note.noteDao.siloedNullifier.toString(), note.noteDao);
158
188
  }
159
- if (a.txIndexInBlock !== b.txIndexInBlock) {
160
- return a.txIndexInBlock - b.txIndexInBlock;
161
- }
162
- return a.noteIndexInTx - b.noteIndexInTx;
189
+
190
+ // Sort by block number, then by tx index within block, then by note index within tx
191
+ return [...foundNotes.values()].sort((a, b) => {
192
+ if (a.l2BlockNumber !== b.l2BlockNumber) {
193
+ return a.l2BlockNumber - b.l2BlockNumber;
194
+ }
195
+ if (a.txIndexInBlock !== b.txIndexInBlock) {
196
+ return a.txIndexInBlock - b.txIndexInBlock;
197
+ }
198
+ return a.noteIndexInTx - b.noteIndexInTx;
199
+ });
163
200
  });
164
201
  }
165
202
 
@@ -179,41 +216,46 @@ export class NoteStore implements StagedStore {
179
216
  * @throws Error if any nullifier is not found in this notes store
180
217
  */
181
218
  applyNullifiers(nullifiers: DataInBlock<Fr>[], jobId: string): Promise<NoteDao[]> {
182
- return this.#withJobLock(jobId, async () => {
183
- if (nullifiers.length === 0) {
184
- return [];
185
- }
219
+ if (nullifiers.length === 0) {
220
+ return Promise.resolve([]);
221
+ }
186
222
 
187
- const notesToNullify = await Promise.all(
188
- nullifiers.map(async nullifierInBlock => {
189
- const nullifier = nullifierInBlock.data.toString();
223
+ if (nullifiers.some(n => n.l2BlockNumber === 0)) {
224
+ return Promise.reject(new Error('applyNullifiers: nullifiers cannot have been emitted at block 0'));
225
+ }
190
226
 
191
- const storedNote = await this.#readNote(nullifier, jobId);
192
- if (!storedNote) {
193
- throw new Error(`Attempted to mark a note as nullified which does not exist in PXE DB`);
194
- }
227
+ return this.#withJobLock(jobId, () =>
228
+ this.#store.transactionAsync(async () => {
229
+ const notesToNullify = await Promise.all(
230
+ nullifiers.map(async nullifierInBlock => {
231
+ const nullifier = nullifierInBlock.data.toString();
195
232
 
196
- return { storedNote: await this.#readNote(nullifier, jobId), blockNumber: nullifierInBlock.l2BlockNumber };
197
- }),
198
- );
233
+ const storedNote = await this.#readNote(nullifier, jobId);
234
+ if (!storedNote) {
235
+ throw new Error(`Attempted to mark a note as nullified which does not exist in PXE DB`);
236
+ }
199
237
 
200
- const notesNullifiedInThisCall: Map<string, NoteDao> = new Map();
201
- for (const noteToNullify of notesToNullify) {
202
- // Safe to coerce (!) because we throw if we find any undefined above
203
- const note = noteToNullify.storedNote!;
238
+ return { storedNote, blockNumber: nullifierInBlock.l2BlockNumber };
239
+ }),
240
+ );
204
241
 
205
- // Skip already nullified notes
206
- if (note.isNullified()) {
207
- continue;
208
- }
242
+ const notesNullifiedInThisCall: Map<string, NoteDao> = new Map();
243
+ for (const noteToNullify of notesToNullify) {
244
+ const note = noteToNullify.storedNote;
209
245
 
210
- note.markAsNullified(noteToNullify.blockNumber);
211
- this.#writeNote(note, jobId);
212
- notesNullifiedInThisCall.set(note.noteDao.siloedNullifier.toString(), note.noteDao);
213
- }
246
+ // Skip already nullified notes
247
+ if (note.isNullified()) {
248
+ continue;
249
+ }
214
250
 
215
- return [...notesNullifiedInThisCall.values()];
216
- });
251
+ note.markAsNullified(noteToNullify.blockNumber);
252
+ this.#writeNote(note, jobId);
253
+ notesNullifiedInThisCall.set(note.noteDao.siloedNullifier.toString(), note.noteDao);
254
+ }
255
+
256
+ return [...notesNullifiedInThisCall.values()];
257
+ }),
258
+ );
217
259
  }
218
260
 
219
261
  /**
@@ -244,18 +286,23 @@ export class NoteStore implements StagedStore {
244
286
  * @param blockNumber - Notes created after this block number will be deleted
245
287
  */
246
288
  async #deleteActiveNotesAfterBlock(blockNumber: number): Promise<void> {
247
- const notes = await toArray(this.#notes.valuesAsync());
248
- for (const noteBuffer of notes) {
289
+ // Collect notes to delete during iteration to keep IndexedDB transaction alive.
290
+ const notesToDelete: { nullifier: string; contractAddress: string }[] = [];
291
+ for await (const noteBuffer of this.#notes.valuesAsync()) {
249
292
  const storedNote = StoredNote.fromBuffer(noteBuffer);
250
293
  if (storedNote.noteDao.l2BlockNumber > blockNumber) {
251
- const noteNullifier = storedNote.noteDao.siloedNullifier.toString();
252
- await this.#notes.delete(noteNullifier);
253
- await this.#nullifiersByContractAddress.deleteValue(
254
- storedNote.noteDao.contractAddress.toString(),
255
- noteNullifier,
256
- );
294
+ notesToDelete.push({
295
+ nullifier: storedNote.noteDao.siloedNullifier.toString(),
296
+ contractAddress: storedNote.noteDao.contractAddress.toString(),
297
+ });
257
298
  }
258
299
  }
300
+
301
+ // Delete all collected notes. Each delete is a DB operation that keeps the transaction alive.
302
+ for (const { nullifier, contractAddress } of notesToDelete) {
303
+ await this.#notes.delete(nullifier);
304
+ await this.#nullifiersByContractAddress.deleteValue(contractAddress, nullifier);
305
+ }
259
306
  }
260
307
 
261
308
  /**
@@ -268,62 +315,69 @@ export class NoteStore implements StagedStore {
268
315
  * @param anchorBlockNumber - Upper bound for the block range to process
269
316
  */
270
317
  async #rewindNullifiedNotesAfterBlock(blockNumber: number, anchorBlockNumber: number): Promise<void> {
271
- const currentBlockNumber = blockNumber + 1;
272
- for (let i = currentBlockNumber; i <= anchorBlockNumber; i++) {
273
- const noteNullifiersToReinsert: string[] = await toArray(
274
- this.#nullifiersByNullificationBlockNumber.getValuesAsync(i),
275
- );
276
-
277
- const nullifiedNoteBuffers = await Promise.all(
278
- noteNullifiersToReinsert.map(async noteNullifier => {
279
- const note = await this.#notes.getAsync(noteNullifier);
280
-
281
- if (!note) {
282
- throw new Error(`PXE DB integrity error: no note found with nullifier ${noteNullifier}`);
283
- }
284
-
285
- return note;
286
- }),
287
- );
288
-
289
- const storedNotes = nullifiedNoteBuffers.map(buffer => StoredNote.fromBuffer(buffer));
318
+ // First pass: collect all nullifiers for all blocks, starting reads during iteration to keep tx alive.
319
+ const nullifiersByBlock: Map<number, { nullifier: string; noteReadPromise: Promise<Buffer | undefined> }[]> =
320
+ new Map();
321
+
322
+ for (let i = blockNumber + 1; i <= anchorBlockNumber; i++) {
323
+ const blockNullifiers: { nullifier: string; noteReadPromise: Promise<Buffer | undefined> }[] = [];
324
+ for await (const nullifier of this.#nullifiersByNullificationBlockNumber.getValuesAsync(i)) {
325
+ // Start read immediately during iteration to keep IndexedDB transaction alive
326
+ blockNullifiers.push({ nullifier, noteReadPromise: this.#notes.getAsync(nullifier) });
327
+ }
328
+ if (blockNullifiers.length > 0) {
329
+ nullifiersByBlock.set(i, blockNullifiers);
330
+ }
331
+ }
290
332
 
291
- for (const storedNote of storedNotes) {
292
- const noteNullifier = storedNote.noteDao.siloedNullifier.toString();
293
- const scopes = storedNote.scopes;
333
+ // Second pass: await reads and perform writes
334
+ for (const [block, nullifiers] of nullifiersByBlock) {
335
+ for (const { nullifier, noteReadPromise } of nullifiers) {
336
+ const noteBuffer = await noteReadPromise;
337
+ if (!noteBuffer) {
338
+ throw new Error(`PXE DB integrity error: no note found with nullifier ${nullifier}`);
339
+ }
294
340
 
295
- if (scopes.size === 0) {
296
- // We should never run into this error because notes always have a scope assigned to them - either on initial
297
- // insertion via `addNotes` or when removing their nullifiers.
298
- throw new Error(`No scopes found for nullified note with nullifier ${noteNullifier}`);
341
+ const storedNote = StoredNote.fromBuffer(noteBuffer);
342
+ if (storedNote.scopes.size === 0) {
343
+ throw new Error(`No scopes found for nullified note with nullifier ${nullifier}`);
299
344
  }
300
345
 
301
346
  storedNote.markAsActive();
302
347
 
303
348
  await Promise.all([
304
- this.#notes.set(noteNullifier, storedNote.toBuffer()),
305
- this.#nullifiersByNullificationBlockNumber.deleteValue(i, noteNullifier),
349
+ this.#notes.set(nullifier, storedNote.toBuffer()),
350
+ this.#nullifiersByNullificationBlockNumber.deleteValue(block, nullifier),
306
351
  ]);
307
352
  }
308
353
  }
309
354
  }
310
355
 
311
- commit(jobId: string): Promise<void> {
312
- return this.#withJobLock(jobId, async () => {
313
- for (const [nullifier, storedNote] of this.#getNotesForJob(jobId)) {
314
- await this.#notes.set(nullifier, storedNote.toBuffer());
315
- await this.#nullifiersByContractAddress.set(storedNote.noteDao.contractAddress.toString(), nullifier);
316
- if (storedNote.nullifiedAt !== undefined) {
317
- await this.#nullifiersByNullificationBlockNumber.set(storedNote.nullifiedAt, nullifier);
318
- }
356
+ /**
357
+ * Commits in memory job data to persistent storage.
358
+ *
359
+ * Called by JobCoordinator when a job completes successfully.
360
+ *
361
+ * Note: JobCoordinator wraps all commits in a single transaction, so we don't need our own transactionAsync here
362
+ * (and using one would throw on IndexedDB as it does not support nested txs).
363
+ *
364
+ * @param jobId - The jobId identifying which staged data to commit
365
+ */
366
+ async commit(jobId: string): Promise<void> {
367
+ for (const [nullifier, storedNote] of this.#getNotesForJob(jobId)) {
368
+ await this.#notes.set(nullifier, storedNote.toBuffer());
369
+ await this.#nullifiersByContractAddress.set(storedNote.noteDao.contractAddress.toString(), nullifier);
370
+ if (storedNote.nullifiedAt !== undefined) {
371
+ await this.#nullifiersByNullificationBlockNumber.set(storedNote.nullifiedAt, nullifier);
319
372
  }
373
+ }
320
374
 
321
- this.#clearJobData(jobId);
322
- });
375
+ this.#clearJobData(jobId);
323
376
  }
324
377
 
325
378
  discardStaged(jobId: string): Promise<void> {
326
- return this.#withJobLock(jobId, () => Promise.resolve(this.#clearJobData(jobId)));
379
+ this.#clearJobData(jobId);
380
+ return Promise.resolve();
327
381
  }
328
382
 
329
383
  #clearJobData(jobId: string) {
@@ -358,19 +412,4 @@ export class NoteStore implements StagedStore {
358
412
  }
359
413
  return notesForJob;
360
414
  }
361
-
362
- async #nullifiersOfContract(contractAddress: AztecAddress, jobId: string): Promise<Set<string>> {
363
- // Collect persisted nullifiers for this contract
364
- const persistedNullifiers: string[] = await toArray(
365
- this.#nullifiersByContractAddress.getValuesAsync(contractAddress.toString()),
366
- );
367
-
368
- // Collect staged nullifiers from the job where the note's contract matches
369
- const stagedNullifiers = this.#getNotesForJob(jobId)
370
- .values()
371
- .filter(storedNote => storedNote.noteDao.contractAddress.equals(contractAddress))
372
- .map(storedNote => storedNote.noteDao.siloedNullifier.toString());
373
-
374
- return new Set([...persistedNullifiers, ...stagedNullifiers]);
375
- }
376
415
  }