@aztec/pxe 0.67.1 → 0.68.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 (37) hide show
  1. package/dest/database/kv_pxe_database.d.ts +3 -3
  2. package/dest/database/kv_pxe_database.d.ts.map +1 -1
  3. package/dest/database/kv_pxe_database.js +5 -5
  4. package/dest/database/pxe_database.d.ts +6 -6
  5. package/dest/database/pxe_database.d.ts.map +1 -1
  6. package/dest/kernel_oracle/index.js +2 -2
  7. package/dest/kernel_prover/hints/build_private_kernel_reset_private_inputs.js +2 -2
  8. package/dest/kernel_prover/kernel_prover.d.ts.map +1 -1
  9. package/dest/kernel_prover/kernel_prover.js +5 -2
  10. package/dest/kernel_prover/test/test_circuit_prover.d.ts +1 -2
  11. package/dest/kernel_prover/test/test_circuit_prover.d.ts.map +1 -1
  12. package/dest/kernel_prover/test/test_circuit_prover.js +4 -10
  13. package/dest/pxe_service/error_enriching.d.ts.map +1 -1
  14. package/dest/pxe_service/error_enriching.js +6 -3
  15. package/dest/pxe_service/pxe_service.d.ts +6 -5
  16. package/dest/pxe_service/pxe_service.d.ts.map +1 -1
  17. package/dest/pxe_service/pxe_service.js +33 -24
  18. package/dest/simulator_oracle/index.d.ts +8 -6
  19. package/dest/simulator_oracle/index.d.ts.map +1 -1
  20. package/dest/simulator_oracle/index.js +170 -158
  21. package/dest/simulator_oracle/tagging_utils.d.ts +16 -0
  22. package/dest/simulator_oracle/tagging_utils.d.ts.map +1 -0
  23. package/dest/simulator_oracle/tagging_utils.js +25 -0
  24. package/dest/utils/create_pxe_service.d.ts.map +1 -1
  25. package/dest/utils/create_pxe_service.js +8 -5
  26. package/package.json +17 -17
  27. package/src/database/kv_pxe_database.ts +6 -4
  28. package/src/database/pxe_database.ts +6 -6
  29. package/src/kernel_oracle/index.ts +1 -1
  30. package/src/kernel_prover/hints/build_private_kernel_reset_private_inputs.ts +1 -1
  31. package/src/kernel_prover/kernel_prover.ts +11 -1
  32. package/src/kernel_prover/test/test_circuit_prover.ts +6 -22
  33. package/src/pxe_service/error_enriching.ts +5 -2
  34. package/src/pxe_service/pxe_service.ts +44 -35
  35. package/src/simulator_oracle/index.ts +203 -188
  36. package/src/simulator_oracle/tagging_utils.ts +31 -0
  37. package/src/utils/create_pxe_service.ts +7 -4
@@ -24,7 +24,7 @@ import {
24
24
  type L1_TO_L2_MSG_TREE_HEIGHT,
25
25
  PrivateLog,
26
26
  computeAddressSecret,
27
- computeTaggingSecret,
27
+ computeTaggingSecretPoint,
28
28
  } from '@aztec/circuits.js';
29
29
  import { type FunctionArtifact, getFunctionArtifact } from '@aztec/foundation/abi';
30
30
  import { poseidon2Hash } from '@aztec/foundation/crypto';
@@ -38,6 +38,7 @@ import { type IncomingNoteDao } from '../database/incoming_note_dao.js';
38
38
  import { type PxeDatabase } from '../database/index.js';
39
39
  import { produceNoteDaos } from '../note_decryption_utils/produce_note_daos.js';
40
40
  import { getAcirSimulator } from '../simulator/index.js';
41
+ import { WINDOW_HALF_SIZE, getIndexedTaggingSecretsForTheWindow, getInitialIndexesMap } from './tagging_utils.js';
41
42
 
42
43
  /**
43
44
  * A data oracle that provides information needed for simulating a transaction.
@@ -252,33 +253,33 @@ export class SimulatorOracle implements DBOracle {
252
253
  * finally the index specified tag. We will then query the node with this tag for each address in the address book.
253
254
  * @returns The full list of the users contact addresses.
254
255
  */
255
- public getContacts(): Promise<AztecAddress[]> {
256
- return this.db.getContactAddresses();
256
+ public getSenders(): Promise<AztecAddress[]> {
257
+ return this.db.getSenderAddresses();
257
258
  }
258
259
 
259
260
  /**
260
- * Returns the tagging secret for a given sender and recipient pair. For this to work, the ivpsk_m of the sender must be known.
261
+ * Returns the tagging secret for a given sender and recipient pair. For this to work, the ivsk_m of the sender must be known.
261
262
  * Includes the next index to be used used for tagging with this secret.
262
263
  * @param contractAddress - The contract address to silo the secret for
263
264
  * @param sender - The address sending the note
264
265
  * @param recipient - The address receiving the note
265
- * @returns A siloed tagging secret that can be used to tag notes.
266
+ * @returns An indexed tagging secret that can be used to tag notes.
266
267
  */
267
- public async getAppTaggingSecretAsSender(
268
+ public async getIndexedTaggingSecretAsSender(
268
269
  contractAddress: AztecAddress,
269
270
  sender: AztecAddress,
270
271
  recipient: AztecAddress,
271
272
  ): Promise<IndexedTaggingSecret> {
272
273
  await this.syncTaggedLogsAsSender(contractAddress, sender, recipient);
273
274
 
274
- const secret = await this.#calculateTaggingSecret(contractAddress, sender, recipient);
275
- const [index] = await this.db.getTaggingSecretsIndexesAsSender([secret]);
275
+ const appTaggingSecret = await this.#calculateAppTaggingSecret(contractAddress, sender, recipient);
276
+ const [index] = await this.db.getTaggingSecretsIndexesAsSender([appTaggingSecret]);
276
277
 
277
- return new IndexedTaggingSecret(secret, index);
278
+ return new IndexedTaggingSecret(appTaggingSecret, index);
278
279
  }
279
280
 
280
281
  /**
281
- * Increments the tagging secret for a given sender and recipient pair. For this to work, the ivpsk_m of the sender must be known.
282
+ * Increments the tagging secret for a given sender and recipient pair. For this to work, the ivsk_m of the sender must be known.
282
283
  * @param contractAddress - The contract address to silo the secret for
283
284
  * @param sender - The address sending the note
284
285
  * @param recipient - The address receiving the note
@@ -288,7 +289,7 @@ export class SimulatorOracle implements DBOracle {
288
289
  sender: AztecAddress,
289
290
  recipient: AztecAddress,
290
291
  ): Promise<void> {
291
- const secret = await this.#calculateTaggingSecret(contractAddress, sender, recipient);
292
+ const secret = await this.#calculateAppTaggingSecret(contractAddress, sender, recipient);
292
293
  const contractName = await this.contractDataOracle.getDebugContractName(contractAddress);
293
294
  this.log.debug(`Incrementing app tagging secret at ${contractName}(${contractAddress})`, {
294
295
  secret,
@@ -302,37 +303,38 @@ export class SimulatorOracle implements DBOracle {
302
303
  await this.db.setTaggingSecretsIndexesAsSender([new IndexedTaggingSecret(secret, index + 1)]);
303
304
  }
304
305
 
305
- async #calculateTaggingSecret(contractAddress: AztecAddress, sender: AztecAddress, recipient: AztecAddress) {
306
+ async #calculateAppTaggingSecret(contractAddress: AztecAddress, sender: AztecAddress, recipient: AztecAddress) {
306
307
  const senderCompleteAddress = await this.getCompleteAddress(sender);
307
308
  const senderIvsk = await this.keyStore.getMasterIncomingViewingSecretKey(sender);
308
- const sharedSecret = computeTaggingSecret(senderCompleteAddress, senderIvsk, recipient);
309
- // Silo the secret to the app so it can't be used to track other app's notes
310
- const siloedSecret = poseidon2Hash([sharedSecret.x, sharedSecret.y, contractAddress]);
311
- return siloedSecret;
309
+ const secretPoint = computeTaggingSecretPoint(senderCompleteAddress, senderIvsk, recipient);
310
+ // Silo the secret so it can't be used to track other app's notes
311
+ const appSecret = poseidon2Hash([secretPoint.x, secretPoint.y, contractAddress]);
312
+ return appSecret;
312
313
  }
313
314
 
314
315
  /**
315
- * Returns the siloed tagging secrets for a given recipient and all the senders in the address book
316
+ * Returns the indexed tagging secrets for a given recipient and all the senders in the address book
316
317
  * This method should be exposed as an oracle call to allow aztec.nr to perform the orchestration
317
318
  * of the syncTaggedLogs and processTaggedLogs methods. However, it is not possible to do so at the moment,
318
319
  * so we're keeping it private for now.
319
320
  * @param contractAddress - The contract address to silo the secret for
320
321
  * @param recipient - The address receiving the notes
321
- * @returns A list of siloed tagging secrets
322
+ * @returns A list of indexed tagging secrets
322
323
  */
323
- async #getAppTaggingSecretsForContacts(
324
+ async #getIndexedTaggingSecretsForSenders(
324
325
  contractAddress: AztecAddress,
325
326
  recipient: AztecAddress,
326
327
  ): Promise<IndexedTaggingSecret[]> {
327
328
  const recipientCompleteAddress = await this.getCompleteAddress(recipient);
328
329
  const recipientIvsk = await this.keyStore.getMasterIncomingViewingSecretKey(recipient);
329
330
 
330
- // We implicitly add all PXE accounts as contacts, this helps us decrypt tags on notes that we send to ourselves (recipient = us, sender = us)
331
- const contacts = [...(await this.db.getContactAddresses()), ...(await this.keyStore.getAccounts())].filter(
331
+ // We implicitly add all PXE accounts as senders, this helps us decrypt tags on notes that we send to ourselves
332
+ // (recipient = us, sender = us)
333
+ const senders = [...(await this.db.getSenderAddresses()), ...(await this.keyStore.getAccounts())].filter(
332
334
  (address, index, self) => index === self.findIndex(otherAddress => otherAddress.equals(address)),
333
335
  );
334
- const appTaggingSecrets = contacts.map(contact => {
335
- const sharedSecret = computeTaggingSecret(recipientCompleteAddress, recipientIvsk, contact);
336
+ const appTaggingSecrets = senders.map(contact => {
337
+ const sharedSecret = computeTaggingSecretPoint(recipientCompleteAddress, recipientIvsk, contact);
336
338
  return poseidon2Hash([sharedSecret.x, sharedSecret.y, contractAddress]);
337
339
  });
338
340
  const indexes = await this.db.getTaggingSecretsIndexesAsRecipient(appTaggingSecrets);
@@ -351,65 +353,63 @@ export class SimulatorOracle implements DBOracle {
351
353
  sender: AztecAddress,
352
354
  recipient: AztecAddress,
353
355
  ): Promise<void> {
354
- const appTaggingSecret = await this.#calculateTaggingSecret(contractAddress, sender, recipient);
355
- let [currentIndex] = await this.db.getTaggingSecretsIndexesAsSender([appTaggingSecret]);
356
-
357
- const INDEX_OFFSET = 10;
358
-
359
- let previousEmptyBack = 0;
360
- let currentEmptyBack = 0;
361
- let currentEmptyFront: number;
362
-
363
- // The below code is trying to find the index of the start of the first window in which for all elements of window, we do not see logs.
364
- // We take our window size, and fetch the node for these logs. We store both the amount of empty consecutive slots from the front and the back.
365
- // We use our current empty consecutive slots from the front, as well as the previous consecutive empty slots from the back to see if we ever hit a time where there
366
- // is a window in which we see the combination of them to be greater than the window's size. If true, we rewind current index to the start of said window and use it.
367
- // Assuming two windows of 5:
368
- // [0, 1, 0, 1, 0], [0, 0, 0, 0, 0]
369
- // We can see that when processing the second window, the previous amount of empty slots from the back of the window (1), added with the empty elements from the front of the window (5)
370
- // is greater than 5 (6) and therefore we have found a window to use.
371
- // We simply need to take the number of elements (10) - the size of the window (5) - the number of consecutive empty elements from the back of the last window (1) = 4;
372
- // This is the first index of our desired window.
373
- // Note that if we ever see a situation like so:
374
- // [0, 1, 0, 1, 0], [0, 0, 0, 0, 1]
375
- // This also returns the correct index (4), but this is indicative of a problem / desync. i.e. we should never have a window that has a log that exists after the window.
376
-
356
+ const appTaggingSecret = await this.#calculateAppTaggingSecret(contractAddress, sender, recipient);
357
+ const [oldIndex] = await this.db.getTaggingSecretsIndexesAsSender([appTaggingSecret]);
358
+
359
+ // This algorithm works such that:
360
+ // 1. If we find minimum consecutive empty logs in a window of logs we set the index to the index of the last log
361
+ // we found and quit.
362
+ // 2. If we don't find minimum consecutive empty logs in a window of logs we slide the window to latest log index
363
+ // and repeat the process.
364
+ const MIN_CONSECUTIVE_EMPTY_LOGS = 10;
365
+ const WINDOW_SIZE = MIN_CONSECUTIVE_EMPTY_LOGS * 2;
366
+
367
+ let [numConsecutiveEmptyLogs, currentIndex] = [0, oldIndex];
377
368
  do {
378
- const currentTags = [...new Array(INDEX_OFFSET)].map((_, i) => {
369
+ // We compute the tags for the current window of indexes
370
+ const currentTags = [...new Array(WINDOW_SIZE)].map((_, i) => {
379
371
  const indexedAppTaggingSecret = new IndexedTaggingSecret(appTaggingSecret, currentIndex + i);
380
372
  return indexedAppTaggingSecret.computeSiloedTag(recipient, contractAddress);
381
373
  });
382
- previousEmptyBack = currentEmptyBack;
383
374
 
375
+ // We fetch the logs for the tags
384
376
  const possibleLogs = await this.aztecNode.getLogsByTags(currentTags);
385
377
 
386
- const indexOfFirstLog = possibleLogs.findIndex(possibleLog => possibleLog.length !== 0);
387
- currentEmptyFront = indexOfFirstLog === -1 ? INDEX_OFFSET : indexOfFirstLog;
388
-
378
+ // We find the index of the last log in the window that is not empty
389
379
  const indexOfLastLog = possibleLogs.findLastIndex(possibleLog => possibleLog.length !== 0);
390
- currentEmptyBack = indexOfLastLog === -1 ? INDEX_OFFSET : INDEX_OFFSET - 1 - indexOfLastLog;
391
380
 
392
- currentIndex += INDEX_OFFSET;
393
- } while (currentEmptyFront + previousEmptyBack < INDEX_OFFSET);
381
+ if (indexOfLastLog === -1) {
382
+ // We haven't found any logs in the current window so we stop looking
383
+ break;
384
+ }
394
385
 
395
- // We unwind the entire current window and the amount of consecutive empty slots from the previous window
396
- const newIndex = currentIndex - (INDEX_OFFSET + previousEmptyBack);
386
+ // We move the current index to that of the last log we found
387
+ currentIndex += indexOfLastLog + 1;
397
388
 
398
- await this.db.setTaggingSecretsIndexesAsSender([new IndexedTaggingSecret(appTaggingSecret, newIndex)]);
389
+ // We compute the number of consecutive empty logs we found and repeat the process if we haven't found enough.
390
+ numConsecutiveEmptyLogs = WINDOW_SIZE - indexOfLastLog - 1;
391
+ } while (numConsecutiveEmptyLogs < MIN_CONSECUTIVE_EMPTY_LOGS);
399
392
 
400
393
  const contractName = await this.contractDataOracle.getDebugContractName(contractAddress);
401
- this.log.debug(`Syncing logs for sender ${sender} at contract ${contractName}(${contractAddress})`, {
402
- sender,
403
- secret: appTaggingSecret,
404
- index: currentIndex,
405
- contractName,
406
- contractAddress,
407
- });
394
+ if (currentIndex !== oldIndex) {
395
+ await this.db.setTaggingSecretsIndexesAsSender([new IndexedTaggingSecret(appTaggingSecret, currentIndex)]);
396
+
397
+ this.log.debug(`Syncing logs for sender ${sender} at contract ${contractName}(${contractAddress})`, {
398
+ sender,
399
+ secret: appTaggingSecret,
400
+ index: currentIndex,
401
+ contractName,
402
+ contractAddress,
403
+ });
404
+ } else {
405
+ this.log.debug(`No new logs found for sender ${sender} at contract ${contractName}(${contractAddress})`);
406
+ }
408
407
  }
409
408
 
410
409
  /**
411
410
  * Synchronizes the logs tagged with scoped addresses and all the senders in the address book.
412
- * Returns the unsynched logs and updates the indexes of the secrets used to tag them until there are no more logs to sync.
411
+ * Returns the unsynched logs and updates the indexes of the secrets used to tag them until there are no more logs
412
+ * to sync.
413
413
  * @param contractAddress - The address of the contract that the logs are tagged for
414
414
  * @param recipient - The address of the recipient
415
415
  * @returns A list of encrypted logs tagged with the recipient's address
@@ -419,125 +419,137 @@ export class SimulatorOracle implements DBOracle {
419
419
  maxBlockNumber: number,
420
420
  scopes?: AztecAddress[],
421
421
  ): Promise<Map<string, TxScopedL2Log[]>> {
422
+ // Ideally this algorithm would be implemented in noir, exposing its building blocks as oracles.
423
+ // However it is impossible at the moment due to the language not supporting nested slices.
424
+ // This nesting is necessary because for a given set of tags we don't
425
+ // know how many logs we will get back. Furthermore, these logs are of undetermined
426
+ // length, since we don't really know the note they correspond to until we decrypt them.
427
+
422
428
  const recipients = scopes ? scopes : await this.keyStore.getAccounts();
423
- const result = new Map<string, TxScopedL2Log[]>();
429
+ // A map of logs going from recipient address to logs. Note that the logs might have been processed before
430
+ // due to us having a sliding window that "looks back" for logs as well. (We look back as there is no guarantee
431
+ // that a logs will be received ordered by a given tax index and that the tags won't be reused).
432
+ const logsMap = new Map<string, TxScopedL2Log[]>();
424
433
  const contractName = await this.contractDataOracle.getDebugContractName(contractAddress);
425
434
  for (const recipient of recipients) {
426
- const logs: TxScopedL2Log[] = [];
427
- // Ideally this algorithm would be implemented in noir, exposing its building blocks as oracles.
428
- // However it is impossible at the moment due to the language not supporting nested slices.
429
- // This nesting is necessary because for a given set of tags we don't
430
- // know how many logs we will get back. Furthermore, these logs are of undetermined
431
- // length, since we don't really know the note they correspond to until we decrypt them.
432
-
433
- // 1. Get all the secrets for the recipient and sender pairs (#9365)
434
- const appTaggingSecrets = await this.#getAppTaggingSecretsForContacts(contractAddress, recipient);
435
-
436
- // 1.1 Set up a sliding window with an offset. Chances are the sender might have messed up
437
- // and inadvertently incremented their index without use getting any logs (for example, in case
438
- // of a revert). If we stopped looking for logs the first time
439
- // we receive 0 for a tag, we might never receive anything from that sender again.
440
- // Also there's a possibility that we have advanced our index, but the sender has reused it, so
441
- // we might have missed some logs. For these reasons, we have to look both back and ahead of the
442
- // stored index
443
- const INDEX_OFFSET = 10;
444
- type SearchState = {
445
- currentTagggingSecrets: IndexedTaggingSecret[];
446
- maxIndexesToCheck: { [k: string]: number };
447
- initialSecretIndexes: { [k: string]: number };
448
- secretsToIncrement: { [k: string]: number };
449
- };
450
- const searchState = appTaggingSecrets.reduce<SearchState>(
451
- (acc, appTaggingSecret) => ({
452
- // Start looking for logs before the stored index
453
- currentTagggingSecrets: acc.currentTagggingSecrets.concat([
454
- new IndexedTaggingSecret(appTaggingSecret.secret, Math.max(0, appTaggingSecret.index - INDEX_OFFSET)),
455
- ]),
456
- // Keep looking for logs beyond the stored index
457
- maxIndexesToCheck: {
458
- ...acc.maxIndexesToCheck,
459
- ...{ [appTaggingSecret.secret.toString()]: appTaggingSecret.index + INDEX_OFFSET },
460
- },
461
- // Keeps track of the secrets we have to increment in the database
462
- secretsToIncrement: {},
463
- // Store the initial set of indexes for the secrets
464
- initialSecretIndexes: {
465
- ...acc.initialSecretIndexes,
466
- ...{ [appTaggingSecret.secret.toString()]: appTaggingSecret.index },
467
- },
468
- }),
469
- { currentTagggingSecrets: [], maxIndexesToCheck: {}, secretsToIncrement: {}, initialSecretIndexes: {} },
470
- );
435
+ const logsForRecipient: TxScopedL2Log[] = [];
436
+
437
+ // Get all the secrets for the recipient and sender pairs (#9365)
438
+ const secrets = await this.#getIndexedTaggingSecretsForSenders(contractAddress, recipient);
439
+
440
+ // We fetch logs for a window of indexes in a range:
441
+ // <latest_log_index - WINDOW_HALF_SIZE, latest_log_index + WINDOW_HALF_SIZE>.
442
+ //
443
+ // We use this window approach because it could happen that a sender might have messed up and inadvertently
444
+ // incremented their index without us getting any logs (for example, in case of a revert). If we stopped looking
445
+ // for logs the first time we don't receive any logs for a tag, we might never receive anything from that sender again.
446
+ // Also there's a possibility that we have advanced our index, but the sender has reused it, so we might have missed
447
+ // some logs. For these reasons, we have to look both back and ahead of the stored index.
448
+ let secretsAndWindows = secrets.map(secret => {
449
+ return {
450
+ appTaggingSecret: secret.appTaggingSecret,
451
+ leftMostIndex: Math.max(0, secret.index - WINDOW_HALF_SIZE),
452
+ rightMostIndex: secret.index + WINDOW_HALF_SIZE,
453
+ };
454
+ });
471
455
 
472
- let { currentTagggingSecrets } = searchState;
473
- const { maxIndexesToCheck, secretsToIncrement, initialSecretIndexes } = searchState;
456
+ // As we iterate we store the largest index we have seen for a given secret to later on store it in the db.
457
+ const newLargestIndexMapToStore: { [k: string]: number } = {};
474
458
 
475
- while (currentTagggingSecrets.length > 0) {
476
- // 2. Compute tags using the secrets, recipient and index. Obtain logs for each tag (#9380)
477
- const currentTags = currentTagggingSecrets.map(taggingSecret =>
478
- taggingSecret.computeSiloedTag(recipient, contractAddress),
459
+ // The initial/unmodified indexes of the secrets stored in a key-value map where key is the app tagging secret.
460
+ const initialIndexesMap = getInitialIndexesMap(secrets);
461
+
462
+ while (secretsAndWindows.length > 0) {
463
+ const secretsForTheWholeWindow = getIndexedTaggingSecretsForTheWindow(secretsAndWindows);
464
+ const tagsForTheWholeWindow = secretsForTheWholeWindow.map(secret =>
465
+ secret.computeSiloedTag(recipient, contractAddress),
479
466
  );
480
- const logsByTags = await this.aztecNode.getLogsByTags(currentTags);
481
- const newTaggingSecrets: IndexedTaggingSecret[] = [];
467
+
468
+ // We store the new largest indexes we find in the iteration in the following map to later on construct
469
+ // a new set of secrets and windows to fetch logs for.
470
+ const newLargestIndexMapForIteration: { [k: string]: number } = {};
471
+
472
+ // Fetch the logs for the tags and iterate over them
473
+ const logsByTags = await this.aztecNode.getLogsByTags(tagsForTheWholeWindow);
474
+
482
475
  logsByTags.forEach((logsByTag, logIndex) => {
483
- const { secret: currentSecret, index: currentIndex } = currentTagggingSecrets[logIndex];
484
- const currentSecretAsStr = currentSecret.toString();
485
- this.log.debug(`Syncing logs for recipient ${recipient} at contract ${contractName}(${contractAddress})`, {
486
- recipient,
487
- secret: currentSecret,
488
- index: currentIndex,
489
- contractName,
490
- contractAddress,
491
- });
492
- // 3.1. Append logs to the list and increment the index for the tags that have logs (#9380)
493
476
  if (logsByTag.length > 0) {
494
- const newIndex = currentIndex + 1;
495
- this.log.debug(
496
- `Found ${logsByTag.length} logs as recipient ${recipient}. Incrementing index to ${newIndex} at contract ${contractName}(${contractAddress})`,
497
- {
498
- recipient,
499
- secret: currentSecret,
500
- newIndex,
501
- contractName,
502
- contractAddress,
503
- },
504
- );
505
- logs.push(...logsByTag);
506
-
507
- if (currentIndex >= initialSecretIndexes[currentSecretAsStr]) {
508
- // 3.2. Increment the index for the tags that have logs, provided they're higher than the one
509
- // we have stored in the db (#9380)
510
- secretsToIncrement[currentSecretAsStr] = newIndex;
511
- // 3.3. Slide the window forwards if we have found logs beyond the initial index
512
- maxIndexesToCheck[currentSecretAsStr] = currentIndex + INDEX_OFFSET;
477
+ // The logs for the given tag exist so we store them for later processing
478
+ logsForRecipient.push(...logsByTag);
479
+
480
+ // We retrieve the indexed tagging secret corresponding to the log as I need that to evaluate whether
481
+ // a new largest index have been found.
482
+ const secretCorrespondingToLog = secretsForTheWholeWindow[logIndex];
483
+ const initialIndex = initialIndexesMap[secretCorrespondingToLog.appTaggingSecret.toString()];
484
+
485
+ this.log.debug(`Found ${logsByTag.length} logs as recipient ${recipient}`, {
486
+ recipient,
487
+ secret: secretCorrespondingToLog.appTaggingSecret,
488
+ contractName,
489
+ contractAddress,
490
+ });
491
+
492
+ if (
493
+ secretCorrespondingToLog.index >= initialIndex &&
494
+ (newLargestIndexMapForIteration[secretCorrespondingToLog.appTaggingSecret.toString()] === undefined ||
495
+ secretCorrespondingToLog.index >=
496
+ newLargestIndexMapForIteration[secretCorrespondingToLog.appTaggingSecret.toString()])
497
+ ) {
498
+ // We have found a new largest index so we store it for later processing (storing it in the db + fetching
499
+ // the difference of the window sets of current and the next iteration)
500
+ newLargestIndexMapForIteration[secretCorrespondingToLog.appTaggingSecret.toString()] =
501
+ secretCorrespondingToLog.index + 1;
502
+
503
+ this.log.debug(
504
+ `Incrementing index to ${
505
+ secretCorrespondingToLog.index + 1
506
+ } at contract ${contractName}(${contractAddress})`,
507
+ );
513
508
  }
514
509
  }
515
- // 3.4 Keep increasing the index (inside the window) temporarily for the tags that have no logs
516
- // There's a chance the sender missed some and we want to catch up
517
- if (currentIndex < maxIndexesToCheck[currentSecretAsStr]) {
518
- const newTaggingSecret = new IndexedTaggingSecret(currentSecret, currentIndex + 1);
519
- newTaggingSecrets.push(newTaggingSecret);
520
- }
521
510
  });
522
- await this.db.setTaggingSecretsIndexesAsRecipient(
523
- Object.keys(secretsToIncrement).map(
524
- secret => new IndexedTaggingSecret(Fr.fromHexString(secret), secretsToIncrement[secret]),
525
- ),
526
- );
527
- currentTagggingSecrets = newTaggingSecrets;
511
+
512
+ // Now based on the new largest indexes we found, we will construct a new secrets and windows set to fetch logs
513
+ // for. Note that it's very unlikely that a new log from the current window would appear between the iterations
514
+ // so we fetch the logs only for the difference of the window sets.
515
+ const newSecretsAndWindows = [];
516
+ for (const [appTaggingSecret, newIndex] of Object.entries(newLargestIndexMapForIteration)) {
517
+ const secret = secrets.find(secret => secret.appTaggingSecret.toString() === appTaggingSecret);
518
+ if (secret) {
519
+ newSecretsAndWindows.push({
520
+ appTaggingSecret: secret.appTaggingSecret,
521
+ // We set the left most index to the new index to avoid fetching the same logs again
522
+ leftMostIndex: newIndex,
523
+ rightMostIndex: newIndex + WINDOW_HALF_SIZE,
524
+ });
525
+
526
+ // We store the new largest index in the map to later store it in the db.
527
+ newLargestIndexMapToStore[appTaggingSecret] = newIndex;
528
+ } else {
529
+ throw new Error(
530
+ `Secret not found for appTaggingSecret ${appTaggingSecret}. This is a bug as it should never happen!`,
531
+ );
532
+ }
533
+ }
534
+
535
+ // Now we set the new secrets and windows and proceed to the next iteration.
536
+ secretsAndWindows = newSecretsAndWindows;
528
537
  }
529
538
 
530
- result.set(
539
+ // We filter the logs by block number and store them in the map.
540
+ logsMap.set(
531
541
  recipient.toString(),
532
- // Remove logs with a block number higher than the max block number
533
- // Duplicates are likely to happen due to the sliding window, so we also filter them out
534
- logs.filter(
535
- (log, index, self) =>
536
- log.blockNumber <= maxBlockNumber && index === self.findIndex(otherLog => otherLog.equals(log)),
542
+ logsForRecipient.filter(log => log.blockNumber <= maxBlockNumber),
543
+ );
544
+
545
+ // At this point we have processed all the logs for the recipient so we store the new largest indexes in the db.
546
+ await this.db.setTaggingSecretsIndexesAsRecipient(
547
+ Object.entries(newLargestIndexMapToStore).map(
548
+ ([appTaggingSecret, index]) => new IndexedTaggingSecret(Fr.fromHexString(appTaggingSecret), index),
537
549
  ),
538
550
  );
539
551
  }
540
- return result;
552
+ return logsMap;
541
553
  }
542
554
 
543
555
  /**
@@ -628,27 +640,30 @@ export class SimulatorOracle implements DBOracle {
628
640
  });
629
641
  });
630
642
  }
631
- const nullifiedNotes: IncomingNoteDao[] = [];
632
- const currentNotesForRecipient = await this.db.getIncomingNotes({ owner: recipient });
633
- const nullifiersToCheck = currentNotesForRecipient.map(note => note.siloedNullifier);
634
- const currentBlockNumber = await this.getBlockNumber();
635
- const nullifierIndexes = await this.aztecNode.findNullifiersIndexesWithBlock(currentBlockNumber, nullifiersToCheck);
636
-
637
- const foundNullifiers = nullifiersToCheck
638
- .map((nullifier, i) => {
639
- if (nullifierIndexes[i] !== undefined) {
640
- return { ...nullifierIndexes[i], ...{ data: nullifier } } as InBlock<Fr>;
641
- }
642
- })
643
- .filter(nullifier => nullifier !== undefined) as InBlock<Fr>[];
644
-
645
- await this.db.removeNullifiedNotes(foundNullifiers, recipient.toAddressPoint());
646
- nullifiedNotes.forEach(noteDao => {
647
- this.log.verbose(`Removed note for contract ${noteDao.contractAddress} at slot ${noteDao.storageSlot}`, {
648
- contract: noteDao.contractAddress,
649
- slot: noteDao.storageSlot,
650
- nullifier: noteDao.siloedNullifier.toString(),
643
+ }
644
+
645
+ public async removeNullifiedNotes(contractAddress: AztecAddress) {
646
+ for (const recipient of await this.keyStore.getAccounts()) {
647
+ const currentNotesForRecipient = await this.db.getIncomingNotes({ contractAddress, owner: recipient });
648
+ const nullifiersToCheck = currentNotesForRecipient.map(note => note.siloedNullifier);
649
+ const nullifierIndexes = await this.aztecNode.findNullifiersIndexesWithBlock('latest', nullifiersToCheck);
650
+
651
+ const foundNullifiers = nullifiersToCheck
652
+ .map((nullifier, i) => {
653
+ if (nullifierIndexes[i] !== undefined) {
654
+ return { ...nullifierIndexes[i], ...{ data: nullifier } } as InBlock<Fr>;
655
+ }
656
+ })
657
+ .filter(nullifier => nullifier !== undefined) as InBlock<Fr>[];
658
+
659
+ const nullifiedNotes = await this.db.removeNullifiedNotes(foundNullifiers, recipient.toAddressPoint());
660
+ nullifiedNotes.forEach(noteDao => {
661
+ this.log.verbose(`Removed note for contract ${noteDao.contractAddress} at slot ${noteDao.storageSlot}`, {
662
+ contract: noteDao.contractAddress,
663
+ slot: noteDao.storageSlot,
664
+ nullifier: noteDao.siloedNullifier.toString(),
665
+ });
651
666
  });
652
- });
667
+ }
653
668
  }
654
669
  }
@@ -0,0 +1,31 @@
1
+ import { type Fr, IndexedTaggingSecret } from '@aztec/circuits.js';
2
+
3
+ // Half the size of the window we slide over the tagging secret indexes.
4
+ export const WINDOW_HALF_SIZE = 10;
5
+
6
+ export function getIndexedTaggingSecretsForTheWindow(
7
+ secretsAndWindows: { appTaggingSecret: Fr; leftMostIndex: number; rightMostIndex: number }[],
8
+ ): IndexedTaggingSecret[] {
9
+ const secrets: IndexedTaggingSecret[] = [];
10
+ for (const secretAndWindow of secretsAndWindows) {
11
+ for (let i = secretAndWindow.leftMostIndex; i <= secretAndWindow.rightMostIndex; i++) {
12
+ secrets.push(new IndexedTaggingSecret(secretAndWindow.appTaggingSecret, i));
13
+ }
14
+ }
15
+ return secrets;
16
+ }
17
+
18
+ /**
19
+ * Creates a map from app tagging secret to initial index.
20
+ * @param indexedTaggingSecrets - The indexed tagging secrets to get the initial indexes from.
21
+ * @returns The map from app tagging secret to initial index.
22
+ */
23
+ export function getInitialIndexesMap(indexedTaggingSecrets: IndexedTaggingSecret[]): { [k: string]: number } {
24
+ const initialIndexes: { [k: string]: number } = {};
25
+
26
+ for (const indexedTaggingSecret of indexedTaggingSecrets) {
27
+ initialIndexes[indexedTaggingSecret.appTaggingSecret.toString()] = indexedTaggingSecret.index;
28
+ }
29
+
30
+ return initialIndexes;
31
+ }
@@ -1,4 +1,5 @@
1
1
  import { BBNativePrivateKernelProver } from '@aztec/bb-prover';
2
+ import { BBWasmPrivateKernelProver } from '@aztec/bb-prover/wasm';
2
3
  import { type AztecNode, type PrivateKernelProver } from '@aztec/circuit-types';
3
4
  import { randomBytes } from '@aztec/foundation/crypto';
4
5
  import { createLogger } from '@aztec/foundation/log';
@@ -59,9 +60,11 @@ function createProver(config: PXEServiceConfig, logSuffix?: string) {
59
60
 
60
61
  // (@PhilWindle) Temporary validation until WASM is implemented
61
62
  if (!config.bbBinaryPath || !config.bbWorkingDirectory) {
62
- throw new Error(`Prover must be configured with binary path and working directory`);
63
+ return new BBWasmPrivateKernelProver(16);
64
+ } else {
65
+ const bbConfig = config as Required<Pick<PXEServiceConfig, 'bbBinaryPath' | 'bbWorkingDirectory'>> &
66
+ PXEServiceConfig;
67
+ const log = createLogger('pxe:bb-native-prover' + (logSuffix ? `:${logSuffix}` : ''));
68
+ return BBNativePrivateKernelProver.new({ bbSkipCleanup: false, ...bbConfig }, log);
63
69
  }
64
- const bbConfig = config as Required<Pick<PXEServiceConfig, 'bbBinaryPath' | 'bbWorkingDirectory'>> & PXEServiceConfig;
65
- const log = createLogger('pxe:bb-native-prover' + (logSuffix ? `:${logSuffix}` : ''));
66
- return BBNativePrivateKernelProver.new({ bbSkipCleanup: false, ...bbConfig }, log);
67
70
  }