@aztec/pxe 0.62.0 → 0.63.1

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/config/index.d.ts +2 -3
  2. package/dest/config/index.d.ts.map +1 -1
  3. package/dest/config/index.js +4 -5
  4. package/dest/contract_data_oracle/index.d.ts +1 -0
  5. package/dest/contract_data_oracle/index.d.ts.map +1 -1
  6. package/dest/contract_data_oracle/index.js +5 -1
  7. package/dest/database/incoming_note_dao.d.ts +1 -1
  8. package/dest/database/incoming_note_dao.d.ts.map +1 -1
  9. package/dest/database/kv_pxe_database.d.ts +5 -12
  10. package/dest/database/kv_pxe_database.d.ts.map +1 -1
  11. package/dest/database/kv_pxe_database.js +31 -62
  12. package/dest/database/outgoing_note_dao.d.ts +1 -1
  13. package/dest/database/outgoing_note_dao.d.ts.map +1 -1
  14. package/dest/database/pxe_database.d.ts +17 -25
  15. package/dest/database/pxe_database.d.ts.map +1 -1
  16. package/dest/kernel_oracle/index.d.ts +3 -3
  17. package/dest/kernel_oracle/index.d.ts.map +1 -1
  18. package/dest/kernel_prover/hints/build_private_kernel_reset_private_inputs.d.ts.map +1 -1
  19. package/dest/kernel_prover/hints/build_private_kernel_reset_private_inputs.js +12 -4
  20. package/dest/kernel_prover/kernel_prover.d.ts +3 -1
  21. package/dest/kernel_prover/kernel_prover.d.ts.map +1 -1
  22. package/dest/kernel_prover/kernel_prover.js +39 -7
  23. package/dest/kernel_prover/test/test_circuit_prover.d.ts +1 -0
  24. package/dest/kernel_prover/test/test_circuit_prover.d.ts.map +1 -1
  25. package/dest/kernel_prover/test/test_circuit_prover.js +5 -1
  26. package/dest/{note_processor/utils → note_decryption_utils}/add_public_values_to_payload.d.ts +1 -1
  27. package/dest/note_decryption_utils/add_public_values_to_payload.d.ts.map +1 -0
  28. package/dest/{note_processor/utils → note_decryption_utils}/add_public_values_to_payload.js +1 -1
  29. package/dest/note_decryption_utils/brute_force_note_info.d.ts.map +1 -0
  30. package/dest/{note_processor/utils → note_decryption_utils}/brute_force_note_info.js +1 -1
  31. package/dest/note_decryption_utils/index.d.ts.map +1 -0
  32. package/dest/{note_processor/utils → note_decryption_utils}/index.js +1 -1
  33. package/dest/{note_processor/utils → note_decryption_utils}/produce_note_daos.d.ts +5 -8
  34. package/dest/note_decryption_utils/produce_note_daos.d.ts.map +1 -0
  35. package/dest/note_decryption_utils/produce_note_daos.js +47 -0
  36. package/dest/note_decryption_utils/produce_note_daos_for_key.d.ts +8 -0
  37. package/dest/note_decryption_utils/produce_note_daos_for_key.d.ts.map +1 -0
  38. package/dest/note_decryption_utils/produce_note_daos_for_key.js +17 -0
  39. package/dest/pxe_http/pxe_http_server.d.ts +1 -2
  40. package/dest/pxe_http/pxe_http_server.d.ts.map +1 -1
  41. package/dest/pxe_http/pxe_http_server.js +5 -51
  42. package/dest/pxe_service/create_pxe_service.d.ts.map +1 -1
  43. package/dest/pxe_service/create_pxe_service.js +7 -4
  44. package/dest/pxe_service/error_enriching.d.ts.map +1 -1
  45. package/dest/pxe_service/error_enriching.js +7 -6
  46. package/dest/pxe_service/pxe_service.d.ts +11 -25
  47. package/dest/pxe_service/pxe_service.d.ts.map +1 -1
  48. package/dest/pxe_service/pxe_service.js +45 -48
  49. package/dest/pxe_service/test/pxe_test_suite.d.ts.map +1 -1
  50. package/dest/pxe_service/test/pxe_test_suite.js +4 -3
  51. package/dest/simulator_oracle/index.d.ts +15 -16
  52. package/dest/simulator_oracle/index.d.ts.map +1 -1
  53. package/dest/simulator_oracle/index.js +195 -65
  54. package/dest/synchronizer/synchronizer.d.ts +0 -48
  55. package/dest/synchronizer/synchronizer.d.ts.map +1 -1
  56. package/dest/synchronizer/synchronizer.js +3 -201
  57. package/package.json +14 -14
  58. package/src/config/index.ts +4 -7
  59. package/src/contract_data_oracle/index.ts +5 -0
  60. package/src/database/incoming_note_dao.ts +1 -1
  61. package/src/database/kv_pxe_database.ts +33 -70
  62. package/src/database/outgoing_note_dao.ts +1 -1
  63. package/src/database/pxe_database.ts +19 -28
  64. package/src/kernel_prover/hints/build_private_kernel_reset_private_inputs.ts +13 -3
  65. package/src/kernel_prover/kernel_prover.ts +49 -5
  66. package/src/kernel_prover/test/test_circuit_prover.ts +8 -4
  67. package/src/{note_processor/utils → note_decryption_utils}/add_public_values_to_payload.ts +1 -1
  68. package/src/{note_processor/utils → note_decryption_utils}/produce_note_daos.ts +6 -16
  69. package/src/{note_processor/utils → note_decryption_utils}/produce_note_daos_for_key.ts +6 -15
  70. package/src/pxe_http/pxe_http_server.ts +5 -84
  71. package/src/pxe_service/create_pxe_service.ts +9 -3
  72. package/src/pxe_service/error_enriching.ts +12 -5
  73. package/src/pxe_service/pxe_service.ts +61 -78
  74. package/src/pxe_service/test/pxe_test_suite.ts +6 -2
  75. package/src/simulator_oracle/index.ts +280 -60
  76. package/src/synchronizer/synchronizer.ts +3 -253
  77. package/dest/database/deferred_note_dao.d.ts +0 -40
  78. package/dest/database/deferred_note_dao.d.ts.map +0 -1
  79. package/dest/database/deferred_note_dao.js +0 -38
  80. package/dest/note_processor/index.d.ts +0 -2
  81. package/dest/note_processor/index.d.ts.map +0 -1
  82. package/dest/note_processor/index.js +0 -2
  83. package/dest/note_processor/note_processor.d.ts +0 -83
  84. package/dest/note_processor/note_processor.d.ts.map +0 -1
  85. package/dest/note_processor/note_processor.js +0 -231
  86. package/dest/note_processor/utils/add_public_values_to_payload.d.ts.map +0 -1
  87. package/dest/note_processor/utils/brute_force_note_info.d.ts.map +0 -1
  88. package/dest/note_processor/utils/index.d.ts.map +0 -1
  89. package/dest/note_processor/utils/produce_note_daos.d.ts.map +0 -1
  90. package/dest/note_processor/utils/produce_note_daos.js +0 -51
  91. package/dest/note_processor/utils/produce_note_daos_for_key.d.ts +0 -9
  92. package/dest/note_processor/utils/produce_note_daos_for_key.d.ts.map +0 -1
  93. package/dest/note_processor/utils/produce_note_daos_for_key.js +0 -26
  94. package/src/database/deferred_note_dao.ts +0 -47
  95. package/src/note_processor/index.ts +0 -1
  96. package/src/note_processor/note_processor.ts +0 -358
  97. /package/dest/{note_processor/utils → note_decryption_utils}/brute_force_note_info.d.ts +0 -0
  98. /package/dest/{note_processor/utils → note_decryption_utils}/index.d.ts +0 -0
  99. /package/src/{note_processor/utils → note_decryption_utils}/brute_force_note_info.ts +0 -0
  100. /package/src/{note_processor/utils → note_decryption_utils}/index.ts +0 -0
@@ -1,34 +1,42 @@
1
1
  import {
2
2
  type AztecNode,
3
- type EncryptedL2NoteLog,
3
+ L1NotePayload,
4
4
  type L2Block,
5
+ type L2BlockNumber,
5
6
  MerkleTreeId,
6
7
  type NoteStatus,
7
8
  type NullifierMembershipWitness,
8
9
  type PublicDataWitness,
10
+ type TxEffect,
11
+ type TxScopedL2Log,
9
12
  getNonNullifiedL1ToL2MessageWitness,
10
13
  } from '@aztec/circuit-types';
11
14
  import {
12
15
  type AztecAddress,
13
16
  type CompleteAddress,
14
17
  type ContractInstance,
15
- type Fr,
18
+ Fr,
16
19
  type FunctionSelector,
17
20
  type Header,
18
21
  IndexedTaggingSecret,
19
22
  type KeyValidationRequest,
20
23
  type L1_TO_L2_MSG_TREE_HEIGHT,
21
- TaggingSecret,
24
+ computeAddressSecret,
25
+ computePoint,
22
26
  computeTaggingSecret,
23
27
  } from '@aztec/circuits.js';
24
28
  import { type FunctionArtifact, getFunctionArtifact } from '@aztec/foundation/abi';
25
29
  import { poseidon2Hash } from '@aztec/foundation/crypto';
26
30
  import { createDebugLogger } from '@aztec/foundation/log';
27
31
  import { type KeyStore } from '@aztec/key-store';
28
- import { type DBOracle, MessageLoadOracleInputs } from '@aztec/simulator';
32
+ import { type AcirSimulator, type DBOracle, MessageLoadOracleInputs } from '@aztec/simulator';
29
33
 
30
34
  import { type ContractDataOracle } from '../contract_data_oracle/index.js';
35
+ import { type IncomingNoteDao } from '../database/incoming_note_dao.js';
31
36
  import { type PxeDatabase } from '../database/index.js';
37
+ import { type OutgoingNoteDao } from '../database/outgoing_note_dao.js';
38
+ import { produceNoteDaos } from '../note_decryption_utils/produce_note_daos.js';
39
+ import { getAcirSimulator } from '../simulator/index.js';
32
40
 
33
41
  /**
34
42
  * A data oracle that provides information needed for simulating a transaction.
@@ -153,7 +161,7 @@ export class SimulatorOracle implements DBOracle {
153
161
  * @returns - The index of the commitment. Undefined if it does not exist in the tree.
154
162
  */
155
163
  async getCommitmentIndex(commitment: Fr) {
156
- return await this.aztecNode.findLeafIndex('latest', MerkleTreeId.NOTE_HASH_TREE, commitment);
164
+ return await this.findLeafIndex('latest', MerkleTreeId.NOTE_HASH_TREE, commitment);
157
165
  }
158
166
 
159
167
  // We need this in public as part of the EXISTS calls - but isn't used in private
@@ -162,11 +170,16 @@ export class SimulatorOracle implements DBOracle {
162
170
  }
163
171
 
164
172
  async getNullifierIndex(nullifier: Fr) {
165
- return await this.aztecNode.findLeafIndex('latest', MerkleTreeId.NULLIFIER_TREE, nullifier);
173
+ return await this.findLeafIndex('latest', MerkleTreeId.NULLIFIER_TREE, nullifier);
166
174
  }
167
175
 
168
- public async findLeafIndex(blockNumber: number, treeId: MerkleTreeId, leafValue: Fr): Promise<bigint | undefined> {
169
- return await this.aztecNode.findLeafIndex(blockNumber, treeId, leafValue);
176
+ public async findLeafIndex(
177
+ blockNumber: L2BlockNumber,
178
+ treeId: MerkleTreeId,
179
+ leafValue: Fr,
180
+ ): Promise<bigint | undefined> {
181
+ const [leafIndex] = await this.aztecNode.findLeavesIndexes(blockNumber, treeId, [leafValue]);
182
+ return leafIndex;
170
183
  }
171
184
 
172
185
  public async getSiblingPath(blockNumber: number, treeId: MerkleTreeId, leafIndex: bigint): Promise<Fr[]> {
@@ -244,20 +257,20 @@ export class SimulatorOracle implements DBOracle {
244
257
 
245
258
  /**
246
259
  * Returns the tagging secret for a given sender and recipient pair. For this to work, the ivpsk_m of the sender must be known.
247
- * Includes the last known index used for tagging with this secret.
260
+ * Includes the next index to be used used for tagging with this secret.
248
261
  * @param contractAddress - The contract address to silo the secret for
249
262
  * @param sender - The address sending the note
250
263
  * @param recipient - The address receiving the note
251
264
  * @returns A siloed tagging secret that can be used to tag notes.
252
265
  */
253
- public async getAppTaggingSecret(
266
+ public async getAppTaggingSecretAsSender(
254
267
  contractAddress: AztecAddress,
255
268
  sender: AztecAddress,
256
269
  recipient: AztecAddress,
257
270
  ): Promise<IndexedTaggingSecret> {
258
- const directionalSecret = await this.#calculateDirectionalSecret(contractAddress, sender, recipient);
259
- const [index] = await this.db.getTaggingSecretsIndexes([directionalSecret]);
260
- return IndexedTaggingSecret.fromTaggingSecret(directionalSecret, index);
271
+ const secret = await this.#calculateTaggingSecret(contractAddress, sender, recipient);
272
+ const [index] = await this.db.getTaggingSecretsIndexesAsSender([secret]);
273
+ return new IndexedTaggingSecret(secret, index);
261
274
  }
262
275
 
263
276
  /**
@@ -266,94 +279,301 @@ export class SimulatorOracle implements DBOracle {
266
279
  * @param sender - The address sending the note
267
280
  * @param recipient - The address receiving the note
268
281
  */
269
- public async incrementAppTaggingSecret(
282
+ public async incrementAppTaggingSecretIndexAsSender(
270
283
  contractAddress: AztecAddress,
271
284
  sender: AztecAddress,
272
285
  recipient: AztecAddress,
273
286
  ): Promise<void> {
274
- const directionalSecret = await this.#calculateDirectionalSecret(contractAddress, sender, recipient);
275
- await this.db.incrementTaggingSecretsIndexes([directionalSecret]);
287
+ const secret = await this.#calculateTaggingSecret(contractAddress, sender, recipient);
288
+ const contractName = await this.contractDataOracle.getDebugContractName(contractAddress);
289
+ this.log.verbose(
290
+ `Incrementing secret ${secret} as sender ${sender} for recipient: ${recipient} at contract: ${contractName}(${contractAddress})`,
291
+ );
292
+ await this.db.incrementTaggingSecretsIndexesAsSender([secret]);
276
293
  }
277
294
 
278
- async #calculateDirectionalSecret(contractAddress: AztecAddress, sender: AztecAddress, recipient: AztecAddress) {
295
+ async #calculateTaggingSecret(contractAddress: AztecAddress, sender: AztecAddress, recipient: AztecAddress) {
279
296
  const senderCompleteAddress = await this.getCompleteAddress(sender);
280
297
  const senderIvsk = await this.keyStore.getMasterIncomingViewingSecretKey(sender);
281
298
  const sharedSecret = computeTaggingSecret(senderCompleteAddress, senderIvsk, recipient);
282
299
  // Silo the secret to the app so it can't be used to track other app's notes
283
300
  const siloedSecret = poseidon2Hash([sharedSecret.x, sharedSecret.y, contractAddress]);
284
- // Get the index of the secret, ensuring the directionality (sender -> recipient)
285
- const directionalSecret = new TaggingSecret(siloedSecret, recipient);
286
- return directionalSecret;
301
+ return siloedSecret;
287
302
  }
288
303
 
289
304
  /**
290
305
  * Returns the siloed tagging secrets for a given recipient and all the senders in the address book
306
+ * This method should be exposed as an oracle call to allow aztec.nr to perform the orchestration
307
+ * of the syncTaggedLogs and processTaggedLogs methods. However, it is not possible to do so at the moment,
308
+ * so we're keeping it private for now.
291
309
  * @param contractAddress - The contract address to silo the secret for
292
310
  * @param recipient - The address receiving the notes
293
311
  * @returns A list of siloed tagging secrets
294
312
  */
295
- public async getAppTaggingSecretsForSenders(
313
+ async #getAppTaggingSecretsForContacts(
296
314
  contractAddress: AztecAddress,
297
315
  recipient: AztecAddress,
298
316
  ): Promise<IndexedTaggingSecret[]> {
299
317
  const recipientCompleteAddress = await this.getCompleteAddress(recipient);
300
318
  const recipientIvsk = await this.keyStore.getMasterIncomingViewingSecretKey(recipient);
301
319
 
302
- // We implicitly add the recipient as a contact, this helps us decrypt tags on notes that we send to ourselves (recipient = us, sender = us)
303
- const contacts = [...this.db.getContactAddresses(), recipient];
320
+ // We implicitly add all PXE accounts as contacts, this helps us decrypt tags on notes that we send to ourselves (recipient = us, sender = us)
321
+ const contacts = [...this.db.getContactAddresses(), ...(await this.keyStore.getAccounts())].filter(
322
+ (address, index, self) => index === self.findIndex(otherAddress => otherAddress.equals(address)),
323
+ );
304
324
  const appTaggingSecrets = contacts.map(contact => {
305
325
  const sharedSecret = computeTaggingSecret(recipientCompleteAddress, recipientIvsk, contact);
306
326
  return poseidon2Hash([sharedSecret.x, sharedSecret.y, contractAddress]);
307
327
  });
308
- // Ensure the directionality (sender -> recipient)
309
- const directionalSecrets = appTaggingSecrets.map(secret => new TaggingSecret(secret, recipient));
310
- const indexes = await this.db.getTaggingSecretsIndexes(directionalSecrets);
311
- return directionalSecrets.map((directionalSecret, i) =>
312
- IndexedTaggingSecret.fromTaggingSecret(directionalSecret, indexes[i]),
313
- );
328
+ const indexes = await this.db.getTaggingSecretsIndexesAsRecipient(appTaggingSecrets);
329
+ return appTaggingSecrets.map((secret, i) => new IndexedTaggingSecret(secret, indexes[i]));
314
330
  }
315
331
 
316
332
  /**
317
- * Synchronizes the logs tagged with the recipient's address and all the senders in the addressbook.
333
+ * Synchronizes the logs tagged with scoped addresses and all the senders in the addressbook.
318
334
  * Returns the unsynched logs and updates the indexes of the secrets used to tag them until there are no more logs to sync.
319
335
  * @param contractAddress - The address of the contract that the logs are tagged for
320
336
  * @param recipient - The address of the recipient
321
337
  * @returns A list of encrypted logs tagged with the recipient's address
322
338
  */
323
- public async syncTaggedLogs(contractAddress: AztecAddress, recipient: AztecAddress): Promise<EncryptedL2NoteLog[]> {
324
- // Ideally this algorithm would be implemented in noir, exposing its building blocks as oracles.
325
- // However it is impossible at the moment due to the language not supporting nested slices.
326
- // This nesting is necessary because for a given set of tags we don't
327
- // know how many logs we will get back. Furthermore, these logs are of undetermined
328
- // length, since we don't really know the note they correspond to until we decrypt them.
329
-
330
- // 1. Get all the secrets for the recipient and sender pairs (#9365)
331
- let appTaggingSecrets = await this.getAppTaggingSecretsForSenders(contractAddress, recipient);
332
-
333
- const logs: EncryptedL2NoteLog[] = [];
334
- while (appTaggingSecrets.length > 0) {
335
- // 2. Compute tags using the secrets, recipient and index. Obtain logs for each tag (#9380)
336
- const currentTags = appTaggingSecrets.map(({ secret, recipient, index }) =>
337
- poseidon2Hash([secret, recipient, index]),
339
+ public async syncTaggedLogs(
340
+ contractAddress: AztecAddress,
341
+ maxBlockNumber: number,
342
+ scopes?: AztecAddress[],
343
+ ): Promise<Map<string, TxScopedL2Log[]>> {
344
+ const recipients = scopes ? scopes : await this.keyStore.getAccounts();
345
+ const result = new Map<string, TxScopedL2Log[]>();
346
+ const contractName = await this.contractDataOracle.getDebugContractName(contractAddress);
347
+ for (const recipient of recipients) {
348
+ const logs: TxScopedL2Log[] = [];
349
+ // Ideally this algorithm would be implemented in noir, exposing its building blocks as oracles.
350
+ // However it is impossible at the moment due to the language not supporting nested slices.
351
+ // This nesting is necessary because for a given set of tags we don't
352
+ // know how many logs we will get back. Furthermore, these logs are of undetermined
353
+ // length, since we don't really know the note they correspond to until we decrypt them.
354
+
355
+ // 1. Get all the secrets for the recipient and sender pairs (#9365)
356
+ const appTaggingSecrets = await this.#getAppTaggingSecretsForContacts(contractAddress, recipient);
357
+
358
+ // 1.1 Set up a sliding window with an offset. Chances are the sender might have messed up
359
+ // and inadvertedly incremented their index without use getting any logs (for example, in case
360
+ // of a revert). If we stopped looking for logs the first time
361
+ // we receive 0 for a tag, we might never receive anything from that sender again.
362
+ // Also there's a possibility that we have advanced our index, but the sender has reused it, so
363
+ // we might have missed some logs. For these reasons, we have to look both back and ahead of the
364
+ // stored index
365
+ const INDEX_OFFSET = 10;
366
+ type SearchState = {
367
+ currentTagggingSecrets: IndexedTaggingSecret[];
368
+ maxIndexesToCheck: { [k: string]: number };
369
+ initialSecretIndexes: { [k: string]: number };
370
+ secretsToIncrement: { [k: string]: number };
371
+ };
372
+ const searchState = appTaggingSecrets.reduce<SearchState>(
373
+ (acc, appTaggingSecret) => ({
374
+ // Start looking for logs before the stored index
375
+ currentTagggingSecrets: acc.currentTagggingSecrets.concat([
376
+ new IndexedTaggingSecret(appTaggingSecret.secret, Math.max(0, appTaggingSecret.index - INDEX_OFFSET)),
377
+ ]),
378
+ // Keep looking for logs beyond the stored index
379
+ maxIndexesToCheck: {
380
+ ...acc.maxIndexesToCheck,
381
+ ...{ [appTaggingSecret.secret.toString()]: appTaggingSecret.index + INDEX_OFFSET },
382
+ },
383
+ // Keeps track of the secrets we have to increment in the database
384
+ secretsToIncrement: {},
385
+ // Store the initial set of indexes for the secrets
386
+ initialSecretIndexes: {
387
+ ...acc.initialSecretIndexes,
388
+ ...{ [appTaggingSecret.secret.toString()]: appTaggingSecret.index },
389
+ },
390
+ }),
391
+ { currentTagggingSecrets: [], maxIndexesToCheck: {}, secretsToIncrement: {}, initialSecretIndexes: {} },
392
+ );
393
+
394
+ let { currentTagggingSecrets } = searchState;
395
+ const { maxIndexesToCheck, secretsToIncrement, initialSecretIndexes } = searchState;
396
+
397
+ while (currentTagggingSecrets.length > 0) {
398
+ // 2. Compute tags using the secrets, recipient and index. Obtain logs for each tag (#9380)
399
+ const currentTags = currentTagggingSecrets.map(taggingSecret => taggingSecret.computeTag(recipient));
400
+ const logsByTags = await this.aztecNode.getLogsByTags(currentTags);
401
+ const newTaggingSecrets: IndexedTaggingSecret[] = [];
402
+ logsByTags.forEach((logsByTag, logIndex) => {
403
+ const { secret: currentSecret, index: currentIndex } = currentTagggingSecrets[logIndex];
404
+ const currentSecretAsStr = currentSecret.toString();
405
+ this.log.debug(
406
+ `Syncing logs for recipient ${recipient}, secret ${currentSecretAsStr}:${currentIndex} at contract: ${contractName}(${contractAddress})`,
407
+ );
408
+ // 3.1. Append logs to the list and increment the index for the tags that have logs (#9380)
409
+ if (logsByTag.length > 0) {
410
+ this.log.verbose(
411
+ `Found ${
412
+ logsByTag.length
413
+ } logs for secret ${currentSecretAsStr} as recipient ${recipient}. Incrementing index to ${
414
+ currentIndex + 1
415
+ } at contract: ${contractName}(${contractAddress})`,
416
+ );
417
+ logs.push(...logsByTag);
418
+
419
+ if (currentIndex >= initialSecretIndexes[currentSecretAsStr]) {
420
+ // 3.2. Increment the index for the tags that have logs, provided they're higher than the one
421
+ // we have stored in the db (#9380)
422
+ secretsToIncrement[currentSecretAsStr] = currentIndex + 1;
423
+ // 3.3. Slide the window forwards if we have found logs beyond the initial index
424
+ maxIndexesToCheck[currentSecretAsStr] = currentIndex + INDEX_OFFSET;
425
+ }
426
+ }
427
+ // 3.4 Keep increasing the index (inside the window) temporarily for the tags that have no logs
428
+ // There's a chance the sender missed some and we want to catch up
429
+ if (currentIndex < maxIndexesToCheck[currentSecretAsStr]) {
430
+ const newTaggingSecret = new IndexedTaggingSecret(currentSecret, currentIndex + 1);
431
+ newTaggingSecrets.push(newTaggingSecret);
432
+ }
433
+ });
434
+ await this.db.setTaggingSecretsIndexesAsRecipient(
435
+ Object.keys(secretsToIncrement).map(
436
+ secret => new IndexedTaggingSecret(Fr.fromString(secret), secretsToIncrement[secret]),
437
+ ),
438
+ );
439
+ currentTagggingSecrets = newTaggingSecrets;
440
+ }
441
+
442
+ result.set(
443
+ recipient.toString(),
444
+ logs.filter(log => log.blockNumber <= maxBlockNumber),
445
+ );
446
+ }
447
+ return result;
448
+ }
449
+
450
+ /**
451
+ * Decrypts logs tagged for a recipient and returns them.
452
+ * @param scopedLogs - The logs to decrypt.
453
+ * @param recipient - The recipient of the logs.
454
+ * @param simulator - The simulator to use for decryption.
455
+ * @returns The decrypted notes.
456
+ */
457
+ async #decryptTaggedLogs(scopedLogs: TxScopedL2Log[], recipient: AztecAddress, simulator?: AcirSimulator) {
458
+ const recipientCompleteAddress = await this.getCompleteAddress(recipient);
459
+ const ivskM = await this.keyStore.getMasterSecretKey(
460
+ recipientCompleteAddress.publicKeys.masterIncomingViewingPublicKey,
461
+ );
462
+ const addressSecret = computeAddressSecret(recipientCompleteAddress.getPreaddress(), ivskM);
463
+ const ovskM = await this.keyStore.getMasterSecretKey(
464
+ recipientCompleteAddress.publicKeys.masterOutgoingViewingPublicKey,
465
+ );
466
+ // Since we could have notes with the same index for different txs, we need
467
+ // to keep track of them scoping by txHash
468
+ const excludedIndices: Map<string, Set<number>> = new Map();
469
+ const incomingNotes: IncomingNoteDao[] = [];
470
+ const outgoingNotes: OutgoingNoteDao[] = [];
471
+
472
+ const txEffectsCache = new Map<string, TxEffect | undefined>();
473
+
474
+ for (const scopedLog of scopedLogs) {
475
+ const incomingNotePayload = L1NotePayload.decryptAsIncoming(
476
+ scopedLog.logData,
477
+ addressSecret,
478
+ scopedLog.isFromPublic,
338
479
  );
339
- const logsByTags = await this.aztecNode.getLogsByTags(currentTags);
340
- const newTaggingSecrets: IndexedTaggingSecret[] = [];
341
- logsByTags.forEach((logsByTag, index) => {
342
- // 3.1. Append logs to the list and increment the index for the tags that have logs (#9380)
343
- if (logsByTag.length > 0) {
344
- logs.push(...logsByTag);
345
- // 3.2. Increment the index for the tags that have logs (#9380)
346
- newTaggingSecrets.push(
347
- new IndexedTaggingSecret(appTaggingSecrets[index].secret, recipient, appTaggingSecrets[index].index + 1),
480
+ const outgoingNotePayload = L1NotePayload.decryptAsOutgoing(scopedLog.logData, ovskM, scopedLog.isFromPublic);
481
+
482
+ if (incomingNotePayload || outgoingNotePayload) {
483
+ if (incomingNotePayload && outgoingNotePayload && !incomingNotePayload.equals(outgoingNotePayload)) {
484
+ this.log.warn(
485
+ `Incoming and outgoing note payloads do not match. Incoming: ${JSON.stringify(
486
+ incomingNotePayload,
487
+ )}, Outgoing: ${JSON.stringify(outgoingNotePayload)}`,
348
488
  );
489
+ continue;
490
+ }
491
+
492
+ const payload = incomingNotePayload || outgoingNotePayload;
493
+
494
+ const txEffect =
495
+ txEffectsCache.get(scopedLog.txHash.toString()) ?? (await this.aztecNode.getTxEffect(scopedLog.txHash));
496
+
497
+ if (!txEffect) {
498
+ this.log.warn(`No tx effect found for ${scopedLog.txHash} while decrypting tagged logs`);
499
+ continue;
349
500
  }
501
+
502
+ txEffectsCache.set(scopedLog.txHash.toString(), txEffect);
503
+
504
+ if (!excludedIndices.has(scopedLog.txHash.toString())) {
505
+ excludedIndices.set(scopedLog.txHash.toString(), new Set());
506
+ }
507
+ const { incomingNote, outgoingNote } = await produceNoteDaos(
508
+ // I don't like this at all, but we need a simulator to run `computeNoteHashAndOptionallyANullifier`. This generates
509
+ // a chicken-and-egg problem due to this oracle requiring a simulator, which in turn requires this oracle. Furthermore, since jest doesn't allow
510
+ // mocking ESM exports, we have to pollute the method even more by providing a simulator parameter so tests can inject a fake one.
511
+ simulator ?? getAcirSimulator(this.db, this.aztecNode, this.keyStore, this.contractDataOracle),
512
+ this.db,
513
+ incomingNotePayload ? computePoint(recipient) : undefined,
514
+ outgoingNotePayload ? recipientCompleteAddress.publicKeys.masterOutgoingViewingPublicKey : undefined,
515
+ payload!,
516
+ txEffect.txHash,
517
+ txEffect.noteHashes,
518
+ scopedLog.dataStartIndexForTx,
519
+ excludedIndices.get(scopedLog.txHash.toString())!,
520
+ this.log,
521
+ );
522
+
523
+ if (incomingNote) {
524
+ incomingNotes.push(incomingNote);
525
+ }
526
+ if (outgoingNote) {
527
+ outgoingNotes.push(outgoingNote);
528
+ }
529
+ }
530
+ }
531
+ return { incomingNotes, outgoingNotes };
532
+ }
533
+
534
+ /**
535
+ * Processes the tagged logs returned by syncTaggedLogs by decrypting them and storing them in the database.
536
+ * @param logs - The logs to process.
537
+ * @param recipient - The recipient of the logs.
538
+ */
539
+ public async processTaggedLogs(
540
+ logs: TxScopedL2Log[],
541
+ recipient: AztecAddress,
542
+ simulator?: AcirSimulator,
543
+ ): Promise<void> {
544
+ const { incomingNotes, outgoingNotes } = await this.#decryptTaggedLogs(logs, recipient, simulator);
545
+ if (incomingNotes.length || outgoingNotes.length) {
546
+ await this.db.addNotes(incomingNotes, outgoingNotes, recipient);
547
+ incomingNotes.forEach(noteDao => {
548
+ this.log.verbose(
549
+ `Added incoming note for contract ${noteDao.contractAddress} at slot ${
550
+ noteDao.storageSlot
551
+ } with nullifier ${noteDao.siloedNullifier.toString()}`,
552
+ );
553
+ });
554
+ outgoingNotes.forEach(noteDao => {
555
+ this.log.verbose(`Added outgoing note for contract ${noteDao.contractAddress} at slot ${noteDao.storageSlot}`);
350
556
  });
351
- // 4. Consolidate in db and replace initial appTaggingSecrets with the new ones (updated indexes)
352
- await this.db.incrementTaggingSecretsIndexes(
353
- newTaggingSecrets.map(secret => new TaggingSecret(secret.secret, recipient)),
354
- );
355
- appTaggingSecrets = newTaggingSecrets;
356
557
  }
357
- return logs;
558
+ const nullifiedNotes: IncomingNoteDao[] = [];
559
+ const currentNotesForRecipient = await this.db.getIncomingNotes({ owner: recipient });
560
+ const nullifierIndexes = await this.aztecNode.findLeavesIndexes(
561
+ 'latest',
562
+ MerkleTreeId.NULLIFIER_TREE,
563
+ currentNotesForRecipient.map(note => note.siloedNullifier),
564
+ );
565
+
566
+ const foundNullifiers = currentNotesForRecipient
567
+ .filter((_, i) => nullifierIndexes[i] !== undefined)
568
+ .map(note => note.siloedNullifier);
569
+
570
+ await this.db.removeNullifiedNotes(foundNullifiers, computePoint(recipient));
571
+ nullifiedNotes.forEach(noteDao => {
572
+ this.log.verbose(
573
+ `Removed note for contract ${noteDao.contractAddress} at slot ${
574
+ noteDao.storageSlot
575
+ } with nullifier ${noteDao.siloedNullifier.toString()}`,
576
+ );
577
+ });
358
578
  }
359
579
  }