@aztec/pxe 0.63.0 → 0.64.0

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 (47) hide show
  1. package/dest/database/contracts/contract_artifact_db.d.ts +1 -0
  2. package/dest/database/contracts/contract_artifact_db.d.ts.map +1 -1
  3. package/dest/database/incoming_note_dao.d.ts +10 -1
  4. package/dest/database/incoming_note_dao.d.ts.map +1 -1
  5. package/dest/database/incoming_note_dao.js +18 -5
  6. package/dest/database/kv_pxe_database.d.ts +6 -3
  7. package/dest/database/kv_pxe_database.d.ts.map +1 -1
  8. package/dest/database/kv_pxe_database.js +123 -22
  9. package/dest/database/outgoing_note_dao.d.ts +10 -1
  10. package/dest/database/outgoing_note_dao.d.ts.map +1 -1
  11. package/dest/database/outgoing_note_dao.js +18 -5
  12. package/dest/database/pxe_database.d.ts +22 -5
  13. package/dest/database/pxe_database.d.ts.map +1 -1
  14. package/dest/database/pxe_database_test_suite.d.ts.map +1 -1
  15. package/dest/database/pxe_database_test_suite.js +65 -16
  16. package/dest/kernel_oracle/index.js +2 -2
  17. package/dest/note_decryption_utils/produce_note_daos.d.ts +1 -1
  18. package/dest/note_decryption_utils/produce_note_daos.d.ts.map +1 -1
  19. package/dest/note_decryption_utils/produce_note_daos.js +5 -5
  20. package/dest/note_decryption_utils/produce_note_daos_for_key.d.ts +1 -1
  21. package/dest/note_decryption_utils/produce_note_daos_for_key.d.ts.map +1 -1
  22. package/dest/note_decryption_utils/produce_note_daos_for_key.js +3 -3
  23. package/dest/pxe_service/create_pxe_service.d.ts.map +1 -1
  24. package/dest/pxe_service/create_pxe_service.js +6 -3
  25. package/dest/pxe_service/pxe_service.d.ts +5 -4
  26. package/dest/pxe_service/pxe_service.d.ts.map +1 -1
  27. package/dest/pxe_service/pxe_service.js +51 -25
  28. package/dest/simulator_oracle/index.d.ts +8 -0
  29. package/dest/simulator_oracle/index.d.ts.map +1 -1
  30. package/dest/simulator_oracle/index.js +67 -10
  31. package/dest/synchronizer/synchronizer.d.ts +13 -28
  32. package/dest/synchronizer/synchronizer.d.ts.map +1 -1
  33. package/dest/synchronizer/synchronizer.js +42 -64
  34. package/package.json +14 -14
  35. package/src/database/contracts/contract_artifact_db.ts +1 -0
  36. package/src/database/incoming_note_dao.ts +46 -1
  37. package/src/database/kv_pxe_database.ts +148 -23
  38. package/src/database/outgoing_note_dao.ts +43 -1
  39. package/src/database/pxe_database.ts +25 -5
  40. package/src/database/pxe_database_test_suite.ts +79 -17
  41. package/src/kernel_oracle/index.ts +1 -1
  42. package/src/note_decryption_utils/produce_note_daos.ts +8 -0
  43. package/src/note_decryption_utils/produce_note_daos_for_key.ts +5 -1
  44. package/src/pxe_service/create_pxe_service.ts +7 -4
  45. package/src/pxe_service/pxe_service.ts +109 -72
  46. package/src/simulator_oracle/index.ts +95 -17
  47. package/src/synchronizer/synchronizer.ts +60 -71
@@ -1,4 +1,10 @@
1
- import { type IncomingNotesFilter, MerkleTreeId, NoteStatus, type OutgoingNotesFilter } from '@aztec/circuit-types';
1
+ import {
2
+ type InBlock,
3
+ type IncomingNotesFilter,
4
+ MerkleTreeId,
5
+ NoteStatus,
6
+ type OutgoingNotesFilter,
7
+ } from '@aztec/circuit-types';
2
8
  import {
3
9
  AztecAddress,
4
10
  CompleteAddress,
@@ -7,9 +13,8 @@ import {
7
13
  type IndexedTaggingSecret,
8
14
  type PublicKey,
9
15
  SerializableContractInstance,
10
- computePoint,
11
16
  } from '@aztec/circuits.js';
12
- import { type ContractArtifact } from '@aztec/foundation/abi';
17
+ import { type ContractArtifact, FunctionSelector, FunctionType } from '@aztec/foundation/abi';
13
18
  import { toBufferBE } from '@aztec/foundation/bigint-buffer';
14
19
  import { Fr } from '@aztec/foundation/fields';
15
20
  import {
@@ -39,11 +44,14 @@ export class KVPxeDatabase implements PxeDatabase {
39
44
  #notes: AztecMap<string, Buffer>;
40
45
  #nullifiedNotes: AztecMap<string, Buffer>;
41
46
  #nullifierToNoteId: AztecMap<string, string>;
47
+ #nullifiersByBlockNumber: AztecMultiMap<number, string>;
42
48
 
49
+ #nullifiedNotesToScope: AztecMultiMap<string, string>;
43
50
  #nullifiedNotesByContract: AztecMultiMap<string, string>;
44
51
  #nullifiedNotesByStorageSlot: AztecMultiMap<string, string>;
45
52
  #nullifiedNotesByTxHash: AztecMultiMap<string, string>;
46
53
  #nullifiedNotesByAddressPoint: AztecMultiMap<string, string>;
54
+ #nullifiedNotesByNullifier: AztecMap<string, string>;
47
55
  #syncedBlockPerPublicKey: AztecMap<string, number>;
48
56
  #contractArtifacts: AztecMap<string, Buffer>;
49
57
  #contractInstances: AztecMap<string, Buffer>;
@@ -56,6 +64,7 @@ export class KVPxeDatabase implements PxeDatabase {
56
64
  #outgoingNotesByOvpkM: AztecMultiMap<string, string>;
57
65
 
58
66
  #scopes: AztecSet<string>;
67
+ #notesToScope: AztecMultiMap<string, string>;
59
68
  #notesByContractAndScope: Map<string, AztecMultiMap<string, string>>;
60
69
  #notesByStorageSlotAndScope: Map<string, AztecMultiMap<string, string>>;
61
70
  #notesByTxHashAndScope: Map<string, AztecMultiMap<string, string>>;
@@ -87,11 +96,14 @@ export class KVPxeDatabase implements PxeDatabase {
87
96
  this.#notes = db.openMap('notes');
88
97
  this.#nullifiedNotes = db.openMap('nullified_notes');
89
98
  this.#nullifierToNoteId = db.openMap('nullifier_to_note');
99
+ this.#nullifiersByBlockNumber = db.openMultiMap('nullifier_to_block_number');
90
100
 
101
+ this.#nullifiedNotesToScope = db.openMultiMap('nullified_notes_to_scope');
91
102
  this.#nullifiedNotesByContract = db.openMultiMap('nullified_notes_by_contract');
92
103
  this.#nullifiedNotesByStorageSlot = db.openMultiMap('nullified_notes_by_storage_slot');
93
104
  this.#nullifiedNotesByTxHash = db.openMultiMap('nullified_notes_by_tx_hash');
94
105
  this.#nullifiedNotesByAddressPoint = db.openMultiMap('nullified_notes_by_address_point');
106
+ this.#nullifiedNotesByNullifier = db.openMap('nullified_notes_by_nullifier');
95
107
 
96
108
  this.#outgoingNotes = db.openMap('outgoing_notes');
97
109
  this.#outgoingNotesByContract = db.openMultiMap('outgoing_notes_by_contract');
@@ -100,6 +112,7 @@ export class KVPxeDatabase implements PxeDatabase {
100
112
  this.#outgoingNotesByOvpkM = db.openMultiMap('outgoing_notes_by_ovpk_m');
101
113
 
102
114
  this.#scopes = db.openSet('scopes');
115
+ this.#notesToScope = db.openMultiMap('notes_to_scope');
103
116
  this.#notesByContractAndScope = new Map<string, AztecMultiMap<string, string>>();
104
117
  this.#notesByStorageSlotAndScope = new Map<string, AztecMultiMap<string, string>>();
105
118
  this.#notesByTxHashAndScope = new Map<string, AztecMultiMap<string, string>>();
@@ -128,6 +141,19 @@ export class KVPxeDatabase implements PxeDatabase {
128
141
  }
129
142
 
130
143
  public async addContractArtifact(id: Fr, contract: ContractArtifact): Promise<void> {
144
+ const privateSelectors = contract.functions
145
+ .filter(functionArtifact => functionArtifact.functionType === FunctionType.PRIVATE)
146
+ .map(privateFunctionArtifact =>
147
+ FunctionSelector.fromNameAndParameters(
148
+ privateFunctionArtifact.name,
149
+ privateFunctionArtifact.parameters,
150
+ ).toString(),
151
+ );
152
+
153
+ if (privateSelectors.length !== new Set(privateSelectors).size) {
154
+ throw new Error('Repeated function selectors of private functions');
155
+ }
156
+
131
157
  await this.#contractArtifacts.set(id.toString(), contractArtifactToBuffer(contract));
132
158
  }
133
159
 
@@ -195,6 +221,7 @@ export class KVPxeDatabase implements PxeDatabase {
195
221
  // Had we stored them by their nullifier, they would be returned in random order
196
222
  const noteIndex = toBufferBE(dao.index, 32).toString('hex');
197
223
  void this.#notes.set(noteIndex, dao.toBuffer());
224
+ void this.#notesToScope.set(noteIndex, scope.toString());
198
225
  void this.#nullifierToNoteId.set(dao.siloedNullifier.toString(), noteIndex);
199
226
 
200
227
  void this.#notesByContractAndScope.get(scope.toString())!.set(dao.contractAddress.toString(), noteIndex);
@@ -214,8 +241,92 @@ export class KVPxeDatabase implements PxeDatabase {
214
241
  });
215
242
  }
216
243
 
244
+ public removeNotesAfter(blockNumber: number): Promise<void> {
245
+ return this.db.transaction(() => {
246
+ for (const note of this.#notes.values()) {
247
+ const noteDao = IncomingNoteDao.fromBuffer(note);
248
+ if (noteDao.l2BlockNumber > blockNumber) {
249
+ const noteIndex = toBufferBE(noteDao.index, 32).toString('hex');
250
+ void this.#notes.delete(noteIndex);
251
+ void this.#notesToScope.delete(noteIndex);
252
+ void this.#nullifierToNoteId.delete(noteDao.siloedNullifier.toString());
253
+ for (const scope of this.#scopes.entries()) {
254
+ void this.#notesByAddressPointAndScope.get(scope)!.deleteValue(noteDao.addressPoint.toString(), noteIndex);
255
+ void this.#notesByTxHashAndScope.get(scope)!.deleteValue(noteDao.txHash.toString(), noteIndex);
256
+ void this.#notesByContractAndScope.get(scope)!.deleteValue(noteDao.contractAddress.toString(), noteIndex);
257
+ void this.#notesByStorageSlotAndScope.get(scope)!.deleteValue(noteDao.storageSlot.toString(), noteIndex);
258
+ }
259
+ }
260
+ }
261
+
262
+ for (const note of this.#outgoingNotes.values()) {
263
+ const noteDao = OutgoingNoteDao.fromBuffer(note);
264
+ if (noteDao.l2BlockNumber > blockNumber) {
265
+ const noteIndex = toBufferBE(noteDao.index, 32).toString('hex');
266
+ void this.#outgoingNotes.delete(noteIndex);
267
+ void this.#outgoingNotesByContract.deleteValue(noteDao.contractAddress.toString(), noteIndex);
268
+ void this.#outgoingNotesByStorageSlot.deleteValue(noteDao.storageSlot.toString(), noteIndex);
269
+ void this.#outgoingNotesByTxHash.deleteValue(noteDao.txHash.toString(), noteIndex);
270
+ void this.#outgoingNotesByOvpkM.deleteValue(noteDao.ovpkM.toString(), noteIndex);
271
+ }
272
+ }
273
+ });
274
+ }
275
+
276
+ public async unnullifyNotesAfter(blockNumber: number): Promise<void> {
277
+ const nullifiersToUndo: string[] = [];
278
+ const currentBlockNumber = blockNumber + 1;
279
+ const maxBlockNumber = this.getBlockNumber() ?? currentBlockNumber;
280
+ for (let i = currentBlockNumber; i <= maxBlockNumber; i++) {
281
+ nullifiersToUndo.push(...this.#nullifiersByBlockNumber.getValues(i));
282
+ }
283
+
284
+ const notesIndexesToReinsert = await this.db.transaction(() =>
285
+ nullifiersToUndo.map(nullifier => this.#nullifiedNotesByNullifier.get(nullifier)),
286
+ );
287
+ const nullifiedNoteBuffers = await this.db.transaction(() => {
288
+ return notesIndexesToReinsert
289
+ .filter(noteIndex => noteIndex != undefined)
290
+ .map(noteIndex => this.#nullifiedNotes.get(noteIndex!));
291
+ });
292
+ const noteDaos = nullifiedNoteBuffers
293
+ .filter(buffer => buffer != undefined)
294
+ .map(buffer => IncomingNoteDao.fromBuffer(buffer!));
295
+
296
+ await this.db.transaction(() => {
297
+ for (const dao of noteDaos) {
298
+ const noteIndex = toBufferBE(dao.index, 32).toString('hex');
299
+ void this.#notes.set(noteIndex, dao.toBuffer());
300
+ void this.#nullifierToNoteId.set(dao.siloedNullifier.toString(), noteIndex);
301
+
302
+ let scopes = Array.from(this.#nullifiedNotesToScope.getValues(noteIndex) ?? []);
303
+
304
+ if (scopes.length === 0) {
305
+ scopes = [new AztecAddress(dao.addressPoint.x).toString()];
306
+ }
307
+
308
+ for (const scope of scopes) {
309
+ void this.#notesByContractAndScope.get(scope)!.set(dao.contractAddress.toString(), noteIndex);
310
+ void this.#notesByStorageSlotAndScope.get(scope)!.set(dao.storageSlot.toString(), noteIndex);
311
+ void this.#notesByTxHashAndScope.get(scope)!.set(dao.txHash.toString(), noteIndex);
312
+ void this.#notesByAddressPointAndScope.get(scope)!.set(dao.addressPoint.toString(), noteIndex);
313
+ void this.#notesToScope.set(noteIndex, scope);
314
+ }
315
+
316
+ void this.#nullifiedNotes.delete(noteIndex);
317
+ void this.#nullifiedNotesToScope.delete(noteIndex);
318
+ void this.#nullifiersByBlockNumber.deleteValue(dao.l2BlockNumber, dao.siloedNullifier.toString());
319
+ void this.#nullifiedNotesByContract.deleteValue(dao.contractAddress.toString(), noteIndex);
320
+ void this.#nullifiedNotesByStorageSlot.deleteValue(dao.storageSlot.toString(), noteIndex);
321
+ void this.#nullifiedNotesByTxHash.deleteValue(dao.txHash.toString(), noteIndex);
322
+ void this.#nullifiedNotesByAddressPoint.deleteValue(dao.addressPoint.toString(), noteIndex);
323
+ void this.#nullifiedNotesByNullifier.delete(dao.siloedNullifier.toString());
324
+ }
325
+ });
326
+ }
327
+
217
328
  getIncomingNotes(filter: IncomingNotesFilter): Promise<IncomingNoteDao[]> {
218
- const publicKey: PublicKey | undefined = filter.owner ? computePoint(filter.owner) : undefined;
329
+ const publicKey: PublicKey | undefined = filter.owner ? filter.owner.toAddressPoint() : undefined;
219
330
 
220
331
  filter.status = filter.status ?? NoteStatus.ACTIVE;
221
332
 
@@ -350,7 +461,7 @@ export class KVPxeDatabase implements PxeDatabase {
350
461
  return Promise.resolve(notes);
351
462
  }
352
463
 
353
- removeNullifiedNotes(nullifiers: Fr[], accountAddressPoint: PublicKey): Promise<IncomingNoteDao[]> {
464
+ removeNullifiedNotes(nullifiers: InBlock<Fr>[], accountAddressPoint: PublicKey): Promise<IncomingNoteDao[]> {
354
465
  if (nullifiers.length === 0) {
355
466
  return Promise.resolve([]);
356
467
  }
@@ -358,7 +469,8 @@ export class KVPxeDatabase implements PxeDatabase {
358
469
  return this.#db.transaction(() => {
359
470
  const nullifiedNotes: IncomingNoteDao[] = [];
360
471
 
361
- for (const nullifier of nullifiers) {
472
+ for (const blockScopedNullifier of nullifiers) {
473
+ const { data: nullifier, l2BlockNumber: blockNumber } = blockScopedNullifier;
362
474
  const noteIndex = this.#nullifierToNoteId.get(nullifier.toString());
363
475
  if (!noteIndex) {
364
476
  continue;
@@ -370,7 +482,7 @@ export class KVPxeDatabase implements PxeDatabase {
370
482
  // note doesn't exist. Maybe it got nullified already
371
483
  continue;
372
484
  }
373
-
485
+ const noteScopes = this.#notesToScope.getValues(noteIndex) ?? [];
374
486
  const note = IncomingNoteDao.fromBuffer(noteBuffer);
375
487
  if (!note.addressPoint.equals(accountAddressPoint)) {
376
488
  // tried to nullify someone else's note
@@ -380,19 +492,27 @@ export class KVPxeDatabase implements PxeDatabase {
380
492
  nullifiedNotes.push(note);
381
493
 
382
494
  void this.#notes.delete(noteIndex);
495
+ void this.#notesToScope.delete(noteIndex);
383
496
 
384
- for (const scope in this.#scopes.entries()) {
497
+ for (const scope of this.#scopes.entries()) {
385
498
  void this.#notesByAddressPointAndScope.get(scope)!.deleteValue(accountAddressPoint.toString(), noteIndex);
386
499
  void this.#notesByTxHashAndScope.get(scope)!.deleteValue(note.txHash.toString(), noteIndex);
387
500
  void this.#notesByContractAndScope.get(scope)!.deleteValue(note.contractAddress.toString(), noteIndex);
388
501
  void this.#notesByStorageSlotAndScope.get(scope)!.deleteValue(note.storageSlot.toString(), noteIndex);
389
502
  }
390
503
 
504
+ if (noteScopes !== undefined) {
505
+ for (const scope of noteScopes) {
506
+ void this.#nullifiedNotesToScope.set(noteIndex, scope);
507
+ }
508
+ }
391
509
  void this.#nullifiedNotes.set(noteIndex, note.toBuffer());
510
+ void this.#nullifiersByBlockNumber.set(blockNumber, nullifier.toString());
392
511
  void this.#nullifiedNotesByContract.set(note.contractAddress.toString(), noteIndex);
393
512
  void this.#nullifiedNotesByStorageSlot.set(note.storageSlot.toString(), noteIndex);
394
513
  void this.#nullifiedNotesByTxHash.set(note.txHash.toString(), noteIndex);
395
514
  void this.#nullifiedNotesByAddressPoint.set(note.addressPoint.toString(), noteIndex);
515
+ void this.#nullifiedNotesByNullifier.set(nullifier.toString(), noteIndex);
396
516
 
397
517
  void this.#nullifierToNoteId.delete(nullifier.toString());
398
518
  }
@@ -548,25 +668,19 @@ export class KVPxeDatabase implements PxeDatabase {
548
668
  return incomingNotesSize + outgoingNotesSize + treeRootsSize + authWitsSize + addressesSize;
549
669
  }
550
670
 
551
- async incrementTaggingSecretsIndexesAsSender(appTaggingSecrets: Fr[]): Promise<void> {
552
- await this.#incrementTaggingSecretsIndexes(appTaggingSecrets, this.#taggingSecretIndexesForSenders);
671
+ async setTaggingSecretsIndexesAsSender(indexedSecrets: IndexedTaggingSecret[]): Promise<void> {
672
+ await this.#setTaggingSecretsIndexes(indexedSecrets, this.#taggingSecretIndexesForSenders);
553
673
  }
554
674
 
555
- async #incrementTaggingSecretsIndexes(appTaggingSecrets: Fr[], storageMap: AztecMap<string, number>): Promise<void> {
556
- const indexes = await this.#getTaggingSecretsIndexes(appTaggingSecrets, storageMap);
557
- await this.db.transaction(() => {
558
- indexes.forEach((taggingSecretIndex, listIndex) => {
559
- const nextIndex = taggingSecretIndex + 1;
560
- void storageMap.set(appTaggingSecrets[listIndex].toString(), nextIndex);
561
- });
562
- });
675
+ async setTaggingSecretsIndexesAsRecipient(indexedSecrets: IndexedTaggingSecret[]): Promise<void> {
676
+ await this.#setTaggingSecretsIndexes(indexedSecrets, this.#taggingSecretIndexesForRecipients);
563
677
  }
564
678
 
565
- async setTaggingSecretsIndexesAsRecipient(indexedSecrets: IndexedTaggingSecret[]): Promise<void> {
566
- await this.db.transaction(() => {
567
- indexedSecrets.forEach(indexedSecret => {
568
- void this.#taggingSecretIndexesForRecipients.set(indexedSecret.secret.toString(), indexedSecret.index);
569
- });
679
+ #setTaggingSecretsIndexes(indexedSecrets: IndexedTaggingSecret[], storageMap: AztecMap<string, number>) {
680
+ return this.db.transaction(() => {
681
+ indexedSecrets.forEach(
682
+ indexedSecret => void storageMap.set(indexedSecret.secret.toString(), indexedSecret.index),
683
+ );
570
684
  });
571
685
  }
572
686
 
@@ -581,4 +695,15 @@ export class KVPxeDatabase implements PxeDatabase {
581
695
  #getTaggingSecretsIndexes(appTaggingSecrets: Fr[], storageMap: AztecMap<string, number>): Promise<number[]> {
582
696
  return this.db.transaction(() => appTaggingSecrets.map(secret => storageMap.get(`${secret.toString()}`) ?? 0));
583
697
  }
698
+
699
+ async resetNoteSyncData(): Promise<void> {
700
+ await this.db.transaction(() => {
701
+ for (const recipient of this.#taggingSecretIndexesForRecipients.keys()) {
702
+ void this.#taggingSecretIndexesForRecipients.delete(recipient);
703
+ }
704
+ for (const sender of this.#taggingSecretIndexesForSenders.keys()) {
705
+ void this.#taggingSecretIndexesForSenders.delete(sender);
706
+ }
707
+ });
708
+ }
584
709
  }
@@ -1,4 +1,4 @@
1
- import { type L1NotePayload, Note, TxHash } from '@aztec/circuit-types';
1
+ import { type L1NotePayload, Note, TxHash, randomTxHash } from '@aztec/circuit-types';
2
2
  import { AztecAddress, Fr, Point, type PublicKey } from '@aztec/circuits.js';
3
3
  import { NoteSelector } from '@aztec/foundation/abi';
4
4
  import { toBigIntBE } from '@aztec/foundation/bigint-buffer';
@@ -21,6 +21,10 @@ export class OutgoingNoteDao {
21
21
  public noteTypeId: NoteSelector,
22
22
  /** The hash of the tx the note was created in. */
23
23
  public txHash: TxHash,
24
+ /** The L2 block number in which the tx with this note was included. */
25
+ public l2BlockNumber: number,
26
+ /** The L2 block hash in which the tx with this note was included. */
27
+ public l2BlockHash: string,
24
28
  /** The nonce of the note. */
25
29
  public nonce: Fr,
26
30
  /**
@@ -38,6 +42,8 @@ export class OutgoingNoteDao {
38
42
  note: Note,
39
43
  payload: L1NotePayload,
40
44
  noteInfo: NoteInfo,
45
+ l2BlockNumber: number,
46
+ l2BlockHash: string,
41
47
  dataStartIndexForTx: number,
42
48
  ovpkM: PublicKey,
43
49
  ) {
@@ -48,6 +54,8 @@ export class OutgoingNoteDao {
48
54
  payload.storageSlot,
49
55
  payload.noteTypeId,
50
56
  noteInfo.txHash,
57
+ l2BlockNumber,
58
+ l2BlockHash,
51
59
  noteInfo.nonce,
52
60
  noteInfo.noteHash,
53
61
  noteHashIndexInTheWholeTree,
@@ -62,6 +70,8 @@ export class OutgoingNoteDao {
62
70
  this.storageSlot,
63
71
  this.noteTypeId,
64
72
  this.txHash.buffer,
73
+ this.l2BlockNumber,
74
+ Fr.fromString(this.l2BlockHash),
65
75
  this.nonce,
66
76
  this.noteHash,
67
77
  this.index,
@@ -76,6 +86,8 @@ export class OutgoingNoteDao {
76
86
  const storageSlot = Fr.fromBuffer(reader);
77
87
  const noteTypeId = reader.readObject(NoteSelector);
78
88
  const txHash = new TxHash(reader.readBytes(TxHash.SIZE));
89
+ const l2BlockNumber = reader.readNumber();
90
+ const l2BlockHash = Fr.fromBuffer(reader).toString();
79
91
  const nonce = Fr.fromBuffer(reader);
80
92
  const noteHash = Fr.fromBuffer(reader);
81
93
  const index = toBigIntBE(reader.readBytes(32));
@@ -87,6 +99,8 @@ export class OutgoingNoteDao {
87
99
  storageSlot,
88
100
  noteTypeId,
89
101
  txHash,
102
+ l2BlockNumber,
103
+ l2BlockHash,
90
104
  nonce,
91
105
  noteHash,
92
106
  index,
@@ -111,4 +125,32 @@ export class OutgoingNoteDao {
111
125
  const noteSize = 4 + this.note.items.length * Fr.SIZE_IN_BYTES;
112
126
  return noteSize + AztecAddress.SIZE_IN_BYTES + Fr.SIZE_IN_BYTES * 2 + TxHash.SIZE + Point.SIZE_IN_BYTES;
113
127
  }
128
+
129
+ static random({
130
+ note = Note.random(),
131
+ contractAddress = AztecAddress.random(),
132
+ txHash = randomTxHash(),
133
+ storageSlot = Fr.random(),
134
+ noteTypeId = NoteSelector.random(),
135
+ nonce = Fr.random(),
136
+ l2BlockNumber = Math.floor(Math.random() * 1000),
137
+ l2BlockHash = Fr.random().toString(),
138
+ noteHash = Fr.random(),
139
+ index = Fr.random().toBigInt(),
140
+ ovpkM = Point.random(),
141
+ }: Partial<OutgoingNoteDao> = {}) {
142
+ return new OutgoingNoteDao(
143
+ note,
144
+ contractAddress,
145
+ storageSlot,
146
+ noteTypeId,
147
+ txHash,
148
+ l2BlockNumber,
149
+ l2BlockHash,
150
+ nonce,
151
+ noteHash,
152
+ index,
153
+ ovpkM,
154
+ );
155
+ }
114
156
  }
@@ -1,4 +1,4 @@
1
- import { type IncomingNotesFilter, type OutgoingNotesFilter } from '@aztec/circuit-types';
1
+ import { type InBlock, type IncomingNotesFilter, type OutgoingNotesFilter } from '@aztec/circuit-types';
2
2
  import {
3
3
  type CompleteAddress,
4
4
  type ContractInstanceWithAddress,
@@ -96,7 +96,7 @@ export interface PxeDatabase extends ContractArtifactDatabase, ContractInstanceD
96
96
  * @param account - A PublicKey instance representing the account for which the records are being removed.
97
97
  * @returns Removed notes.
98
98
  */
99
- removeNullifiedNotes(nullifiers: Fr[], account: PublicKey): Promise<IncomingNoteDao[]>;
99
+ removeNullifiedNotes(nullifiers: InBlock<Fr>[], account: PublicKey): Promise<IncomingNoteDao[]>;
100
100
 
101
101
  /**
102
102
  * Gets the most recently processed block number.
@@ -202,11 +202,11 @@ export interface PxeDatabase extends ContractArtifactDatabase, ContractInstanceD
202
202
  getTaggingSecretsIndexesAsSender(appTaggingSecrets: Fr[]): Promise<number[]>;
203
203
 
204
204
  /**
205
- * Increments the index for the provided app siloed tagging secrets in the senders database
206
- * To be used when the generated tags have been used as sender
205
+ * Sets the index for the provided app siloed tagging secrets
206
+ * To be used when the generated tags have been "seen" as a sender
207
207
  * @param appTaggingSecrets - The app siloed tagging secrets.
208
208
  */
209
- incrementTaggingSecretsIndexesAsSender(appTaggingSecrets: Fr[]): Promise<void>;
209
+ setTaggingSecretsIndexesAsSender(indexedTaggingSecrets: IndexedTaggingSecret[]): Promise<void>;
210
210
 
211
211
  /**
212
212
  * Sets the index for the provided app siloed tagging secrets
@@ -214,4 +214,24 @@ export interface PxeDatabase extends ContractArtifactDatabase, ContractInstanceD
214
214
  * @param appTaggingSecrets - The app siloed tagging secrets.
215
215
  */
216
216
  setTaggingSecretsIndexesAsRecipient(indexedTaggingSecrets: IndexedTaggingSecret[]): Promise<void>;
217
+
218
+ /**
219
+ * Deletes all notes synched after this block number.
220
+ * @param blockNumber - All notes strictly after this block number are removed.
221
+ */
222
+ removeNotesAfter(blockNumber: number): Promise<void>;
223
+
224
+ /**
225
+ * Restores notes nullified after the given block.
226
+ * @param blockNumber - All nullifiers strictly after this block are removed.
227
+ */
228
+ unnullifyNotesAfter(blockNumber: number): Promise<void>;
229
+
230
+ /**
231
+ * Resets the indexes used to sync notes to 0 for every sender and recipient, causing the next sync process to
232
+ * start from scratch, taking longer than usual.
233
+ * This can help fix desynchronization issues, including finding logs that had previously been overlooked, and
234
+ * is also required to deal with chain reorgs.
235
+ */
236
+ resetNoteSyncData(): Promise<void>;
217
237
  }
@@ -5,17 +5,16 @@ import {
5
5
  INITIAL_L2_BLOCK_NUM,
6
6
  PublicKeys,
7
7
  SerializableContractInstance,
8
- computePoint,
9
8
  } from '@aztec/circuits.js';
10
9
  import { makeHeader } from '@aztec/circuits.js/testing';
10
+ import { FunctionType } from '@aztec/foundation/abi';
11
11
  import { randomInt } from '@aztec/foundation/crypto';
12
12
  import { Fr, Point } from '@aztec/foundation/fields';
13
13
  import { BenchmarkingContractArtifact } from '@aztec/noir-contracts.js/Benchmarking';
14
+ import { TestContractArtifact } from '@aztec/noir-contracts.js/Test';
14
15
 
15
- import { type IncomingNoteDao } from './incoming_note_dao.js';
16
- import { randomIncomingNoteDao } from './incoming_note_dao.test.js';
17
- import { type OutgoingNoteDao } from './outgoing_note_dao.js';
18
- import { randomOutgoingNoteDao } from './outgoing_note_dao.test.js';
16
+ import { IncomingNoteDao } from './incoming_note_dao.js';
17
+ import { OutgoingNoteDao } from './outgoing_note_dao.js';
19
18
  import { type PxeDatabase } from './pxe_database.js';
20
19
 
21
20
  /**
@@ -102,7 +101,7 @@ export function describePxeDatabase(getDatabase: () => PxeDatabase) {
102
101
 
103
102
  [
104
103
  () => ({ owner: owners[0].address }),
105
- () => notes.filter(note => note.addressPoint.equals(computePoint(owners[0].address))),
104
+ () => notes.filter(note => note.addressPoint.equals(owners[0].address.toAddressPoint())),
106
105
  ],
107
106
 
108
107
  [
@@ -121,11 +120,12 @@ export function describePxeDatabase(getDatabase: () => PxeDatabase) {
121
120
  storageSlots = Array.from({ length: 2 }).map(() => Fr.random());
122
121
 
123
122
  notes = Array.from({ length: 10 }).map((_, i) =>
124
- randomIncomingNoteDao({
123
+ IncomingNoteDao.random({
125
124
  contractAddress: contractAddresses[i % contractAddresses.length],
126
125
  storageSlot: storageSlots[i % storageSlots.length],
127
- addressPoint: computePoint(owners[i % owners.length].address),
126
+ addressPoint: owners[i % owners.length].address.toAddressPoint(),
128
127
  index: BigInt(i),
128
+ l2BlockNumber: i,
129
129
  }),
130
130
  );
131
131
 
@@ -156,9 +156,13 @@ export function describePxeDatabase(getDatabase: () => PxeDatabase) {
156
156
 
157
157
  // Nullify all notes and use the same filter as other test cases
158
158
  for (const owner of owners) {
159
- const notesToNullify = notes.filter(note => note.addressPoint.equals(computePoint(owner.address)));
160
- const nullifiers = notesToNullify.map(note => note.siloedNullifier);
161
- await expect(database.removeNullifiedNotes(nullifiers, computePoint(owner.address))).resolves.toEqual(
159
+ const notesToNullify = notes.filter(note => note.addressPoint.equals(owner.address.toAddressPoint()));
160
+ const nullifiers = notesToNullify.map(note => ({
161
+ data: note.siloedNullifier,
162
+ l2BlockNumber: note.l2BlockNumber,
163
+ l2BlockHash: note.l2BlockHash,
164
+ }));
165
+ await expect(database.removeNullifiedNotes(nullifiers, owner.address.toAddressPoint())).resolves.toEqual(
162
166
  notesToNullify,
163
167
  );
164
168
  }
@@ -171,8 +175,12 @@ export function describePxeDatabase(getDatabase: () => PxeDatabase) {
171
175
  it('skips nullified notes by default or when requesting active', async () => {
172
176
  await database.addNotes(notes, []);
173
177
 
174
- const notesToNullify = notes.filter(note => note.addressPoint.equals(computePoint(owners[0].address)));
175
- const nullifiers = notesToNullify.map(note => note.siloedNullifier);
178
+ const notesToNullify = notes.filter(note => note.addressPoint.equals(owners[0].address.toAddressPoint()));
179
+ const nullifiers = notesToNullify.map(note => ({
180
+ data: note.siloedNullifier,
181
+ l2BlockNumber: note.l2BlockNumber,
182
+ l2BlockHash: note.l2BlockHash,
183
+ }));
176
184
  await expect(database.removeNullifiedNotes(nullifiers, notesToNullify[0].addressPoint)).resolves.toEqual(
177
185
  notesToNullify,
178
186
  );
@@ -184,11 +192,35 @@ export function describePxeDatabase(getDatabase: () => PxeDatabase) {
184
192
  expect(actualNotesWithActive).toEqual(notes.filter(note => !notesToNullify.includes(note)));
185
193
  });
186
194
 
195
+ it('handles note unnullification', async () => {
196
+ await database.setHeader(makeHeader(randomInt(1000), 100, 0 /** slot number */));
197
+ await database.addNotes(notes, []);
198
+
199
+ const notesToNullify = notes.filter(note => note.addressPoint.equals(owners[0].address.toAddressPoint()));
200
+ const nullifiers = notesToNullify.map(note => ({
201
+ data: note.siloedNullifier,
202
+ l2BlockNumber: 99,
203
+ l2BlockHash: Fr.random().toString(),
204
+ }));
205
+ await expect(database.removeNullifiedNotes(nullifiers, notesToNullify[0].addressPoint)).resolves.toEqual(
206
+ notesToNullify,
207
+ );
208
+ await expect(database.unnullifyNotesAfter(98)).resolves.toEqual(undefined);
209
+
210
+ const result = await database.getIncomingNotes({ status: NoteStatus.ACTIVE, owner: owners[0].address });
211
+
212
+ expect(result.sort()).toEqual([...notesToNullify].sort());
213
+ });
214
+
187
215
  it('returns active and nullified notes when requesting either', async () => {
188
216
  await database.addNotes(notes, []);
189
217
 
190
- const notesToNullify = notes.filter(note => note.addressPoint.equals(computePoint(owners[0].address)));
191
- const nullifiers = notesToNullify.map(note => note.siloedNullifier);
218
+ const notesToNullify = notes.filter(note => note.addressPoint.equals(owners[0].address.toAddressPoint()));
219
+ const nullifiers = notesToNullify.map(note => ({
220
+ data: note.siloedNullifier,
221
+ l2BlockNumber: note.l2BlockNumber,
222
+ l2BlockHash: note.l2BlockHash,
223
+ }));
192
224
  await expect(database.removeNullifiedNotes(nullifiers, notesToNullify[0].addressPoint)).resolves.toEqual(
193
225
  notesToNullify,
194
226
  );
@@ -246,7 +278,16 @@ export function describePxeDatabase(getDatabase: () => PxeDatabase) {
246
278
  ).resolves.toEqual([notes[0]]);
247
279
 
248
280
  await expect(
249
- database.removeNullifiedNotes([notes[0].siloedNullifier], computePoint(owners[0].address)),
281
+ database.removeNullifiedNotes(
282
+ [
283
+ {
284
+ data: notes[0].siloedNullifier,
285
+ l2BlockHash: notes[0].l2BlockHash,
286
+ l2BlockNumber: notes[0].l2BlockNumber,
287
+ },
288
+ ],
289
+ owners[0].address.toAddressPoint(),
290
+ ),
250
291
  ).resolves.toEqual([notes[0]]);
251
292
 
252
293
  await expect(
@@ -260,6 +301,14 @@ export function describePxeDatabase(getDatabase: () => PxeDatabase) {
260
301
  }),
261
302
  ).resolves.toEqual([]);
262
303
  });
304
+
305
+ it('removes notes after a given block', async () => {
306
+ await database.addNotes(notes, [], owners[0].address);
307
+
308
+ await database.removeNotesAfter(5);
309
+ const result = await database.getIncomingNotes({ scopes: [owners[0].address] });
310
+ expect(new Set(result)).toEqual(new Set(notes.slice(0, 6)));
311
+ });
263
312
  });
264
313
 
265
314
  describe('outgoing notes', () => {
@@ -307,7 +356,7 @@ export function describePxeDatabase(getDatabase: () => PxeDatabase) {
307
356
  storageSlots = Array.from({ length: 2 }).map(() => Fr.random());
308
357
 
309
358
  notes = Array.from({ length: 10 }).map((_, i) =>
310
- randomOutgoingNoteDao({
359
+ OutgoingNoteDao.random({
311
360
  contractAddress: contractAddresses[i % contractAddresses.length],
312
361
  storageSlot: storageSlots[i % storageSlots.length],
313
362
  ovpkM: owners[i % owners.length].publicKeys.masterOutgoingViewingPublicKey,
@@ -391,6 +440,19 @@ export function describePxeDatabase(getDatabase: () => PxeDatabase) {
391
440
  await expect(database.getContractArtifact(id)).resolves.toEqual(artifact);
392
441
  });
393
442
 
443
+ it('does not store a contract artifact with a duplicate private function selector', async () => {
444
+ const artifact = TestContractArtifact;
445
+ const index = artifact.functions.findIndex(fn => fn.functionType === FunctionType.PRIVATE);
446
+
447
+ const copiedFn = structuredClone(artifact.functions[index]);
448
+ artifact.functions.push(copiedFn);
449
+
450
+ const id = Fr.random();
451
+ await expect(database.addContractArtifact(id, artifact)).rejects.toThrow(
452
+ 'Repeated function selectors of private functions',
453
+ );
454
+ });
455
+
394
456
  it('stores a contract instance', async () => {
395
457
  const address = AztecAddress.random();
396
458
  const instance = SerializableContractInstance.random().withAddress(address);
@@ -70,7 +70,7 @@ export class KernelOracle implements ProvingDataOracle {
70
70
  }
71
71
 
72
72
  async getNoteHashTreeRoot(): Promise<Fr> {
73
- const header = await this.node.getHeader(this.blockNumber);
73
+ const header = await this.node.getBlockHeader(this.blockNumber);
74
74
  return header.state.partial.noteHashTree.root;
75
75
  }
76
76
 
@@ -34,6 +34,8 @@ export async function produceNoteDaos(
34
34
  ovpkM: PublicKey | undefined,
35
35
  payload: L1NotePayload,
36
36
  txHash: TxHash,
37
+ l2BlockNumber: number,
38
+ l2BlockHash: string,
37
39
  noteHashes: Fr[],
38
40
  dataStartIndexForTx: number,
39
41
  excludedIndices: Set<number>,
@@ -56,6 +58,8 @@ export async function produceNoteDaos(
56
58
  addressPoint,
57
59
  payload,
58
60
  txHash,
61
+ l2BlockNumber,
62
+ l2BlockHash,
59
63
  noteHashes,
60
64
  dataStartIndexForTx,
61
65
  excludedIndices,
@@ -74,6 +78,8 @@ export async function produceNoteDaos(
74
78
  incomingNote.storageSlot,
75
79
  incomingNote.noteTypeId,
76
80
  incomingNote.txHash,
81
+ incomingNote.l2BlockNumber,
82
+ incomingNote.l2BlockHash,
77
83
  incomingNote.nonce,
78
84
  incomingNote.noteHash,
79
85
  incomingNote.index,
@@ -86,6 +92,8 @@ export async function produceNoteDaos(
86
92
  ovpkM,
87
93
  payload,
88
94
  txHash,
95
+ l2BlockNumber,
96
+ l2BlockHash,
89
97
  noteHashes,
90
98
  dataStartIndexForTx,
91
99
  excludedIndices,