@aztec/pxe 0.0.1-commit.1142ef1 → 0.0.1-commit.18ccd8f0

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 (211) hide show
  1. package/dest/bin/check_oracle_version.js +1 -1
  2. package/dest/block_synchronizer/block_synchronizer.d.ts +9 -5
  3. package/dest/block_synchronizer/block_synchronizer.d.ts.map +1 -1
  4. package/dest/block_synchronizer/block_synchronizer.js +66 -21
  5. package/dest/config/index.d.ts +3 -1
  6. package/dest/config/index.d.ts.map +1 -1
  7. package/dest/config/index.js +17 -0
  8. package/dest/contract_function_simulator/benchmarked_node.d.ts +9 -0
  9. package/dest/contract_function_simulator/benchmarked_node.d.ts.map +1 -0
  10. package/dest/contract_function_simulator/benchmarked_node.js +77 -0
  11. package/dest/contract_function_simulator/contract_function_simulator.d.ts +5 -7
  12. package/dest/contract_function_simulator/contract_function_simulator.d.ts.map +1 -1
  13. package/dest/contract_function_simulator/contract_function_simulator.js +30 -20
  14. package/dest/contract_function_simulator/execution_note_cache.d.ts +18 -9
  15. package/dest/contract_function_simulator/execution_note_cache.d.ts.map +1 -1
  16. package/dest/contract_function_simulator/execution_note_cache.js +45 -28
  17. package/dest/contract_function_simulator/index.d.ts +2 -2
  18. package/dest/contract_function_simulator/index.d.ts.map +1 -1
  19. package/dest/contract_function_simulator/index.js +1 -1
  20. package/dest/contract_function_simulator/noir-structs/event_validation_request.d.ts +1 -1
  21. package/dest/contract_function_simulator/noir-structs/event_validation_request.js +1 -1
  22. package/dest/contract_function_simulator/noir-structs/note_validation_request.d.ts +1 -1
  23. package/dest/contract_function_simulator/noir-structs/note_validation_request.js +1 -1
  24. package/dest/contract_function_simulator/noir-structs/utility_context.d.ts +4 -10
  25. package/dest/contract_function_simulator/noir-structs/utility_context.d.ts.map +1 -1
  26. package/dest/contract_function_simulator/noir-structs/utility_context.js +7 -18
  27. package/dest/contract_function_simulator/oracle/interfaces.d.ts +15 -11
  28. package/dest/contract_function_simulator/oracle/interfaces.d.ts.map +1 -1
  29. package/dest/contract_function_simulator/oracle/message_load_oracle_inputs.d.ts +3 -1
  30. package/dest/contract_function_simulator/oracle/message_load_oracle_inputs.d.ts.map +1 -1
  31. package/dest/contract_function_simulator/oracle/note_packing_utils.d.ts +6 -6
  32. package/dest/contract_function_simulator/oracle/note_packing_utils.d.ts.map +1 -1
  33. package/dest/contract_function_simulator/oracle/note_packing_utils.js +8 -8
  34. package/dest/contract_function_simulator/oracle/oracle.d.ts +10 -8
  35. package/dest/contract_function_simulator/oracle/oracle.d.ts.map +1 -1
  36. package/dest/contract_function_simulator/oracle/oracle.js +43 -32
  37. package/dest/contract_function_simulator/oracle/private_execution.d.ts +2 -26
  38. package/dest/contract_function_simulator/oracle/private_execution.d.ts.map +1 -1
  39. package/dest/contract_function_simulator/oracle/private_execution.js +0 -35
  40. package/dest/contract_function_simulator/oracle/private_execution_oracle.d.ts +10 -1
  41. package/dest/contract_function_simulator/oracle/private_execution_oracle.d.ts.map +1 -1
  42. package/dest/contract_function_simulator/oracle/private_execution_oracle.js +19 -7
  43. package/dest/contract_function_simulator/oracle/utility_execution_oracle.d.ts +25 -16
  44. package/dest/contract_function_simulator/oracle/utility_execution_oracle.d.ts.map +1 -1
  45. package/dest/contract_function_simulator/oracle/utility_execution_oracle.js +69 -71
  46. package/dest/contract_sync/index.d.ts +23 -0
  47. package/dest/contract_sync/index.d.ts.map +1 -0
  48. package/dest/contract_sync/index.js +54 -0
  49. package/dest/debug/pxe_debug_utils.d.ts +16 -6
  50. package/dest/debug/pxe_debug_utils.d.ts.map +1 -1
  51. package/dest/debug/pxe_debug_utils.js +20 -10
  52. package/dest/entrypoints/client/bundle/utils.d.ts +1 -1
  53. package/dest/entrypoints/client/bundle/utils.d.ts.map +1 -1
  54. package/dest/entrypoints/client/bundle/utils.js +10 -5
  55. package/dest/entrypoints/client/lazy/utils.d.ts +2 -2
  56. package/dest/entrypoints/client/lazy/utils.d.ts.map +1 -1
  57. package/dest/entrypoints/client/lazy/utils.js +11 -6
  58. package/dest/entrypoints/pxe_creation_options.d.ts +3 -2
  59. package/dest/entrypoints/pxe_creation_options.d.ts.map +1 -1
  60. package/dest/entrypoints/server/index.d.ts +3 -1
  61. package/dest/entrypoints/server/index.d.ts.map +1 -1
  62. package/dest/entrypoints/server/index.js +2 -0
  63. package/dest/entrypoints/server/utils.d.ts +1 -1
  64. package/dest/entrypoints/server/utils.d.ts.map +1 -1
  65. package/dest/entrypoints/server/utils.js +19 -8
  66. package/dest/events/event_service.d.ts +4 -3
  67. package/dest/events/event_service.d.ts.map +1 -1
  68. package/dest/events/event_service.js +17 -19
  69. package/dest/events/private_event_filter_validator.d.ts +5 -5
  70. package/dest/events/private_event_filter_validator.d.ts.map +1 -1
  71. package/dest/events/private_event_filter_validator.js +5 -6
  72. package/dest/job_coordinator/job_coordinator.d.ts +3 -2
  73. package/dest/job_coordinator/job_coordinator.d.ts.map +1 -1
  74. package/dest/job_coordinator/job_coordinator.js +3 -2
  75. package/dest/logs/log_service.d.ts +4 -2
  76. package/dest/logs/log_service.d.ts.map +1 -1
  77. package/dest/logs/log_service.js +22 -14
  78. package/dest/notes/note_service.d.ts +5 -4
  79. package/dest/notes/note_service.d.ts.map +1 -1
  80. package/dest/notes/note_service.js +30 -34
  81. package/dest/oracle_version.d.ts +3 -3
  82. package/dest/oracle_version.d.ts.map +1 -1
  83. package/dest/oracle_version.js +4 -3
  84. package/dest/private_kernel/hints/index.d.ts +2 -2
  85. package/dest/private_kernel/hints/index.d.ts.map +1 -1
  86. package/dest/private_kernel/hints/index.js +1 -1
  87. package/dest/private_kernel/hints/private_kernel_reset_private_inputs_builder.d.ts +28 -0
  88. package/dest/private_kernel/hints/private_kernel_reset_private_inputs_builder.d.ts.map +1 -0
  89. package/dest/private_kernel/hints/{build_private_kernel_reset_private_inputs.js → private_kernel_reset_private_inputs_builder.js} +12 -6
  90. package/dest/private_kernel/private_kernel_execution_prover.d.ts +3 -2
  91. package/dest/private_kernel/private_kernel_execution_prover.d.ts.map +1 -1
  92. package/dest/private_kernel/private_kernel_execution_prover.js +3 -3
  93. package/dest/private_kernel/private_kernel_oracle.d.ts +23 -28
  94. package/dest/private_kernel/private_kernel_oracle.d.ts.map +1 -1
  95. package/dest/private_kernel/private_kernel_oracle.js +90 -2
  96. package/dest/pxe.d.ts +7 -36
  97. package/dest/pxe.d.ts.map +1 -1
  98. package/dest/pxe.js +43 -83
  99. package/dest/storage/capsule_store/capsule_store.d.ts +24 -9
  100. package/dest/storage/capsule_store/capsule_store.d.ts.map +1 -1
  101. package/dest/storage/capsule_store/capsule_store.js +132 -23
  102. package/dest/storage/contract_store/contract_store.d.ts +1 -2
  103. package/dest/storage/contract_store/contract_store.d.ts.map +1 -1
  104. package/dest/storage/contract_store/contract_store.js +0 -12
  105. package/dest/storage/note_store/note_store.d.ts +45 -56
  106. package/dest/storage/note_store/note_store.d.ts.map +1 -1
  107. package/dest/storage/note_store/note_store.js +241 -262
  108. package/dest/storage/note_store/stored_note.d.ts +16 -0
  109. package/dest/storage/note_store/stored_note.d.ts.map +1 -0
  110. package/dest/storage/note_store/stored_note.js +43 -0
  111. package/dest/storage/private_event_store/private_event_store.d.ts +43 -8
  112. package/dest/storage/private_event_store/private_event_store.d.ts.map +1 -1
  113. package/dest/storage/private_event_store/private_event_store.js +199 -109
  114. package/dest/storage/private_event_store/stored_private_event.d.ts +23 -0
  115. package/dest/storage/private_event_store/stored_private_event.d.ts.map +1 -0
  116. package/dest/storage/private_event_store/stored_private_event.js +56 -0
  117. package/dest/storage/tagging_store/recipient_tagging_store.d.ts +15 -8
  118. package/dest/storage/tagging_store/recipient_tagging_store.d.ts.map +1 -1
  119. package/dest/storage/tagging_store/recipient_tagging_store.js +69 -12
  120. package/dest/storage/tagging_store/sender_tagging_store.d.ts +19 -9
  121. package/dest/storage/tagging_store/sender_tagging_store.d.ts.map +1 -1
  122. package/dest/storage/tagging_store/sender_tagging_store.js +109 -27
  123. package/dest/tagging/get_all_logs_by_tags.d.ts +24 -0
  124. package/dest/tagging/get_all_logs_by_tags.d.ts.map +1 -0
  125. package/dest/tagging/get_all_logs_by_tags.js +46 -0
  126. package/dest/tagging/index.d.ts +2 -1
  127. package/dest/tagging/index.d.ts.map +1 -1
  128. package/dest/tagging/index.js +1 -0
  129. package/dest/tagging/recipient_sync/load_private_logs_for_sender_recipient_pair.d.ts +3 -2
  130. package/dest/tagging/recipient_sync/load_private_logs_for_sender_recipient_pair.d.ts.map +1 -1
  131. package/dest/tagging/recipient_sync/load_private_logs_for_sender_recipient_pair.js +6 -6
  132. package/dest/tagging/recipient_sync/utils/load_logs_for_range.d.ts +3 -2
  133. package/dest/tagging/recipient_sync/utils/load_logs_for_range.d.ts.map +1 -1
  134. package/dest/tagging/recipient_sync/utils/load_logs_for_range.js +5 -2
  135. package/dest/tagging/sender_sync/sync_sender_tagging_indexes.d.ts +3 -2
  136. package/dest/tagging/sender_sync/sync_sender_tagging_indexes.d.ts.map +1 -1
  137. package/dest/tagging/sender_sync/sync_sender_tagging_indexes.js +7 -7
  138. package/dest/tagging/sender_sync/utils/get_status_change_of_pending.d.ts +1 -1
  139. package/dest/tagging/sender_sync/utils/get_status_change_of_pending.d.ts.map +1 -1
  140. package/dest/tagging/sender_sync/utils/get_status_change_of_pending.js +5 -8
  141. package/dest/tagging/sender_sync/utils/load_and_store_new_tagging_indexes.d.ts +5 -2
  142. package/dest/tagging/sender_sync/utils/load_and_store_new_tagging_indexes.d.ts.map +1 -1
  143. package/dest/tagging/sender_sync/utils/load_and_store_new_tagging_indexes.js +10 -5
  144. package/package.json +18 -18
  145. package/src/bin/check_oracle_version.ts +1 -0
  146. package/src/block_synchronizer/block_synchronizer.ts +85 -33
  147. package/src/config/index.ts +14 -0
  148. package/src/contract_function_simulator/benchmarked_node.ts +103 -0
  149. package/src/contract_function_simulator/contract_function_simulator.ts +34 -24
  150. package/src/contract_function_simulator/execution_note_cache.ts +44 -25
  151. package/src/contract_function_simulator/index.ts +1 -1
  152. package/src/contract_function_simulator/noir-structs/event_validation_request.ts +1 -1
  153. package/src/contract_function_simulator/noir-structs/note_validation_request.ts +1 -1
  154. package/src/contract_function_simulator/noir-structs/utility_context.ts +6 -25
  155. package/src/contract_function_simulator/oracle/interfaces.ts +20 -10
  156. package/src/contract_function_simulator/oracle/note_packing_utils.ts +10 -10
  157. package/src/contract_function_simulator/oracle/oracle.ts +56 -41
  158. package/src/contract_function_simulator/oracle/private_execution.ts +1 -67
  159. package/src/contract_function_simulator/oracle/private_execution_oracle.ts +35 -8
  160. package/src/contract_function_simulator/oracle/utility_execution_oracle.ts +90 -76
  161. package/src/contract_sync/index.ts +98 -0
  162. package/src/debug/pxe_debug_utils.ts +26 -11
  163. package/src/entrypoints/client/bundle/utils.ts +4 -13
  164. package/src/entrypoints/client/lazy/utils.ts +5 -13
  165. package/src/entrypoints/pxe_creation_options.ts +2 -1
  166. package/src/entrypoints/server/index.ts +2 -0
  167. package/src/entrypoints/server/utils.ts +15 -19
  168. package/src/events/event_service.ts +15 -21
  169. package/src/events/private_event_filter_validator.ts +3 -5
  170. package/src/job_coordinator/job_coordinator.ts +4 -3
  171. package/src/logs/log_service.ts +33 -12
  172. package/src/notes/note_service.ts +37 -40
  173. package/src/oracle_version.ts +4 -3
  174. package/src/private_kernel/hints/index.ts +1 -1
  175. package/src/private_kernel/hints/{build_private_kernel_reset_private_inputs.ts → private_kernel_reset_private_inputs_builder.ts} +32 -20
  176. package/src/private_kernel/private_kernel_execution_prover.ts +7 -4
  177. package/src/private_kernel/private_kernel_oracle.ts +116 -37
  178. package/src/pxe.ts +77 -117
  179. package/src/storage/capsule_store/capsule_store.ts +159 -23
  180. package/src/storage/contract_store/contract_store.ts +0 -20
  181. package/src/storage/note_store/note_store.ts +283 -319
  182. package/src/storage/note_store/stored_note.ts +48 -0
  183. package/src/storage/private_event_store/private_event_store.ts +270 -125
  184. package/src/storage/private_event_store/stored_private_event.ts +73 -0
  185. package/src/storage/tagging_store/recipient_tagging_store.ts +89 -13
  186. package/src/storage/tagging_store/sender_tagging_store.ts +128 -27
  187. package/src/tagging/get_all_logs_by_tags.ts +68 -0
  188. package/src/tagging/index.ts +1 -0
  189. package/src/tagging/recipient_sync/load_private_logs_for_sender_recipient_pair.ts +16 -5
  190. package/src/tagging/recipient_sync/utils/load_logs_for_range.ts +7 -1
  191. package/src/tagging/sender_sync/sync_sender_tagging_indexes.ts +9 -6
  192. package/src/tagging/sender_sync/utils/get_status_change_of_pending.ts +5 -17
  193. package/src/tagging/sender_sync/utils/load_and_store_new_tagging_indexes.ts +16 -4
  194. package/dest/contract_function_simulator/proxied_node.d.ts +0 -9
  195. package/dest/contract_function_simulator/proxied_node.d.ts.map +0 -1
  196. package/dest/contract_function_simulator/proxied_node.js +0 -27
  197. package/dest/private_kernel/hints/build_private_kernel_reset_private_inputs.d.ts +0 -28
  198. package/dest/private_kernel/hints/build_private_kernel_reset_private_inputs.d.ts.map +0 -1
  199. package/dest/private_kernel/private_kernel_oracle_impl.d.ts +0 -46
  200. package/dest/private_kernel/private_kernel_oracle_impl.d.ts.map +0 -1
  201. package/dest/private_kernel/private_kernel_oracle_impl.js +0 -85
  202. package/dest/public_storage/public_storage_service.d.ts +0 -24
  203. package/dest/public_storage/public_storage_service.d.ts.map +0 -1
  204. package/dest/public_storage/public_storage_service.js +0 -26
  205. package/dest/tree_membership/tree_membership_service.d.ts +0 -52
  206. package/dest/tree_membership/tree_membership_service.d.ts.map +0 -1
  207. package/dest/tree_membership/tree_membership_service.js +0 -84
  208. package/src/contract_function_simulator/proxied_node.ts +0 -33
  209. package/src/private_kernel/private_kernel_oracle_impl.ts +0 -127
  210. package/src/public_storage/public_storage_service.ts +0 -33
  211. package/src/tree_membership/tree_membership_service.ts +0 -112
@@ -0,0 +1,48 @@
1
+ import { BlockNumber } from '@aztec/foundation/branded-types';
2
+ import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize';
3
+ import { NoteDao } from '@aztec/stdlib/note';
4
+
5
+ export class StoredNote {
6
+ constructor(
7
+ readonly noteDao: NoteDao,
8
+ readonly scopes: Set<string>,
9
+ private _nullifiedAt: BlockNumber | undefined = undefined,
10
+ ) {}
11
+
12
+ static fromBuffer(buffer: Buffer) {
13
+ const reader = BufferReader.asReader(buffer);
14
+
15
+ const noteDao = NoteDao.fromBuffer(reader);
16
+ const scopes = reader.readVector({ fromBuffer: (r: BufferReader) => r.readString() });
17
+
18
+ const nullifiedAtRaw = reader.readNumber();
19
+ const nullifiedAt = nullifiedAtRaw === 0 ? undefined : (nullifiedAtRaw as BlockNumber);
20
+
21
+ return new StoredNote(noteDao, new Set(scopes), nullifiedAt);
22
+ }
23
+
24
+ toBuffer(): Buffer {
25
+ const scopesArray = [...this.scopes];
26
+ return serializeToBuffer(this.noteDao, scopesArray.length, ...scopesArray, this._nullifiedAt ?? 0);
27
+ }
28
+
29
+ addScope(scope: string) {
30
+ this.scopes.add(scope);
31
+ }
32
+
33
+ markAsNullified(blockNumber: BlockNumber) {
34
+ this._nullifiedAt = blockNumber;
35
+ }
36
+
37
+ markAsActive() {
38
+ this._nullifiedAt = undefined;
39
+ }
40
+
41
+ isNullified() {
42
+ return this._nullifiedAt !== undefined;
43
+ }
44
+
45
+ get nullifiedAt() {
46
+ return this._nullifiedAt;
47
+ }
48
+ }
@@ -1,14 +1,16 @@
1
1
  import { BlockNumber } from '@aztec/foundation/branded-types';
2
2
  import { Fr } from '@aztec/foundation/curves/bn254';
3
+ import { toArray } from '@aztec/foundation/iterable';
3
4
  import { createLogger } from '@aztec/foundation/log';
4
- import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize';
5
- import type { AztecAsyncKVStore, AztecAsyncMap } from '@aztec/kv-store';
5
+ import { Semaphore } from '@aztec/foundation/queue';
6
+ import type { AztecAsyncKVStore, AztecAsyncMap, AztecAsyncMultiMap } from '@aztec/kv-store';
6
7
  import type { EventSelector } from '@aztec/stdlib/abi';
7
8
  import type { AztecAddress } from '@aztec/stdlib/aztec-address';
8
- import { L2BlockHash } from '@aztec/stdlib/block';
9
- import { type InTx, TxHash } from '@aztec/stdlib/tx';
9
+ import type { InTx, TxHash } from '@aztec/stdlib/tx';
10
10
 
11
+ import type { StagedStore } from '../../job_coordinator/job_coordinator.js';
11
12
  import type { PackedPrivateEvent } from '../../pxe.js';
13
+ import { StoredPrivateEvent } from './stored_private_event.js';
12
14
 
13
15
  export type PrivateEventStoreFilter = {
14
16
  contractAddress: AztecAddress;
@@ -18,55 +20,53 @@ export type PrivateEventStoreFilter = {
18
20
  txHash?: TxHash;
19
21
  };
20
22
 
21
- type PrivateEventEntry = {
22
- randomness: Fr; // Note that this value is currently not being returned on queries and is therefore temporarily unused
23
- msgContent: Buffer;
24
- eventCommitmentIndex: number;
25
- l2BlockNumber: number;
26
- l2BlockHash: Buffer;
27
- txHash: Buffer;
28
- /** The lookup key for #eventsByContractScopeSelector, used for cleanup during rollback */
29
- lookupKey: string;
30
- };
31
-
32
23
  type PrivateEventMetadata = InTx & {
33
24
  contractAddress: AztecAddress;
34
25
  scope: AztecAddress;
26
+ /** The index of the tx within the block */
27
+ txIndexInBlock: number;
28
+ /** The index of the event within the tx (based on nullifier position) */
29
+ eventIndexInTx: number;
35
30
  };
36
31
 
37
32
  /**
38
33
  * Stores decrypted private event logs.
39
34
  */
40
- export class PrivateEventStore {
35
+ export class PrivateEventStore implements StagedStore {
36
+ readonly storeName: string = 'private_event';
37
+
41
38
  #store: AztecAsyncKVStore;
42
- /** Map storing the actual private event log entries, keyed by eventCommitmentIndex */
43
- #eventLogs: AztecAsyncMap<number, PrivateEventEntry>;
44
- /** Map from contractAddress_scope_eventSelector to eventCommitmentIndex[] for efficient lookup */
45
- #eventsByContractScopeSelector: AztecAsyncMap<string, number[]>;
46
- /** Map from block number to eventCommitmentIndex[] for rollback support */
47
- #eventsByBlockNumber: AztecAsyncMap<number, number[]>;
48
- /** Map from eventCommitmentIndex to boolean indicating if log has been seen. */
49
- #seenLogs: AztecAsyncMap<number, boolean>;
39
+ /** Actual private event log entries, keyed by siloedEventCommitment */
40
+ #events: AztecAsyncMap<string, Buffer>;
41
+ /** Multi-map from contractAddress_eventSelector to siloedEventCommitment for efficient lookup */
42
+ #eventsByContractAndEventSelector: AztecAsyncMultiMap<string, string>;
43
+ /** Multi-map from block number to siloedEventCommitment for rollback support */
44
+ #eventsByBlockNumber: AztecAsyncMultiMap<number, string>;
45
+
46
+ /** jobId => eventId (event siloed nullifier) => StoredPrivateEvent */
47
+ #eventsForJob: Map<string, Map<string, StoredPrivateEvent>>;
48
+
49
+ /** Per-job locks to prevent concurrent writes from affecting each other. */
50
+ #jobLocks: Map<string, Semaphore>;
50
51
 
51
52
  logger = createLogger('private_event_store');
52
53
 
53
54
  constructor(store: AztecAsyncKVStore) {
54
55
  this.#store = store;
55
- this.#eventLogs = this.#store.openMap('private_event_logs');
56
- this.#eventsByContractScopeSelector = this.#store.openMap('events_by_contract_scope_selector');
57
- this.#seenLogs = this.#store.openMap('seen_logs');
58
- this.#eventsByBlockNumber = this.#store.openMap('events_by_block_number');
59
- }
56
+ this.#events = this.#store.openMap('private_event_logs');
57
+ this.#eventsByContractAndEventSelector = this.#store.openMultiMap('events_by_contract_selector');
58
+ this.#eventsByBlockNumber = this.#store.openMultiMap('events_by_block_number');
60
59
 
61
- #keyFor(contractAddress: AztecAddress, scope: AztecAddress, eventSelector: EventSelector): string {
62
- return `${contractAddress.toString()}_${scope.toString()}_${eventSelector.toString()}`;
60
+ this.#eventsForJob = new Map();
61
+ this.#jobLocks = new Map();
63
62
  }
64
63
 
65
64
  /**
66
65
  * Store a private event log.
67
66
  * @param eventSelector - The event selector of the event.
67
+ * @param randomness - The randomness used for the event commitment.
68
68
  * @param msgContent - The content of the event.
69
- * @param eventCommitmentIndex - The index of the event commitment in the nullifier tree.
69
+ * @param siloedEventCommitment - The siloed event commitment (used as unique identifier).
70
70
  * @param metadata
71
71
  * contractAddress - The address of the contract that emitted the event.
72
72
  * scope - The address to which the event is scoped.
@@ -77,41 +77,46 @@ export class PrivateEventStore {
77
77
  eventSelector: EventSelector,
78
78
  randomness: Fr,
79
79
  msgContent: Fr[],
80
- eventCommitmentIndex: number,
80
+ siloedEventCommitment: Fr,
81
81
  metadata: PrivateEventMetadata,
82
- ): Promise<void> {
83
- const { contractAddress, scope, txHash, l2BlockNumber, l2BlockHash } = metadata;
84
-
85
- return this.#store.transactionAsync(async () => {
86
- const key = this.#keyFor(contractAddress, scope, eventSelector);
87
-
88
- // Check if this exact log has already been stored using eventCommitmentIndex as unique identifier
89
- const hasBeenSeen = await this.#seenLogs.getAsync(eventCommitmentIndex);
90
- if (hasBeenSeen) {
91
- this.logger.verbose('Ignoring duplicate event log', { txHash: txHash.toString(), eventCommitmentIndex });
92
- return;
93
- }
94
-
95
- this.logger.verbose('storing private event log', { contractAddress, scope, msgContent, l2BlockNumber });
82
+ jobId: string,
83
+ ) {
84
+ return this.#withJobLock(jobId, async () => {
85
+ const { contractAddress, scope, txHash, l2BlockNumber, l2BlockHash, txIndexInBlock, eventIndexInTx } = metadata;
86
+ const eventId = siloedEventCommitment.toString();
96
87
 
97
- await this.#eventLogs.set(eventCommitmentIndex, {
98
- randomness,
99
- msgContent: serializeToBuffer(msgContent),
88
+ this.logger.verbose('storing private event log (job stage)', {
89
+ eventId,
90
+ contractAddress,
91
+ scope,
92
+ msgContent,
100
93
  l2BlockNumber,
101
- l2BlockHash: l2BlockHash.toBuffer(),
102
- eventCommitmentIndex,
103
- txHash: txHash.toBuffer(),
104
- lookupKey: key,
105
94
  });
106
95
 
107
- const existingIndices = (await this.#eventsByContractScopeSelector.getAsync(key)) || [];
108
- await this.#eventsByContractScopeSelector.set(key, [...existingIndices, eventCommitmentIndex]);
96
+ const existing = await this.#readEvent(eventId, jobId);
109
97
 
110
- const existingBlockIndices = (await this.#eventsByBlockNumber.getAsync(l2BlockNumber)) || [];
111
- await this.#eventsByBlockNumber.set(l2BlockNumber, [...existingBlockIndices, eventCommitmentIndex]);
112
-
113
- // Mark this log as seen using eventCommitmentIndex
114
- await this.#seenLogs.set(eventCommitmentIndex, true);
98
+ if (existing) {
99
+ // If we already stored this event, we still want to make sure to track it for the given scope
100
+ existing.addScope(scope.toString());
101
+ this.#writeEvent(eventId, existing, jobId);
102
+ } else {
103
+ this.#writeEvent(
104
+ eventId,
105
+ new StoredPrivateEvent(
106
+ randomness,
107
+ msgContent,
108
+ l2BlockNumber,
109
+ l2BlockHash,
110
+ txHash,
111
+ txIndexInBlock,
112
+ eventIndexInTx,
113
+ contractAddress,
114
+ eventSelector,
115
+ new Set([scope.toString()]),
116
+ ),
117
+ jobId,
118
+ );
119
+ }
115
120
  });
116
121
  }
117
122
 
@@ -123,94 +128,234 @@ export class PrivateEventStore {
123
128
  * fromBlock: The block number to search from (inclusive).
124
129
  * toBlock: The block number to search upto (exclusive).
125
130
  * scope: - The addresses that decrypted the logs.
126
- * @returns - The event log contents, augmented with metadata about
127
- * the transaction and block it the event was included in .
131
+ * @returns - The event log contents, augmented with metadata about the transaction and block in which the event was
132
+ * included.
128
133
  */
129
134
  public async getPrivateEvents(
130
135
  eventSelector: EventSelector,
131
136
  filter: PrivateEventStoreFilter,
132
137
  ): Promise<PackedPrivateEvent[]> {
133
- const events: Array<{ eventCommitmentIndex: number; event: PackedPrivateEvent }> = [];
138
+ const events: Array<{
139
+ l2BlockNumber: number;
140
+ txIndexInBlock: number;
141
+ eventIndexInTx: number;
142
+ event: PackedPrivateEvent;
143
+ }> = [];
134
144
 
135
- for (const scope of filter.scopes) {
136
- const key = this.#keyFor(filter.contractAddress, scope, eventSelector);
137
- const eventCommitmentIndices = (await this.#eventsByContractScopeSelector.getAsync(key)) || [];
145
+ const key = this.#keyFor(filter.contractAddress, eventSelector);
146
+ const targetScopes = new Set(filter.scopes.map(s => s.toString()));
138
147
 
139
- for (const eventCommitmentIndex of eventCommitmentIndices) {
140
- const entry = await this.#eventLogs.getAsync(eventCommitmentIndex);
141
- if (!entry || entry.l2BlockNumber < filter.fromBlock || entry.l2BlockNumber >= filter.toBlock) {
142
- continue;
143
- }
148
+ const eventIds: string[] = await toArray(this.#eventsByContractAndEventSelector.getValuesAsync(key));
144
149
 
145
- // Convert buffer back to Fr array
146
- const reader = BufferReader.asReader(entry.msgContent);
147
- const numFields = entry.msgContent.length / Fr.SIZE_IN_BYTES;
148
- const msgContent = reader.readArray(numFields, Fr);
149
- const txHash = TxHash.fromBuffer(entry.txHash);
150
- const l2BlockHash = L2BlockHash.fromBuffer(entry.l2BlockHash);
150
+ for (const eventId of eventIds) {
151
+ const eventBuffer = await this.#events.getAsync(eventId);
151
152
 
152
- if (filter.txHash && !txHash.equals(filter.txHash)) {
153
- continue;
154
- }
153
+ // Defensive, if it happens, there's a problem with how we're handling #eventsByContractAndEventSelector
154
+ if (!eventBuffer) {
155
+ this.logger.verbose(
156
+ `EventId ${eventId} does not exist in main index but it is referenced from contract event selector index`,
157
+ );
158
+ continue;
159
+ }
155
160
 
156
- events.push({
157
- eventCommitmentIndex: entry.eventCommitmentIndex,
158
- event: {
159
- packedEvent: msgContent,
160
- l2BlockNumber: BlockNumber(entry.l2BlockNumber),
161
- txHash,
162
- l2BlockHash,
163
- eventSelector,
164
- },
165
- });
161
+ const storedPrivateEvent = StoredPrivateEvent.fromBuffer(eventBuffer);
162
+
163
+ // Filter by block range
164
+ if (storedPrivateEvent.l2BlockNumber < filter.fromBlock || storedPrivateEvent.l2BlockNumber >= filter.toBlock) {
165
+ continue;
166
+ }
167
+
168
+ // Filter by scopes
169
+ if (storedPrivateEvent.scopes.intersection(targetScopes).size === 0) {
170
+ continue;
171
+ }
172
+
173
+ // Filter by txHash
174
+ if (filter.txHash && !storedPrivateEvent.txHash.equals(filter.txHash)) {
175
+ continue;
166
176
  }
177
+
178
+ events.push({
179
+ l2BlockNumber: storedPrivateEvent.l2BlockNumber,
180
+ txIndexInBlock: storedPrivateEvent.txIndexInBlock,
181
+ eventIndexInTx: storedPrivateEvent.eventIndexInTx,
182
+ event: {
183
+ packedEvent: storedPrivateEvent.msgContent,
184
+ l2BlockNumber: BlockNumber(storedPrivateEvent.l2BlockNumber),
185
+ txHash: storedPrivateEvent.txHash,
186
+ l2BlockHash: storedPrivateEvent.l2BlockHash,
187
+ eventSelector,
188
+ },
189
+ });
167
190
  }
168
191
 
169
- // Sort by eventCommitmentIndex only
170
- events.sort((a, b) => a.eventCommitmentIndex - b.eventCommitmentIndex);
192
+ // Sort by block number, then by tx index within block, then by event index within tx
193
+ events.sort((a, b) => {
194
+ if (a.l2BlockNumber !== b.l2BlockNumber) {
195
+ return a.l2BlockNumber - b.l2BlockNumber;
196
+ }
197
+ if (a.txIndexInBlock !== b.txIndexInBlock) {
198
+ return a.txIndexInBlock - b.txIndexInBlock;
199
+ }
200
+ return a.eventIndexInTx - b.eventIndexInTx;
201
+ });
202
+
171
203
  return events.map(ev => ev.event);
172
204
  }
173
205
 
174
206
  /**
175
207
  * Rolls back private events that were stored after a given `blockNumber` and up to `synchedBlockNumber` (the block
176
208
  * number up to which PXE managed to sync before the reorg happened).
209
+ *
210
+ * We don't need staged writes for a rollback since it's handled in the context of a blockchain rewind.
211
+ *
212
+ * Rollbacks are handled by the BlockSynchronizer, which runs a DB transaction across stores when it detects a
213
+ * re-org, including setting the new anchor block after rolling back.
214
+ *
215
+ * So if anything fails in the process of rolling back any store, all DB changes occurring during rollbacks will be
216
+ * lost and the anchor block will not be updated; which means this code will eventually need to run again
217
+ * (i.e.: PXE will detect it's basing it work on an invalid block hash, then which re-triggers rewind).
218
+ *
219
+ * For further details, refer to `BlockSynchronizer#handleBlockStreamEvent`.
220
+ *
221
+ * IMPORTANT: This method must be called within a transaction to ensure atomicity.
177
222
  */
178
- public async rollbackEventsAfterBlock(blockNumber: number, synchedBlockNumber: number): Promise<void> {
179
- await this.#store.transactionAsync(async () => {
180
- let removedCount = 0;
181
-
182
- for (let block = blockNumber + 1; block <= synchedBlockNumber; block++) {
183
- const indices = await this.#eventsByBlockNumber.getAsync(block);
184
- if (indices) {
185
- await this.#eventsByBlockNumber.delete(block);
186
-
187
- for (const eventCommitmentIndex of indices) {
188
- const entry = await this.#eventLogs.getAsync(eventCommitmentIndex);
189
- if (!entry) {
190
- throw new Error(`Event log not found for eventCommitmentIndex ${eventCommitmentIndex}`);
191
- }
192
-
193
- await this.#eventLogs.delete(eventCommitmentIndex);
194
- await this.#seenLogs.delete(eventCommitmentIndex);
195
-
196
- // Update #eventsByContractScopeSelector using the stored lookupKey
197
- const existingIndices = await this.#eventsByContractScopeSelector.getAsync(entry.lookupKey);
198
- if (!existingIndices || existingIndices.length === 0) {
199
- throw new Error(`No indices found in #eventsByContractScopeSelector for key ${entry.lookupKey}`);
200
- }
201
- const filteredIndices = existingIndices.filter(idx => idx !== eventCommitmentIndex);
202
- if (filteredIndices.length === 0) {
203
- await this.#eventsByContractScopeSelector.delete(entry.lookupKey);
204
- } else {
205
- await this.#eventsByContractScopeSelector.set(entry.lookupKey, filteredIndices);
206
- }
207
-
208
- removedCount++;
223
+ public async rollback(blockNumber: number, synchedBlockNumber: number): Promise<void> {
224
+ let removedCount = 0;
225
+
226
+ for (let block = blockNumber + 1; block <= synchedBlockNumber; block++) {
227
+ const eventIds: string[] = await toArray(this.#eventsByBlockNumber.getValuesAsync(block));
228
+
229
+ if (eventIds.length > 0) {
230
+ await this.#eventsByBlockNumber.delete(block);
231
+
232
+ for (const eventId of eventIds) {
233
+ const buffer = await this.#events.getAsync(eventId);
234
+ if (!buffer) {
235
+ throw new Error(`Event not found for eventId ${eventId}`);
209
236
  }
237
+
238
+ const entry = StoredPrivateEvent.fromBuffer(buffer);
239
+ await this.#events.delete(eventId);
240
+ await this.#eventsByContractAndEventSelector.deleteValue(
241
+ this.#keyFor(entry.contractAddress, entry.eventSelector),
242
+ eventId,
243
+ );
244
+
245
+ removedCount++;
210
246
  }
211
247
  }
248
+ }
249
+
250
+ this.logger.verbose(`Rolled back ${removedCount} private events after block ${blockNumber}`);
251
+ }
252
+
253
+ /**
254
+ * Commits in memory job data to persistent storage.
255
+ *
256
+ * Called by JobCoordinator when a job completes successfully.
257
+ *
258
+ * Note: JobCoordinator wraps all commits in a single transaction, so we don't need our own transactionAsync here
259
+ * (and using one would throw on IndexedDB as it does not support nested txs).
260
+ *
261
+ * @param jobId - The jobId identifying which staged data to commit
262
+ */
263
+ commit(jobId: string): Promise<void> {
264
+ return this.#withJobLock(jobId, async () => {
265
+ for (const [eventId, entry] of this.#getEventsForJob(jobId).entries()) {
266
+ const lookupKey = this.#keyFor(entry.contractAddress, entry.eventSelector);
267
+ this.logger.verbose('storing private event log', { eventId, lookupKey });
268
+
269
+ await Promise.all([
270
+ this.#events.set(eventId, entry.toBuffer()),
271
+ this.#eventsByContractAndEventSelector.set(lookupKey, eventId),
272
+ this.#eventsByBlockNumber.set(entry.l2BlockNumber, eventId),
273
+ ]);
274
+ }
212
275
 
213
- this.logger.verbose(`Rolled back ${removedCount} private events after block ${blockNumber}`);
276
+ this.#clearJobData(jobId);
214
277
  });
215
278
  }
279
+
280
+ /**
281
+ * Discards in memory job data without persisting it.
282
+ */
283
+ discardStaged(jobId: string): Promise<void> {
284
+ return this.#withJobLock(jobId, () => Promise.resolve(this.#clearJobData(jobId)));
285
+ }
286
+
287
+ /**
288
+ * Reads an event from in-memory job data first, falling back to persistent storage if not found.
289
+ *
290
+ * Returns undefined if the event does not exist in the store overall.
291
+ */
292
+ async #readEvent(eventId: string, jobId: string): Promise<StoredPrivateEvent | undefined> {
293
+ const eventForJob = this.#getEventsForJob(jobId).get(eventId);
294
+ if (eventForJob) {
295
+ return eventForJob;
296
+ }
297
+
298
+ const buffer = await this.#events.getAsync(eventId);
299
+ return buffer ? StoredPrivateEvent.fromBuffer(buffer) : undefined;
300
+ }
301
+
302
+ /**
303
+ * Writes an event to in-memory job data.
304
+ *
305
+ * Writes are only allowed in a job context. Events modified during a job will only be persisted when `commit` is
306
+ * called.
307
+ */
308
+ #writeEvent(eventId: string, entry: StoredPrivateEvent, jobId: string) {
309
+ this.#getEventsForJob(jobId).set(eventId, entry);
310
+ }
311
+
312
+ /**
313
+ * Get in-memory data only visible to @param jobId
314
+ */
315
+ #getEventsForJob(jobId: string): Map<string, StoredPrivateEvent> {
316
+ let eventsForJob = this.#eventsForJob.get(jobId);
317
+ if (eventsForJob === undefined) {
318
+ eventsForJob = new Map();
319
+ this.#eventsForJob.set(jobId, eventsForJob);
320
+ }
321
+ return eventsForJob;
322
+ }
323
+
324
+ /**
325
+ * Clear data structures supporting a specific job.
326
+ */
327
+ #clearJobData(jobId: string) {
328
+ this.#eventsForJob.delete(jobId);
329
+ this.#jobLocks.delete(jobId);
330
+ }
331
+
332
+ /**
333
+ * Ensures a function can only run once it acquires a unique per-job lock, and handles proper lock release after it
334
+ * runs.
335
+ *
336
+ * This primitive allows concurrent writes on this store without risking data corruption due to unsound write
337
+ * interleaving.
338
+ */
339
+ async #withJobLock<T>(jobId: string, fn: () => Promise<T>): Promise<T> {
340
+ let lock = this.#jobLocks.get(jobId);
341
+ if (!lock) {
342
+ lock = new Semaphore(1);
343
+ this.#jobLocks.set(jobId, lock);
344
+ }
345
+ await lock.acquire();
346
+ try {
347
+ return await fn();
348
+ } finally {
349
+ lock.release();
350
+ }
351
+ }
352
+
353
+ /**
354
+ * Returns a string key based on @param contractAddress and @param eventSelector.
355
+ *
356
+ * The returned key is meant to be used when interacting with index #eventsByContractAndEventSelector.
357
+ */
358
+ #keyFor(contractAddress: AztecAddress, eventSelector: EventSelector): string {
359
+ return `${contractAddress.toString()}_${eventSelector.toString()}`;
360
+ }
216
361
  }
@@ -0,0 +1,73 @@
1
+ import { Fr } from '@aztec/foundation/curves/bn254';
2
+ import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize';
3
+ import { EventSelector } from '@aztec/stdlib/abi';
4
+ import { AztecAddress } from '@aztec/stdlib/aztec-address';
5
+ import { BlockHash } from '@aztec/stdlib/block';
6
+ import { TxHash } from '@aztec/stdlib/tx';
7
+
8
+ /** Serializable private event entry with scope tracking. */
9
+ export class StoredPrivateEvent {
10
+ constructor(
11
+ readonly randomness: Fr,
12
+ readonly msgContent: Fr[],
13
+ readonly l2BlockNumber: number,
14
+ readonly l2BlockHash: BlockHash,
15
+ readonly txHash: TxHash,
16
+ readonly txIndexInBlock: number,
17
+ readonly eventIndexInTx: number,
18
+ readonly contractAddress: AztecAddress,
19
+ readonly eventSelector: EventSelector,
20
+ readonly scopes: Set<string>,
21
+ ) {}
22
+
23
+ addScope(scope: string) {
24
+ this.scopes.add(scope);
25
+ }
26
+
27
+ toBuffer(): Buffer {
28
+ const scopesArray = [...this.scopes];
29
+ return serializeToBuffer(
30
+ this.randomness,
31
+ this.msgContent.length,
32
+ ...this.msgContent,
33
+ this.l2BlockNumber,
34
+ this.l2BlockHash,
35
+ this.txHash,
36
+ this.txIndexInBlock,
37
+ this.eventIndexInTx,
38
+ this.contractAddress,
39
+ this.eventSelector.toBuffer(),
40
+ scopesArray.length,
41
+ ...scopesArray,
42
+ );
43
+ }
44
+
45
+ static fromBuffer(buffer: Buffer): StoredPrivateEvent {
46
+ const reader = BufferReader.asReader(buffer);
47
+
48
+ const randomness = Fr.fromBuffer(reader);
49
+ const msgContentLength = reader.readNumber();
50
+ const msgContent = reader.readArray(msgContentLength, Fr);
51
+ const l2BlockNumber = reader.readNumber();
52
+ const l2BlockHash = BlockHash.fromBuffer(reader);
53
+ const txHash = TxHash.fromBuffer(reader);
54
+ const txIndexInBlock = reader.readNumber();
55
+ const eventIndexInTx = reader.readNumber();
56
+ const contractAddress = AztecAddress.fromBuffer(reader);
57
+ const eventSelector = EventSelector.fromBuffer(reader);
58
+ const scopes = reader.readVector({ fromBuffer: (r: BufferReader) => r.readString() });
59
+
60
+ return new StoredPrivateEvent(
61
+ randomness,
62
+ msgContent,
63
+ l2BlockNumber,
64
+ l2BlockHash,
65
+ txHash,
66
+ txIndexInBlock,
67
+ eventIndexInTx,
68
+ contractAddress,
69
+ eventSelector,
70
+ new Set(scopes),
71
+ );
72
+ }
73
+ }