@aztec/pxe 0.0.1-commit.d1f2d6c → 0.0.1-commit.d431d1c

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 (100) hide show
  1. package/dest/block_synchronizer/block_synchronizer.d.ts +4 -6
  2. package/dest/block_synchronizer/block_synchronizer.d.ts.map +1 -1
  3. package/dest/block_synchronizer/block_synchronizer.js +11 -51
  4. package/dest/config/index.d.ts +1 -3
  5. package/dest/config/index.d.ts.map +1 -1
  6. package/dest/config/index.js +0 -17
  7. package/dest/contract_function_simulator/contract_function_simulator.d.ts +1 -1
  8. package/dest/contract_function_simulator/contract_function_simulator.d.ts.map +1 -1
  9. package/dest/contract_function_simulator/contract_function_simulator.js +4 -1
  10. package/dest/contract_function_simulator/oracle/interfaces.d.ts +4 -7
  11. package/dest/contract_function_simulator/oracle/interfaces.d.ts.map +1 -1
  12. package/dest/contract_function_simulator/oracle/oracle.d.ts +2 -4
  13. package/dest/contract_function_simulator/oracle/oracle.d.ts.map +1 -1
  14. package/dest/contract_function_simulator/oracle/oracle.js +9 -19
  15. package/dest/contract_function_simulator/oracle/private_execution.d.ts +6 -5
  16. package/dest/contract_function_simulator/oracle/private_execution.d.ts.map +1 -1
  17. package/dest/contract_function_simulator/oracle/private_execution.js +1 -10
  18. package/dest/contract_function_simulator/oracle/private_execution_oracle.d.ts +1 -9
  19. package/dest/contract_function_simulator/oracle/private_execution_oracle.d.ts.map +1 -1
  20. package/dest/contract_function_simulator/oracle/private_execution_oracle.js +5 -17
  21. package/dest/contract_function_simulator/oracle/utility_execution_oracle.d.ts +6 -14
  22. package/dest/contract_function_simulator/oracle/utility_execution_oracle.d.ts.map +1 -1
  23. package/dest/contract_function_simulator/oracle/utility_execution_oracle.js +33 -31
  24. package/dest/debug/pxe_debug_utils.d.ts +1 -1
  25. package/dest/debug/pxe_debug_utils.d.ts.map +1 -1
  26. package/dest/debug/pxe_debug_utils.js +1 -2
  27. package/dest/logs/log_service.d.ts +1 -1
  28. package/dest/logs/log_service.d.ts.map +1 -1
  29. package/dest/logs/log_service.js +11 -18
  30. package/dest/notes/note_service.d.ts +2 -3
  31. package/dest/notes/note_service.d.ts.map +1 -1
  32. package/dest/notes/note_service.js +6 -8
  33. package/dest/oracle_version.d.ts +3 -3
  34. package/dest/oracle_version.d.ts.map +1 -1
  35. package/dest/oracle_version.js +3 -4
  36. package/dest/pxe.d.ts +1 -1
  37. package/dest/pxe.d.ts.map +1 -1
  38. package/dest/pxe.js +7 -10
  39. package/dest/storage/note_store/note_store.d.ts +55 -43
  40. package/dest/storage/note_store/note_store.d.ts.map +1 -1
  41. package/dest/storage/note_store/note_store.js +252 -238
  42. package/dest/storage/private_event_store/private_event_store.d.ts +4 -17
  43. package/dest/storage/private_event_store/private_event_store.d.ts.map +1 -1
  44. package/dest/storage/private_event_store/private_event_store.js +135 -163
  45. package/dest/tagging/index.d.ts +1 -2
  46. package/dest/tagging/index.d.ts.map +1 -1
  47. package/dest/tagging/index.js +0 -1
  48. package/dest/tagging/recipient_sync/load_private_logs_for_sender_recipient_pair.d.ts +2 -3
  49. package/dest/tagging/recipient_sync/load_private_logs_for_sender_recipient_pair.d.ts.map +1 -1
  50. package/dest/tagging/recipient_sync/load_private_logs_for_sender_recipient_pair.js +2 -2
  51. package/dest/tagging/recipient_sync/utils/load_logs_for_range.d.ts +2 -3
  52. package/dest/tagging/recipient_sync/utils/load_logs_for_range.d.ts.map +1 -1
  53. package/dest/tagging/recipient_sync/utils/load_logs_for_range.js +2 -5
  54. package/dest/tagging/sender_sync/sync_sender_tagging_indexes.d.ts +2 -3
  55. package/dest/tagging/sender_sync/sync_sender_tagging_indexes.d.ts.map +1 -1
  56. package/dest/tagging/sender_sync/sync_sender_tagging_indexes.js +2 -2
  57. package/dest/tagging/sender_sync/utils/get_status_change_of_pending.d.ts +1 -1
  58. package/dest/tagging/sender_sync/utils/get_status_change_of_pending.d.ts.map +1 -1
  59. package/dest/tagging/sender_sync/utils/get_status_change_of_pending.js +8 -5
  60. package/dest/tagging/sender_sync/utils/load_and_store_new_tagging_indexes.d.ts +2 -3
  61. package/dest/tagging/sender_sync/utils/load_and_store_new_tagging_indexes.d.ts.map +1 -1
  62. package/dest/tagging/sender_sync/utils/load_and_store_new_tagging_indexes.js +4 -7
  63. package/dest/tree_membership/tree_membership_service.d.ts +50 -0
  64. package/dest/tree_membership/tree_membership_service.d.ts.map +1 -0
  65. package/dest/tree_membership/tree_membership_service.js +75 -0
  66. package/package.json +16 -16
  67. package/src/block_synchronizer/block_synchronizer.ts +14 -53
  68. package/src/config/index.ts +0 -14
  69. package/src/contract_function_simulator/contract_function_simulator.ts +9 -1
  70. package/src/contract_function_simulator/oracle/interfaces.ts +3 -12
  71. package/src/contract_function_simulator/oracle/oracle.ts +13 -26
  72. package/src/contract_function_simulator/oracle/private_execution.ts +1 -20
  73. package/src/contract_function_simulator/oracle/private_execution_oracle.ts +6 -33
  74. package/src/contract_function_simulator/oracle/utility_execution_oracle.ts +38 -46
  75. package/src/debug/pxe_debug_utils.ts +1 -2
  76. package/src/logs/log_service.ts +8 -25
  77. package/src/notes/note_service.ts +11 -15
  78. package/src/oracle_version.ts +3 -4
  79. package/src/pxe.ts +8 -32
  80. package/src/storage/note_store/note_store.ts +313 -279
  81. package/src/storage/private_event_store/private_event_store.ts +175 -214
  82. package/src/tagging/index.ts +0 -1
  83. package/src/tagging/recipient_sync/load_private_logs_for_sender_recipient_pair.ts +1 -11
  84. package/src/tagging/recipient_sync/utils/load_logs_for_range.ts +1 -7
  85. package/src/tagging/sender_sync/sync_sender_tagging_indexes.ts +1 -3
  86. package/src/tagging/sender_sync/utils/get_status_change_of_pending.ts +17 -5
  87. package/src/tagging/sender_sync/utils/load_and_store_new_tagging_indexes.ts +3 -12
  88. package/src/tree_membership/tree_membership_service.ts +97 -0
  89. package/dest/storage/note_store/stored_note.d.ts +0 -16
  90. package/dest/storage/note_store/stored_note.d.ts.map +0 -1
  91. package/dest/storage/note_store/stored_note.js +0 -43
  92. package/dest/storage/private_event_store/stored_private_event.d.ts +0 -23
  93. package/dest/storage/private_event_store/stored_private_event.d.ts.map +0 -1
  94. package/dest/storage/private_event_store/stored_private_event.js +0 -56
  95. package/dest/tagging/get_all_logs_by_tags.d.ts +0 -24
  96. package/dest/tagging/get_all_logs_by_tags.d.ts.map +0 -1
  97. package/dest/tagging/get_all_logs_by_tags.js +0 -46
  98. package/src/storage/note_store/stored_note.ts +0 -48
  99. package/src/storage/private_event_store/stored_private_event.ts +0 -73
  100. package/src/tagging/get_all_logs_by_tags.ts +0 -68
@@ -1,16 +1,15 @@
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';
4
3
  import { createLogger } from '@aztec/foundation/log';
5
- import { Semaphore } from '@aztec/foundation/queue';
4
+ import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize';
6
5
  import type { AztecAsyncKVStore, AztecAsyncMap, AztecAsyncMultiMap } from '@aztec/kv-store';
7
6
  import type { EventSelector } from '@aztec/stdlib/abi';
8
7
  import type { AztecAddress } from '@aztec/stdlib/aztec-address';
9
- import type { InTx, TxHash } from '@aztec/stdlib/tx';
8
+ import { L2BlockHash } from '@aztec/stdlib/block';
9
+ import { type InTx, TxHash } from '@aztec/stdlib/tx';
10
10
 
11
11
  import type { StagedStore } from '../../job_coordinator/job_coordinator.js';
12
12
  import type { PackedPrivateEvent } from '../../pxe.js';
13
- import { StoredPrivateEvent } from './stored_private_event.js';
14
13
 
15
14
  export type PrivateEventStoreFilter = {
16
15
  contractAddress: AztecAddress;
@@ -20,6 +19,20 @@ export type PrivateEventStoreFilter = {
20
19
  txHash?: TxHash;
21
20
  };
22
21
 
22
+ type PrivateEventEntry = {
23
+ randomness: Fr; // Note that this value is currently not being returned on queries and is therefore temporarily unused
24
+ msgContent: Buffer;
25
+ l2BlockNumber: number;
26
+ l2BlockHash: Buffer;
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;
34
+ };
35
+
23
36
  type PrivateEventMetadata = InTx & {
24
37
  contractAddress: AztecAddress;
25
38
  scope: AztecAddress;
@@ -36,29 +49,104 @@ export class PrivateEventStore implements StagedStore {
36
49
  readonly storeName: string = 'private_event';
37
50
 
38
51
  #store: AztecAsyncKVStore;
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>;
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>;
43
56
  /** Multi-map from block number to siloedEventCommitment for rollback support */
44
57
  #eventsByBlockNumber: AztecAsyncMultiMap<number, string>;
58
+ /** Map from siloedEventCommitment to boolean indicating if log has been seen. */
59
+ #seenLogs: AztecAsyncMap<string, boolean>;
45
60
 
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>;
61
+ /** jobId => eventId (event siloed nullifier) => PrivateEventEntry */
62
+ #eventLogsInJobStage: Map<string, Map<string, PrivateEventEntry>>;
51
63
 
52
64
  logger = createLogger('private_event_store');
53
65
 
54
66
  constructor(store: AztecAsyncKVStore) {
55
67
  this.#store = store;
56
- this.#events = this.#store.openMap('private_event_logs');
57
- this.#eventsByContractAndEventSelector = this.#store.openMultiMap('events_by_contract_selector');
68
+ this.#eventLogs = this.#store.openMap('private_event_logs');
69
+ this.#eventsByContractScopeSelector = this.#store.openMultiMap('events_by_contract_scope_selector');
70
+ this.#seenLogs = this.#store.openMap('seen_logs');
58
71
  this.#eventsByBlockNumber = this.#store.openMultiMap('events_by_block_number');
59
72
 
60
- this.#eventsForJob = new Map();
61
- this.#jobLocks = new Map();
73
+ this.#eventLogsInJobStage = new Map();
74
+ }
75
+
76
+ #keyFor(contractAddress: AztecAddress, scope: AztecAddress, eventSelector: EventSelector): string {
77
+ return `${contractAddress.toString()}_${scope.toString()}_${eventSelector.toString()}`;
78
+ }
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
+ }
62
150
  }
63
151
 
64
152
  /**
@@ -80,43 +168,39 @@ export class PrivateEventStore implements StagedStore {
80
168
  siloedEventCommitment: Fr,
81
169
  metadata: PrivateEventMetadata,
82
170
  jobId: string,
83
- ) {
84
- return this.#withJobLock(jobId, async () => {
85
- const { contractAddress, scope, txHash, l2BlockNumber, l2BlockHash, txIndexInBlock, eventIndexInTx } = metadata;
171
+ ): Promise<void> {
172
+ const { contractAddress, scope, txHash, l2BlockNumber, l2BlockHash, txIndexInBlock, eventIndexInTx } = metadata;
173
+
174
+ return this.#store.transactionAsync(async () => {
175
+ const key = this.#keyFor(contractAddress, scope, eventSelector);
176
+
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.
86
179
  const eventId = siloedEventCommitment.toString();
87
180
 
181
+ const hasBeenSeen = await this.#isSeenLog(jobId, eventId);
182
+ if (hasBeenSeen) {
183
+ this.logger.verbose('Ignoring duplicate event log', { txHash: txHash.toString(), siloedEventCommitment });
184
+ return;
185
+ }
186
+
88
187
  this.logger.verbose('storing private event log (job stage)', {
89
- eventId,
90
188
  contractAddress,
91
189
  scope,
92
190
  msgContent,
93
191
  l2BlockNumber,
94
192
  });
95
193
 
96
- const existing = await this.#readEvent(eventId, jobId);
97
-
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
- }
194
+ this.#addEventLogToStage(jobId, eventId, {
195
+ randomness,
196
+ msgContent: serializeToBuffer(msgContent),
197
+ l2BlockNumber,
198
+ l2BlockHash: l2BlockHash.toBuffer(),
199
+ txHash: txHash.toBuffer(),
200
+ txIndexInBlock,
201
+ eventIndexInTx,
202
+ lookupKey: key,
203
+ });
120
204
  });
121
205
  }
122
206
 
@@ -131,78 +215,64 @@ export class PrivateEventStore implements StagedStore {
131
215
  * @returns - The event log contents, augmented with metadata about the transaction and block in which the event was
132
216
  * included.
133
217
  */
134
- public getPrivateEvents(
218
+ public async getPrivateEvents(
135
219
  eventSelector: EventSelector,
136
220
  filter: PrivateEventStoreFilter,
137
221
  ): Promise<PackedPrivateEvent[]> {
138
- return this.#store.transactionAsync(async () => {
139
- const events: Array<{
140
- l2BlockNumber: number;
141
- txIndexInBlock: number;
142
- eventIndexInTx: number;
143
- event: PackedPrivateEvent;
144
- }> = [];
222
+ const events: Array<{
223
+ l2BlockNumber: number;
224
+ txIndexInBlock: number;
225
+ eventIndexInTx: number;
226
+ event: PackedPrivateEvent;
227
+ }> = [];
145
228
 
146
- const key = this.#keyFor(filter.contractAddress, eventSelector);
147
- const targetScopes = new Set(filter.scopes.map(s => s.toString()));
148
-
149
- const eventIds: string[] = await toArray(this.#eventsByContractAndEventSelector.getValuesAsync(key));
229
+ for (const scope of filter.scopes) {
230
+ const eventIds = await this.#getEventSiloedNullifiers(filter.contractAddress, scope, eventSelector);
150
231
 
151
232
  for (const eventId of eventIds) {
152
- const eventBuffer = await this.#events.getAsync(eventId);
153
-
154
- // Defensive, if it happens, there's a problem with how we're handling #eventsByContractAndEventSelector
155
- if (!eventBuffer) {
156
- this.logger.verbose(
157
- `EventId ${eventId} does not exist in main index but it is referenced from contract event selector index`,
158
- );
159
- continue;
160
- }
161
-
162
- const storedPrivateEvent = StoredPrivateEvent.fromBuffer(eventBuffer);
233
+ const entry = await this.#getEventLogBySiloedNullifier(eventId);
163
234
 
164
- // Filter by block range
165
- if (storedPrivateEvent.l2BlockNumber < filter.fromBlock || storedPrivateEvent.l2BlockNumber >= filter.toBlock) {
235
+ if (!entry || entry.l2BlockNumber < filter.fromBlock || entry.l2BlockNumber >= filter.toBlock) {
166
236
  continue;
167
237
  }
168
238
 
169
- // Filter by scopes
170
- if (storedPrivateEvent.scopes.intersection(targetScopes).size === 0) {
171
- continue;
172
- }
239
+ // Convert buffer back to Fr array
240
+ const reader = BufferReader.asReader(entry.msgContent);
241
+ const numFields = entry.msgContent.length / Fr.SIZE_IN_BYTES;
242
+ const msgContent = reader.readArray(numFields, Fr);
243
+ const txHash = TxHash.fromBuffer(entry.txHash);
244
+ const l2BlockHash = L2BlockHash.fromBuffer(entry.l2BlockHash);
173
245
 
174
- // Filter by txHash
175
- if (filter.txHash && !storedPrivateEvent.txHash.equals(filter.txHash)) {
246
+ if (filter.txHash && !txHash.equals(filter.txHash)) {
176
247
  continue;
177
248
  }
178
249
 
179
250
  events.push({
180
- l2BlockNumber: storedPrivateEvent.l2BlockNumber,
181
- txIndexInBlock: storedPrivateEvent.txIndexInBlock,
182
- eventIndexInTx: storedPrivateEvent.eventIndexInTx,
251
+ l2BlockNumber: entry.l2BlockNumber,
252
+ txIndexInBlock: entry.txIndexInBlock,
253
+ eventIndexInTx: entry.eventIndexInTx,
183
254
  event: {
184
- packedEvent: storedPrivateEvent.msgContent,
185
- l2BlockNumber: BlockNumber(storedPrivateEvent.l2BlockNumber),
186
- txHash: storedPrivateEvent.txHash,
187
- l2BlockHash: storedPrivateEvent.l2BlockHash,
255
+ packedEvent: msgContent,
256
+ l2BlockNumber: BlockNumber(entry.l2BlockNumber),
257
+ txHash,
258
+ l2BlockHash,
188
259
  eventSelector,
189
260
  },
190
261
  });
191
262
  }
263
+ }
192
264
 
193
- // Sort by block number, then by tx index within block, then by event index within tx
194
- events.sort((a, b) => {
195
- if (a.l2BlockNumber !== b.l2BlockNumber) {
196
- return a.l2BlockNumber - b.l2BlockNumber;
197
- }
198
- if (a.txIndexInBlock !== b.txIndexInBlock) {
199
- return a.txIndexInBlock - b.txIndexInBlock;
200
- }
201
- return a.eventIndexInTx - b.eventIndexInTx;
202
- });
203
-
204
- return events.map(ev => ev.event);
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;
205
274
  });
275
+ return events.map(ev => ev.event);
206
276
  }
207
277
 
208
278
  /**
@@ -226,23 +296,23 @@ export class PrivateEventStore implements StagedStore {
226
296
  let removedCount = 0;
227
297
 
228
298
  for (let block = blockNumber + 1; block <= synchedBlockNumber; block++) {
229
- const eventIds: string[] = await toArray(this.#eventsByBlockNumber.getValuesAsync(block));
299
+ const eventIds: string[] = [];
300
+ for await (const eventId of this.#eventsByBlockNumber.getValuesAsync(block)) {
301
+ eventIds.push(eventId);
302
+ }
230
303
 
231
304
  if (eventIds.length > 0) {
232
305
  await this.#eventsByBlockNumber.delete(block);
233
306
 
234
307
  for (const eventId of eventIds) {
235
- const buffer = await this.#events.getAsync(eventId);
236
- if (!buffer) {
237
- throw new Error(`Event not found for eventId ${eventId}`);
308
+ const entry = await this.#eventLogs.getAsync(eventId);
309
+ if (!entry) {
310
+ throw new Error(`Event log not found for eventId ${eventId}`);
238
311
  }
239
312
 
240
- const entry = StoredPrivateEvent.fromBuffer(buffer);
241
- await this.#events.delete(eventId);
242
- await this.#eventsByContractAndEventSelector.deleteValue(
243
- this.#keyFor(entry.contractAddress, entry.eventSelector),
244
- eventId,
245
- );
313
+ await this.#eventLogs.delete(eventId);
314
+ await this.#seenLogs.delete(eventId);
315
+ await this.#eventsByContractScopeSelector.deleteValue(entry.lookupKey, eventId);
246
316
 
247
317
  removedCount++;
248
318
  }
@@ -251,113 +321,4 @@ export class PrivateEventStore implements StagedStore {
251
321
 
252
322
  this.logger.verbose(`Rolled back ${removedCount} private events after block ${blockNumber}`);
253
323
  }
254
-
255
- /**
256
- * Commits in memory job data to persistent storage.
257
- *
258
- * Called by JobCoordinator when a job completes successfully.
259
- *
260
- * Note: JobCoordinator wraps all commits in a single transaction, so we don't need our own transactionAsync here
261
- * (and using one would throw on IndexedDB as it does not support nested txs).
262
- *
263
- * @param jobId - The jobId identifying which staged data to commit
264
- */
265
- commit(jobId: string): Promise<void> {
266
- return this.#withJobLock(jobId, async () => {
267
- for (const [eventId, entry] of this.#getEventsForJob(jobId).entries()) {
268
- const lookupKey = this.#keyFor(entry.contractAddress, entry.eventSelector);
269
- this.logger.verbose('storing private event log', { eventId, lookupKey });
270
-
271
- await Promise.all([
272
- this.#events.set(eventId, entry.toBuffer()),
273
- this.#eventsByContractAndEventSelector.set(lookupKey, eventId),
274
- this.#eventsByBlockNumber.set(entry.l2BlockNumber, eventId),
275
- ]);
276
- }
277
-
278
- this.#clearJobData(jobId);
279
- });
280
- }
281
-
282
- /**
283
- * Discards in memory job data without persisting it.
284
- */
285
- discardStaged(jobId: string): Promise<void> {
286
- return this.#withJobLock(jobId, () => Promise.resolve(this.#clearJobData(jobId)));
287
- }
288
-
289
- /**
290
- * Reads an event from in-memory job data first, falling back to persistent storage if not found.
291
- *
292
- * Returns undefined if the event does not exist in the store overall.
293
- */
294
- async #readEvent(eventId: string, jobId: string): Promise<StoredPrivateEvent | undefined> {
295
- const eventForJob = this.#getEventsForJob(jobId).get(eventId);
296
- if (eventForJob) {
297
- return eventForJob;
298
- }
299
-
300
- const buffer = await this.#events.getAsync(eventId);
301
- return buffer ? StoredPrivateEvent.fromBuffer(buffer) : undefined;
302
- }
303
-
304
- /**
305
- * Writes an event to in-memory job data.
306
- *
307
- * Writes are only allowed in a job context. Events modified during a job will only be persisted when `commit` is
308
- * called.
309
- */
310
- #writeEvent(eventId: string, entry: StoredPrivateEvent, jobId: string) {
311
- this.#getEventsForJob(jobId).set(eventId, entry);
312
- }
313
-
314
- /**
315
- * Get in-memory data only visible to @param jobId
316
- */
317
- #getEventsForJob(jobId: string): Map<string, StoredPrivateEvent> {
318
- let eventsForJob = this.#eventsForJob.get(jobId);
319
- if (eventsForJob === undefined) {
320
- eventsForJob = new Map();
321
- this.#eventsForJob.set(jobId, eventsForJob);
322
- }
323
- return eventsForJob;
324
- }
325
-
326
- /**
327
- * Clear data structures supporting a specific job.
328
- */
329
- #clearJobData(jobId: string) {
330
- this.#eventsForJob.delete(jobId);
331
- this.#jobLocks.delete(jobId);
332
- }
333
-
334
- /**
335
- * Ensures a function can only run once it acquires a unique per-job lock, and handles proper lock release after it
336
- * runs.
337
- *
338
- * This primitive allows concurrent writes on this store without risking data corruption due to unsound write
339
- * interleaving.
340
- */
341
- async #withJobLock<T>(jobId: string, fn: () => Promise<T>): Promise<T> {
342
- let lock = this.#jobLocks.get(jobId);
343
- if (!lock) {
344
- lock = new Semaphore(1);
345
- this.#jobLocks.set(jobId, lock);
346
- }
347
- await lock.acquire();
348
- try {
349
- return await fn();
350
- } finally {
351
- lock.release();
352
- }
353
- }
354
-
355
- /**
356
- * Returns a string key based on @param contractAddress and @param eventSelector.
357
- *
358
- * The returned key is meant to be used when interacting with index #eventsByContractAndEventSelector.
359
- */
360
- #keyFor(contractAddress: AztecAddress, eventSelector: EventSelector): string {
361
- return `${contractAddress.toString()}_${eventSelector.toString()}`;
362
- }
363
324
  }
@@ -12,7 +12,6 @@
12
12
  export { loadPrivateLogsForSenderRecipientPair } from './recipient_sync/load_private_logs_for_sender_recipient_pair.js';
13
13
  export { syncSenderTaggingIndexes } from './sender_sync/sync_sender_tagging_indexes.js';
14
14
  export { UNFINALIZED_TAGGING_INDEXES_WINDOW_LEN } from './constants.js';
15
- export { getAllPrivateLogsByTags, getAllPublicLogsByTagsFromContract } from './get_all_logs_by_tags.js';
16
15
 
17
16
  // Re-export tagging-related types from stdlib
18
17
  export { DirectionalAppTaggingSecret, Tag, SiloedTag } from '@aztec/stdlib/logs';
@@ -1,6 +1,5 @@
1
1
  import type { BlockNumber } from '@aztec/foundation/branded-types';
2
2
  import type { AztecAddress } from '@aztec/stdlib/aztec-address';
3
- import type { L2BlockHash } from '@aztec/stdlib/block';
4
3
  import type { AztecNode } from '@aztec/stdlib/interfaces/client';
5
4
  import type { DirectionalAppTaggingSecret, TxScopedL2Log } from '@aztec/stdlib/logs';
6
5
 
@@ -22,7 +21,6 @@ export async function loadPrivateLogsForSenderRecipientPair(
22
21
  aztecNode: AztecNode,
23
22
  taggingStore: RecipientTaggingStore,
24
23
  anchorBlockNumber: BlockNumber,
25
- anchorBlockHash: L2BlockHash,
26
24
  jobId: string,
27
25
  ): Promise<TxScopedL2Log[]> {
28
26
  // # Explanation of how the algorithm works
@@ -94,15 +92,7 @@ export async function loadPrivateLogsForSenderRecipientPair(
94
92
 
95
93
  while (true) {
96
94
  // Get private logs with their block timestamps and corresponding tagging indexes
97
- const privateLogsWithIndexes = await loadLogsForRange(
98
- secret,
99
- app,
100
- aztecNode,
101
- start,
102
- end,
103
- anchorBlockNumber,
104
- anchorBlockHash,
105
- );
95
+ const privateLogsWithIndexes = await loadLogsForRange(secret, app, aztecNode, start, end, anchorBlockNumber);
106
96
 
107
97
  if (privateLogsWithIndexes.length === 0) {
108
98
  break;
@@ -1,12 +1,9 @@
1
1
  import type { BlockNumber } from '@aztec/foundation/branded-types';
2
2
  import type { AztecAddress } from '@aztec/stdlib/aztec-address';
3
- import type { L2BlockHash } from '@aztec/stdlib/block';
4
3
  import type { AztecNode } from '@aztec/stdlib/interfaces/client';
5
4
  import type { DirectionalAppTaggingSecret, PreTag, TxScopedL2Log } from '@aztec/stdlib/logs';
6
5
  import { SiloedTag, Tag } from '@aztec/stdlib/logs';
7
6
 
8
- import { getAllPrivateLogsByTags } from '../../get_all_logs_by_tags.js';
9
-
10
7
  /**
11
8
  * Gets private logs with their corresponding block timestamps and tagging indexes for the given index range, `app` and
12
9
  * `secret`. At most load logs from blocks up to and including `anchorBlockNumber`. `start` is inclusive and `end` is
@@ -19,7 +16,6 @@ export async function loadLogsForRange(
19
16
  start: number,
20
17
  end: number,
21
18
  anchorBlockNumber: BlockNumber,
22
- anchorBlockHash: L2BlockHash,
23
19
  ): Promise<Array<{ log: TxScopedL2Log; taggingIndex: number }>> {
24
20
  // Derive tags for the window
25
21
  const preTags: PreTag[] = Array(end - start)
@@ -29,9 +25,7 @@ export async function loadLogsForRange(
29
25
  Promise.all(tags.map(tag => SiloedTag.compute(tag, app))),
30
26
  );
31
27
 
32
- // We use the utility function below to retrieve all logs for the tags across all pages, so we don't need to handle
33
- // pagination here.
34
- const logs = await getAllPrivateLogsByTags(aztecNode, siloedTags, anchorBlockHash);
28
+ const logs = await aztecNode.getPrivateLogsByTags(siloedTags);
35
29
 
36
30
  // Pair logs with their corresponding tagging indexes
37
31
  const logsWithIndexes: Array<{ log: TxScopedL2Log; taggingIndex: number }> = [];
@@ -1,5 +1,4 @@
1
1
  import type { AztecAddress } from '@aztec/stdlib/aztec-address';
2
- import type { L2BlockHash } from '@aztec/stdlib/block';
3
2
  import type { AztecNode } from '@aztec/stdlib/interfaces/server';
4
3
  import type { DirectionalAppTaggingSecret } from '@aztec/stdlib/logs';
5
4
 
@@ -27,7 +26,6 @@ export async function syncSenderTaggingIndexes(
27
26
  app: AztecAddress,
28
27
  aztecNode: AztecNode,
29
28
  taggingStore: SenderTaggingStore,
30
- anchorBlockHash: L2BlockHash,
31
29
  jobId: string,
32
30
  ): Promise<void> {
33
31
  // # Explanation of how syncing works
@@ -59,7 +57,7 @@ export async function syncSenderTaggingIndexes(
59
57
  while (true) {
60
58
  // Load and store indexes for the current window. These indexes may already exist in the database if txs using
61
59
  // them were previously sent from this PXE. Any duplicates are handled by the tagging data provider.
62
- await loadAndStoreNewTaggingIndexes(secret, app, start, end, aztecNode, taggingStore, anchorBlockHash, jobId);
60
+ await loadAndStoreNewTaggingIndexes(secret, app, start, end, aztecNode, taggingStore, jobId);
63
61
 
64
62
  // Retrieve all indexes within the current window from storage and update their status accordingly.
65
63
  const pendingTxHashes = await taggingStore.getTxHashesOfPendingIndexes(secret, start, end, jobId);
@@ -9,8 +9,11 @@ export async function getStatusChangeOfPending(
9
9
  pending: TxHash[],
10
10
  aztecNode: AztecNode,
11
11
  ): Promise<{ txHashesToFinalize: TxHash[]; txHashesToDrop: TxHash[] }> {
12
- // Get receipts for all pending tx hashes.
13
- const receipts = await Promise.all(pending.map(pendingTxHash => aztecNode.getTxReceipt(pendingTxHash)));
12
+ // Get receipts for all pending tx hashes and the finalized block number.
13
+ const [receipts, tips] = await Promise.all([
14
+ Promise.all(pending.map(pendingTxHash => aztecNode.getTxReceipt(pendingTxHash))),
15
+ aztecNode.getL2Tips(),
16
+ ]);
14
17
 
15
18
  const txHashesToFinalize: TxHash[] = [];
16
19
  const txHashesToDrop: TxHash[] = [];
@@ -19,16 +22,25 @@ export async function getStatusChangeOfPending(
19
22
  const receipt = receipts[i];
20
23
  const txHash = pending[i];
21
24
 
22
- if (receipt.status === TxStatus.FINALIZED && receipt.hasExecutionSucceeded()) {
25
+ if (
26
+ receipt.status === TxStatus.SUCCESS &&
27
+ receipt.blockNumber &&
28
+ receipt.blockNumber <= tips.finalized.block.number
29
+ ) {
23
30
  // Tx has been included in a block and the corresponding block is finalized --> we mark the indexes as
24
31
  // finalized.
25
32
  txHashesToFinalize.push(txHash);
26
- } else if (receipt.isDropped() || receipt.hasExecutionReverted()) {
33
+ } else if (
34
+ receipt.status === TxStatus.DROPPED ||
35
+ receipt.status === TxStatus.APP_LOGIC_REVERTED ||
36
+ receipt.status === TxStatus.TEARDOWN_REVERTED ||
37
+ receipt.status === TxStatus.BOTH_REVERTED
38
+ ) {
27
39
  // Tx was dropped or reverted --> we drop the corresponding pending indexes.
28
40
  // TODO(#17615): Don't drop pending indexes corresponding to non-revertible phases.
29
41
  txHashesToDrop.push(txHash);
30
42
  } else {
31
- // Tx is still pending, not yet finalized, or was mined successfully but not yet finalized --> we don't do anything.
43
+ // Tx is still pending or the corresponding block is not yet finalized --> we don't do anything.
32
44
  }
33
45
  }
34
46
 
@@ -1,12 +1,10 @@
1
1
  import type { AztecAddress } from '@aztec/stdlib/aztec-address';
2
- import type { L2BlockHash } from '@aztec/stdlib/block';
3
2
  import type { AztecNode } from '@aztec/stdlib/interfaces/server';
4
3
  import type { DirectionalAppTaggingSecret, PreTag } from '@aztec/stdlib/logs';
5
4
  import { SiloedTag, Tag } from '@aztec/stdlib/logs';
6
5
  import { TxHash } from '@aztec/stdlib/tx';
7
6
 
8
7
  import type { SenderTaggingStore } from '../../../storage/tagging_store/sender_tagging_store.js';
9
- import { getAllPrivateLogsByTags } from '../../get_all_logs_by_tags.js';
10
8
 
11
9
  /**
12
10
  * Loads tagging indexes from the Aztec node and stores them in the tagging data provider.
@@ -29,7 +27,6 @@ export async function loadAndStoreNewTaggingIndexes(
29
27
  end: number,
30
28
  aztecNode: AztecNode,
31
29
  taggingStore: SenderTaggingStore,
32
- anchorBlockHash: L2BlockHash,
33
30
  jobId: string,
34
31
  ) {
35
32
  // We compute the tags for the current window of indexes
@@ -40,7 +37,7 @@ export async function loadAndStoreNewTaggingIndexes(
40
37
  preTagsForWindow.map(async preTag => SiloedTag.compute(await Tag.compute(preTag), app)),
41
38
  );
42
39
 
43
- const txsForTags = await getTxsContainingTags(siloedTagsForWindow, aztecNode, anchorBlockHash);
40
+ const txsForTags = await getTxsContainingTags(siloedTagsForWindow, aztecNode);
44
41
  const highestIndexMap = getTxHighestIndexMap(txsForTags, preTagsForWindow);
45
42
 
46
43
  // Now we iterate over the map, reconstruct the preTags and tx hash and store them in the db.
@@ -52,14 +49,8 @@ export async function loadAndStoreNewTaggingIndexes(
52
49
 
53
50
  // Returns txs that used the given tags. A tag might have been used in multiple txs and for this reason we return
54
51
  // an array for each tag.
55
- async function getTxsContainingTags(
56
- tags: SiloedTag[],
57
- aztecNode: AztecNode,
58
- anchorBlockHash: L2BlockHash,
59
- ): Promise<TxHash[][]> {
60
- // We use the utility function below to retrieve all logs for the tags across all pages, so we don't need to handle
61
- // pagination here.
62
- const allLogs = await getAllPrivateLogsByTags(aztecNode, tags, anchorBlockHash);
52
+ async function getTxsContainingTags(tags: SiloedTag[], aztecNode: AztecNode): Promise<TxHash[][]> {
53
+ const allLogs = await aztecNode.getPrivateLogsByTags(tags);
63
54
  return allLogs.map(logs => logs.map(log => log.txHash));
64
55
  }
65
56