@aztec/pxe 0.71.0 → 0.72.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.
@@ -27,6 +27,7 @@ import {
27
27
  MAX_NOTE_HASHES_PER_TX,
28
28
  PRIVATE_LOG_SIZE_IN_FIELDS,
29
29
  PrivateLog,
30
+ PublicLog,
30
31
  computeAddressSecret,
31
32
  computeTaggingSecretPoint,
32
33
  } from '@aztec/circuits.js';
@@ -329,7 +330,7 @@ export class SimulatorOracle implements DBOracle {
329
330
  async #calculateAppTaggingSecret(contractAddress: AztecAddress, sender: AztecAddress, recipient: AztecAddress) {
330
331
  const senderCompleteAddress = await this.getCompleteAddress(sender);
331
332
  const senderIvsk = await this.keyStore.getMasterIncomingViewingSecretKey(sender);
332
- const secretPoint = computeTaggingSecretPoint(senderCompleteAddress, senderIvsk, recipient);
333
+ const secretPoint = await computeTaggingSecretPoint(senderCompleteAddress, senderIvsk, recipient);
333
334
  // Silo the secret so it can't be used to track other app's notes
334
335
  const appSecret = poseidon2Hash([secretPoint.x, secretPoint.y, contractAddress]);
335
336
  return appSecret;
@@ -356,10 +357,12 @@ export class SimulatorOracle implements DBOracle {
356
357
  const senders = [...(await this.db.getSenderAddresses()), ...(await this.keyStore.getAccounts())].filter(
357
358
  (address, index, self) => index === self.findIndex(otherAddress => otherAddress.equals(address)),
358
359
  );
359
- const appTaggingSecrets = senders.map(contact => {
360
- const sharedSecret = computeTaggingSecretPoint(recipientCompleteAddress, recipientIvsk, contact);
361
- return poseidon2Hash([sharedSecret.x, sharedSecret.y, contractAddress]);
362
- });
360
+ const appTaggingSecrets = await Promise.all(
361
+ senders.map(async contact => {
362
+ const sharedSecret = await computeTaggingSecretPoint(recipientCompleteAddress, recipientIvsk, contact);
363
+ return poseidon2Hash([sharedSecret.x, sharedSecret.y, contractAddress]);
364
+ }),
365
+ );
363
366
  const indexes = await this.db.getTaggingSecretsIndexesAsRecipient(appTaggingSecrets);
364
367
  return appTaggingSecrets.map((secret, i) => new IndexedTaggingSecret(secret, indexes[i]));
365
368
  }
@@ -499,15 +502,31 @@ export class SimulatorOracle implements DBOracle {
499
502
 
500
503
  logsByTags.forEach((logsByTag, logIndex) => {
501
504
  if (logsByTag.length > 0) {
505
+ // Check that public logs have the correct contract address
506
+ const checkedLogsbyTag = logsByTag.filter(
507
+ l => !l.isFromPublic || PublicLog.fromBuffer(l.logData).contractAddress.equals(contractAddress),
508
+ );
509
+ if (checkedLogsbyTag.length < logsByTag.length) {
510
+ const discarded = logsByTag.filter(
511
+ log => checkedLogsbyTag.find(filteredLog => filteredLog.equals(log)) === undefined,
512
+ );
513
+ this.log.warn(
514
+ `Discarded ${
515
+ logsByTag.length - checkedLogsbyTag.length
516
+ } public logs with mismatched contract address ${contractAddress}:`,
517
+ discarded.map(l => PublicLog.fromBuffer(l.logData)),
518
+ );
519
+ }
520
+
502
521
  // The logs for the given tag exist so we store them for later processing
503
- logsForRecipient.push(...logsByTag);
522
+ logsForRecipient.push(...checkedLogsbyTag);
504
523
 
505
524
  // We retrieve the indexed tagging secret corresponding to the log as I need that to evaluate whether
506
525
  // a new largest index have been found.
507
526
  const secretCorrespondingToLog = secretsForTheWholeWindow[logIndex];
508
527
  const initialIndex = initialIndexesMap[secretCorrespondingToLog.appTaggingSecret.toString()];
509
528
 
510
- this.log.debug(`Found ${logsByTag.length} logs as recipient ${recipient}`, {
529
+ this.log.debug(`Found ${checkedLogsbyTag.length} logs as recipient ${recipient}`, {
511
530
  recipient,
512
531
  secret: secretCorrespondingToLog.appTaggingSecret,
513
532
  contractName,
@@ -588,7 +607,7 @@ export class SimulatorOracle implements DBOracle {
588
607
  const ivskM = await this.keyStore.getMasterSecretKey(
589
608
  recipientCompleteAddress.publicKeys.masterIncomingViewingPublicKey,
590
609
  );
591
- const addressSecret = computeAddressSecret(recipientCompleteAddress.getPreaddress(), ivskM);
610
+ const addressSecret = await computeAddressSecret(recipientCompleteAddress.getPreaddress(), ivskM);
592
611
 
593
612
  // Since we could have notes with the same index for different txs, we need
594
613
  // to keep track of them scoping by txHash
@@ -597,8 +616,8 @@ export class SimulatorOracle implements DBOracle {
597
616
 
598
617
  for (const scopedLog of scopedLogs) {
599
618
  const payload = scopedLog.isFromPublic
600
- ? L1NotePayload.decryptAsIncomingFromPublic(scopedLog.logData, addressSecret)
601
- : L1NotePayload.decryptAsIncoming(PrivateLog.fromBuffer(scopedLog.logData), addressSecret);
619
+ ? await L1NotePayload.decryptAsIncomingFromPublic(PublicLog.fromBuffer(scopedLog.logData), addressSecret)
620
+ : await L1NotePayload.decryptAsIncoming(PrivateLog.fromBuffer(scopedLog.logData), addressSecret);
602
621
 
603
622
  if (!payload) {
604
623
  this.log.verbose('Unable to decrypt log');
@@ -703,7 +722,7 @@ export class SimulatorOracle implements DBOracle {
703
722
  })
704
723
  .filter(nullifier => nullifier !== undefined) as InBlock<Fr>[];
705
724
 
706
- const nullifiedNotes = await this.db.removeNullifiedNotes(foundNullifiers, recipient.toAddressPoint());
725
+ const nullifiedNotes = await this.db.removeNullifiedNotes(foundNullifiers, await recipient.toAddressPoint());
707
726
  nullifiedNotes.forEach(noteDao => {
708
727
  this.log.verbose(`Removed note for contract ${noteDao.contractAddress} at slot ${noteDao.storageSlot}`, {
709
728
  contract: noteDao.contractAddress,
@@ -724,21 +743,30 @@ export class SimulatorOracle implements DBOracle {
724
743
  txHash: Fr,
725
744
  recipient: AztecAddress,
726
745
  ): Promise<NoteDao> {
746
+ // We need to validate that the note does indeed exist in the world state to avoid adding notes that are then
747
+ // impossible to prove.
748
+
727
749
  const receipt = await this.aztecNode.getTxReceipt(new TxHash(txHash));
728
750
  if (receipt === undefined) {
729
751
  throw new Error(`Failed to fetch tx receipt for tx hash ${txHash} when searching for note hashes`);
730
752
  }
731
- const { blockNumber, blockHash } = receipt;
732
753
 
754
+ // Siloed and unique hashes are computed by us instead of relying on values sent by the contract to make sure
755
+ // we're not e.g. storing notes that belong to some other contract, which would constitute a security breach.
733
756
  const uniqueNoteHash = computeUniqueNoteHash(nonce, siloNoteHash(contractAddress, noteHash));
734
757
  const siloedNullifier = siloNullifier(contractAddress, nullifier);
735
758
 
759
+ // We store notes by their index in the global note hash tree, which has the convenient side effect of validating
760
+ // note existence in said tree. Note that while this is technically a historical query, we perform it at the latest
761
+ // locally synced block number which *should* be recent enough to be available. We avoid querying at 'latest' since
762
+ // we want to avoid accidentally processing notes that only exist ahead in time of the locally synced state.
763
+ const syncedBlockNumber = await this.db.getBlockNumber();
736
764
  const uniqueNoteHashTreeIndex = (
737
- await this.aztecNode.findLeavesIndexes(blockNumber!, MerkleTreeId.NOTE_HASH_TREE, [uniqueNoteHash])
765
+ await this.aztecNode.findLeavesIndexes(syncedBlockNumber!, MerkleTreeId.NOTE_HASH_TREE, [uniqueNoteHash])
738
766
  )[0];
739
767
  if (uniqueNoteHashTreeIndex === undefined) {
740
768
  throw new Error(
741
- `Note hash ${noteHash} (uniqued as ${uniqueNoteHash}) is not present on the tree at block ${blockNumber} (from tx ${txHash})`,
769
+ `Note hash ${noteHash} (uniqued as ${uniqueNoteHash}) is not present on the tree at block ${syncedBlockNumber} (from tx ${txHash})`,
742
770
  );
743
771
  }
744
772
 
@@ -750,10 +778,10 @@ export class SimulatorOracle implements DBOracle {
750
778
  noteHash,
751
779
  siloedNullifier,
752
780
  new TxHash(txHash),
753
- blockNumber!,
754
- blockHash!.toString(),
781
+ receipt.blockNumber!,
782
+ receipt.blockHash!.toString(),
755
783
  uniqueNoteHashTreeIndex,
756
- recipient.toAddressPoint(),
784
+ await recipient.toAddressPoint(),
757
785
  NoteSelector.empty(), // todo: remove
758
786
  );
759
787
  }