@aztec/pxe 0.0.1-commit.c7c42ec → 0.0.1-commit.f295ac2

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 (180) hide show
  1. package/dest/block_synchronizer/block_synchronizer.d.ts +6 -2
  2. package/dest/block_synchronizer/block_synchronizer.d.ts.map +1 -1
  3. package/dest/block_synchronizer/block_synchronizer.js +21 -13
  4. package/dest/config/package_info.js +1 -1
  5. package/dest/contract_function_simulator/benchmarked_node.d.ts +9 -0
  6. package/dest/contract_function_simulator/benchmarked_node.d.ts.map +1 -0
  7. package/dest/contract_function_simulator/benchmarked_node.js +77 -0
  8. package/dest/contract_function_simulator/contract_function_simulator.d.ts +8 -9
  9. package/dest/contract_function_simulator/contract_function_simulator.d.ts.map +1 -1
  10. package/dest/contract_function_simulator/contract_function_simulator.js +33 -16
  11. package/dest/contract_function_simulator/execution_note_cache.d.ts +18 -9
  12. package/dest/contract_function_simulator/execution_note_cache.d.ts.map +1 -1
  13. package/dest/contract_function_simulator/execution_note_cache.js +45 -28
  14. package/dest/contract_function_simulator/noir-structs/event_validation_request.d.ts +3 -2
  15. package/dest/contract_function_simulator/noir-structs/event_validation_request.d.ts.map +1 -1
  16. package/dest/contract_function_simulator/noir-structs/event_validation_request.js +5 -2
  17. package/dest/contract_function_simulator/noir-structs/utility_context.d.ts +4 -10
  18. package/dest/contract_function_simulator/noir-structs/utility_context.d.ts.map +1 -1
  19. package/dest/contract_function_simulator/noir-structs/utility_context.js +7 -18
  20. package/dest/contract_function_simulator/oracle/interfaces.d.ts +9 -8
  21. package/dest/contract_function_simulator/oracle/interfaces.d.ts.map +1 -1
  22. package/dest/contract_function_simulator/oracle/message_load_oracle_inputs.d.ts +3 -1
  23. package/dest/contract_function_simulator/oracle/message_load_oracle_inputs.d.ts.map +1 -1
  24. package/dest/contract_function_simulator/oracle/note_packing_utils.d.ts +4 -4
  25. package/dest/contract_function_simulator/oracle/note_packing_utils.d.ts.map +1 -1
  26. package/dest/contract_function_simulator/oracle/note_packing_utils.js +5 -5
  27. package/dest/contract_function_simulator/oracle/oracle.d.ts +6 -6
  28. package/dest/contract_function_simulator/oracle/oracle.d.ts.map +1 -1
  29. package/dest/contract_function_simulator/oracle/oracle.js +20 -19
  30. package/dest/contract_function_simulator/oracle/private_execution.d.ts +5 -9
  31. package/dest/contract_function_simulator/oracle/private_execution.d.ts.map +1 -1
  32. package/dest/contract_function_simulator/oracle/private_execution.js +11 -10
  33. package/dest/contract_function_simulator/oracle/private_execution_oracle.d.ts +8 -18
  34. package/dest/contract_function_simulator/oracle/private_execution_oracle.d.ts.map +1 -1
  35. package/dest/contract_function_simulator/oracle/private_execution_oracle.js +10 -35
  36. package/dest/contract_function_simulator/oracle/utility_execution_oracle.d.ts +14 -12
  37. package/dest/contract_function_simulator/oracle/utility_execution_oracle.d.ts.map +1 -1
  38. package/dest/contract_function_simulator/oracle/utility_execution_oracle.js +54 -52
  39. package/dest/entrypoints/client/bundle/utils.d.ts +1 -1
  40. package/dest/entrypoints/client/bundle/utils.d.ts.map +1 -1
  41. package/dest/entrypoints/client/bundle/utils.js +10 -1
  42. package/dest/entrypoints/client/lazy/utils.d.ts +1 -1
  43. package/dest/entrypoints/client/lazy/utils.d.ts.map +1 -1
  44. package/dest/entrypoints/client/lazy/utils.js +10 -1
  45. package/dest/entrypoints/pxe_creation_options.d.ts +3 -2
  46. package/dest/entrypoints/pxe_creation_options.d.ts.map +1 -1
  47. package/dest/entrypoints/server/index.d.ts +2 -1
  48. package/dest/entrypoints/server/index.d.ts.map +1 -1
  49. package/dest/entrypoints/server/index.js +1 -0
  50. package/dest/entrypoints/server/utils.d.ts +1 -1
  51. package/dest/entrypoints/server/utils.d.ts.map +1 -1
  52. package/dest/entrypoints/server/utils.js +11 -7
  53. package/dest/events/event_service.d.ts +4 -3
  54. package/dest/events/event_service.d.ts.map +1 -1
  55. package/dest/events/event_service.js +17 -19
  56. package/dest/events/private_event_filter_validator.d.ts +5 -5
  57. package/dest/events/private_event_filter_validator.d.ts.map +1 -1
  58. package/dest/events/private_event_filter_validator.js +5 -6
  59. package/dest/job_coordinator/job_coordinator.d.ts +74 -0
  60. package/dest/job_coordinator/job_coordinator.d.ts.map +1 -0
  61. package/dest/job_coordinator/job_coordinator.js +93 -0
  62. package/dest/logs/log_service.d.ts +3 -2
  63. package/dest/logs/log_service.d.ts.map +1 -1
  64. package/dest/logs/log_service.js +5 -3
  65. package/dest/notes/note_service.d.ts +3 -3
  66. package/dest/notes/note_service.d.ts.map +1 -1
  67. package/dest/notes/note_service.js +23 -28
  68. package/dest/oracle_version.d.ts +2 -2
  69. package/dest/oracle_version.js +2 -2
  70. package/dest/private_kernel/hints/index.d.ts +2 -2
  71. package/dest/private_kernel/hints/index.d.ts.map +1 -1
  72. package/dest/private_kernel/hints/index.js +1 -1
  73. package/dest/private_kernel/hints/private_kernel_reset_private_inputs_builder.d.ts +28 -0
  74. package/dest/private_kernel/hints/private_kernel_reset_private_inputs_builder.d.ts.map +1 -0
  75. package/dest/private_kernel/hints/{build_private_kernel_reset_private_inputs.js → private_kernel_reset_private_inputs_builder.js} +13 -7
  76. package/dest/private_kernel/private_kernel_execution_prover.d.ts +1 -1
  77. package/dest/private_kernel/private_kernel_execution_prover.d.ts.map +1 -1
  78. package/dest/private_kernel/private_kernel_execution_prover.js +4 -5
  79. package/dest/private_kernel/private_kernel_oracle.d.ts +24 -28
  80. package/dest/private_kernel/private_kernel_oracle.d.ts.map +1 -1
  81. package/dest/private_kernel/private_kernel_oracle.js +90 -2
  82. package/dest/pxe.d.ts +8 -36
  83. package/dest/pxe.d.ts.map +1 -1
  84. package/dest/pxe.js +65 -87
  85. package/dest/storage/capsule_store/capsule_store.d.ts +24 -9
  86. package/dest/storage/capsule_store/capsule_store.d.ts.map +1 -1
  87. package/dest/storage/capsule_store/capsule_store.js +132 -23
  88. package/dest/storage/contract_store/contract_store.d.ts +2 -1
  89. package/dest/storage/contract_store/contract_store.d.ts.map +1 -1
  90. package/dest/storage/contract_store/contract_store.js +12 -0
  91. package/dest/storage/note_store/note_store.d.ts +6 -5
  92. package/dest/storage/note_store/note_store.d.ts.map +1 -1
  93. package/dest/storage/note_store/note_store.js +89 -94
  94. package/dest/storage/private_event_store/private_event_store.d.ts +33 -6
  95. package/dest/storage/private_event_store/private_event_store.d.ts.map +1 -1
  96. package/dest/storage/private_event_store/private_event_store.js +139 -32
  97. package/dest/storage/tagging_store/recipient_tagging_store.d.ts +15 -8
  98. package/dest/storage/tagging_store/recipient_tagging_store.d.ts.map +1 -1
  99. package/dest/storage/tagging_store/recipient_tagging_store.js +69 -12
  100. package/dest/storage/tagging_store/sender_tagging_store.d.ts +19 -9
  101. package/dest/storage/tagging_store/sender_tagging_store.d.ts.map +1 -1
  102. package/dest/storage/tagging_store/sender_tagging_store.js +110 -28
  103. package/dest/tagging/constants.d.ts +2 -0
  104. package/dest/tagging/constants.d.ts.map +1 -0
  105. package/dest/tagging/constants.js +10 -0
  106. package/dest/tagging/index.d.ts +2 -2
  107. package/dest/tagging/index.d.ts.map +1 -1
  108. package/dest/tagging/index.js +1 -10
  109. package/dest/tagging/recipient_sync/load_private_logs_for_sender_recipient_pair.d.ts +2 -2
  110. package/dest/tagging/recipient_sync/load_private_logs_for_sender_recipient_pair.d.ts.map +1 -1
  111. package/dest/tagging/recipient_sync/load_private_logs_for_sender_recipient_pair.js +7 -7
  112. package/dest/tagging/sender_sync/sync_sender_tagging_indexes.d.ts +2 -2
  113. package/dest/tagging/sender_sync/sync_sender_tagging_indexes.d.ts.map +1 -1
  114. package/dest/tagging/sender_sync/sync_sender_tagging_indexes.js +8 -8
  115. package/dest/tagging/sender_sync/utils/get_status_change_of_pending.d.ts +1 -1
  116. package/dest/tagging/sender_sync/utils/get_status_change_of_pending.d.ts.map +1 -1
  117. package/dest/tagging/sender_sync/utils/get_status_change_of_pending.js +2 -2
  118. package/dest/tagging/sender_sync/utils/load_and_store_new_tagging_indexes.d.ts +4 -2
  119. package/dest/tagging/sender_sync/utils/load_and_store_new_tagging_indexes.d.ts.map +1 -1
  120. package/dest/tagging/sender_sync/utils/load_and_store_new_tagging_indexes.js +4 -2
  121. package/dest/tree_membership/tree_membership_service.d.ts +9 -11
  122. package/dest/tree_membership/tree_membership_service.d.ts.map +1 -1
  123. package/dest/tree_membership/tree_membership_service.js +25 -34
  124. package/package.json +18 -18
  125. package/src/block_synchronizer/block_synchronizer.ts +30 -12
  126. package/src/config/package_info.ts +1 -1
  127. package/src/contract_function_simulator/benchmarked_node.ts +103 -0
  128. package/src/contract_function_simulator/contract_function_simulator.ts +42 -16
  129. package/src/contract_function_simulator/execution_note_cache.ts +44 -25
  130. package/src/contract_function_simulator/noir-structs/event_validation_request.ts +4 -0
  131. package/src/contract_function_simulator/noir-structs/utility_context.ts +6 -25
  132. package/src/contract_function_simulator/oracle/interfaces.ts +8 -7
  133. package/src/contract_function_simulator/oracle/note_packing_utils.ts +6 -6
  134. package/src/contract_function_simulator/oracle/oracle.ts +24 -22
  135. package/src/contract_function_simulator/oracle/private_execution.ts +10 -19
  136. package/src/contract_function_simulator/oracle/private_execution_oracle.ts +13 -38
  137. package/src/contract_function_simulator/oracle/utility_execution_oracle.ts +56 -50
  138. package/src/entrypoints/client/bundle/utils.ts +7 -1
  139. package/src/entrypoints/client/lazy/utils.ts +7 -2
  140. package/src/entrypoints/pxe_creation_options.ts +2 -1
  141. package/src/entrypoints/server/index.ts +1 -0
  142. package/src/entrypoints/server/utils.ts +11 -15
  143. package/src/events/event_service.ts +17 -21
  144. package/src/events/private_event_filter_validator.ts +3 -5
  145. package/src/job_coordinator/job_coordinator.ts +149 -0
  146. package/src/logs/log_service.ts +3 -1
  147. package/src/notes/note_service.ts +23 -29
  148. package/src/oracle_version.ts +2 -2
  149. package/src/private_kernel/hints/index.ts +1 -1
  150. package/src/private_kernel/hints/{build_private_kernel_reset_private_inputs.ts → private_kernel_reset_private_inputs_builder.ts} +33 -22
  151. package/src/private_kernel/private_kernel_execution_prover.ts +3 -5
  152. package/src/private_kernel/private_kernel_oracle.ts +116 -36
  153. package/src/pxe.ts +98 -116
  154. package/src/storage/capsule_store/capsule_store.ts +159 -23
  155. package/src/storage/contract_store/contract_store.ts +20 -0
  156. package/src/storage/note_store/note_store.ts +98 -95
  157. package/src/storage/private_event_store/private_event_store.ts +199 -37
  158. package/src/storage/tagging_store/recipient_tagging_store.ts +89 -13
  159. package/src/storage/tagging_store/sender_tagging_store.ts +129 -28
  160. package/src/tagging/constants.ts +10 -0
  161. package/src/tagging/index.ts +1 -11
  162. package/src/tagging/recipient_sync/load_private_logs_for_sender_recipient_pair.ts +10 -6
  163. package/src/tagging/sender_sync/sync_sender_tagging_indexes.ts +8 -7
  164. package/src/tagging/sender_sync/utils/get_status_change_of_pending.ts +6 -2
  165. package/src/tagging/sender_sync/utils/load_and_store_new_tagging_indexes.ts +4 -1
  166. package/src/tree_membership/tree_membership_service.ts +27 -42
  167. package/dest/contract_function_simulator/proxied_node.d.ts +0 -9
  168. package/dest/contract_function_simulator/proxied_node.d.ts.map +0 -1
  169. package/dest/contract_function_simulator/proxied_node.js +0 -27
  170. package/dest/private_kernel/hints/build_private_kernel_reset_private_inputs.d.ts +0 -28
  171. package/dest/private_kernel/hints/build_private_kernel_reset_private_inputs.d.ts.map +0 -1
  172. package/dest/private_kernel/private_kernel_oracle_impl.d.ts +0 -46
  173. package/dest/private_kernel/private_kernel_oracle_impl.d.ts.map +0 -1
  174. package/dest/private_kernel/private_kernel_oracle_impl.js +0 -86
  175. package/dest/public_storage/public_storage_service.d.ts +0 -24
  176. package/dest/public_storage/public_storage_service.d.ts.map +0 -1
  177. package/dest/public_storage/public_storage_service.js +0 -26
  178. package/src/contract_function_simulator/proxied_node.ts +0 -33
  179. package/src/private_kernel/private_kernel_oracle_impl.ts +0 -133
  180. package/src/public_storage/public_storage_service.ts +0 -33
@@ -2,12 +2,13 @@ import { BlockNumber } from '@aztec/foundation/branded-types';
2
2
  import { Fr } from '@aztec/foundation/curves/bn254';
3
3
  import { createLogger } from '@aztec/foundation/log';
4
4
  import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize';
5
- import type { AztecAsyncArray, AztecAsyncKVStore, AztecAsyncMap } from '@aztec/kv-store';
5
+ import type { AztecAsyncKVStore, AztecAsyncMap, AztecAsyncMultiMap } from '@aztec/kv-store';
6
6
  import type { EventSelector } from '@aztec/stdlib/abi';
7
7
  import type { AztecAddress } from '@aztec/stdlib/aztec-address';
8
8
  import { L2BlockHash } from '@aztec/stdlib/block';
9
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';
12
13
 
13
14
  export type PrivateEventStoreFilter = {
@@ -19,48 +20,141 @@ export type PrivateEventStoreFilter = {
19
20
  };
20
21
 
21
22
  type PrivateEventEntry = {
23
+ randomness: Fr; // Note that this value is currently not being returned on queries and is therefore temporarily unused
22
24
  msgContent: Buffer;
23
- eventCommitmentIndex: number;
24
25
  l2BlockNumber: number;
25
26
  l2BlockHash: Buffer;
26
27
  txHash: Buffer;
28
+ /** The index of the tx within the block, used for ordering events */
29
+ txIndexInBlock: number;
30
+ /** The index of the event within the tx (based on nullifier position), used for ordering events */
31
+ eventIndexInTx: number;
32
+ /** The lookup key for #eventsByContractScopeSelector, used for cleanup during rollback */
33
+ lookupKey: string;
27
34
  };
28
35
 
29
36
  type PrivateEventMetadata = InTx & {
30
37
  contractAddress: AztecAddress;
31
38
  scope: AztecAddress;
39
+ /** The index of the tx within the block */
40
+ txIndexInBlock: number;
41
+ /** The index of the event within the tx (based on nullifier position) */
42
+ eventIndexInTx: number;
32
43
  };
33
44
 
34
45
  /**
35
46
  * Stores decrypted private event logs.
36
47
  */
37
- export class PrivateEventStore {
48
+ export class PrivateEventStore implements StagedStore {
49
+ readonly storeName: string = 'private_event';
50
+
38
51
  #store: AztecAsyncKVStore;
39
- /** Array storing the actual private event log entries containing the log content and block number */
40
- #eventLogs: AztecAsyncArray<PrivateEventEntry>;
41
- /** Map from contract_address_scope_eventSelector to array of indices into #eventLogs for efficient lookup */
42
- #eventLogIndex: AztecAsyncMap<string, number[]>;
43
- /** Map from eventCommitmentIndex to boolean indicating if log has been seen. */
44
- #seenLogs: AztecAsyncMap<number, boolean>;
52
+ /** Map storing the actual private event log entries, keyed by siloedEventCommitment */
53
+ #eventLogs: AztecAsyncMap<string, PrivateEventEntry>;
54
+ /** Multi-map from contractAddress_scope_eventSelector to siloedEventCommitment for efficient lookup */
55
+ #eventsByContractScopeSelector: AztecAsyncMultiMap<string, string>;
56
+ /** Multi-map from block number to siloedEventCommitment for rollback support */
57
+ #eventsByBlockNumber: AztecAsyncMultiMap<number, string>;
58
+ /** Map from siloedEventCommitment to boolean indicating if log has been seen. */
59
+ #seenLogs: AztecAsyncMap<string, boolean>;
60
+
61
+ /** jobId => eventId (event siloed nullifier) => PrivateEventEntry */
62
+ #eventLogsInJobStage: Map<string, Map<string, PrivateEventEntry>>;
45
63
 
46
64
  logger = createLogger('private_event_store');
47
65
 
48
66
  constructor(store: AztecAsyncKVStore) {
49
67
  this.#store = store;
50
- this.#eventLogs = this.#store.openArray('private_event_logs');
51
- this.#eventLogIndex = this.#store.openMap('private_event_log_index');
68
+ this.#eventLogs = this.#store.openMap('private_event_logs');
69
+ this.#eventsByContractScopeSelector = this.#store.openMultiMap('events_by_contract_scope_selector');
52
70
  this.#seenLogs = this.#store.openMap('seen_logs');
71
+ this.#eventsByBlockNumber = this.#store.openMultiMap('events_by_block_number');
72
+
73
+ this.#eventLogsInJobStage = new Map();
53
74
  }
54
75
 
55
76
  #keyFor(contractAddress: AztecAddress, scope: AztecAddress, eventSelector: EventSelector): string {
56
77
  return `${contractAddress.toString()}_${scope.toString()}_${eventSelector.toString()}`;
57
78
  }
58
79
 
80
+ async commit(jobId: string): Promise<void> {
81
+ await Promise.all(
82
+ [...this.#getEventLogsInJobStage(jobId).entries()].map(async ([eventId, eventEntry]) => {
83
+ this.logger.verbose('storing private event log (KV store)', eventEntry);
84
+
85
+ await Promise.all([
86
+ this.#eventLogs.set(eventId, eventEntry),
87
+ this.#eventsByContractScopeSelector.set(eventEntry.lookupKey, eventId),
88
+ this.#eventsByBlockNumber.set(eventEntry.l2BlockNumber, eventId),
89
+ this.#seenLogs.set(eventId, true),
90
+ ]);
91
+ }),
92
+ );
93
+
94
+ return this.discardStaged(jobId);
95
+ }
96
+
97
+ discardStaged(jobId: string): Promise<void> {
98
+ this.#eventLogsInJobStage.delete(jobId);
99
+ return Promise.resolve();
100
+ }
101
+
102
+ #getEventLogsInJobStage(jobId: string): Map<string, PrivateEventEntry> {
103
+ let jobStage = this.#eventLogsInJobStage.get(jobId);
104
+ if (jobStage === undefined) {
105
+ jobStage = new Map();
106
+ this.#eventLogsInJobStage.set(jobId, jobStage);
107
+ }
108
+ return jobStage;
109
+ }
110
+
111
+ async #isSeenLog(jobId: string, eventId: string): Promise<boolean> {
112
+ const eventLogsInJobStage = this.#getEventLogsInJobStage(jobId).get(eventId);
113
+ return !!eventLogsInJobStage || !!(await this.#seenLogs.getAsync(eventId));
114
+ }
115
+
116
+ #addEventLogToStage(jobId: string, eventId: string, eventEntry: PrivateEventEntry) {
117
+ this.#getEventLogsInJobStage(jobId).set(eventId, eventEntry);
118
+ }
119
+
120
+ async #getEventSiloedNullifiers(
121
+ contractAddress: AztecAddress,
122
+ scope: AztecAddress,
123
+ eventSelector: EventSelector,
124
+ jobId?: string,
125
+ ): Promise<string[]> {
126
+ const key = this.#keyFor(contractAddress, scope, eventSelector);
127
+ const eventSiloedNullifiersInStorage: string[] = [];
128
+ for await (const eventId of this.#eventsByContractScopeSelector.getValuesAsync(key)) {
129
+ eventSiloedNullifiersInStorage.push(eventId);
130
+ }
131
+
132
+ if (!jobId) {
133
+ return eventSiloedNullifiersInStorage;
134
+ }
135
+
136
+ const eventSiloedNullifiersInJobStage = new Set(
137
+ [...this.#getEventLogsInJobStage(jobId).entries()]
138
+ .filter(([_, entry]) => entry.lookupKey === key)
139
+ .map(([idx, _]) => idx),
140
+ );
141
+ return [...new Set(eventSiloedNullifiersInStorage).union(eventSiloedNullifiersInJobStage)];
142
+ }
143
+
144
+ async #getEventLogBySiloedNullifier(eventId: string, jobId?: string): Promise<PrivateEventEntry | undefined> {
145
+ if (jobId) {
146
+ return this.#getEventLogsInJobStage(jobId).get(eventId) ?? (await this.#eventLogs.getAsync(eventId));
147
+ } else {
148
+ return await this.#eventLogs.getAsync(eventId);
149
+ }
150
+ }
151
+
59
152
  /**
60
153
  * Store a private event log.
61
154
  * @param eventSelector - The event selector of the event.
155
+ * @param randomness - The randomness used for the event commitment.
62
156
  * @param msgContent - The content of the event.
63
- * @param eventCommitmentIndex - The index of the event commitment in the nullifier tree.
157
+ * @param siloedEventCommitment - The siloed event commitment (used as unique identifier).
64
158
  * @param metadata
65
159
  * contractAddress - The address of the contract that emitted the event.
66
160
  * scope - The address to which the event is scoped.
@@ -69,38 +163,44 @@ export class PrivateEventStore {
69
163
  */
70
164
  storePrivateEventLog(
71
165
  eventSelector: EventSelector,
166
+ randomness: Fr,
72
167
  msgContent: Fr[],
73
- eventCommitmentIndex: number,
168
+ siloedEventCommitment: Fr,
74
169
  metadata: PrivateEventMetadata,
170
+ jobId: string,
75
171
  ): Promise<void> {
76
- const { contractAddress, scope, txHash, l2BlockNumber, l2BlockHash } = metadata;
172
+ const { contractAddress, scope, txHash, l2BlockNumber, l2BlockHash, txIndexInBlock, eventIndexInTx } = metadata;
77
173
 
78
174
  return this.#store.transactionAsync(async () => {
79
175
  const key = this.#keyFor(contractAddress, scope, eventSelector);
80
176
 
81
- // Check if this exact log has already been stored using eventCommitmentIndex as unique identifier
82
- const hasBeenSeen = await this.#seenLogs.getAsync(eventCommitmentIndex);
177
+ // The siloed event commitment is guaranteed to be unique as it's inserted into the nullifier tree. For this
178
+ // reason we use it as id.
179
+ const eventId = siloedEventCommitment.toString();
180
+
181
+ const hasBeenSeen = await this.#isSeenLog(jobId, eventId);
83
182
  if (hasBeenSeen) {
84
- this.logger.verbose('Ignoring duplicate event log', { txHash: txHash.toString(), eventCommitmentIndex });
183
+ this.logger.verbose('Ignoring duplicate event log', { txHash: txHash.toString(), siloedEventCommitment });
85
184
  return;
86
185
  }
87
186
 
88
- this.logger.verbose('storing private event log', { contractAddress, scope, msgContent, l2BlockNumber });
187
+ this.logger.verbose('storing private event log (job stage)', {
188
+ contractAddress,
189
+ scope,
190
+ msgContent,
191
+ l2BlockNumber,
192
+ });
89
193
 
90
- const index = await this.#eventLogs.lengthAsync();
91
- await this.#eventLogs.push({
194
+ this.#addEventLogToStage(jobId, eventId, {
195
+ randomness,
92
196
  msgContent: serializeToBuffer(msgContent),
93
197
  l2BlockNumber,
94
198
  l2BlockHash: l2BlockHash.toBuffer(),
95
- eventCommitmentIndex,
96
199
  txHash: txHash.toBuffer(),
200
+ txIndexInBlock,
201
+ eventIndexInTx,
202
+ lookupKey: key,
97
203
  });
98
-
99
- const existingIndices = (await this.#eventLogIndex.getAsync(key)) || [];
100
- await this.#eventLogIndex.set(key, [...existingIndices, index]);
101
-
102
- // Mark this log as seen using eventCommitmentIndex
103
- await this.#seenLogs.set(eventCommitmentIndex, true);
104
204
  });
105
205
  }
106
206
 
@@ -112,21 +212,26 @@ export class PrivateEventStore {
112
212
  * fromBlock: The block number to search from (inclusive).
113
213
  * toBlock: The block number to search upto (exclusive).
114
214
  * scope: - The addresses that decrypted the logs.
115
- * @returns - The event log contents, augmented with metadata about
116
- * the transaction and block it the event was included in .
215
+ * @returns - The event log contents, augmented with metadata about the transaction and block in which the event was
216
+ * included.
117
217
  */
118
218
  public async getPrivateEvents(
119
219
  eventSelector: EventSelector,
120
220
  filter: PrivateEventStoreFilter,
121
221
  ): Promise<PackedPrivateEvent[]> {
122
- const events: Array<{ eventCommitmentIndex: number; event: PackedPrivateEvent }> = [];
222
+ const events: Array<{
223
+ l2BlockNumber: number;
224
+ txIndexInBlock: number;
225
+ eventIndexInTx: number;
226
+ event: PackedPrivateEvent;
227
+ }> = [];
123
228
 
124
229
  for (const scope of filter.scopes) {
125
- const key = this.#keyFor(filter.contractAddress, scope, eventSelector);
126
- const indices = (await this.#eventLogIndex.getAsync(key)) || [];
230
+ const eventIds = await this.#getEventSiloedNullifiers(filter.contractAddress, scope, eventSelector);
231
+
232
+ for (const eventId of eventIds) {
233
+ const entry = await this.#getEventLogBySiloedNullifier(eventId);
127
234
 
128
- for (const index of indices) {
129
- const entry = await this.#eventLogs.atAsync(index);
130
235
  if (!entry || entry.l2BlockNumber < filter.fromBlock || entry.l2BlockNumber >= filter.toBlock) {
131
236
  continue;
132
237
  }
@@ -143,7 +248,9 @@ export class PrivateEventStore {
143
248
  }
144
249
 
145
250
  events.push({
146
- eventCommitmentIndex: entry.eventCommitmentIndex,
251
+ l2BlockNumber: entry.l2BlockNumber,
252
+ txIndexInBlock: entry.txIndexInBlock,
253
+ eventIndexInTx: entry.eventIndexInTx,
147
254
  event: {
148
255
  packedEvent: msgContent,
149
256
  l2BlockNumber: BlockNumber(entry.l2BlockNumber),
@@ -155,8 +262,63 @@ export class PrivateEventStore {
155
262
  }
156
263
  }
157
264
 
158
- // Sort by eventCommitmentIndex only
159
- events.sort((a, b) => a.eventCommitmentIndex - b.eventCommitmentIndex);
265
+ // Sort by block number, then by tx index within block, then by event index within tx
266
+ events.sort((a, b) => {
267
+ if (a.l2BlockNumber !== b.l2BlockNumber) {
268
+ return a.l2BlockNumber - b.l2BlockNumber;
269
+ }
270
+ if (a.txIndexInBlock !== b.txIndexInBlock) {
271
+ return a.txIndexInBlock - b.txIndexInBlock;
272
+ }
273
+ return a.eventIndexInTx - b.eventIndexInTx;
274
+ });
160
275
  return events.map(ev => ev.event);
161
276
  }
277
+
278
+ /**
279
+ * Rolls back private events that were stored after a given `blockNumber` and up to `synchedBlockNumber` (the block
280
+ * number up to which PXE managed to sync before the reorg happened).
281
+ *
282
+ * We don't need staged writes for a rollback since it's handled in the context of a blockchain rewind.
283
+ *
284
+ * Rollbacks are handled by the BlockSynchronizer, which runs a DB transaction across stores when it detects a
285
+ * re-org, including setting the new anchor block after rolling back.
286
+ *
287
+ * So if anything fails in the process of rolling back any store, all DB changes occurring during rollbacks will be
288
+ * lost and the anchor block will not be updated; which means this code will eventually need to run again
289
+ * (i.e.: PXE will detect it's basing it work on an invalid block hash, then which re-triggers rewind).
290
+ *
291
+ * For further details, refer to `BlockSynchronizer#handleBlockStreamEvent`.
292
+ *
293
+ * IMPORTANT: This method must be called within a transaction to ensure atomicity.
294
+ */
295
+ public async rollback(blockNumber: number, synchedBlockNumber: number): Promise<void> {
296
+ let removedCount = 0;
297
+
298
+ for (let block = blockNumber + 1; block <= synchedBlockNumber; block++) {
299
+ const eventIds: string[] = [];
300
+ for await (const eventId of this.#eventsByBlockNumber.getValuesAsync(block)) {
301
+ eventIds.push(eventId);
302
+ }
303
+
304
+ if (eventIds.length > 0) {
305
+ await this.#eventsByBlockNumber.delete(block);
306
+
307
+ for (const eventId of eventIds) {
308
+ const entry = await this.#eventLogs.getAsync(eventId);
309
+ if (!entry) {
310
+ throw new Error(`Event log not found for eventId ${eventId}`);
311
+ }
312
+
313
+ await this.#eventLogs.delete(eventId);
314
+ await this.#seenLogs.delete(eventId);
315
+ await this.#eventsByContractScopeSelector.deleteValue(entry.lookupKey, eventId);
316
+
317
+ removedCount++;
318
+ }
319
+ }
320
+ }
321
+
322
+ this.logger.verbose(`Rolled back ${removedCount} private events after block ${blockNumber}`);
323
+ }
162
324
  }
@@ -1,6 +1,8 @@
1
1
  import type { AztecAsyncKVStore, AztecAsyncMap } from '@aztec/kv-store';
2
2
  import type { DirectionalAppTaggingSecret } from '@aztec/stdlib/logs';
3
3
 
4
+ import type { StagedStore } from '../../job_coordinator/job_coordinator.js';
5
+
4
6
  /**
5
7
  * Data provider of tagging data used when syncing the logs as a recipient. The sender counterpart of this class
6
8
  * is called SenderTaggingStore. We have the providers separate for the sender and recipient because
@@ -8,46 +10,120 @@ import type { DirectionalAppTaggingSecret } from '@aztec/stdlib/logs';
8
10
  *
9
11
  * @dev Chain reorgs do not need to be handled here because both the finalized and aged indexes refer to finalized
10
12
  * blocks, which by definition cannot be affected by reorgs.
11
- *
12
- * TODO(benesjan): Relocate to yarn-project/pxe/src/storage/tagging_store
13
13
  */
14
- export class RecipientTaggingStore {
14
+ export class RecipientTaggingStore implements StagedStore {
15
+ storeName: string = 'recipient_tagging';
16
+
15
17
  #store: AztecAsyncKVStore;
16
18
 
17
19
  #highestAgedIndex: AztecAsyncMap<string, number>;
18
20
  #highestFinalizedIndex: AztecAsyncMap<string, number>;
19
21
 
22
+ // jobId => secret => number
23
+ #highestAgedIndexForJob: Map<string, Map<string, number>>;
24
+
25
+ // jobId => secret => number
26
+ #highestFinalizedIndexForJob: Map<string, Map<string, number>>;
27
+
20
28
  constructor(store: AztecAsyncKVStore) {
21
29
  this.#store = store;
22
30
 
23
31
  this.#highestAgedIndex = this.#store.openMap('highest_aged_index');
24
32
  this.#highestFinalizedIndex = this.#store.openMap('highest_finalized_index');
33
+
34
+ this.#highestAgedIndexForJob = new Map();
35
+ this.#highestFinalizedIndexForJob = new Map();
36
+ }
37
+
38
+ #getHighestAgedIndexForJob(jobId: string): Map<string, number> {
39
+ let highestAgedIndexForJob = this.#highestAgedIndexForJob.get(jobId);
40
+ if (!highestAgedIndexForJob) {
41
+ highestAgedIndexForJob = new Map();
42
+ this.#highestAgedIndexForJob.set(jobId, highestAgedIndexForJob);
43
+ }
44
+ return highestAgedIndexForJob;
45
+ }
46
+
47
+ async #readHighestAgedIndex(jobId: string, secret: string): Promise<number | undefined> {
48
+ return this.#getHighestAgedIndexForJob(jobId).get(secret) ?? (await this.#highestAgedIndex.getAsync(secret));
49
+ }
50
+
51
+ #writeHighestAgedIndex(jobId: string, secret: string, index: number) {
52
+ this.#getHighestAgedIndexForJob(jobId).set(secret, index);
53
+ }
54
+
55
+ #getHighestFinalizedIndexForJob(jobId: string): Map<string, number> {
56
+ let jobStagedHighestFinalizedIndex = this.#highestFinalizedIndexForJob.get(jobId);
57
+ if (!jobStagedHighestFinalizedIndex) {
58
+ jobStagedHighestFinalizedIndex = new Map();
59
+ this.#highestFinalizedIndexForJob.set(jobId, jobStagedHighestFinalizedIndex);
60
+ }
61
+ return jobStagedHighestFinalizedIndex;
62
+ }
63
+
64
+ async #readHighestFinalizedIndex(jobId: string, secret: string): Promise<number | undefined> {
65
+ return (
66
+ this.#getHighestFinalizedIndexForJob(jobId).get(secret) ?? (await this.#highestFinalizedIndex.getAsync(secret))
67
+ );
68
+ }
69
+
70
+ #writeHighestFinalizedIndex(jobId: string, secret: string, index: number) {
71
+ this.#getHighestFinalizedIndexForJob(jobId).set(secret, index);
72
+ }
73
+
74
+ /**
75
+ * Writes all job-specific in-memory data to persistent storage.
76
+ *
77
+ * @remark This method must run in a DB transaction context. It's designed to be called from JobCoordinator#commitJob.
78
+ */
79
+ async commit(jobId: string): Promise<void> {
80
+ const highestAgedIndexForJob = this.#highestAgedIndexForJob.get(jobId);
81
+ if (highestAgedIndexForJob) {
82
+ for (const [secret, index] of highestAgedIndexForJob.entries()) {
83
+ await this.#highestAgedIndex.set(secret, index);
84
+ }
85
+ }
86
+
87
+ const highestFinalizedIndexForJob = this.#highestFinalizedIndexForJob.get(jobId);
88
+ if (highestFinalizedIndexForJob) {
89
+ for (const [secret, index] of highestFinalizedIndexForJob.entries()) {
90
+ await this.#highestFinalizedIndex.set(secret, index);
91
+ }
92
+ }
93
+
94
+ return this.discardStaged(jobId);
95
+ }
96
+
97
+ discardStaged(jobId: string): Promise<void> {
98
+ this.#highestAgedIndexForJob.delete(jobId);
99
+ this.#highestFinalizedIndexForJob.delete(jobId);
100
+ return Promise.resolve();
25
101
  }
26
102
 
27
- getHighestAgedIndex(secret: DirectionalAppTaggingSecret): Promise<number | undefined> {
28
- return this.#highestAgedIndex.getAsync(secret.toString());
103
+ getHighestAgedIndex(secret: DirectionalAppTaggingSecret, jobId: string): Promise<number | undefined> {
104
+ return this.#readHighestAgedIndex(jobId, secret.toString());
29
105
  }
30
106
 
31
- async updateHighestAgedIndex(secret: DirectionalAppTaggingSecret, index: number): Promise<void> {
32
- const currentIndex = await this.#highestAgedIndex.getAsync(secret.toString());
107
+ async updateHighestAgedIndex(secret: DirectionalAppTaggingSecret, index: number, jobId: string): Promise<void> {
108
+ const currentIndex = await this.#readHighestAgedIndex(jobId, secret.toString());
33
109
  if (currentIndex !== undefined && index <= currentIndex) {
34
110
  // Log sync should never set a lower highest aged index.
35
111
  throw new Error(`New highest aged index (${index}) must be higher than the current one (${currentIndex})`);
36
112
  }
37
- await this.#highestAgedIndex.set(secret.toString(), index);
113
+ this.#writeHighestAgedIndex(jobId, secret.toString(), index);
38
114
  }
39
115
 
40
- getHighestFinalizedIndex(secret: DirectionalAppTaggingSecret): Promise<number | undefined> {
41
- return this.#highestFinalizedIndex.getAsync(secret.toString());
116
+ getHighestFinalizedIndex(secret: DirectionalAppTaggingSecret, jobId: string): Promise<number | undefined> {
117
+ return this.#readHighestFinalizedIndex(jobId, secret.toString());
42
118
  }
43
119
 
44
- async updateHighestFinalizedIndex(secret: DirectionalAppTaggingSecret, index: number): Promise<void> {
45
- const currentIndex = await this.#highestFinalizedIndex.getAsync(secret.toString());
120
+ async updateHighestFinalizedIndex(secret: DirectionalAppTaggingSecret, index: number, jobId: string): Promise<void> {
121
+ const currentIndex = await this.#readHighestFinalizedIndex(jobId, secret.toString());
46
122
  if (currentIndex !== undefined && index < currentIndex) {
47
123
  // Log sync should never set a lower highest finalized index but it can happen that it would try to set the same
48
124
  // one because we are loading logs from highest aged index + 1 and not from the highest finalized index.
49
125
  throw new Error(`New highest finalized index (${index}) must be higher than the current one (${currentIndex})`);
50
126
  }
51
- await this.#highestFinalizedIndex.set(secret.toString(), index);
127
+ this.#writeHighestFinalizedIndex(jobId, secret.toString(), index);
52
128
  }
53
129
  }