@aztec/pxe 0.70.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.
Files changed (50) hide show
  1. package/dest/config/index.d.ts.map +1 -1
  2. package/dest/config/index.js +3 -3
  3. package/dest/database/kv_pxe_database.d.ts +7 -5
  4. package/dest/database/kv_pxe_database.d.ts.map +1 -1
  5. package/dest/database/kv_pxe_database.js +33 -11
  6. package/dest/database/note_dao.d.ts +2 -4
  7. package/dest/database/note_dao.d.ts.map +1 -1
  8. package/dest/database/note_dao.js +3 -7
  9. package/dest/database/outgoing_note_dao.d.ts +2 -4
  10. package/dest/database/outgoing_note_dao.d.ts.map +1 -1
  11. package/dest/database/outgoing_note_dao.js +3 -7
  12. package/dest/database/pxe_database.d.ts +29 -12
  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 +162 -67
  16. package/dest/note_decryption_utils/add_public_values_to_payload.js +2 -2
  17. package/dest/pxe_service/pxe_service.d.ts +6 -6
  18. package/dest/pxe_service/pxe_service.d.ts.map +1 -1
  19. package/dest/pxe_service/pxe_service.js +31 -28
  20. package/dest/pxe_service/test/pxe_test_suite.js +9 -9
  21. package/dest/simulator_oracle/index.d.ts +12 -20
  22. package/dest/simulator_oracle/index.d.ts.map +1 -1
  23. package/dest/simulator_oracle/index.js +123 -69
  24. package/package.json +15 -15
  25. package/src/config/index.ts +2 -1
  26. package/src/database/kv_pxe_database.ts +46 -12
  27. package/src/database/note_dao.ts +6 -34
  28. package/src/database/outgoing_note_dao.ts +6 -33
  29. package/src/database/pxe_database.ts +31 -12
  30. package/src/database/pxe_database_test_suite.ts +189 -75
  31. package/src/note_decryption_utils/add_public_values_to_payload.ts +1 -1
  32. package/src/pxe_service/pxe_service.ts +47 -46
  33. package/src/pxe_service/test/pxe_test_suite.ts +8 -8
  34. package/src/simulator_oracle/index.ts +237 -88
  35. package/dest/note_decryption_utils/brute_force_note_info.d.ts +0 -31
  36. package/dest/note_decryption_utils/brute_force_note_info.d.ts.map +0 -1
  37. package/dest/note_decryption_utils/brute_force_note_info.js +0 -54
  38. package/dest/note_decryption_utils/index.d.ts +0 -3
  39. package/dest/note_decryption_utils/index.d.ts.map +0 -1
  40. package/dest/note_decryption_utils/index.js +0 -2
  41. package/dest/note_decryption_utils/produce_note_daos.d.ts +0 -28
  42. package/dest/note_decryption_utils/produce_note_daos.d.ts.map +0 -1
  43. package/dest/note_decryption_utils/produce_note_daos.js +0 -33
  44. package/dest/note_decryption_utils/produce_note_daos_for_key.d.ts +0 -8
  45. package/dest/note_decryption_utils/produce_note_daos_for_key.d.ts.map +0 -1
  46. package/dest/note_decryption_utils/produce_note_daos_for_key.js +0 -17
  47. package/src/note_decryption_utils/brute_force_note_info.ts +0 -90
  48. package/src/note_decryption_utils/index.ts +0 -2
  49. package/src/note_decryption_utils/produce_note_daos.ts +0 -69
  50. package/src/note_decryption_utils/produce_note_daos_for_key.ts +0 -59
@@ -1,14 +1,16 @@
1
1
  import {
2
2
  type AztecNode,
3
+ type FunctionCall,
3
4
  type InBlock,
4
5
  L1NotePayload,
5
6
  type L2Block,
6
7
  type L2BlockNumber,
7
8
  MerkleTreeId,
9
+ Note,
8
10
  type NoteStatus,
9
11
  type NullifierMembershipWitness,
10
12
  type PublicDataWitness,
11
- type TxEffect,
13
+ TxHash,
12
14
  type TxScopedL2Log,
13
15
  getNonNullifiedL1ToL2MessageWitness,
14
16
  } from '@aztec/circuit-types';
@@ -18,15 +20,25 @@ import {
18
20
  type CompleteAddress,
19
21
  type ContractInstance,
20
22
  Fr,
21
- type FunctionSelector,
23
+ FunctionSelector,
22
24
  IndexedTaggingSecret,
23
25
  type KeyValidationRequest,
24
26
  type L1_TO_L2_MSG_TREE_HEIGHT,
27
+ MAX_NOTE_HASHES_PER_TX,
28
+ PRIVATE_LOG_SIZE_IN_FIELDS,
25
29
  PrivateLog,
30
+ PublicLog,
26
31
  computeAddressSecret,
27
32
  computeTaggingSecretPoint,
28
33
  } from '@aztec/circuits.js';
29
- import { type FunctionArtifact, getFunctionArtifact } from '@aztec/foundation/abi';
34
+ import { computeUniqueNoteHash, siloNoteHash, siloNullifier } from '@aztec/circuits.js/hash';
35
+ import {
36
+ type FunctionArtifact,
37
+ FunctionType,
38
+ NoteSelector,
39
+ encodeArguments,
40
+ getFunctionArtifact,
41
+ } from '@aztec/foundation/abi';
30
42
  import { poseidon2Hash } from '@aztec/foundation/crypto';
31
43
  import { createLogger } from '@aztec/foundation/log';
32
44
  import { type KeyStore } from '@aztec/key-store';
@@ -37,10 +49,10 @@ import {
37
49
  type SimulationProvider,
38
50
  } from '@aztec/simulator/client';
39
51
 
40
- import { type ContractDataOracle } from '../contract_data_oracle/index.js';
52
+ import { ContractDataOracle } from '../contract_data_oracle/index.js';
41
53
  import { type PxeDatabase } from '../database/index.js';
42
- import { type NoteDao } from '../database/note_dao.js';
43
- import { produceNoteDaos } from '../note_decryption_utils/produce_note_daos.js';
54
+ import { NoteDao } from '../database/note_dao.js';
55
+ import { getOrderedNoteItems } from '../note_decryption_utils/add_public_values_to_payload.js';
44
56
  import { getAcirSimulator } from '../simulator/index.js';
45
57
  import { WINDOW_HALF_SIZE, getIndexedTaggingSecretsForTheWindow, getInitialIndexesMap } from './tagging_utils.js';
46
58
 
@@ -318,7 +330,7 @@ export class SimulatorOracle implements DBOracle {
318
330
  async #calculateAppTaggingSecret(contractAddress: AztecAddress, sender: AztecAddress, recipient: AztecAddress) {
319
331
  const senderCompleteAddress = await this.getCompleteAddress(sender);
320
332
  const senderIvsk = await this.keyStore.getMasterIncomingViewingSecretKey(sender);
321
- const secretPoint = computeTaggingSecretPoint(senderCompleteAddress, senderIvsk, recipient);
333
+ const secretPoint = await computeTaggingSecretPoint(senderCompleteAddress, senderIvsk, recipient);
322
334
  // Silo the secret so it can't be used to track other app's notes
323
335
  const appSecret = poseidon2Hash([secretPoint.x, secretPoint.y, contractAddress]);
324
336
  return appSecret;
@@ -345,10 +357,12 @@ export class SimulatorOracle implements DBOracle {
345
357
  const senders = [...(await this.db.getSenderAddresses()), ...(await this.keyStore.getAccounts())].filter(
346
358
  (address, index, self) => index === self.findIndex(otherAddress => otherAddress.equals(address)),
347
359
  );
348
- const appTaggingSecrets = senders.map(contact => {
349
- const sharedSecret = computeTaggingSecretPoint(recipientCompleteAddress, recipientIvsk, contact);
350
- return poseidon2Hash([sharedSecret.x, sharedSecret.y, contractAddress]);
351
- });
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
+ );
352
366
  const indexes = await this.db.getTaggingSecretsIndexesAsRecipient(appTaggingSecrets);
353
367
  return appTaggingSecrets.map((secret, i) => new IndexedTaggingSecret(secret, indexes[i]));
354
368
  }
@@ -431,6 +445,8 @@ export class SimulatorOracle implements DBOracle {
431
445
  maxBlockNumber: number,
432
446
  scopes?: AztecAddress[],
433
447
  ): Promise<Map<string, TxScopedL2Log[]>> {
448
+ this.log.verbose('Searching for tagged logs', { contract: contractAddress });
449
+
434
450
  // Ideally this algorithm would be implemented in noir, exposing its building blocks as oracles.
435
451
  // However it is impossible at the moment due to the language not supporting nested slices.
436
452
  // This nesting is necessary because for a given set of tags we don't
@@ -486,15 +502,31 @@ export class SimulatorOracle implements DBOracle {
486
502
 
487
503
  logsByTags.forEach((logsByTag, logIndex) => {
488
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
+
489
521
  // The logs for the given tag exist so we store them for later processing
490
- logsForRecipient.push(...logsByTag);
522
+ logsForRecipient.push(...checkedLogsbyTag);
491
523
 
492
524
  // We retrieve the indexed tagging secret corresponding to the log as I need that to evaluate whether
493
525
  // a new largest index have been found.
494
526
  const secretCorrespondingToLog = secretsForTheWholeWindow[logIndex];
495
527
  const initialIndex = initialIndexesMap[secretCorrespondingToLog.appTaggingSecret.toString()];
496
528
 
497
- this.log.debug(`Found ${logsByTag.length} logs as recipient ${recipient}`, {
529
+ this.log.debug(`Found ${checkedLogsbyTag.length} logs as recipient ${recipient}`, {
498
530
  recipient,
499
531
  secret: secretCorrespondingToLog.appTaggingSecret,
500
532
  contractName,
@@ -568,69 +600,41 @@ export class SimulatorOracle implements DBOracle {
568
600
  * Decrypts logs tagged for a recipient and returns them.
569
601
  * @param scopedLogs - The logs to decrypt.
570
602
  * @param recipient - The recipient of the logs.
571
- * @param simulator - The simulator to use for decryption.
572
603
  * @returns The decrypted notes.
573
604
  */
574
- async #decryptTaggedLogs(scopedLogs: TxScopedL2Log[], recipient: AztecAddress, simulator?: AcirSimulator) {
605
+ async #decryptTaggedLogs(scopedLogs: TxScopedL2Log[], recipient: AztecAddress) {
575
606
  const recipientCompleteAddress = await this.getCompleteAddress(recipient);
576
607
  const ivskM = await this.keyStore.getMasterSecretKey(
577
608
  recipientCompleteAddress.publicKeys.masterIncomingViewingPublicKey,
578
609
  );
579
- const addressSecret = computeAddressSecret(recipientCompleteAddress.getPreaddress(), ivskM);
610
+ const addressSecret = await computeAddressSecret(recipientCompleteAddress.getPreaddress(), ivskM);
580
611
 
581
612
  // Since we could have notes with the same index for different txs, we need
582
613
  // to keep track of them scoping by txHash
583
614
  const excludedIndices: Map<string, Set<number>> = new Map();
584
- const notes: NoteDao[] = [];
585
-
586
- const txEffectsCache = new Map<string, InBlock<TxEffect> | undefined>();
615
+ const decrypted = [];
587
616
 
588
617
  for (const scopedLog of scopedLogs) {
589
- const notePayload = scopedLog.isFromPublic
590
- ? L1NotePayload.decryptAsIncomingFromPublic(scopedLog.logData, addressSecret)
591
- : L1NotePayload.decryptAsIncoming(PrivateLog.fromBuffer(scopedLog.logData), addressSecret);
592
-
593
- if (notePayload) {
594
- const payload = notePayload;
618
+ const payload = scopedLog.isFromPublic
619
+ ? await L1NotePayload.decryptAsIncomingFromPublic(PublicLog.fromBuffer(scopedLog.logData), addressSecret)
620
+ : await L1NotePayload.decryptAsIncoming(PrivateLog.fromBuffer(scopedLog.logData), addressSecret);
595
621
 
596
- const txEffect =
597
- txEffectsCache.get(scopedLog.txHash.toString()) ?? (await this.aztecNode.getTxEffect(scopedLog.txHash));
598
-
599
- if (!txEffect) {
600
- this.log.warn(`No tx effect found for ${scopedLog.txHash} while decrypting tagged logs`);
601
- continue;
602
- }
622
+ if (!payload) {
623
+ this.log.verbose('Unable to decrypt log');
624
+ continue;
625
+ }
603
626
 
604
- txEffectsCache.set(scopedLog.txHash.toString(), txEffect);
627
+ if (!excludedIndices.has(scopedLog.txHash.toString())) {
628
+ excludedIndices.set(scopedLog.txHash.toString(), new Set());
629
+ }
605
630
 
606
- if (!excludedIndices.has(scopedLog.txHash.toString())) {
607
- excludedIndices.set(scopedLog.txHash.toString(), new Set());
608
- }
609
- const { note } = await produceNoteDaos(
610
- // I don't like this at all, but we need a simulator to run `computeNoteHashAndOptionallyANullifier`. This generates
611
- // a chicken-and-egg problem due to this oracle requiring a simulator, which in turn requires this oracle. Furthermore, since jest doesn't allow
612
- // mocking ESM exports, we have to pollute the method even more by providing a simulator parameter so tests can inject a fake one.
613
- simulator ??
614
- getAcirSimulator(this.db, this.aztecNode, this.keyStore, this.simulationProvider, this.contractDataOracle),
615
- this.db,
616
- notePayload ? recipient.toAddressPoint() : undefined,
617
- payload!,
618
- txEffect.data.txHash,
619
- txEffect.data.nullifiers[0],
620
- txEffect.l2BlockNumber,
621
- txEffect.l2BlockHash,
622
- txEffect.data.noteHashes,
623
- scopedLog.dataStartIndexForTx,
624
- excludedIndices.get(scopedLog.txHash.toString())!,
625
- this.log,
626
- );
631
+ const note = await getOrderedNoteItems(this.db, payload);
632
+ const plaintext = [payload.storageSlot, payload.noteTypeId.toField(), ...note.items];
627
633
 
628
- if (note) {
629
- notes.push(note);
630
- }
631
- }
634
+ decrypted.push({ plaintext, txHash: scopedLog.txHash, contractAddress: payload.contractAddress });
632
635
  }
633
- return { notes };
636
+
637
+ return decrypted;
634
638
  }
635
639
 
636
640
  /**
@@ -643,20 +647,68 @@ export class SimulatorOracle implements DBOracle {
643
647
  recipient: AztecAddress,
644
648
  simulator?: AcirSimulator,
645
649
  ): Promise<void> {
646
- const { notes } = await this.#decryptTaggedLogs(logs, recipient, simulator);
647
- if (notes.length) {
648
- await this.db.addNotes(notes, recipient);
649
- notes.forEach(noteDao => {
650
- this.log.verbose(`Added incoming note for contract ${noteDao.contractAddress} at slot ${noteDao.storageSlot}`, {
651
- contract: noteDao.contractAddress,
652
- slot: noteDao.storageSlot,
653
- nullifier: noteDao.siloedNullifier.toString(),
654
- });
655
- });
650
+ const decryptedLogs = await this.#decryptTaggedLogs(logs, recipient);
651
+
652
+ // We've produced the full NoteDao, which we'd be able to simply insert into the database. However, this is
653
+ // only a temporary measure as we migrate from the PXE-driven discovery into the new contract-driven approach. We
654
+ // discard most of the work done up to this point and reconstruct the note plaintext to then hand over to the
655
+ // contract for further processing.
656
+ for (const decryptedLog of decryptedLogs) {
657
+ // Log processing requires the note hashes in the tx in which the note was created. We are now assuming that the
658
+ // note was included in the same block in which the log was delivered - note that partial notes will not work this
659
+ // way.
660
+ const txEffect = await this.aztecNode.getTxEffect(decryptedLog.txHash);
661
+ if (!txEffect) {
662
+ throw new Error(`Could not find tx effect for tx hash ${decryptedLog.txHash}`);
663
+ }
664
+
665
+ // This will trigger calls to the deliverNote oracle
666
+ await this.callProcessLog(
667
+ decryptedLog.contractAddress,
668
+ decryptedLog.plaintext,
669
+ decryptedLog.txHash,
670
+ txEffect.data.noteHashes,
671
+ txEffect.data.nullifiers[0],
672
+ recipient,
673
+ simulator,
674
+ );
656
675
  }
676
+ return;
677
+ }
678
+
679
+ // Called when notes are delivered, usually as a result to a call to the process_log contract function
680
+ public async deliverNote(
681
+ contractAddress: AztecAddress,
682
+ storageSlot: Fr,
683
+ nonce: Fr,
684
+ content: Fr[],
685
+ noteHash: Fr,
686
+ nullifier: Fr,
687
+ txHash: Fr,
688
+ recipient: AztecAddress,
689
+ ): Promise<void> {
690
+ const noteDao = await this.produceNoteDao(
691
+ contractAddress,
692
+ storageSlot,
693
+ nonce,
694
+ content,
695
+ noteHash,
696
+ nullifier,
697
+ txHash,
698
+ recipient,
699
+ );
700
+
701
+ await this.db.addNotes([noteDao], recipient);
702
+ this.log.verbose('Added note', {
703
+ contract: noteDao.contractAddress,
704
+ slot: noteDao.storageSlot,
705
+ nullifier: noteDao.siloedNullifier.toString,
706
+ });
657
707
  }
658
708
 
659
709
  public async removeNullifiedNotes(contractAddress: AztecAddress) {
710
+ this.log.verbose('Removing nullified notes', { contract: contractAddress });
711
+
660
712
  for (const recipient of await this.keyStore.getAccounts()) {
661
713
  const currentNotesForRecipient = await this.db.getNotes({ contractAddress, owner: recipient });
662
714
  const nullifiersToCheck = currentNotesForRecipient.map(note => note.siloedNullifier);
@@ -670,7 +722,7 @@ export class SimulatorOracle implements DBOracle {
670
722
  })
671
723
  .filter(nullifier => nullifier !== undefined) as InBlock<Fr>[];
672
724
 
673
- const nullifiedNotes = await this.db.removeNullifiedNotes(foundNullifiers, recipient.toAddressPoint());
725
+ const nullifiedNotes = await this.db.removeNullifiedNotes(foundNullifiers, await recipient.toAddressPoint());
674
726
  nullifiedNotes.forEach(noteDao => {
675
727
  this.log.verbose(`Removed note for contract ${noteDao.contractAddress} at slot ${noteDao.storageSlot}`, {
676
728
  contract: noteDao.contractAddress,
@@ -681,25 +733,122 @@ export class SimulatorOracle implements DBOracle {
681
733
  }
682
734
  }
683
735
 
684
- /**
685
- * Used by contracts during execution to store arbitrary data in the local PXE database. The data is siloed/scoped
686
- * to a specific `contract`.
687
- * @param contract - An address of a contract that is requesting to store the data.
688
- * @param key - A field element representing the key to store the data under.
689
- * @param values - An array of field elements representing the data to store.
690
- */
691
- store(contract: AztecAddress, key: Fr, values: Fr[]): Promise<void> {
692
- return this.db.store(contract, key, values);
736
+ async produceNoteDao(
737
+ contractAddress: AztecAddress,
738
+ storageSlot: Fr,
739
+ nonce: Fr,
740
+ content: Fr[],
741
+ noteHash: Fr,
742
+ nullifier: Fr,
743
+ txHash: Fr,
744
+ recipient: AztecAddress,
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
+
749
+ const receipt = await this.aztecNode.getTxReceipt(new TxHash(txHash));
750
+ if (receipt === undefined) {
751
+ throw new Error(`Failed to fetch tx receipt for tx hash ${txHash} when searching for note hashes`);
752
+ }
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.
756
+ const uniqueNoteHash = computeUniqueNoteHash(nonce, siloNoteHash(contractAddress, noteHash));
757
+ const siloedNullifier = siloNullifier(contractAddress, nullifier);
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();
764
+ const uniqueNoteHashTreeIndex = (
765
+ await this.aztecNode.findLeavesIndexes(syncedBlockNumber!, MerkleTreeId.NOTE_HASH_TREE, [uniqueNoteHash])
766
+ )[0];
767
+ if (uniqueNoteHashTreeIndex === undefined) {
768
+ throw new Error(
769
+ `Note hash ${noteHash} (uniqued as ${uniqueNoteHash}) is not present on the tree at block ${syncedBlockNumber} (from tx ${txHash})`,
770
+ );
771
+ }
772
+
773
+ return new NoteDao(
774
+ new Note(content),
775
+ contractAddress,
776
+ storageSlot,
777
+ nonce,
778
+ noteHash,
779
+ siloedNullifier,
780
+ new TxHash(txHash),
781
+ receipt.blockNumber!,
782
+ receipt.blockHash!.toString(),
783
+ uniqueNoteHashTreeIndex,
784
+ await recipient.toAddressPoint(),
785
+ NoteSelector.empty(), // todo: remove
786
+ );
693
787
  }
694
788
 
695
- /**
696
- * Used by contracts during execution to load arbitrary data from the local PXE database. The data is siloed/scoped
697
- * to a specific `contract`.
698
- * @param contract - An address of a contract that is requesting to load the data.
699
- * @param key - A field element representing the key under which to load the data..
700
- * @returns An array of field elements representing the stored data or `null` if no data is stored under the key.
701
- */
702
- load(contract: AztecAddress, key: Fr): Promise<Fr[] | null> {
703
- return this.db.load(contract, key);
789
+ async callProcessLog(
790
+ contractAddress: AztecAddress,
791
+ logPlaintext: Fr[],
792
+ txHash: TxHash,
793
+ noteHashes: Fr[],
794
+ firstNullifier: Fr,
795
+ recipient: AztecAddress,
796
+ simulator?: AcirSimulator,
797
+ ) {
798
+ const artifact: FunctionArtifact | undefined = await new ContractDataOracle(this.db).getFunctionArtifactByName(
799
+ contractAddress,
800
+ 'process_log',
801
+ );
802
+ if (!artifact) {
803
+ throw new Error(
804
+ `Mandatory implementation of "process_log" missing in noir contract ${contractAddress.toString()}.`,
805
+ );
806
+ }
807
+
808
+ const execRequest: FunctionCall = {
809
+ name: artifact.name,
810
+ to: contractAddress,
811
+ selector: FunctionSelector.fromNameAndParameters(artifact),
812
+ type: FunctionType.UNCONSTRAINED,
813
+ isStatic: artifact.isStatic,
814
+ args: encodeArguments(artifact, [
815
+ toBoundedVec(logPlaintext, PRIVATE_LOG_SIZE_IN_FIELDS),
816
+ txHash.toString(),
817
+ toBoundedVec(noteHashes, MAX_NOTE_HASHES_PER_TX),
818
+ firstNullifier,
819
+ recipient,
820
+ ]),
821
+ returnTypes: artifact.returnTypes,
822
+ };
823
+
824
+ await (
825
+ simulator ??
826
+ getAcirSimulator(this.db, this.aztecNode, this.keyStore, this.simulationProvider, this.contractDataOracle)
827
+ ).runUnconstrained(
828
+ execRequest,
829
+ artifact,
830
+ contractAddress,
831
+ [], // empty scope as this call should not require access to private information
832
+ );
704
833
  }
834
+
835
+ dbStore(contractAddress: AztecAddress, slot: Fr, values: Fr[]): Promise<void> {
836
+ return this.db.dbStore(contractAddress, slot, values);
837
+ }
838
+
839
+ dbLoad(contractAddress: AztecAddress, slot: Fr): Promise<Fr[] | null> {
840
+ return this.db.dbLoad(contractAddress, slot);
841
+ }
842
+
843
+ dbDelete(contractAddress: AztecAddress, slot: Fr): Promise<void> {
844
+ return this.db.dbDelete(contractAddress, slot);
845
+ }
846
+
847
+ dbCopy(contractAddress: AztecAddress, srcSlot: Fr, dstSlot: Fr, numEntries: number): Promise<void> {
848
+ return this.db.dbCopy(contractAddress, srcSlot, dstSlot, numEntries);
849
+ }
850
+ }
851
+
852
+ function toBoundedVec(array: Fr[], maxLength: number) {
853
+ return { storage: array.concat(Array(maxLength - array.length).fill(new Fr(0))), len: array.length };
705
854
  }
@@ -1,31 +0,0 @@
1
- import { type Note, type TxHash } from '@aztec/circuit-types';
2
- import { type AztecAddress } from '@aztec/circuits.js';
3
- import { type NoteSelector } from '@aztec/foundation/abi';
4
- import { Fr } from '@aztec/foundation/fields';
5
- import { type AcirSimulator } from '@aztec/simulator/client';
6
- export interface NoteInfo {
7
- noteHashIndex: number;
8
- nonce: Fr;
9
- noteHash: Fr;
10
- siloedNullifier: Fr;
11
- txHash: TxHash;
12
- }
13
- /**
14
- * Finds nonce, index, inner hash and siloed nullifier for a given note.
15
- * @dev Finds the index in the note hash tree by computing the note hash with different nonce and see which hash for
16
- * the current tx matches this value.
17
- * @remarks This method assists in identifying spent notes in the note hash tree.
18
- * @param uniqueNoteHashes - Note hashes in the tx. One of them should correspond to the note we are looking for
19
- * @param txHash - Hash of a tx the note was emitted in.
20
- * @param contractAddress - Address of the contract the note was emitted in.
21
- * @param storageSlot - Storage slot of the note.
22
- * @param noteTypeId - Type of the note.
23
- * @param note - Note items.
24
- * @param excludedIndices - Indices that have been assigned a note in the same tx. Notes in a tx can have the same
25
- * l1NotePayload. We need to find a different index for each replicate.
26
- * @param computeNullifier - A flag indicating whether to compute the nullifier or just return 0.
27
- * @returns Nonce, index, inner hash and siloed nullifier for a given note.
28
- * @throws If cannot find the nonce for the note.
29
- */
30
- export declare function bruteForceNoteInfo(simulator: AcirSimulator, uniqueNoteHashes: Fr[], txHash: TxHash, firstNullifier: Fr, contractAddress: AztecAddress, storageSlot: Fr, noteTypeId: NoteSelector, note: Note, excludedIndices: Set<number>, computeNullifier: boolean): Promise<NoteInfo>;
31
- //# sourceMappingURL=brute_force_note_info.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"brute_force_note_info.d.ts","sourceRoot":"","sources":["../../src/note_decryption_utils/brute_force_note_info.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,IAAI,EAAE,KAAK,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,EAAE,EAAE,EAAE,MAAM,0BAA0B,CAAC;AAC9C,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAE7D,MAAM,WAAW,QAAQ;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,EAAE,CAAC;IACV,QAAQ,EAAE,EAAE,CAAC;IACb,eAAe,EAAE,EAAE,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,kBAAkB,CACtC,SAAS,EAAE,aAAa,EACxB,gBAAgB,EAAE,EAAE,EAAE,EACtB,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,EAAE,EAClB,eAAe,EAAE,YAAY,EAC7B,WAAW,EAAE,EAAE,EACf,UAAU,EAAE,YAAY,EACxB,IAAI,EAAE,IAAI,EACV,eAAe,EAAE,GAAG,CAAC,MAAM,CAAC,EAC5B,gBAAgB,EAAE,OAAO,GACxB,OAAO,CAAC,QAAQ,CAAC,CA8CnB"}
@@ -1,54 +0,0 @@
1
- import { computeNoteHashNonce, siloNullifier } from '@aztec/circuits.js/hash';
2
- import { Fr } from '@aztec/foundation/fields';
3
- /**
4
- * Finds nonce, index, inner hash and siloed nullifier for a given note.
5
- * @dev Finds the index in the note hash tree by computing the note hash with different nonce and see which hash for
6
- * the current tx matches this value.
7
- * @remarks This method assists in identifying spent notes in the note hash tree.
8
- * @param uniqueNoteHashes - Note hashes in the tx. One of them should correspond to the note we are looking for
9
- * @param txHash - Hash of a tx the note was emitted in.
10
- * @param contractAddress - Address of the contract the note was emitted in.
11
- * @param storageSlot - Storage slot of the note.
12
- * @param noteTypeId - Type of the note.
13
- * @param note - Note items.
14
- * @param excludedIndices - Indices that have been assigned a note in the same tx. Notes in a tx can have the same
15
- * l1NotePayload. We need to find a different index for each replicate.
16
- * @param computeNullifier - A flag indicating whether to compute the nullifier or just return 0.
17
- * @returns Nonce, index, inner hash and siloed nullifier for a given note.
18
- * @throws If cannot find the nonce for the note.
19
- */
20
- export async function bruteForceNoteInfo(simulator, uniqueNoteHashes, txHash, firstNullifier, contractAddress, storageSlot, noteTypeId, note, excludedIndices, computeNullifier) {
21
- let noteHashIndex = 0;
22
- let nonce;
23
- let noteHash;
24
- let uniqueNoteHash;
25
- let innerNullifier;
26
- for (; noteHashIndex < uniqueNoteHashes.length; ++noteHashIndex) {
27
- if (excludedIndices.has(noteHashIndex)) {
28
- continue;
29
- }
30
- const uniqueNoteHashFromTxEffect = uniqueNoteHashes[noteHashIndex];
31
- if (uniqueNoteHashFromTxEffect.equals(Fr.ZERO)) {
32
- break;
33
- }
34
- const expectedNonce = computeNoteHashNonce(firstNullifier, noteHashIndex);
35
- ({ noteHash, uniqueNoteHash, innerNullifier } = await simulator.computeNoteHashAndOptionallyANullifier(contractAddress, expectedNonce, storageSlot, noteTypeId, computeNullifier, note));
36
- if (uniqueNoteHashFromTxEffect.equals(uniqueNoteHash)) {
37
- nonce = expectedNonce;
38
- break;
39
- }
40
- }
41
- if (!nonce) {
42
- // NB: this used to warn the user that a decrypted log didn't match any notes.
43
- // This was previously fine as we didn't chop transient note logs, but now we do (#1641 complete).
44
- throw new Error('Cannot find a matching note hash for the note.');
45
- }
46
- return {
47
- noteHashIndex,
48
- nonce,
49
- noteHash: noteHash,
50
- siloedNullifier: siloNullifier(contractAddress, innerNullifier),
51
- txHash,
52
- };
53
- }
54
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYnJ1dGVfZm9yY2Vfbm90ZV9pbmZvLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL25vdGVfZGVjcnlwdGlvbl91dGlscy9icnV0ZV9mb3JjZV9ub3RlX2luZm8udHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBRUEsT0FBTyxFQUFFLG9CQUFvQixFQUFFLGFBQWEsRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBRTlFLE9BQU8sRUFBRSxFQUFFLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQVc5Qzs7Ozs7Ozs7Ozs7Ozs7OztHQWdCRztBQUNILE1BQU0sQ0FBQyxLQUFLLFVBQVUsa0JBQWtCLENBQ3RDLFNBQXdCLEVBQ3hCLGdCQUFzQixFQUN0QixNQUFjLEVBQ2QsY0FBa0IsRUFDbEIsZUFBNkIsRUFDN0IsV0FBZSxFQUNmLFVBQXdCLEVBQ3hCLElBQVUsRUFDVixlQUE0QixFQUM1QixnQkFBeUI7SUFFekIsSUFBSSxhQUFhLEdBQUcsQ0FBQyxDQUFDO0lBQ3RCLElBQUksS0FBcUIsQ0FBQztJQUMxQixJQUFJLFFBQXdCLENBQUM7SUFDN0IsSUFBSSxjQUE4QixDQUFDO0lBQ25DLElBQUksY0FBOEIsQ0FBQztJQUVuQyxPQUFPLGFBQWEsR0FBRyxnQkFBZ0IsQ0FBQyxNQUFNLEVBQUUsRUFBRSxhQUFhLEVBQUUsQ0FBQztRQUNoRSxJQUFJLGVBQWUsQ0FBQyxHQUFHLENBQUMsYUFBYSxDQUFDLEVBQUUsQ0FBQztZQUN2QyxTQUFTO1FBQ1gsQ0FBQztRQUVELE1BQU0sMEJBQTBCLEdBQUcsZ0JBQWdCLENBQUMsYUFBYSxDQUFDLENBQUM7UUFDbkUsSUFBSSwwQkFBMEIsQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUM7WUFDL0MsTUFBTTtRQUNSLENBQUM7UUFFRCxNQUFNLGFBQWEsR0FBRyxvQkFBb0IsQ0FBQyxjQUFjLEVBQUUsYUFBYSxDQUFDLENBQUM7UUFDMUUsQ0FBQyxFQUFFLFFBQVEsRUFBRSxjQUFjLEVBQUUsY0FBYyxFQUFFLEdBQUcsTUFBTSxTQUFTLENBQUMsc0NBQXNDLENBQ3BHLGVBQWUsRUFDZixhQUFhLEVBQ2IsV0FBVyxFQUNYLFVBQVUsRUFDVixnQkFBZ0IsRUFDaEIsSUFBSSxDQUNMLENBQUMsQ0FBQztRQUVILElBQUksMEJBQTBCLENBQUMsTUFBTSxDQUFDLGNBQWMsQ0FBQyxFQUFFLENBQUM7WUFDdEQsS0FBSyxHQUFHLGFBQWEsQ0FBQztZQUN0QixNQUFNO1FBQ1IsQ0FBQztJQUNILENBQUM7SUFFRCxJQUFJLENBQUMsS0FBSyxFQUFFLENBQUM7UUFDWCw4RUFBOEU7UUFDOUUsa0dBQWtHO1FBQ2xHLE1BQU0sSUFBSSxLQUFLLENBQUMsZ0RBQWdELENBQUMsQ0FBQztJQUNwRSxDQUFDO0lBRUQsT0FBTztRQUNMLGFBQWE7UUFDYixLQUFLO1FBQ0wsUUFBUSxFQUFFLFFBQVM7UUFDbkIsZUFBZSxFQUFFLGFBQWEsQ0FBQyxlQUFlLEVBQUUsY0FBZSxDQUFDO1FBQ2hFLE1BQU07S0FDUCxDQUFDO0FBQ0osQ0FBQyJ9
@@ -1,3 +0,0 @@
1
- export { produceNoteDaos } from './produce_note_daos.js';
2
- export { NoteInfo } from './brute_force_note_info.js';
3
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/note_decryption_utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC"}
@@ -1,2 +0,0 @@
1
- export { produceNoteDaos } from './produce_note_daos.js';
2
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvbm90ZV9kZWNyeXB0aW9uX3V0aWxzL2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQyJ9
@@ -1,28 +0,0 @@
1
- import { type L1NotePayload, type PublicKey, type TxHash } from '@aztec/circuit-types';
2
- import { type Fr } from '@aztec/foundation/fields';
3
- import { type Logger } from '@aztec/foundation/log';
4
- import { type AcirSimulator } from '@aztec/simulator/client';
5
- import { NoteDao } from '../database/note_dao.js';
6
- import { type PxeDatabase } from '../database/pxe_database.js';
7
- /**
8
- * Decodes a note from a transaction that we know was intended for us.
9
- * Throws if we do not yet have the contract corresponding to the note in our database.
10
- * Accepts a set of excluded indices, which are indices that have been assigned a note in the same tx.
11
- * Inserts the index of the note into the excludedIndices set if the note is successfully decoded.
12
- *
13
- * @param simulator - An instance of AcirSimulator.
14
- * @param db - An instance of PxeDatabase.
15
- * @param addressPoint - The public counterpart to the address secret, which is used in the decryption of incoming note logs.
16
- * @param payload - An instance of l1NotePayload.
17
- * @param txHash - The hash of the transaction that created the note. Equivalent to the first nullifier of the transaction.
18
- * @param noteHashes - New note hashes in this transaction, one of which belongs to this note.
19
- * @param dataStartIndexForTx - The next available leaf index for the note hash tree for this transaction.
20
- * @param excludedIndices - Indices that have been assigned a note in the same tx. Notes in a tx can have the same l1NotePayload, we need to find a different index for each replicate.
21
- * @param logger - An instance of Logger.
22
- * @param unencryptedLogs - Unencrypted logs for the transaction (used to complete partial notes).
23
- * @returns An object containing the incoming notes.
24
- */
25
- export declare function produceNoteDaos(simulator: AcirSimulator, db: PxeDatabase, addressPoint: PublicKey | undefined, payload: L1NotePayload, txHash: TxHash, firstNullifier: Fr, l2BlockNumber: number, l2BlockHash: string, noteHashes: Fr[], dataStartIndexForTx: number, excludedIndices: Set<number>, logger: Logger): Promise<{
26
- note: NoteDao | undefined;
27
- }>;
28
- //# sourceMappingURL=produce_note_daos.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"produce_note_daos.d.ts","sourceRoot":"","sources":["../../src/note_decryption_utils/produce_note_daos.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,SAAS,EAAE,KAAK,MAAM,EAAE,MAAM,sBAAsB,CAAC;AACvF,OAAO,EAAE,KAAK,EAAE,EAAE,MAAM,0BAA0B,CAAC;AACnD,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAE7D,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAClD,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAG/D;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,eAAe,CACnC,SAAS,EAAE,aAAa,EACxB,EAAE,EAAE,WAAW,EACf,YAAY,EAAE,SAAS,GAAG,SAAS,EACnC,OAAO,EAAE,aAAa,EACtB,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,EAAE,EAClB,aAAa,EAAE,MAAM,EACrB,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,EAAE,EAAE,EAChB,mBAAmB,EAAE,MAAM,EAC3B,eAAe,EAAE,GAAG,CAAC,MAAM,CAAC,EAC5B,MAAM,EAAE,MAAM,GACb,OAAO,CAAC;IAAE,IAAI,EAAE,OAAO,GAAG,SAAS,CAAA;CAAE,CAAC,CA4BxC"}
@@ -1,33 +0,0 @@
1
- import { NoteDao } from '../database/note_dao.js';
2
- import { produceNoteDaosForKey } from './produce_note_daos_for_key.js';
3
- /**
4
- * Decodes a note from a transaction that we know was intended for us.
5
- * Throws if we do not yet have the contract corresponding to the note in our database.
6
- * Accepts a set of excluded indices, which are indices that have been assigned a note in the same tx.
7
- * Inserts the index of the note into the excludedIndices set if the note is successfully decoded.
8
- *
9
- * @param simulator - An instance of AcirSimulator.
10
- * @param db - An instance of PxeDatabase.
11
- * @param addressPoint - The public counterpart to the address secret, which is used in the decryption of incoming note logs.
12
- * @param payload - An instance of l1NotePayload.
13
- * @param txHash - The hash of the transaction that created the note. Equivalent to the first nullifier of the transaction.
14
- * @param noteHashes - New note hashes in this transaction, one of which belongs to this note.
15
- * @param dataStartIndexForTx - The next available leaf index for the note hash tree for this transaction.
16
- * @param excludedIndices - Indices that have been assigned a note in the same tx. Notes in a tx can have the same l1NotePayload, we need to find a different index for each replicate.
17
- * @param logger - An instance of Logger.
18
- * @param unencryptedLogs - Unencrypted logs for the transaction (used to complete partial notes).
19
- * @returns An object containing the incoming notes.
20
- */
21
- export async function produceNoteDaos(simulator, db, addressPoint, payload, txHash, firstNullifier, l2BlockNumber, l2BlockHash, noteHashes, dataStartIndexForTx, excludedIndices, logger) {
22
- if (!addressPoint) {
23
- throw new Error('addressPoint is undefined. Cannot create note.');
24
- }
25
- let note;
26
- if (addressPoint) {
27
- note = await produceNoteDaosForKey(simulator, db, addressPoint, payload, txHash, firstNullifier, l2BlockNumber, l2BlockHash, noteHashes, dataStartIndexForTx, excludedIndices, logger, NoteDao.fromPayloadAndNoteInfo);
28
- }
29
- return {
30
- note,
31
- };
32
- }
33
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJvZHVjZV9ub3RlX2Rhb3MuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvbm90ZV9kZWNyeXB0aW9uX3V0aWxzL3Byb2R1Y2Vfbm90ZV9kYW9zLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUtBLE9BQU8sRUFBRSxPQUFPLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUVsRCxPQUFPLEVBQUUscUJBQXFCLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUV2RTs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FpQkc7QUFDSCxNQUFNLENBQUMsS0FBSyxVQUFVLGVBQWUsQ0FDbkMsU0FBd0IsRUFDeEIsRUFBZSxFQUNmLFlBQW1DLEVBQ25DLE9BQXNCLEVBQ3RCLE1BQWMsRUFDZCxjQUFrQixFQUNsQixhQUFxQixFQUNyQixXQUFtQixFQUNuQixVQUFnQixFQUNoQixtQkFBMkIsRUFDM0IsZUFBNEIsRUFDNUIsTUFBYztJQUVkLElBQUksQ0FBQyxZQUFZLEVBQUUsQ0FBQztRQUNsQixNQUFNLElBQUksS0FBSyxDQUFDLGdEQUFnRCxDQUFDLENBQUM7SUFDcEUsQ0FBQztJQUVELElBQUksSUFBeUIsQ0FBQztJQUU5QixJQUFJLFlBQVksRUFBRSxDQUFDO1FBQ2pCLElBQUksR0FBRyxNQUFNLHFCQUFxQixDQUNoQyxTQUFTLEVBQ1QsRUFBRSxFQUNGLFlBQVksRUFDWixPQUFPLEVBQ1AsTUFBTSxFQUNOLGNBQWMsRUFDZCxhQUFhLEVBQ2IsV0FBVyxFQUNYLFVBQVUsRUFDVixtQkFBbUIsRUFDbkIsZUFBZSxFQUNmLE1BQU0sRUFDTixPQUFPLENBQUMsc0JBQXNCLENBQy9CLENBQUM7SUFDSixDQUFDO0lBRUQsT0FBTztRQUNMLElBQUk7S0FDTCxDQUFDO0FBQ0osQ0FBQyJ9
@@ -1,8 +0,0 @@
1
- import { type L1NotePayload, type Note, type TxHash } from '@aztec/circuit-types';
2
- import { type Fr, type PublicKey } from '@aztec/circuits.js';
3
- import { type Logger } from '@aztec/foundation/log';
4
- import { type AcirSimulator } from '@aztec/simulator/client';
5
- import { type PxeDatabase } from '../database/pxe_database.js';
6
- import { type NoteInfo } from './brute_force_note_info.js';
7
- export declare function produceNoteDaosForKey<T>(simulator: AcirSimulator, db: PxeDatabase, pkM: PublicKey, payload: L1NotePayload, txHash: TxHash, firstNullifier: Fr, l2BlockNumber: number, l2BlockHash: string, noteHashes: Fr[], dataStartIndexForTx: number, excludedIndices: Set<number>, logger: Logger, daoConstructor: (note: Note, payload: L1NotePayload, noteInfo: NoteInfo, l2BlockNumber: number, l2BlockHash: string, dataStartIndexForTx: number, pkM: PublicKey) => T): Promise<T | undefined>;
8
- //# sourceMappingURL=produce_note_daos_for_key.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"produce_note_daos_for_key.d.ts","sourceRoot":"","sources":["../../src/note_decryption_utils/produce_note_daos_for_key.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,IAAI,EAAE,KAAK,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAClF,OAAO,EAAE,KAAK,EAAE,EAAE,KAAK,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC7D,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAE7D,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAE/D,OAAO,EAAE,KAAK,QAAQ,EAAsB,MAAM,4BAA4B,CAAC;AAE/E,wBAAsB,qBAAqB,CAAC,CAAC,EAC3C,SAAS,EAAE,aAAa,EACxB,EAAE,EAAE,WAAW,EACf,GAAG,EAAE,SAAS,EACd,OAAO,EAAE,aAAa,EACtB,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,EAAE,EAClB,aAAa,EAAE,MAAM,EACrB,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,EAAE,EAAE,EAChB,mBAAmB,EAAE,MAAM,EAC3B,eAAe,EAAE,GAAG,CAAC,MAAM,CAAC,EAC5B,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,CACd,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,aAAa,EACtB,QAAQ,EAAE,QAAQ,EAClB,aAAa,EAAE,MAAM,EACrB,WAAW,EAAE,MAAM,EACnB,mBAAmB,EAAE,MAAM,EAC3B,GAAG,EAAE,SAAS,KACX,CAAC,GACL,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CA2BxB"}