@aztec/pxe 4.0.0-nightly.20260113 → 4.0.0-nightly.20260114

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 (42) hide show
  1. package/dest/contract_function_simulator/oracle/interfaces.d.ts +3 -3
  2. package/dest/contract_function_simulator/oracle/interfaces.d.ts.map +1 -1
  3. package/dest/contract_function_simulator/oracle/note_packing_utils.d.ts +4 -4
  4. package/dest/contract_function_simulator/oracle/note_packing_utils.d.ts.map +1 -1
  5. package/dest/contract_function_simulator/oracle/note_packing_utils.js +5 -5
  6. package/dest/contract_function_simulator/oracle/oracle.js +1 -1
  7. package/dest/contract_function_simulator/oracle/private_execution_oracle.d.ts +1 -1
  8. package/dest/contract_function_simulator/oracle/private_execution_oracle.d.ts.map +1 -1
  9. package/dest/contract_function_simulator/oracle/private_execution_oracle.js +2 -1
  10. package/dest/events/event_service.d.ts +1 -1
  11. package/dest/events/event_service.d.ts.map +1 -1
  12. package/dest/events/event_service.js +8 -12
  13. package/dest/notes/note_service.d.ts +2 -2
  14. package/dest/notes/note_service.d.ts.map +1 -1
  15. package/dest/notes/note_service.js +14 -22
  16. package/dest/private_kernel/private_kernel_oracle.d.ts +23 -28
  17. package/dest/private_kernel/private_kernel_oracle.d.ts.map +1 -1
  18. package/dest/private_kernel/private_kernel_oracle.js +92 -2
  19. package/dest/pxe.d.ts +7 -36
  20. package/dest/pxe.d.ts.map +1 -1
  21. package/dest/pxe.js +8 -58
  22. package/dest/storage/note_store/note_store.d.ts +3 -4
  23. package/dest/storage/note_store/note_store.d.ts.map +1 -1
  24. package/dest/storage/note_store/note_store.js +63 -70
  25. package/dest/storage/private_event_store/private_event_store.d.ts +10 -5
  26. package/dest/storage/private_event_store/private_event_store.d.ts.map +1 -1
  27. package/dest/storage/private_event_store/private_event_store.js +55 -41
  28. package/package.json +16 -16
  29. package/src/contract_function_simulator/oracle/interfaces.ts +2 -2
  30. package/src/contract_function_simulator/oracle/note_packing_utils.ts +6 -6
  31. package/src/contract_function_simulator/oracle/oracle.ts +1 -1
  32. package/src/contract_function_simulator/oracle/private_execution_oracle.ts +1 -0
  33. package/src/events/event_service.ts +12 -26
  34. package/src/notes/note_service.ts +14 -23
  35. package/src/private_kernel/private_kernel_oracle.ts +119 -37
  36. package/src/pxe.ts +8 -81
  37. package/src/storage/note_store/note_store.ts +66 -65
  38. package/src/storage/private_event_store/private_event_store.ts +72 -45
  39. package/dest/private_kernel/private_kernel_oracle_impl.d.ts +0 -46
  40. package/dest/private_kernel/private_kernel_oracle_impl.d.ts.map +0 -1
  41. package/dest/private_kernel/private_kernel_oracle_impl.js +0 -85
  42. package/src/private_kernel/private_kernel_oracle_impl.ts +0 -127
package/src/pxe.ts CHANGED
@@ -19,14 +19,13 @@ import type { AuthWitness } from '@aztec/stdlib/auth-witness';
19
19
  import type { AztecAddress } from '@aztec/stdlib/aztec-address';
20
20
  import {
21
21
  CompleteAddress,
22
- type ContractClassWithId,
23
22
  type ContractInstanceWithAddress,
24
23
  type PartialAddress,
25
24
  computeContractAddressFromInstance,
26
25
  getContractClassFromArtifact,
27
26
  } from '@aztec/stdlib/contract';
28
27
  import { SimulationError } from '@aztec/stdlib/errors';
29
- import { computeProtocolNullifier, siloNullifier } from '@aztec/stdlib/hash';
28
+ import { computeProtocolNullifier } from '@aztec/stdlib/hash';
30
29
  import type { AztecNode, PrivateKernelProver } from '@aztec/stdlib/interfaces/client';
31
30
  import type {
32
31
  PrivateExecutionStep,
@@ -69,7 +68,7 @@ import {
69
68
  PrivateKernelExecutionProver,
70
69
  type PrivateKernelExecutionProverConfig,
71
70
  } from './private_kernel/private_kernel_execution_prover.js';
72
- import { PrivateKernelOracleImpl } from './private_kernel/private_kernel_oracle_impl.js';
71
+ import { PrivateKernelOracle } from './private_kernel/private_kernel_oracle.js';
73
72
  import { AddressStore } from './storage/address_store/address_store.js';
74
73
  import { AnchorBlockStore } from './storage/anchor_block_store/anchor_block_store.js';
75
74
  import { CapsuleStore } from './storage/capsule_store/capsule_store.js';
@@ -276,19 +275,6 @@ export class PXE {
276
275
  this.log.verbose(`Registered protocol contracts in pxe`, registered);
277
276
  }
278
277
 
279
- async #isContractClassPubliclyRegistered(id: Fr): Promise<boolean> {
280
- return !!(await this.node.getContractClass(id));
281
- }
282
-
283
- async #isContractPublished(address: AztecAddress): Promise<boolean> {
284
- return !!(await this.node.getContract(address));
285
- }
286
-
287
- async #isContractInitialized(address: AztecAddress): Promise<boolean> {
288
- const initNullifier = await siloNullifier(address, address.toField());
289
- return !!(await this.node.getNullifierMembershipWitness('latest', initNullifier));
290
- }
291
-
292
278
  // Executes the entrypoint private function, as well as all nested private
293
279
  // functions that might arise.
294
280
  async #executePrivate(
@@ -397,12 +383,7 @@ export class PXE {
397
383
  config: PrivateKernelExecutionProverConfig,
398
384
  ): Promise<PrivateKernelExecutionProofOutput<PrivateKernelTailCircuitPublicInputs>> {
399
385
  const simulationAnchorBlock = privateExecutionResult.getSimulationAnchorBlockNumber();
400
- const kernelOracle = new PrivateKernelOracleImpl(
401
- this.contractStore,
402
- this.keyStore,
403
- this.node,
404
- simulationAnchorBlock,
405
- );
386
+ const kernelOracle = new PrivateKernelOracle(this.contractStore, this.keyStore, this.node, simulationAnchorBlock);
406
387
  const kernelTraceProver = new PrivateKernelExecutionProver(kernelOracle, proofCreator, !this.proverEnabled);
407
388
  this.log.debug(`Executing kernel trace prover (${JSON.stringify(config)})...`);
408
389
  return await kernelTraceProver.proveWithKernels(txExecutionRequest.toTxRequest(), privateExecutionResult, config);
@@ -415,66 +396,12 @@ export class PXE {
415
396
  }
416
397
 
417
398
  /**
418
- * Returns the contract class metadata given a contract class id.
419
- * The metadata consists of its contract class, whether it has been publicly registered, and its artifact.
420
- * @remark - it queries the node to check whether the contract class with the given id has been publicly registered.
421
- * @param id - Identifier of the class.
422
- * @param includeArtifact - Identifier of the class.
423
- * @returns - It returns the contract class metadata, with the artifact field being optional, and will only be returned if true is passed in
424
- * for `includeArtifact`
425
- * TODO(@spalladino): The PXE actually holds artifacts and not classes, what should we return? Also,
426
- * should the pxe query the node for contract public info, and merge it with its own definitions?
427
- * TODO(@spalladino): This method is strictly needed to decide whether to publicly register a class or not
428
- * during a public deployment. We probably want a nicer and more general API for this, but it'll have to
429
- * do for the time being.
399
+ * Returns the contract artifact for a given contract class id, if it's registered in the PXE.
400
+ * @param id - Identifier of the contract class.
401
+ * @returns The contract artifact if found, undefined otherwise.
430
402
  */
431
- public async getContractClassMetadata(
432
- id: Fr,
433
- includeArtifact: boolean = false,
434
- ): Promise<{
435
- contractClass: ContractClassWithId | undefined;
436
- isContractClassPubliclyRegistered: boolean;
437
- artifact: ContractArtifact | undefined;
438
- }> {
439
- const artifact = await this.contractStore.getContractArtifact(id);
440
- if (!artifact) {
441
- this.log.warn(`No artifact found for contract class ${id.toString()} when looking for its metadata`);
442
- }
443
-
444
- return {
445
- contractClass: artifact && (await getContractClassFromArtifact(artifact)),
446
- isContractClassPubliclyRegistered: await this.#isContractClassPubliclyRegistered(id),
447
- artifact: includeArtifact ? artifact : undefined,
448
- };
449
- }
450
-
451
- /**
452
- * Returns the contract metadata given an address.
453
- * The metadata consists of its contract instance, which includes the contract class identifier,
454
- * initialization hash, deployment salt, and public keys hash; whether the contract instance has been initialized;
455
- * and whether the contract instance with the given address has been publicly deployed.
456
- * @remark - it queries the node to check whether the contract instance has been initialized / publicly deployed through a node.
457
- * This query is not dependent on the PXE.
458
- * @param address - The address that the contract instance resides at.
459
- * @returns - It returns the contract metadata
460
- * TODO(@spalladino): Should we return the public keys in plain as well here?
461
- */
462
- public async getContractMetadata(address: AztecAddress): Promise<{
463
- contractInstance: ContractInstanceWithAddress | undefined;
464
- isContractInitialized: boolean;
465
- isContractPublished: boolean;
466
- }> {
467
- let instance;
468
- try {
469
- instance = await this.contractStore.getContractInstance(address);
470
- } catch {
471
- this.log.warn(`No instance found for contract ${address.toString()} when looking for its metadata`);
472
- }
473
- return {
474
- contractInstance: instance,
475
- isContractInitialized: await this.#isContractInitialized(address),
476
- isContractPublished: await this.#isContractPublished(address),
477
- };
403
+ public async getContractArtifact(id: Fr): Promise<ContractArtifact | undefined> {
404
+ return await this.contractStore.getContractArtifact(id);
478
405
  }
479
406
 
480
407
  /**
@@ -1,4 +1,3 @@
1
- import { toBufferBE } from '@aztec/foundation/bigint-buffer';
2
1
  import type { Fr } from '@aztec/foundation/curves/bn254';
3
2
  import { toArray } from '@aztec/foundation/iterable';
4
3
  import type { AztecAsyncKVStore, AztecAsyncMap, AztecAsyncMultiMap } from '@aztec/kv-store';
@@ -15,32 +14,41 @@ import { NoteDao } from '@aztec/stdlib/note';
15
14
  **/
16
15
  export class NoteStore {
17
16
  #store: AztecAsyncKVStore;
17
+
18
+ // Note that we use the siloedNullifier as the note id in the store as it's guaranteed to be unique.
19
+
20
+ /** noteId (siloedNullifier) -> NoteDao (serialized) */
18
21
  #notes: AztecAsyncMap<string, Buffer>;
22
+ /** noteId (siloedNullifier) -> NoteDao (serialized) */
19
23
  #nullifiedNotes: AztecAsyncMap<string, Buffer>;
20
- #nullifierToNoteId: AztecAsyncMap<string, string>;
24
+ /** blockNumber -> siloedNullifier */
21
25
  #nullifiersByBlockNumber: AztecAsyncMultiMap<number, string>;
22
26
 
27
+ /** noteId (siloedNullifier) -> scope */
23
28
  #nullifiedNotesToScope: AztecAsyncMultiMap<string, string>;
29
+ /** contractAddress -> noteId (siloedNullifier) */
24
30
  #nullifiedNotesByContract: AztecAsyncMultiMap<string, string>;
31
+ /** storageSlot -> noteId (siloedNullifier) */
25
32
  #nullifiedNotesByStorageSlot: AztecAsyncMultiMap<string, string>;
26
- #nullifiedNotesByNullifier: AztecAsyncMap<string, string>;
27
33
 
34
+ /** scope (AztecAddress) -> true */
28
35
  #scopes: AztecAsyncMap<string, true>;
36
+ /** noteId (siloedNullifier) -> scope */
29
37
  #notesToScope: AztecAsyncMultiMap<string, string>;
38
+ /** scope -> MultiMap(contractAddress -> noteId) */
30
39
  #notesByContractAndScope: Map<string, AztecAsyncMultiMap<string, string>>;
40
+ /** scope -> MultiMap(storageSlot -> noteId) */
31
41
  #notesByStorageSlotAndScope: Map<string, AztecAsyncMultiMap<string, string>>;
32
42
 
33
43
  private constructor(store: AztecAsyncKVStore) {
34
44
  this.#store = store;
35
45
  this.#notes = store.openMap('notes');
36
46
  this.#nullifiedNotes = store.openMap('nullified_notes');
37
- this.#nullifierToNoteId = store.openMap('nullifier_to_note');
38
47
  this.#nullifiersByBlockNumber = store.openMultiMap('nullifier_to_block_number');
39
48
 
40
49
  this.#nullifiedNotesToScope = store.openMultiMap('nullified_notes_to_scope');
41
50
  this.#nullifiedNotesByContract = store.openMultiMap('nullified_notes_by_contract');
42
51
  this.#nullifiedNotesByStorageSlot = store.openMultiMap('nullified_notes_by_storage_slot');
43
- this.#nullifiedNotesByNullifier = store.openMap('nullified_notes_by_nullifier');
44
52
 
45
53
  this.#scopes = store.openMap('scopes');
46
54
  this.#notesToScope = store.openMultiMap('notes_to_scope');
@@ -92,9 +100,8 @@ export class NoteStore {
92
100
  /**
93
101
  * Adds multiple notes to the data provider under the specified scope.
94
102
  *
95
- * Notes are stored using their index from the notes hash tree as the key, which provides
96
- * uniqueness and maintains creation order. Each note is indexed by multiple criteria
97
- * for efficient retrieval.
103
+ * Notes are stored using their siloedNullifier as the key, which provides uniqueness. Each note is indexed
104
+ * by multiple criteria for efficient retrieval.
98
105
  *
99
106
  * @param notes - Notes to store
100
107
  * @param scope - The scope (user/account) under which to store the notes
@@ -106,13 +113,12 @@ export class NoteStore {
106
113
  }
107
114
 
108
115
  for (const dao of notes) {
109
- const noteIndex = toBufferBE(dao.index, 32).toString('hex');
110
- await this.#notes.set(noteIndex, dao.toBuffer());
111
- await this.#notesToScope.set(noteIndex, scope.toString());
112
- await this.#nullifierToNoteId.set(dao.siloedNullifier.toString(), noteIndex);
116
+ const noteId = dao.siloedNullifier.toString();
117
+ await this.#notes.set(noteId, dao.toBuffer());
118
+ await this.#notesToScope.set(noteId, scope.toString());
113
119
 
114
- await this.#notesByContractAndScope.get(scope.toString())!.set(dao.contractAddress.toString(), noteIndex);
115
- await this.#notesByStorageSlotAndScope.get(scope.toString())!.set(dao.storageSlot.toString(), noteIndex);
120
+ await this.#notesByContractAndScope.get(scope.toString())!.set(dao.contractAddress.toString(), noteId);
121
+ await this.#notesByStorageSlotAndScope.get(scope.toString())!.set(dao.storageSlot.toString(), noteId);
116
122
  }
117
123
  });
118
124
  }
@@ -147,14 +153,13 @@ export class NoteStore {
147
153
  for (const note of notes) {
148
154
  const noteDao = NoteDao.fromBuffer(note);
149
155
  if (noteDao.l2BlockNumber > blockNumber) {
150
- const noteIndex = toBufferBE(noteDao.index, 32).toString('hex');
151
- await this.#notes.delete(noteIndex);
152
- await this.#notesToScope.delete(noteIndex);
153
- await this.#nullifierToNoteId.delete(noteDao.siloedNullifier.toString());
156
+ const noteId = noteDao.siloedNullifier.toString();
157
+ await this.#notes.delete(noteId);
158
+ await this.#notesToScope.delete(noteId);
154
159
  const scopes = await toArray(this.#scopes.keysAsync());
155
160
  for (const scope of scopes) {
156
- await this.#notesByContractAndScope.get(scope)!.deleteValue(noteDao.contractAddress.toString(), noteIndex);
157
- await this.#notesByStorageSlotAndScope.get(scope)!.deleteValue(noteDao.storageSlot.toString(), noteIndex);
161
+ await this.#notesByContractAndScope.get(scope)!.deleteValue(noteDao.contractAddress.toString(), noteId);
162
+ await this.#notesByStorageSlotAndScope.get(scope)!.deleteValue(noteDao.storageSlot.toString(), noteId);
158
163
  }
159
164
  }
160
165
  }
@@ -171,50 +176,45 @@ export class NoteStore {
171
176
  * @param synchedBlockNumber - Upper bound for the block range to process
172
177
  */
173
178
  async #rewindNullifiersAfterBlock(blockNumber: number, synchedBlockNumber: number): Promise<void> {
174
- const nullifiersToUndo: string[] = [];
179
+ const noteIdsToReinsert: string[] = [];
175
180
  const currentBlockNumber = blockNumber + 1;
176
181
  for (let i = currentBlockNumber; i <= synchedBlockNumber; i++) {
177
- nullifiersToUndo.push(...(await toArray(this.#nullifiersByBlockNumber.getValuesAsync(i))));
182
+ // noteId === siloedNullifier.toString(), so we can use nullifiers directly as noteIds
183
+ noteIdsToReinsert.push(...(await toArray(this.#nullifiersByBlockNumber.getValuesAsync(i))));
178
184
  }
179
- const notesIndexesToReinsert = await Promise.all(
180
- nullifiersToUndo.map(nullifier => this.#nullifiedNotesByNullifier.getAsync(nullifier)),
181
- );
182
- const notNullNoteIndexes = notesIndexesToReinsert.filter(noteIndex => noteIndex != undefined);
183
185
  const nullifiedNoteBuffers = await Promise.all(
184
- notNullNoteIndexes.map(noteIndex => this.#nullifiedNotes.getAsync(noteIndex!)),
186
+ noteIdsToReinsert.map(noteId => this.#nullifiedNotes.getAsync(noteId)),
185
187
  );
186
188
  const noteDaos = nullifiedNoteBuffers
187
189
  .filter(buffer => buffer != undefined)
188
190
  .map(buffer => NoteDao.fromBuffer(buffer!));
189
191
 
190
192
  for (const dao of noteDaos) {
191
- const noteIndex = toBufferBE(dao.index, 32).toString('hex');
193
+ const noteId = dao.siloedNullifier.toString();
192
194
 
193
- const scopes = await toArray(this.#nullifiedNotesToScope.getValuesAsync(noteIndex));
195
+ const scopes = await toArray(this.#nullifiedNotesToScope.getValuesAsync(noteId));
194
196
 
195
197
  if (scopes.length === 0) {
196
198
  // We should never run into this error because notes always have a scope assigned to them - either on initial
197
199
  // insertion via `addNotes` or when removing their nullifiers.
198
- throw new Error(`No scopes found for nullified note with index ${noteIndex}`);
200
+ throw new Error(`No scopes found for nullified note with nullifier ${noteId}`);
199
201
  }
200
202
 
201
203
  for (const scope of scopes) {
202
204
  await Promise.all([
203
- this.#notesByContractAndScope.get(scope.toString())!.set(dao.contractAddress.toString(), noteIndex),
204
- this.#notesByStorageSlotAndScope.get(scope.toString())!.set(dao.storageSlot.toString(), noteIndex),
205
- this.#notesToScope.set(noteIndex, scope),
205
+ this.#notesByContractAndScope.get(scope.toString())!.set(dao.contractAddress.toString(), noteId),
206
+ this.#notesByStorageSlotAndScope.get(scope.toString())!.set(dao.storageSlot.toString(), noteId),
207
+ this.#notesToScope.set(noteId, scope),
206
208
  ]);
207
209
  }
208
210
 
209
211
  await Promise.all([
210
- this.#notes.set(noteIndex, dao.toBuffer()),
211
- this.#nullifierToNoteId.set(dao.siloedNullifier.toString(), noteIndex),
212
- this.#nullifiedNotes.delete(noteIndex),
213
- this.#nullifiedNotesToScope.delete(noteIndex),
212
+ this.#notes.set(noteId, dao.toBuffer()),
213
+ this.#nullifiedNotes.delete(noteId),
214
+ this.#nullifiedNotesToScope.delete(noteId),
214
215
  this.#nullifiersByBlockNumber.deleteValue(dao.l2BlockNumber, dao.siloedNullifier.toString()),
215
- this.#nullifiedNotesByContract.deleteValue(dao.contractAddress.toString(), noteIndex),
216
- this.#nullifiedNotesByStorageSlot.deleteValue(dao.storageSlot.toString(), noteIndex),
217
- this.#nullifiedNotesByNullifier.delete(dao.siloedNullifier.toString()),
216
+ this.#nullifiedNotesByContract.deleteValue(dao.contractAddress.toString(), noteId),
217
+ this.#nullifiedNotesByStorageSlot.deleteValue(dao.storageSlot.toString(), noteId),
218
218
  ]);
219
219
  }
220
220
  }
@@ -334,6 +334,17 @@ export class NoteStore {
334
334
  }
335
335
  }
336
336
 
337
+ // Sort by block number, then by tx index within block, then by note index within tx
338
+ deduplicated.sort((a, b) => {
339
+ if (a.l2BlockNumber !== b.l2BlockNumber) {
340
+ return a.l2BlockNumber - b.l2BlockNumber;
341
+ }
342
+ if (a.txIndexInBlock !== b.txIndexInBlock) {
343
+ return a.txIndexInBlock - b.txIndexInBlock;
344
+ }
345
+ return a.noteIndexInTx - b.noteIndexInTx;
346
+ });
347
+
337
348
  return deduplicated;
338
349
  }
339
350
 
@@ -358,25 +369,18 @@ export class NoteStore {
358
369
 
359
370
  for (const blockScopedNullifier of nullifiers) {
360
371
  const { data: nullifier, l2BlockNumber: blockNumber } = blockScopedNullifier;
361
- const nullifierKey = nullifier.toString();
372
+ const noteId = nullifier.toString();
362
373
 
363
- const noteIndex = await this.#nullifierToNoteId.getAsync(nullifierKey);
364
- if (!noteIndex) {
365
- // Check if already nullified?
366
- const alreadyNullified = await this.#nullifiedNotesByNullifier.getAsync(nullifierKey);
367
- if (alreadyNullified) {
374
+ const noteBuffer = await this.#notes.getAsync(noteId);
375
+ if (!noteBuffer) {
376
+ // Check if already nullified (noteId === siloedNullifier, so we can check #nullifiedNotes directly)
377
+ if (await this.#nullifiedNotes.hasAsync(noteId)) {
368
378
  throw new Error(`Nullifier already applied in applyNullifiers`);
369
379
  }
370
380
  throw new Error('Nullifier not found in applyNullifiers');
371
381
  }
372
382
 
373
- const noteBuffer = noteIndex ? await this.#notes.getAsync(noteIndex) : undefined;
374
-
375
- if (!noteBuffer) {
376
- throw new Error('Note not found in applyNullifiers');
377
- }
378
-
379
- const noteScopes = await toArray(this.#notesToScope.getValuesAsync(noteIndex));
383
+ const noteScopes = await toArray(this.#notesToScope.getValuesAsync(noteId));
380
384
  if (noteScopes.length === 0) {
381
385
  // We should never run into this error because notes always have a scope assigned to them - either on initial
382
386
  // insertion via `addNotes` or when removing their nullifiers.
@@ -387,26 +391,23 @@ export class NoteStore {
387
391
 
388
392
  nullifiedNotes.push(note);
389
393
 
390
- await this.#notes.delete(noteIndex);
391
- await this.#notesToScope.delete(noteIndex);
394
+ await this.#notes.delete(noteId);
395
+ await this.#notesToScope.delete(noteId);
392
396
 
393
397
  const scopes = await toArray(this.#scopes.keysAsync());
394
398
 
395
399
  for (const scope of scopes) {
396
- await this.#notesByContractAndScope.get(scope)!.deleteValue(note.contractAddress.toString(), noteIndex);
397
- await this.#notesByStorageSlotAndScope.get(scope)!.deleteValue(note.storageSlot.toString(), noteIndex);
400
+ await this.#notesByContractAndScope.get(scope)!.deleteValue(note.contractAddress.toString(), noteId);
401
+ await this.#notesByStorageSlotAndScope.get(scope)!.deleteValue(note.storageSlot.toString(), noteId);
398
402
  }
399
403
 
400
404
  for (const scope of noteScopes) {
401
- await this.#nullifiedNotesToScope.set(noteIndex, scope);
405
+ await this.#nullifiedNotesToScope.set(noteId, scope);
402
406
  }
403
- await this.#nullifiedNotes.set(noteIndex, note.toBuffer());
404
- await this.#nullifiersByBlockNumber.set(blockNumber, nullifier.toString());
405
- await this.#nullifiedNotesByContract.set(note.contractAddress.toString(), noteIndex);
406
- await this.#nullifiedNotesByStorageSlot.set(note.storageSlot.toString(), noteIndex);
407
- await this.#nullifiedNotesByNullifier.set(nullifier.toString(), noteIndex);
408
-
409
- await this.#nullifierToNoteId.delete(nullifier.toString());
407
+ await this.#nullifiedNotes.set(noteId, note.toBuffer());
408
+ await this.#nullifiersByBlockNumber.set(blockNumber, noteId);
409
+ await this.#nullifiedNotesByContract.set(note.contractAddress.toString(), noteId);
410
+ await this.#nullifiedNotesByStorageSlot.set(note.storageSlot.toString(), noteId);
410
411
  }
411
412
  return nullifiedNotes;
412
413
  });
@@ -21,10 +21,13 @@ export type PrivateEventStoreFilter = {
21
21
  type PrivateEventEntry = {
22
22
  randomness: Fr; // Note that this value is currently not being returned on queries and is therefore temporarily unused
23
23
  msgContent: Buffer;
24
- eventCommitmentIndex: number;
25
24
  l2BlockNumber: number;
26
25
  l2BlockHash: Buffer;
27
26
  txHash: Buffer;
27
+ /** The index of the tx within the block, used for ordering events */
28
+ txIndexInBlock: number;
29
+ /** The index of the event within the tx (based on nullifier position), used for ordering events */
30
+ eventIndexInTx: number;
28
31
  /** The lookup key for #eventsByContractScopeSelector, used for cleanup during rollback */
29
32
  lookupKey: string;
30
33
  };
@@ -32,6 +35,10 @@ type PrivateEventEntry = {
32
35
  type PrivateEventMetadata = InTx & {
33
36
  contractAddress: AztecAddress;
34
37
  scope: AztecAddress;
38
+ /** The index of the tx within the block */
39
+ txIndexInBlock: number;
40
+ /** The index of the event within the tx (based on nullifier position) */
41
+ eventIndexInTx: number;
35
42
  };
36
43
 
37
44
  /**
@@ -39,14 +46,14 @@ type PrivateEventMetadata = InTx & {
39
46
  */
40
47
  export class PrivateEventStore {
41
48
  #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>;
49
+ /** Map storing the actual private event log entries, keyed by siloedEventCommitment */
50
+ #eventLogs: AztecAsyncMap<string, PrivateEventEntry>;
51
+ /** Map from contractAddress_scope_eventSelector to siloedEventCommitment[] for efficient lookup */
52
+ #eventsByContractScopeSelector: AztecAsyncMap<string, string[]>;
53
+ /** Map from block number to siloedEventCommitment[] for rollback support */
54
+ #eventsByBlockNumber: AztecAsyncMap<number, string[]>;
55
+ /** Map from siloedEventCommitment to boolean indicating if log has been seen. */
56
+ #seenLogs: AztecAsyncMap<string, boolean>;
50
57
 
51
58
  logger = createLogger('private_event_store');
52
59
 
@@ -65,8 +72,9 @@ export class PrivateEventStore {
65
72
  /**
66
73
  * Store a private event log.
67
74
  * @param eventSelector - The event selector of the event.
75
+ * @param randomness - The randomness used for the event commitment.
68
76
  * @param msgContent - The content of the event.
69
- * @param eventCommitmentIndex - The index of the event commitment in the nullifier tree.
77
+ * @param siloedEventCommitment - The siloed event commitment (used as unique identifier).
70
78
  * @param metadata
71
79
  * contractAddress - The address of the contract that emitted the event.
72
80
  * scope - The address to which the event is scoped.
@@ -77,41 +85,45 @@ export class PrivateEventStore {
77
85
  eventSelector: EventSelector,
78
86
  randomness: Fr,
79
87
  msgContent: Fr[],
80
- eventCommitmentIndex: number,
88
+ siloedEventCommitment: Fr,
81
89
  metadata: PrivateEventMetadata,
82
90
  ): Promise<void> {
83
- const { contractAddress, scope, txHash, l2BlockNumber, l2BlockHash } = metadata;
91
+ const { contractAddress, scope, txHash, l2BlockNumber, l2BlockHash, txIndexInBlock, eventIndexInTx } = metadata;
84
92
 
85
93
  return this.#store.transactionAsync(async () => {
86
94
  const key = this.#keyFor(contractAddress, scope, eventSelector);
87
95
 
88
- // Check if this exact log has already been stored using eventCommitmentIndex as unique identifier
89
- const hasBeenSeen = await this.#seenLogs.getAsync(eventCommitmentIndex);
96
+ // The siloed event commitment is guaranteed to be unique as it's inserted into the nullifier tree. For this
97
+ // reason we use it as id.
98
+ const eventId = siloedEventCommitment.toString();
99
+
100
+ const hasBeenSeen = await this.#seenLogs.getAsync(eventId);
90
101
  if (hasBeenSeen) {
91
- this.logger.verbose('Ignoring duplicate event log', { txHash: txHash.toString(), eventCommitmentIndex });
102
+ this.logger.verbose('Ignoring duplicate event log', { txHash: txHash.toString(), siloedEventCommitment });
92
103
  return;
93
104
  }
94
105
 
95
106
  this.logger.verbose('storing private event log', { contractAddress, scope, msgContent, l2BlockNumber });
96
107
 
97
- await this.#eventLogs.set(eventCommitmentIndex, {
108
+ await this.#eventLogs.set(eventId, {
98
109
  randomness,
99
110
  msgContent: serializeToBuffer(msgContent),
100
111
  l2BlockNumber,
101
112
  l2BlockHash: l2BlockHash.toBuffer(),
102
- eventCommitmentIndex,
103
113
  txHash: txHash.toBuffer(),
114
+ txIndexInBlock,
115
+ eventIndexInTx,
104
116
  lookupKey: key,
105
117
  });
106
118
 
107
- const existingIndices = (await this.#eventsByContractScopeSelector.getAsync(key)) || [];
108
- await this.#eventsByContractScopeSelector.set(key, [...existingIndices, eventCommitmentIndex]);
119
+ const existingIds = (await this.#eventsByContractScopeSelector.getAsync(key)) || [];
120
+ await this.#eventsByContractScopeSelector.set(key, [...existingIds, eventId]);
109
121
 
110
- const existingBlockIndices = (await this.#eventsByBlockNumber.getAsync(l2BlockNumber)) || [];
111
- await this.#eventsByBlockNumber.set(l2BlockNumber, [...existingBlockIndices, eventCommitmentIndex]);
122
+ const existingBlockIds = (await this.#eventsByBlockNumber.getAsync(l2BlockNumber)) || [];
123
+ await this.#eventsByBlockNumber.set(l2BlockNumber, [...existingBlockIds, eventId]);
112
124
 
113
- // Mark this log as seen using eventCommitmentIndex
114
- await this.#seenLogs.set(eventCommitmentIndex, true);
125
+ // Mark this log as seen
126
+ await this.#seenLogs.set(eventId, true);
115
127
  });
116
128
  }
117
129
 
@@ -123,21 +135,26 @@ export class PrivateEventStore {
123
135
  * fromBlock: The block number to search from (inclusive).
124
136
  * toBlock: The block number to search upto (exclusive).
125
137
  * 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 .
138
+ * @returns - The event log contents, augmented with metadata about the transaction and block in which the event was
139
+ * included.
128
140
  */
129
141
  public async getPrivateEvents(
130
142
  eventSelector: EventSelector,
131
143
  filter: PrivateEventStoreFilter,
132
144
  ): Promise<PackedPrivateEvent[]> {
133
- const events: Array<{ eventCommitmentIndex: number; event: PackedPrivateEvent }> = [];
145
+ const events: Array<{
146
+ l2BlockNumber: number;
147
+ txIndexInBlock: number;
148
+ eventIndexInTx: number;
149
+ event: PackedPrivateEvent;
150
+ }> = [];
134
151
 
135
152
  for (const scope of filter.scopes) {
136
153
  const key = this.#keyFor(filter.contractAddress, scope, eventSelector);
137
- const eventCommitmentIndices = (await this.#eventsByContractScopeSelector.getAsync(key)) || [];
154
+ const eventIds = (await this.#eventsByContractScopeSelector.getAsync(key)) || [];
138
155
 
139
- for (const eventCommitmentIndex of eventCommitmentIndices) {
140
- const entry = await this.#eventLogs.getAsync(eventCommitmentIndex);
156
+ for (const eventId of eventIds) {
157
+ const entry = await this.#eventLogs.getAsync(eventId);
141
158
  if (!entry || entry.l2BlockNumber < filter.fromBlock || entry.l2BlockNumber >= filter.toBlock) {
142
159
  continue;
143
160
  }
@@ -154,7 +171,9 @@ export class PrivateEventStore {
154
171
  }
155
172
 
156
173
  events.push({
157
- eventCommitmentIndex: entry.eventCommitmentIndex,
174
+ l2BlockNumber: entry.l2BlockNumber,
175
+ txIndexInBlock: entry.txIndexInBlock,
176
+ eventIndexInTx: entry.eventIndexInTx,
158
177
  event: {
159
178
  packedEvent: msgContent,
160
179
  l2BlockNumber: BlockNumber(entry.l2BlockNumber),
@@ -166,8 +185,16 @@ export class PrivateEventStore {
166
185
  }
167
186
  }
168
187
 
169
- // Sort by eventCommitmentIndex only
170
- events.sort((a, b) => a.eventCommitmentIndex - b.eventCommitmentIndex);
188
+ // Sort by block number, then by tx index within block, then by event index within tx
189
+ events.sort((a, b) => {
190
+ if (a.l2BlockNumber !== b.l2BlockNumber) {
191
+ return a.l2BlockNumber - b.l2BlockNumber;
192
+ }
193
+ if (a.txIndexInBlock !== b.txIndexInBlock) {
194
+ return a.txIndexInBlock - b.txIndexInBlock;
195
+ }
196
+ return a.eventIndexInTx - b.eventIndexInTx;
197
+ });
171
198
  return events.map(ev => ev.event);
172
199
  }
173
200
 
@@ -181,29 +208,29 @@ export class PrivateEventStore {
181
208
  let removedCount = 0;
182
209
 
183
210
  for (let block = blockNumber + 1; block <= synchedBlockNumber; block++) {
184
- const indices = await this.#eventsByBlockNumber.getAsync(block);
185
- if (indices) {
211
+ const eventIds = await this.#eventsByBlockNumber.getAsync(block);
212
+ if (eventIds) {
186
213
  await this.#eventsByBlockNumber.delete(block);
187
214
 
188
- for (const eventCommitmentIndex of indices) {
189
- const entry = await this.#eventLogs.getAsync(eventCommitmentIndex);
215
+ for (const eventId of eventIds) {
216
+ const entry = await this.#eventLogs.getAsync(eventId);
190
217
  if (!entry) {
191
- throw new Error(`Event log not found for eventCommitmentIndex ${eventCommitmentIndex}`);
218
+ throw new Error(`Event log not found for eventId ${eventId}`);
192
219
  }
193
220
 
194
- await this.#eventLogs.delete(eventCommitmentIndex);
195
- await this.#seenLogs.delete(eventCommitmentIndex);
221
+ await this.#eventLogs.delete(eventId);
222
+ await this.#seenLogs.delete(eventId);
196
223
 
197
224
  // Update #eventsByContractScopeSelector using the stored lookupKey
198
- const existingIndices = await this.#eventsByContractScopeSelector.getAsync(entry.lookupKey);
199
- if (!existingIndices || existingIndices.length === 0) {
200
- throw new Error(`No indices found in #eventsByContractScopeSelector for key ${entry.lookupKey}`);
225
+ const existingIds = await this.#eventsByContractScopeSelector.getAsync(entry.lookupKey);
226
+ if (!existingIds || existingIds.length === 0) {
227
+ throw new Error(`No ids found in #eventsByContractScopeSelector for key ${entry.lookupKey}`);
201
228
  }
202
- const filteredIndices = existingIndices.filter(idx => idx !== eventCommitmentIndex);
203
- if (filteredIndices.length === 0) {
229
+ const filteredIds = existingIds.filter(id => id !== eventId);
230
+ if (filteredIds.length === 0) {
204
231
  await this.#eventsByContractScopeSelector.delete(entry.lookupKey);
205
232
  } else {
206
- await this.#eventsByContractScopeSelector.set(entry.lookupKey, filteredIndices);
233
+ await this.#eventsByContractScopeSelector.set(entry.lookupKey, filteredIds);
207
234
  }
208
235
 
209
236
  removedCount++;
@@ -1,46 +0,0 @@
1
- import { NOTE_HASH_TREE_HEIGHT } from '@aztec/constants';
2
- import type { Fr } from '@aztec/foundation/curves/bn254';
3
- import type { GrumpkinScalar, Point } from '@aztec/foundation/curves/grumpkin';
4
- import { MembershipWitness } from '@aztec/foundation/trees';
5
- import type { KeyStore } from '@aztec/key-store';
6
- import type { FunctionSelector } from '@aztec/stdlib/abi';
7
- import type { AztecAddress } from '@aztec/stdlib/aztec-address';
8
- import type { BlockParameter } from '@aztec/stdlib/block';
9
- import type { AztecNode } from '@aztec/stdlib/interfaces/client';
10
- import { UpdatedClassIdHints } from '@aztec/stdlib/kernel';
11
- import type { NullifierMembershipWitness } from '@aztec/stdlib/trees';
12
- import type { VerificationKeyAsFields } from '@aztec/stdlib/vks';
13
- import type { ContractStore } from '../storage/contract_store/contract_store.js';
14
- import type { PrivateKernelOracle } from './private_kernel_oracle.js';
15
- /**
16
- * A data oracle that provides information needed for simulating a transaction.
17
- */
18
- export declare class PrivateKernelOracleImpl implements PrivateKernelOracle {
19
- private contractStore;
20
- private keyStore;
21
- private node;
22
- private blockNumber;
23
- private log;
24
- constructor(contractStore: ContractStore, keyStore: KeyStore, node: AztecNode, blockNumber?: BlockParameter, log?: import("@aztec/foundation/log").Logger);
25
- getContractAddressPreimage(address: AztecAddress): Promise<{
26
- version: 1;
27
- salt: Fr;
28
- deployer: AztecAddress;
29
- currentContractClassId: Fr;
30
- originalContractClassId: Fr;
31
- initializationHash: Fr;
32
- publicKeys: import("../../../stdlib/dest/keys/public_keys.js").PublicKeys;
33
- address: AztecAddress;
34
- saltedInitializationHash: Fr;
35
- }>;
36
- getContractClassIdPreimage(contractClassId: Fr): Promise<import("@aztec/stdlib/contract").ContractClassIdPreimage>;
37
- getFunctionMembershipWitness(contractClassId: Fr, selector: FunctionSelector): Promise<MembershipWitness<7>>;
38
- getVkMembershipWitness(vk: VerificationKeyAsFields): Promise<MembershipWitness<7>>;
39
- getNoteHashMembershipWitness(noteHash: Fr): Promise<MembershipWitness<typeof NOTE_HASH_TREE_HEIGHT> | undefined>;
40
- getNullifierMembershipWitness(nullifier: Fr): Promise<NullifierMembershipWitness | undefined>;
41
- getNoteHashTreeRoot(): Promise<Fr>;
42
- getMasterSecretKey(masterPublicKey: Point): Promise<GrumpkinScalar>;
43
- getDebugFunctionName(contractAddress: AztecAddress, selector: FunctionSelector): Promise<string>;
44
- getUpdatedClassIdHints(contractAddress: AztecAddress): Promise<UpdatedClassIdHints>;
45
- }
46
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJpdmF0ZV9rZXJuZWxfb3JhY2xlX2ltcGwuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9wcml2YXRlX2tlcm5lbC9wcml2YXRlX2tlcm5lbF9vcmFjbGVfaW1wbC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUscUJBQXFCLEVBQTJDLE1BQU0sa0JBQWtCLENBQUM7QUFDbEcsT0FBTyxLQUFLLEVBQUUsRUFBRSxFQUFFLE1BQU0sZ0NBQWdDLENBQUM7QUFDekQsT0FBTyxLQUFLLEVBQUUsY0FBYyxFQUFFLEtBQUssRUFBRSxNQUFNLG1DQUFtQyxDQUFDO0FBRS9FLE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBQzVELE9BQU8sS0FBSyxFQUFFLFFBQVEsRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBR2pELE9BQU8sS0FBSyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sbUJBQW1CLENBQUM7QUFDMUQsT0FBTyxLQUFLLEVBQUUsWUFBWSxFQUFFLE1BQU0sNkJBQTZCLENBQUM7QUFDaEUsT0FBTyxLQUFLLEVBQUUsY0FBYyxFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFJMUQsT0FBTyxLQUFLLEVBQUUsU0FBUyxFQUFFLE1BQU0saUNBQWlDLENBQUM7QUFDakUsT0FBTyxFQUFFLG1CQUFtQixFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFDM0QsT0FBTyxLQUFLLEVBQUUsMEJBQTBCLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUN0RSxPQUFPLEtBQUssRUFBRSx1QkFBdUIsRUFBRSxNQUFNLG1CQUFtQixDQUFDO0FBRWpFLE9BQU8sS0FBSyxFQUFFLGFBQWEsRUFBRSxNQUFNLDZDQUE2QyxDQUFDO0FBQ2pGLE9BQU8sS0FBSyxFQUFFLG1CQUFtQixFQUFFLE1BQU0sNEJBQTRCLENBQUM7QUFLdEU7O0dBRUc7QUFDSCxxQkFBYSx1QkFBd0IsWUFBVyxtQkFBbUI7SUFFL0QsT0FBTyxDQUFDLGFBQWE7SUFDckIsT0FBTyxDQUFDLFFBQVE7SUFDaEIsT0FBTyxDQUFDLElBQUk7SUFDWixPQUFPLENBQUMsV0FBVztJQUNuQixPQUFPLENBQUMsR0FBRztJQUxiLFlBQ1UsYUFBYSxFQUFFLGFBQWEsRUFDNUIsUUFBUSxFQUFFLFFBQVEsRUFDbEIsSUFBSSxFQUFFLFNBQVMsRUFDZixXQUFXLEdBQUUsY0FBeUIsRUFDdEMsR0FBRyx5Q0FBb0MsRUFDN0M7SUFFUywwQkFBMEIsQ0FBQyxPQUFPLEVBQUUsWUFBWTs7Ozs7Ozs7OztPQVM1RDtJQUVZLDBCQUEwQixDQUFDLGVBQWUsRUFBRSxFQUFFLHFFQU0xRDtJQUVZLDRCQUE0QixDQUFDLGVBQWUsRUFBRSxFQUFFLEVBQUUsUUFBUSxFQUFFLGdCQUFnQixpQ0FReEY7SUFFTSxzQkFBc0IsQ0FBQyxFQUFFLEVBQUUsdUJBQXVCLGlDQUd4RDtJQUVELDRCQUE0QixDQUFDLFFBQVEsRUFBRSxFQUFFLEdBQUcsT0FBTyxDQUFDLGlCQUFpQixDQUFDLE9BQU8scUJBQXFCLENBQUMsR0FBRyxTQUFTLENBQUMsQ0FFL0c7SUFFRCw2QkFBNkIsQ0FBQyxTQUFTLEVBQUUsRUFBRSxHQUFHLE9BQU8sQ0FBQywwQkFBMEIsR0FBRyxTQUFTLENBQUMsQ0FFNUY7SUFFSyxtQkFBbUIsSUFBSSxPQUFPLENBQUMsRUFBRSxDQUFDLENBTXZDO0lBRU0sa0JBQWtCLENBQUMsZUFBZSxFQUFFLEtBQUssR0FBRyxPQUFPLENBQUMsY0FBYyxDQUFDLENBRXpFO0lBRU0sb0JBQW9CLENBQUMsZUFBZSxFQUFFLFlBQVksRUFBRSxRQUFRLEVBQUUsZ0JBQWdCLEdBQUcsT0FBTyxDQUFDLE1BQU0sQ0FBQyxDQUV0RztJQUVZLHNCQUFzQixDQUFDLGVBQWUsRUFBRSxZQUFZLEdBQUcsT0FBTyxDQUFDLG1CQUFtQixDQUFDLENBOEIvRjtDQUNGIn0=