@aztec/pxe 0.70.0 → 0.71.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 (44) 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 +4 -2
  4. package/dest/database/kv_pxe_database.d.ts.map +1 -1
  5. package/dest/database/kv_pxe_database.js +32 -10
  6. package/dest/database/note_dao.d.ts +1 -3
  7. package/dest/database/note_dao.d.ts.map +1 -1
  8. package/dest/database/note_dao.js +1 -5
  9. package/dest/database/outgoing_note_dao.d.ts +1 -3
  10. package/dest/database/outgoing_note_dao.d.ts.map +1 -1
  11. package/dest/database/outgoing_note_dao.js +1 -5
  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 +93 -20
  16. package/dest/note_decryption_utils/add_public_values_to_payload.js +2 -2
  17. package/dest/simulator_oracle/index.d.ts +12 -20
  18. package/dest/simulator_oracle/index.d.ts.map +1 -1
  19. package/dest/simulator_oracle/index.js +99 -59
  20. package/package.json +14 -14
  21. package/src/config/index.ts +2 -1
  22. package/src/database/kv_pxe_database.ts +40 -8
  23. package/src/database/note_dao.ts +1 -29
  24. package/src/database/outgoing_note_dao.ts +1 -28
  25. package/src/database/pxe_database.ts +31 -12
  26. package/src/database/pxe_database_test_suite.ts +118 -19
  27. package/src/note_decryption_utils/add_public_values_to_payload.ts +1 -1
  28. package/src/simulator_oracle/index.ts +198 -77
  29. package/dest/note_decryption_utils/brute_force_note_info.d.ts +0 -31
  30. package/dest/note_decryption_utils/brute_force_note_info.d.ts.map +0 -1
  31. package/dest/note_decryption_utils/brute_force_note_info.js +0 -54
  32. package/dest/note_decryption_utils/index.d.ts +0 -3
  33. package/dest/note_decryption_utils/index.d.ts.map +0 -1
  34. package/dest/note_decryption_utils/index.js +0 -2
  35. package/dest/note_decryption_utils/produce_note_daos.d.ts +0 -28
  36. package/dest/note_decryption_utils/produce_note_daos.d.ts.map +0 -1
  37. package/dest/note_decryption_utils/produce_note_daos.js +0 -33
  38. package/dest/note_decryption_utils/produce_note_daos_for_key.d.ts +0 -8
  39. package/dest/note_decryption_utils/produce_note_daos_for_key.d.ts.map +0 -1
  40. package/dest/note_decryption_utils/produce_note_daos_for_key.js +0 -17
  41. package/src/note_decryption_utils/brute_force_note_info.ts +0 -90
  42. package/src/note_decryption_utils/index.ts +0 -2
  43. package/src/note_decryption_utils/produce_note_daos.ts +0 -69
  44. package/src/note_decryption_utils/produce_note_daos_for_key.ts +0 -59
@@ -406,7 +406,7 @@ export function describePxeDatabase(getDatabase: () => PxeDatabase) {
406
406
  });
407
407
  });
408
408
 
409
- describe('contract store', () => {
409
+ describe('contract non-volatile database', () => {
410
410
  let contract: AztecAddress;
411
411
 
412
412
  beforeEach(() => {
@@ -415,56 +415,155 @@ export function describePxeDatabase(getDatabase: () => PxeDatabase) {
415
415
  });
416
416
 
417
417
  it('stores and loads a single value', async () => {
418
- const key = new Fr(1);
418
+ const slot = new Fr(1);
419
419
  const values = [new Fr(42)];
420
420
 
421
- await database.store(contract, key, values);
422
- const result = await database.load(contract, key);
421
+ await database.dbStore(contract, slot, values);
422
+ const result = await database.dbLoad(contract, slot);
423
423
  expect(result).toEqual(values);
424
424
  });
425
425
 
426
426
  it('stores and loads multiple values', async () => {
427
- const key = new Fr(1);
427
+ const slot = new Fr(1);
428
428
  const values = [new Fr(42), new Fr(43), new Fr(44)];
429
429
 
430
- await database.store(contract, key, values);
431
- const result = await database.load(contract, key);
430
+ await database.dbStore(contract, slot, values);
431
+ const result = await database.dbLoad(contract, slot);
432
432
  expect(result).toEqual(values);
433
433
  });
434
434
 
435
435
  it('overwrites existing values', async () => {
436
- const key = new Fr(1);
436
+ const slot = new Fr(1);
437
437
  const initialValues = [new Fr(42)];
438
438
  const newValues = [new Fr(100)];
439
439
 
440
- await database.store(contract, key, initialValues);
441
- await database.store(contract, key, newValues);
440
+ await database.dbStore(contract, slot, initialValues);
441
+ await database.dbStore(contract, slot, newValues);
442
442
 
443
- const result = await database.load(contract, key);
443
+ const result = await database.dbLoad(contract, slot);
444
444
  expect(result).toEqual(newValues);
445
445
  });
446
446
 
447
447
  it('stores values for different contracts independently', async () => {
448
448
  const anotherContract = AztecAddress.random();
449
- const key = new Fr(1);
449
+ const slot = new Fr(1);
450
450
  const values1 = [new Fr(42)];
451
451
  const values2 = [new Fr(100)];
452
452
 
453
- await database.store(contract, key, values1);
454
- await database.store(anotherContract, key, values2);
453
+ await database.dbStore(contract, slot, values1);
454
+ await database.dbStore(anotherContract, slot, values2);
455
455
 
456
- const result1 = await database.load(contract, key);
457
- const result2 = await database.load(anotherContract, key);
456
+ const result1 = await database.dbLoad(contract, slot);
457
+ const result2 = await database.dbLoad(anotherContract, slot);
458
458
 
459
459
  expect(result1).toEqual(values1);
460
460
  expect(result2).toEqual(values2);
461
461
  });
462
462
 
463
- it('returns null for non-existent keys', async () => {
464
- const key = Fr.random();
465
- const result = await database.load(contract, key);
463
+ it('returns null for non-existent slots', async () => {
464
+ const slot = Fr.random();
465
+ const result = await database.dbLoad(contract, slot);
466
466
  expect(result).toBeNull();
467
467
  });
468
+
469
+ it('deletes a slot', async () => {
470
+ const slot = new Fr(1);
471
+ const values = [new Fr(42)];
472
+
473
+ await database.dbStore(contract, slot, values);
474
+ await database.dbDelete(contract, slot);
475
+
476
+ expect(await database.dbLoad(contract, slot)).toBeNull();
477
+ });
478
+
479
+ it('deletes an empty slot', async () => {
480
+ const slot = new Fr(1);
481
+ await database.dbDelete(contract, slot);
482
+
483
+ expect(await database.dbLoad(contract, slot)).toBeNull();
484
+ });
485
+
486
+ it('copies a single value', async () => {
487
+ const slot = new Fr(1);
488
+ const values = [new Fr(42)];
489
+
490
+ await database.dbStore(contract, slot, values);
491
+
492
+ const dstSlot = new Fr(5);
493
+ await database.dbCopy(contract, slot, dstSlot, 1);
494
+
495
+ expect(await database.dbLoad(contract, dstSlot)).toEqual(values);
496
+ });
497
+
498
+ it('copies multiple non-overlapping values', async () => {
499
+ const src = new Fr(1);
500
+ const valuesArray = [[new Fr(42)], [new Fr(1337)], [new Fr(13)]];
501
+
502
+ await database.dbStore(contract, src, valuesArray[0]);
503
+ await database.dbStore(contract, src.add(new Fr(1)), valuesArray[1]);
504
+ await database.dbStore(contract, src.add(new Fr(2)), valuesArray[2]);
505
+
506
+ const dst = new Fr(5);
507
+ await database.dbCopy(contract, src, dst, 3);
508
+
509
+ expect(await database.dbLoad(contract, dst)).toEqual(valuesArray[0]);
510
+ expect(await database.dbLoad(contract, dst.add(new Fr(1)))).toEqual(valuesArray[1]);
511
+ expect(await database.dbLoad(contract, dst.add(new Fr(2)))).toEqual(valuesArray[2]);
512
+ });
513
+
514
+ it('copies overlapping values with src ahead', async () => {
515
+ const src = new Fr(1);
516
+ const valuesArray = [[new Fr(42)], [new Fr(1337)], [new Fr(13)]];
517
+
518
+ await database.dbStore(contract, src, valuesArray[0]);
519
+ await database.dbStore(contract, src.add(new Fr(1)), valuesArray[1]);
520
+ await database.dbStore(contract, src.add(new Fr(2)), valuesArray[2]);
521
+
522
+ const dst = new Fr(2);
523
+ await database.dbCopy(contract, src, dst, 3);
524
+
525
+ expect(await database.dbLoad(contract, dst)).toEqual(valuesArray[0]);
526
+ expect(await database.dbLoad(contract, dst.add(new Fr(1)))).toEqual(valuesArray[1]);
527
+ expect(await database.dbLoad(contract, dst.add(new Fr(2)))).toEqual(valuesArray[2]);
528
+
529
+ // Slots 2 and 3 (src[1] and src[2]) should have been overwritten since they are also dst[0] and dst[1]
530
+ expect(await database.dbLoad(contract, src)).toEqual(valuesArray[0]); // src[0] (unchanged)
531
+ expect(await database.dbLoad(contract, src.add(new Fr(1)))).toEqual(valuesArray[0]); // dst[0]
532
+ expect(await database.dbLoad(contract, src.add(new Fr(2)))).toEqual(valuesArray[1]); // dst[1]
533
+ });
534
+
535
+ it('copies overlapping values with dst ahead', async () => {
536
+ const src = new Fr(5);
537
+ const valuesArray = [[new Fr(42)], [new Fr(1337)], [new Fr(13)]];
538
+
539
+ await database.dbStore(contract, src, valuesArray[0]);
540
+ await database.dbStore(contract, src.add(new Fr(1)), valuesArray[1]);
541
+ await database.dbStore(contract, src.add(new Fr(2)), valuesArray[2]);
542
+
543
+ const dst = new Fr(4);
544
+ await database.dbCopy(contract, src, dst, 3);
545
+
546
+ expect(await database.dbLoad(contract, dst)).toEqual(valuesArray[0]);
547
+ expect(await database.dbLoad(contract, dst.add(new Fr(1)))).toEqual(valuesArray[1]);
548
+ expect(await database.dbLoad(contract, dst.add(new Fr(2)))).toEqual(valuesArray[2]);
549
+
550
+ // Slots 5 and 6 (src[0] and src[1]) should have been overwritten since they are also dst[1] and dst[2]
551
+ expect(await database.dbLoad(contract, src)).toEqual(valuesArray[1]); // dst[1]
552
+ expect(await database.dbLoad(contract, src.add(new Fr(1)))).toEqual(valuesArray[2]); // dst[2]
553
+ expect(await database.dbLoad(contract, src.add(new Fr(2)))).toEqual(valuesArray[2]); // src[2] (unchanged)
554
+ });
555
+
556
+ it('copying fails if any value is empty', async () => {
557
+ const src = new Fr(1);
558
+ const valuesArray = [[new Fr(42)], [new Fr(1337)], [new Fr(13)]];
559
+
560
+ await database.dbStore(contract, src, valuesArray[0]);
561
+ // We skip src[1]
562
+ await database.dbStore(contract, src.add(new Fr(2)), valuesArray[2]);
563
+
564
+ const dst = new Fr(5);
565
+ await expect(database.dbCopy(contract, src, dst, 3)).rejects.toThrow('Attempted to copy empty slot');
566
+ });
468
567
  });
469
568
  });
470
569
  }
@@ -41,7 +41,7 @@ export async function getOrderedNoteItems(
41
41
  noteFields.sort((a, b) => a.index - b.index);
42
42
 
43
43
  // Now we insert the public fields into the note based on its indices defined in the ABI.
44
- const modifiedNoteItems = privateNoteValues;
44
+ const modifiedNoteItems = [...privateNoteValues];
45
45
  let indexInPublicValues = 0;
46
46
  for (let i = 0; i < noteFields.length; i++) {
47
47
  const noteField = noteFields[i];
@@ -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,24 @@ 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,
26
30
  computeAddressSecret,
27
31
  computeTaggingSecretPoint,
28
32
  } from '@aztec/circuits.js';
29
- import { type FunctionArtifact, getFunctionArtifact } from '@aztec/foundation/abi';
33
+ import { computeUniqueNoteHash, siloNoteHash, siloNullifier } from '@aztec/circuits.js/hash';
34
+ import {
35
+ type FunctionArtifact,
36
+ FunctionType,
37
+ NoteSelector,
38
+ encodeArguments,
39
+ getFunctionArtifact,
40
+ } from '@aztec/foundation/abi';
30
41
  import { poseidon2Hash } from '@aztec/foundation/crypto';
31
42
  import { createLogger } from '@aztec/foundation/log';
32
43
  import { type KeyStore } from '@aztec/key-store';
@@ -37,10 +48,10 @@ import {
37
48
  type SimulationProvider,
38
49
  } from '@aztec/simulator/client';
39
50
 
40
- import { type ContractDataOracle } from '../contract_data_oracle/index.js';
51
+ import { ContractDataOracle } from '../contract_data_oracle/index.js';
41
52
  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';
53
+ import { NoteDao } from '../database/note_dao.js';
54
+ import { getOrderedNoteItems } from '../note_decryption_utils/add_public_values_to_payload.js';
44
55
  import { getAcirSimulator } from '../simulator/index.js';
45
56
  import { WINDOW_HALF_SIZE, getIndexedTaggingSecretsForTheWindow, getInitialIndexesMap } from './tagging_utils.js';
46
57
 
@@ -431,6 +442,8 @@ export class SimulatorOracle implements DBOracle {
431
442
  maxBlockNumber: number,
432
443
  scopes?: AztecAddress[],
433
444
  ): Promise<Map<string, TxScopedL2Log[]>> {
445
+ this.log.verbose('Searching for tagged logs', { contract: contractAddress });
446
+
434
447
  // Ideally this algorithm would be implemented in noir, exposing its building blocks as oracles.
435
448
  // However it is impossible at the moment due to the language not supporting nested slices.
436
449
  // This nesting is necessary because for a given set of tags we don't
@@ -568,10 +581,9 @@ export class SimulatorOracle implements DBOracle {
568
581
  * Decrypts logs tagged for a recipient and returns them.
569
582
  * @param scopedLogs - The logs to decrypt.
570
583
  * @param recipient - The recipient of the logs.
571
- * @param simulator - The simulator to use for decryption.
572
584
  * @returns The decrypted notes.
573
585
  */
574
- async #decryptTaggedLogs(scopedLogs: TxScopedL2Log[], recipient: AztecAddress, simulator?: AcirSimulator) {
586
+ async #decryptTaggedLogs(scopedLogs: TxScopedL2Log[], recipient: AztecAddress) {
575
587
  const recipientCompleteAddress = await this.getCompleteAddress(recipient);
576
588
  const ivskM = await this.keyStore.getMasterSecretKey(
577
589
  recipientCompleteAddress.publicKeys.masterIncomingViewingPublicKey,
@@ -581,56 +593,29 @@ export class SimulatorOracle implements DBOracle {
581
593
  // Since we could have notes with the same index for different txs, we need
582
594
  // to keep track of them scoping by txHash
583
595
  const excludedIndices: Map<string, Set<number>> = new Map();
584
- const notes: NoteDao[] = [];
585
-
586
- const txEffectsCache = new Map<string, InBlock<TxEffect> | undefined>();
596
+ const decrypted = [];
587
597
 
588
598
  for (const scopedLog of scopedLogs) {
589
- const notePayload = scopedLog.isFromPublic
599
+ const payload = scopedLog.isFromPublic
590
600
  ? L1NotePayload.decryptAsIncomingFromPublic(scopedLog.logData, addressSecret)
591
601
  : L1NotePayload.decryptAsIncoming(PrivateLog.fromBuffer(scopedLog.logData), addressSecret);
592
602
 
593
- if (notePayload) {
594
- const payload = notePayload;
595
-
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
- }
603
+ if (!payload) {
604
+ this.log.verbose('Unable to decrypt log');
605
+ continue;
606
+ }
603
607
 
604
- txEffectsCache.set(scopedLog.txHash.toString(), txEffect);
608
+ if (!excludedIndices.has(scopedLog.txHash.toString())) {
609
+ excludedIndices.set(scopedLog.txHash.toString(), new Set());
610
+ }
605
611
 
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
- );
612
+ const note = await getOrderedNoteItems(this.db, payload);
613
+ const plaintext = [payload.storageSlot, payload.noteTypeId.toField(), ...note.items];
627
614
 
628
- if (note) {
629
- notes.push(note);
630
- }
631
- }
615
+ decrypted.push({ plaintext, txHash: scopedLog.txHash, contractAddress: payload.contractAddress });
632
616
  }
633
- return { notes };
617
+
618
+ return decrypted;
634
619
  }
635
620
 
636
621
  /**
@@ -643,20 +628,68 @@ export class SimulatorOracle implements DBOracle {
643
628
  recipient: AztecAddress,
644
629
  simulator?: AcirSimulator,
645
630
  ): 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
- });
631
+ const decryptedLogs = await this.#decryptTaggedLogs(logs, recipient);
632
+
633
+ // We've produced the full NoteDao, which we'd be able to simply insert into the database. However, this is
634
+ // only a temporary measure as we migrate from the PXE-driven discovery into the new contract-driven approach. We
635
+ // discard most of the work done up to this point and reconstruct the note plaintext to then hand over to the
636
+ // contract for further processing.
637
+ for (const decryptedLog of decryptedLogs) {
638
+ // Log processing requires the note hashes in the tx in which the note was created. We are now assuming that the
639
+ // note was included in the same block in which the log was delivered - note that partial notes will not work this
640
+ // way.
641
+ const txEffect = await this.aztecNode.getTxEffect(decryptedLog.txHash);
642
+ if (!txEffect) {
643
+ throw new Error(`Could not find tx effect for tx hash ${decryptedLog.txHash}`);
644
+ }
645
+
646
+ // This will trigger calls to the deliverNote oracle
647
+ await this.callProcessLog(
648
+ decryptedLog.contractAddress,
649
+ decryptedLog.plaintext,
650
+ decryptedLog.txHash,
651
+ txEffect.data.noteHashes,
652
+ txEffect.data.nullifiers[0],
653
+ recipient,
654
+ simulator,
655
+ );
656
656
  }
657
+ return;
658
+ }
659
+
660
+ // Called when notes are delivered, usually as a result to a call to the process_log contract function
661
+ public async deliverNote(
662
+ contractAddress: AztecAddress,
663
+ storageSlot: Fr,
664
+ nonce: Fr,
665
+ content: Fr[],
666
+ noteHash: Fr,
667
+ nullifier: Fr,
668
+ txHash: Fr,
669
+ recipient: AztecAddress,
670
+ ): Promise<void> {
671
+ const noteDao = await this.produceNoteDao(
672
+ contractAddress,
673
+ storageSlot,
674
+ nonce,
675
+ content,
676
+ noteHash,
677
+ nullifier,
678
+ txHash,
679
+ recipient,
680
+ );
681
+
682
+ await this.db.addNotes([noteDao], recipient);
683
+ this.log.verbose('Added note', {
684
+ contract: noteDao.contractAddress,
685
+ slot: noteDao.storageSlot,
686
+ nullifier: noteDao.siloedNullifier.toString,
687
+ });
657
688
  }
658
689
 
659
690
  public async removeNullifiedNotes(contractAddress: AztecAddress) {
691
+ this.log.verbose('Removing nullified notes', { contract: contractAddress });
692
+
660
693
  for (const recipient of await this.keyStore.getAccounts()) {
661
694
  const currentNotesForRecipient = await this.db.getNotes({ contractAddress, owner: recipient });
662
695
  const nullifiersToCheck = currentNotesForRecipient.map(note => note.siloedNullifier);
@@ -681,25 +714,113 @@ export class SimulatorOracle implements DBOracle {
681
714
  }
682
715
  }
683
716
 
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);
717
+ async produceNoteDao(
718
+ contractAddress: AztecAddress,
719
+ storageSlot: Fr,
720
+ nonce: Fr,
721
+ content: Fr[],
722
+ noteHash: Fr,
723
+ nullifier: Fr,
724
+ txHash: Fr,
725
+ recipient: AztecAddress,
726
+ ): Promise<NoteDao> {
727
+ const receipt = await this.aztecNode.getTxReceipt(new TxHash(txHash));
728
+ if (receipt === undefined) {
729
+ throw new Error(`Failed to fetch tx receipt for tx hash ${txHash} when searching for note hashes`);
730
+ }
731
+ const { blockNumber, blockHash } = receipt;
732
+
733
+ const uniqueNoteHash = computeUniqueNoteHash(nonce, siloNoteHash(contractAddress, noteHash));
734
+ const siloedNullifier = siloNullifier(contractAddress, nullifier);
735
+
736
+ const uniqueNoteHashTreeIndex = (
737
+ await this.aztecNode.findLeavesIndexes(blockNumber!, MerkleTreeId.NOTE_HASH_TREE, [uniqueNoteHash])
738
+ )[0];
739
+ if (uniqueNoteHashTreeIndex === undefined) {
740
+ throw new Error(
741
+ `Note hash ${noteHash} (uniqued as ${uniqueNoteHash}) is not present on the tree at block ${blockNumber} (from tx ${txHash})`,
742
+ );
743
+ }
744
+
745
+ return new NoteDao(
746
+ new Note(content),
747
+ contractAddress,
748
+ storageSlot,
749
+ nonce,
750
+ noteHash,
751
+ siloedNullifier,
752
+ new TxHash(txHash),
753
+ blockNumber!,
754
+ blockHash!.toString(),
755
+ uniqueNoteHashTreeIndex,
756
+ recipient.toAddressPoint(),
757
+ NoteSelector.empty(), // todo: remove
758
+ );
693
759
  }
694
760
 
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);
761
+ async callProcessLog(
762
+ contractAddress: AztecAddress,
763
+ logPlaintext: Fr[],
764
+ txHash: TxHash,
765
+ noteHashes: Fr[],
766
+ firstNullifier: Fr,
767
+ recipient: AztecAddress,
768
+ simulator?: AcirSimulator,
769
+ ) {
770
+ const artifact: FunctionArtifact | undefined = await new ContractDataOracle(this.db).getFunctionArtifactByName(
771
+ contractAddress,
772
+ 'process_log',
773
+ );
774
+ if (!artifact) {
775
+ throw new Error(
776
+ `Mandatory implementation of "process_log" missing in noir contract ${contractAddress.toString()}.`,
777
+ );
778
+ }
779
+
780
+ const execRequest: FunctionCall = {
781
+ name: artifact.name,
782
+ to: contractAddress,
783
+ selector: FunctionSelector.fromNameAndParameters(artifact),
784
+ type: FunctionType.UNCONSTRAINED,
785
+ isStatic: artifact.isStatic,
786
+ args: encodeArguments(artifact, [
787
+ toBoundedVec(logPlaintext, PRIVATE_LOG_SIZE_IN_FIELDS),
788
+ txHash.toString(),
789
+ toBoundedVec(noteHashes, MAX_NOTE_HASHES_PER_TX),
790
+ firstNullifier,
791
+ recipient,
792
+ ]),
793
+ returnTypes: artifact.returnTypes,
794
+ };
795
+
796
+ await (
797
+ simulator ??
798
+ getAcirSimulator(this.db, this.aztecNode, this.keyStore, this.simulationProvider, this.contractDataOracle)
799
+ ).runUnconstrained(
800
+ execRequest,
801
+ artifact,
802
+ contractAddress,
803
+ [], // empty scope as this call should not require access to private information
804
+ );
704
805
  }
806
+
807
+ dbStore(contractAddress: AztecAddress, slot: Fr, values: Fr[]): Promise<void> {
808
+ return this.db.dbStore(contractAddress, slot, values);
809
+ }
810
+
811
+ dbLoad(contractAddress: AztecAddress, slot: Fr): Promise<Fr[] | null> {
812
+ return this.db.dbLoad(contractAddress, slot);
813
+ }
814
+
815
+ dbDelete(contractAddress: AztecAddress, slot: Fr): Promise<void> {
816
+ return this.db.dbDelete(contractAddress, slot);
817
+ }
818
+
819
+ dbCopy(contractAddress: AztecAddress, srcSlot: Fr, dstSlot: Fr, numEntries: number): Promise<void> {
820
+ return this.db.dbCopy(contractAddress, srcSlot, dstSlot, numEntries);
821
+ }
822
+ }
823
+
824
+ function toBoundedVec(array: Fr[], maxLength: number) {
825
+ return { storage: array.concat(Array(maxLength - array.length).fill(new Fr(0))), len: array.length };
705
826
  }
@@ -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