@aztec/pxe 0.60.0 → 0.62.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 (69) hide show
  1. package/dest/database/deferred_note_dao.d.ts +8 -21
  2. package/dest/database/deferred_note_dao.d.ts.map +1 -1
  3. package/dest/database/deferred_note_dao.js +9 -19
  4. package/dest/database/incoming_note_dao.d.ts +3 -3
  5. package/dest/database/incoming_note_dao.d.ts.map +1 -1
  6. package/dest/database/incoming_note_dao.js +6 -6
  7. package/dest/database/kv_pxe_database.d.ts +11 -6
  8. package/dest/database/kv_pxe_database.d.ts.map +1 -1
  9. package/dest/database/kv_pxe_database.js +75 -42
  10. package/dest/database/outgoing_note_dao.d.ts +1 -1
  11. package/dest/database/outgoing_note_dao.d.ts.map +1 -1
  12. package/dest/database/outgoing_note_dao.js +3 -3
  13. package/dest/database/pxe_database.d.ts +37 -5
  14. package/dest/database/pxe_database.d.ts.map +1 -1
  15. package/dest/database/pxe_database_test_suite.d.ts.map +1 -1
  16. package/dest/database/pxe_database_test_suite.js +11 -11
  17. package/dest/index.d.ts +1 -1
  18. package/dest/index.d.ts.map +1 -1
  19. package/dest/index.js +1 -1
  20. package/dest/note_processor/note_processor.d.ts +1 -5
  21. package/dest/note_processor/note_processor.d.ts.map +1 -1
  22. package/dest/note_processor/note_processor.js +57 -58
  23. package/dest/note_processor/utils/add_public_values_to_payload.d.ts +10 -0
  24. package/dest/note_processor/utils/add_public_values_to_payload.d.ts.map +1 -0
  25. package/dest/note_processor/utils/add_public_values_to_payload.js +48 -0
  26. package/dest/note_processor/utils/brute_force_note_info.d.ts +8 -3
  27. package/dest/note_processor/utils/brute_force_note_info.d.ts.map +1 -1
  28. package/dest/note_processor/utils/brute_force_note_info.js +6 -3
  29. package/dest/note_processor/utils/produce_note_daos.d.ts +2 -2
  30. package/dest/note_processor/utils/produce_note_daos.d.ts.map +1 -1
  31. package/dest/note_processor/utils/produce_note_daos.js +8 -10
  32. package/dest/note_processor/utils/produce_note_daos_for_key.d.ts +3 -3
  33. package/dest/note_processor/utils/produce_note_daos_for_key.d.ts.map +1 -1
  34. package/dest/note_processor/utils/produce_note_daos_for_key.js +7 -61
  35. package/dest/pxe_http/pxe_http_server.d.ts.map +1 -1
  36. package/dest/pxe_http/pxe_http_server.js +23 -21
  37. package/dest/pxe_service/pxe_service.d.ts +15 -7
  38. package/dest/pxe_service/pxe_service.d.ts.map +1 -1
  39. package/dest/pxe_service/pxe_service.js +59 -42
  40. package/dest/pxe_service/test/pxe_test_suite.d.ts.map +1 -1
  41. package/dest/pxe_service/test/pxe_test_suite.js +2 -32
  42. package/dest/simulator_oracle/index.d.ts +41 -2
  43. package/dest/simulator_oracle/index.d.ts.map +1 -1
  44. package/dest/simulator_oracle/index.js +104 -2
  45. package/dest/synchronizer/synchronizer.d.ts +1 -1
  46. package/dest/synchronizer/synchronizer.d.ts.map +1 -1
  47. package/dest/synchronizer/synchronizer.js +8 -8
  48. package/package.json +16 -14
  49. package/src/database/deferred_note_dao.ts +7 -20
  50. package/src/database/incoming_note_dao.ts +6 -5
  51. package/src/database/kv_pxe_database.ts +91 -45
  52. package/src/database/outgoing_note_dao.ts +4 -3
  53. package/src/database/pxe_database.ts +42 -4
  54. package/src/database/pxe_database_test_suite.ts +12 -20
  55. package/src/index.ts +1 -1
  56. package/src/note_processor/note_processor.ts +91 -87
  57. package/src/note_processor/utils/add_public_values_to_payload.ts +63 -0
  58. package/src/note_processor/utils/brute_force_note_info.ts +11 -3
  59. package/src/note_processor/utils/produce_note_daos.ts +11 -13
  60. package/src/note_processor/utils/produce_note_daos_for_key.ts +19 -114
  61. package/src/pxe_http/pxe_http_server.ts +22 -19
  62. package/src/pxe_service/pxe_service.ts +71 -46
  63. package/src/pxe_service/test/pxe_test_suite.ts +1 -53
  64. package/src/simulator_oracle/index.ts +131 -1
  65. package/src/synchronizer/synchronizer.ts +7 -7
  66. package/dest/note_processor/utils/add_nullable_field_to_payload.d.ts +0 -12
  67. package/dest/note_processor/utils/add_nullable_field_to_payload.d.ts.map +0 -1
  68. package/dest/note_processor/utils/add_nullable_field_to_payload.js +0 -46
  69. package/src/note_processor/utils/add_nullable_field_to_payload.ts +0 -67
@@ -6,6 +6,7 @@ import {
6
6
  EncryptedL2Log,
7
7
  EncryptedL2NoteLog,
8
8
  EncryptedNoteL2BlockL2Logs,
9
+ EventMetadata,
9
10
  ExtendedNote,
10
11
  ExtendedUnencryptedL2Log,
11
12
  L2Block,
@@ -27,7 +28,7 @@ import {
27
28
  UniqueNote,
28
29
  } from '@aztec/circuit-types';
29
30
  import { FunctionSelector, PrivateCircuitPublicInputs, PublicKeys } from '@aztec/circuits.js';
30
- import { NoteSelector } from '@aztec/foundation/abi';
31
+ import { EventSelector, NoteSelector } from '@aztec/foundation/abi';
31
32
  import { AztecAddress } from '@aztec/foundation/aztec-address';
32
33
  import { Buffer32 } from '@aztec/foundation/buffer';
33
34
  import { EthAddress } from '@aztec/foundation/eth-address';
@@ -44,43 +45,45 @@ export function createPXERpcServer(pxeService: PXE): JsonRpcServer {
44
45
  return new JsonRpcServer(
45
46
  pxeService,
46
47
  {
47
- CompleteAddress,
48
+ AuthWitness,
48
49
  AztecAddress,
49
- TxExecutionRequest,
50
- ExtendedUnencryptedL2Log,
51
- FunctionSelector,
52
- TxHash,
53
50
  Buffer32,
51
+ CompleteAddress,
54
52
  EthAddress,
55
- Point,
53
+ EventSelector,
54
+ ExtendedNote,
55
+ ExtendedUnencryptedL2Log,
56
56
  Fr,
57
+ FunctionSelector,
57
58
  GrumpkinScalar,
59
+ L2Block,
60
+ LogId,
58
61
  Note,
59
- ExtendedNote,
62
+ Point,
60
63
  PublicKeys,
61
- UniqueNote,
62
64
  SiblingPath,
63
- AuthWitness,
64
- L2Block,
65
65
  TxEffect,
66
- LogId,
66
+ TxExecutionRequest,
67
+ TxHash,
68
+ UniqueNote,
67
69
  },
68
70
  {
69
- EncryptedNoteL2BlockL2Logs,
70
- EncryptedL2NoteLog,
71
+ CountedPublicExecutionRequest,
72
+ CountedNoteLog,
71
73
  EncryptedL2Log,
72
- UnencryptedL2Log,
74
+ EncryptedL2NoteLog,
75
+ EncryptedNoteL2BlockL2Logs,
76
+ EventMetadata,
73
77
  NoteSelector,
74
78
  NullifierMembershipWitness,
75
- TxSimulationResult,
76
- TxProvingResult,
77
79
  PrivateCircuitPublicInputs,
78
80
  PrivateExecutionResult,
79
- CountedPublicExecutionRequest,
80
- CountedNoteLog,
81
+ TxSimulationResult,
82
+ TxProvingResult,
81
83
  Tx,
82
84
  TxReceipt,
83
85
  UnencryptedL2BlockL2Logs,
86
+ UnencryptedL2Log,
84
87
  },
85
88
  ['start', 'stop'],
86
89
  );
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  type AuthWitness,
3
3
  type AztecNode,
4
- type EventMetadata,
4
+ EventMetadata,
5
5
  EventType,
6
6
  type ExtendedNote,
7
7
  type FunctionCall,
@@ -41,13 +41,16 @@ import {
41
41
  type NodeInfo,
42
42
  type PartialAddress,
43
43
  type PrivateKernelTailCircuitPublicInputs,
44
+ computeAddressSecret,
44
45
  computeContractAddressFromInstance,
45
46
  computeContractClassId,
47
+ computePoint,
46
48
  getContractClassFromArtifact,
47
49
  } from '@aztec/circuits.js';
48
50
  import { computeNoteHashNonce, siloNullifier } from '@aztec/circuits.js/hash';
49
51
  import {
50
52
  type AbiDecoded,
53
+ type AbiType,
51
54
  type ContractArtifact,
52
55
  EventSelector,
53
56
  FunctionSelector,
@@ -123,19 +126,18 @@ export class PXEService implements PXE {
123
126
 
124
127
  private async restoreNoteProcessors() {
125
128
  const accounts = await this.keyStore.getAccounts();
126
- const publicKeys = accounts.map(async account => await this.keyStore.getMasterIncomingViewingPublicKey(account));
127
- const publicKeysSet = new Set(publicKeys.map(k => k.toString()));
129
+ const accountsSet = new Set(accounts.map(k => k.toString()));
128
130
 
129
131
  const registeredAddresses = await this.db.getCompleteAddresses();
130
132
 
131
133
  let count = 0;
132
- for (const address of registeredAddresses) {
133
- if (!publicKeysSet.has(address.publicKeys.masterIncomingViewingPublicKey.toString())) {
134
+ for (const completeAddress of registeredAddresses) {
135
+ if (!accountsSet.has(completeAddress.address.toString())) {
134
136
  continue;
135
137
  }
136
138
 
137
139
  count++;
138
- await this.synchronizer.addAccount(address, this.keyStore, this.config.l2StartingBlock);
140
+ this.synchronizer.addAccount(completeAddress, this.keyStore, this.config.l2StartingBlock);
139
141
  }
140
142
 
141
143
  if (count > 0) {
@@ -194,7 +196,7 @@ export class PXEService implements PXE {
194
196
  this.log.info(`Account:\n "${accountCompleteAddress.address.toString()}"\n already registered.`);
195
197
  return accountCompleteAddress;
196
198
  } else {
197
- await this.synchronizer.addAccount(accountCompleteAddress, this.keyStore, this.config.l2StartingBlock);
199
+ this.synchronizer.addAccount(accountCompleteAddress, this.keyStore, this.config.l2StartingBlock);
198
200
  this.log.info(`Registered account ${accountCompleteAddress.address.toString()}`);
199
201
  this.log.debug(`Registered account\n ${accountCompleteAddress.toReadableString()}`);
200
202
  }
@@ -203,47 +205,56 @@ export class PXEService implements PXE {
203
205
  return accountCompleteAddress;
204
206
  }
205
207
 
206
- public async getRegisteredAccounts(): Promise<CompleteAddress[]> {
207
- // Get complete addresses of both the recipients and the accounts
208
- const completeAddresses = await this.db.getCompleteAddresses();
209
- // Filter out the addresses not corresponding to accounts
208
+ public async registerContact(address: AztecAddress): Promise<AztecAddress> {
210
209
  const accounts = await this.keyStore.getAccounts();
211
- return completeAddresses.filter(completeAddress =>
212
- accounts.find(address => address.equals(completeAddress.address)),
213
- );
210
+ if (accounts.includes(address)) {
211
+ this.log.info(`Account:\n "${address.toString()}"\n already registered.`);
212
+ return address;
213
+ }
214
+
215
+ const wasAdded = await this.db.addContactAddress(address);
216
+
217
+ if (wasAdded) {
218
+ this.log.info(`Added contact:\n ${address.toString()}`);
219
+ } else {
220
+ this.log.info(`Contact:\n "${address.toString()}"\n already registered.`);
221
+ }
222
+
223
+ return address;
214
224
  }
215
225
 
216
- public async getRegisteredAccount(address: AztecAddress): Promise<CompleteAddress | undefined> {
217
- const result = await this.getRegisteredAccounts();
218
- const account = result.find(r => r.address.equals(address));
219
- return Promise.resolve(account);
226
+ public getContacts(): Promise<AztecAddress[]> {
227
+ const contacts = this.db.getContactAddresses();
228
+
229
+ return Promise.resolve(contacts);
220
230
  }
221
231
 
222
- public async registerRecipient(recipient: CompleteAddress): Promise<void> {
223
- const wasAdded = await this.db.addCompleteAddress(recipient);
232
+ public async removeContact(address: AztecAddress): Promise<void> {
233
+ const wasRemoved = await this.db.removeContactAddress(address);
224
234
 
225
- if (wasAdded) {
226
- this.log.info(`Added recipient:\n ${recipient.toReadableString()}`);
235
+ if (wasRemoved) {
236
+ this.log.info(`Removed contact:\n ${address.toString()}`);
227
237
  } else {
228
- this.log.info(`Recipient:\n "${recipient.toReadableString()}"\n already registered.`);
238
+ this.log.info(`Contact:\n "${address.toString()}"\n not in address book.`);
229
239
  }
240
+
241
+ return Promise.resolve();
230
242
  }
231
243
 
232
- public async getRecipients(): Promise<CompleteAddress[]> {
244
+ public async getRegisteredAccounts(): Promise<CompleteAddress[]> {
233
245
  // Get complete addresses of both the recipients and the accounts
234
246
  const completeAddresses = await this.db.getCompleteAddresses();
235
- // Filter out the addresses corresponding to accounts
247
+ // Filter out the addresses not corresponding to accounts
236
248
  const accounts = await this.keyStore.getAccounts();
237
- const recipients = completeAddresses.filter(
238
- completeAddress => !accounts.find(account => account.equals(completeAddress.address)),
249
+ return completeAddresses.filter(completeAddress =>
250
+ accounts.find(address => address.equals(completeAddress.address)),
239
251
  );
240
- return recipients;
241
252
  }
242
253
 
243
- public async getRecipient(address: AztecAddress): Promise<CompleteAddress | undefined> {
244
- const result = await this.getRecipients();
245
- const recipient = result.find(r => r.address.equals(address));
246
- return Promise.resolve(recipient);
254
+ public async getRegisteredAccount(address: AztecAddress): Promise<CompleteAddress | undefined> {
255
+ const result = await this.getRegisteredAccounts();
256
+ const account = result.find(r => r.address.equals(address));
257
+ return Promise.resolve(account);
247
258
  }
248
259
 
249
260
  public async registerContractClass(artifact: ContractArtifact): Promise<void> {
@@ -305,11 +316,11 @@ export class PXEService implements PXE {
305
316
  const extendedNotes = noteDaos.map(async dao => {
306
317
  let owner = filter.owner;
307
318
  if (owner === undefined) {
308
- const completeAddresses = (await this.db.getCompleteAddresses()).find(address =>
309
- address.publicKeys.masterIncomingViewingPublicKey.equals(dao.ivpkM),
319
+ const completeAddresses = (await this.db.getCompleteAddresses()).find(completeAddress =>
320
+ computePoint(completeAddress.address).equals(dao.addressPoint),
310
321
  );
311
322
  if (completeAddresses === undefined) {
312
- throw new Error(`Cannot find complete address for IvpkM ${dao.ivpkM.toString()}`);
323
+ throw new Error(`Cannot find complete address for addressPoint ${dao.addressPoint.toString()}`);
313
324
  }
314
325
  owner = completeAddresses.address;
315
326
  }
@@ -404,7 +415,7 @@ export class PXEService implements PXE {
404
415
  noteHash,
405
416
  siloedNullifier,
406
417
  index,
407
- owner.publicKeys.masterIncomingViewingPublicKey,
418
+ computePoint(owner.address),
408
419
  ),
409
420
  scope,
410
421
  );
@@ -412,11 +423,6 @@ export class PXEService implements PXE {
412
423
  }
413
424
 
414
425
  public async addNullifiedNote(note: ExtendedNote) {
415
- const owner = await this.db.getCompleteAddress(note.owner);
416
- if (!owner) {
417
- throw new Error(`Unknown account: ${note.owner.toString()}`);
418
- }
419
-
420
426
  const nonces = await this.#getNoteNonces(note);
421
427
  if (nonces.length === 0) {
422
428
  throw new Error(`Cannot find the note in tx: ${note.txHash}.`);
@@ -452,7 +458,7 @@ export class PXEService implements PXE {
452
458
  noteHash,
453
459
  Fr.ZERO, // We are not able to derive
454
460
  index,
455
- owner.publicKeys.masterIncomingViewingPublicKey,
461
+ computePoint(note.owner),
456
462
  ),
457
463
  );
458
464
  }
@@ -829,24 +835,25 @@ export class PXEService implements PXE {
829
835
 
830
836
  public getEvents<T>(
831
837
  type: EventType.Encrypted,
832
- eventMetadata: EventMetadata<T>,
838
+ event: { eventSelector: EventSelector; abiType: AbiType; fieldNames: string[] },
833
839
  from: number,
834
840
  limit: number,
835
841
  vpks: Point[],
836
842
  ): Promise<T[]>;
837
843
  public getEvents<T>(
838
844
  type: EventType.Unencrypted,
839
- eventMetadata: EventMetadata<T>,
845
+ event: { eventSelector: EventSelector; abiType: AbiType; fieldNames: string[] },
840
846
  from: number,
841
847
  limit: number,
842
848
  ): Promise<T[]>;
843
849
  public getEvents<T>(
844
850
  type: EventType,
845
- eventMetadata: EventMetadata<T>,
851
+ event: { eventSelector: EventSelector; abiType: AbiType; fieldNames: string[] },
846
852
  from: number,
847
853
  limit: number,
848
854
  vpks: Point[] = [],
849
855
  ): Promise<T[]> {
856
+ const eventMetadata = new EventMetadata<T>(type, event);
850
857
  if (type.includes(EventType.Encrypted)) {
851
858
  return this.getEncryptedEvents(from, limit, eventMetadata, vpks);
852
859
  }
@@ -858,6 +865,7 @@ export class PXEService implements PXE {
858
865
  from: number,
859
866
  limit: number,
860
867
  eventMetadata: EventMetadata<T>,
868
+ // TODO (#9272): Make this better, we should be able to only pass an address now
861
869
  vpks: Point[],
862
870
  ): Promise<T[]> {
863
871
  if (vpks.length === 0) {
@@ -871,7 +879,24 @@ export class PXEService implements PXE {
871
879
 
872
880
  const encryptedLogs = encryptedTxLogs.flatMap(encryptedTxLog => encryptedTxLog.unrollLogs());
873
881
 
874
- const vsks = await Promise.all(vpks.map(vpk => this.keyStore.getMasterSecretKey(vpk)));
882
+ const vsks = await Promise.all(
883
+ vpks.map(async vpk => {
884
+ const [keyPrefix, account] = this.keyStore.getKeyPrefixAndAccount(vpk);
885
+ let secretKey = await this.keyStore.getMasterSecretKey(vpk);
886
+ if (keyPrefix === 'iv') {
887
+ const registeredAccount = await this.getRegisteredAccount(account);
888
+ if (!registeredAccount) {
889
+ throw new Error('No registered account');
890
+ }
891
+
892
+ const preaddress = registeredAccount.getPreaddress();
893
+
894
+ secretKey = computeAddressSecret(preaddress, secretKey);
895
+ }
896
+
897
+ return secretKey;
898
+ }),
899
+ );
875
900
 
876
901
  const visibleEvents = encryptedLogs.flatMap(encryptedLog => {
877
902
  for (const sk of vsks) {
@@ -4,15 +4,7 @@ import {
4
4
  randomContractInstanceWithAddress,
5
5
  randomDeployedContract,
6
6
  } from '@aztec/circuit-types';
7
- import {
8
- AztecAddress,
9
- CompleteAddress,
10
- Fr,
11
- INITIAL_L2_BLOCK_NUM,
12
- Point,
13
- PublicKeys,
14
- getContractClassFromArtifact,
15
- } from '@aztec/circuits.js';
7
+ import { AztecAddress, Fr, INITIAL_L2_BLOCK_NUM, getContractClassFromArtifact } from '@aztec/circuits.js';
16
8
 
17
9
  export const pxeTestSuite = (testName: string, pxeSetup: () => Promise<PXE>) => {
18
10
  describe(testName, () => {
@@ -29,33 +21,11 @@ export const pxeTestSuite = (testName: string, pxeSetup: () => Promise<PXE>) =>
29
21
 
30
22
  // Check that the account is correctly registered using the getAccounts and getRecipients methods
31
23
  const accounts = await pxe.getRegisteredAccounts();
32
- const recipients = await pxe.getRecipients();
33
24
  expect(accounts).toContainEqual(completeAddress);
34
- expect(recipients).not.toContainEqual(completeAddress);
35
25
 
36
26
  // Check that the account is correctly registered using the getAccount and getRecipient methods
37
27
  const account = await pxe.getRegisteredAccount(completeAddress.address);
38
- const recipient = await pxe.getRecipient(completeAddress.address);
39
28
  expect(account).toEqual(completeAddress);
40
- expect(recipient).toBeUndefined();
41
- });
42
-
43
- it('registers a recipient and returns it as a recipient only and not as an account', async () => {
44
- const completeAddress = CompleteAddress.random();
45
-
46
- await pxe.registerRecipient(completeAddress);
47
-
48
- // Check that the recipient is correctly registered using the getAccounts and getRecipients methods
49
- const accounts = await pxe.getRegisteredAccounts();
50
- const recipients = await pxe.getRecipients();
51
- expect(accounts).not.toContainEqual(completeAddress);
52
- expect(recipients).toContainEqual(completeAddress);
53
-
54
- // Check that the recipient is correctly registered using the getAccount and getRecipient methods
55
- const account = await pxe.getRegisteredAccount(completeAddress.address);
56
- const recipient = await pxe.getRecipient(completeAddress.address);
57
- expect(account).toBeUndefined();
58
- expect(recipient).toEqual(completeAddress);
59
29
  });
60
30
 
61
31
  it('does not throw when registering the same account twice (just ignores the second attempt)', async () => {
@@ -66,28 +36,6 @@ export const pxeTestSuite = (testName: string, pxeSetup: () => Promise<PXE>) =>
66
36
  await pxe.registerAccount(randomSecretKey, randomPartialAddress);
67
37
  });
68
38
 
69
- // Disabled as CompleteAddress constructor now performs preimage validation.
70
- it.skip('cannot register a recipient with the same aztec address but different pub key or partial address', async () => {
71
- const recipient1 = CompleteAddress.random();
72
- const recipient2 = new CompleteAddress(
73
- recipient1.address,
74
- new PublicKeys(Point.random(), Point.random(), Point.random(), Point.random()),
75
- Fr.random(),
76
- );
77
-
78
- await pxe.registerRecipient(recipient1);
79
- await expect(() => pxe.registerRecipient(recipient2)).rejects.toThrow(
80
- `Complete address with aztec address ${recipient1.address}`,
81
- );
82
- });
83
-
84
- it('does not throw when registering the same recipient twice (just ignores the second attempt)', async () => {
85
- const completeAddress = CompleteAddress.random();
86
-
87
- await pxe.registerRecipient(completeAddress);
88
- await pxe.registerRecipient(completeAddress);
89
- });
90
-
91
39
  it('successfully adds a contract', async () => {
92
40
  const contracts = [randomDeployedContract(), randomDeployedContract()];
93
41
  for (const contract of contracts) {
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  type AztecNode,
3
+ type EncryptedL2NoteLog,
3
4
  type L2Block,
4
5
  MerkleTreeId,
5
6
  type NoteStatus,
@@ -14,10 +15,14 @@ import {
14
15
  type Fr,
15
16
  type FunctionSelector,
16
17
  type Header,
18
+ IndexedTaggingSecret,
17
19
  type KeyValidationRequest,
18
20
  type L1_TO_L2_MSG_TREE_HEIGHT,
21
+ TaggingSecret,
22
+ computeTaggingSecret,
19
23
  } from '@aztec/circuits.js';
20
24
  import { type FunctionArtifact, getFunctionArtifact } from '@aztec/foundation/abi';
25
+ import { poseidon2Hash } from '@aztec/foundation/crypto';
21
26
  import { createDebugLogger } from '@aztec/foundation/log';
22
27
  import { type KeyStore } from '@aztec/key-store';
23
28
  import { type DBOracle, MessageLoadOracleInputs } from '@aztec/simulator';
@@ -46,7 +51,7 @@ export class SimulatorOracle implements DBOracle {
46
51
  if (!completeAddress) {
47
52
  throw new Error(
48
53
  `No public key registered for address ${account}.
49
- Register it by calling pxe.registerRecipient(...) or pxe.registerAccount(...).\nSee docs for context: https://docs.aztec.network/reference/common_errors/aztecnr-errors#simulation-error-no-public-key-registered-for-address-0x0-register-it-by-calling-pxeregisterrecipient-or-pxeregisteraccount`,
54
+ Register it by calling pxe.registerAccount(...).\nSee docs for context: https://docs.aztec.network/reference/common_errors/aztecnr-errors#simulation-error-no-public-key-registered-for-address-0x0-register-it-by-calling-pxeregisterrecipient-or-pxeregisteraccount`,
50
55
  );
51
56
  }
52
57
  return completeAddress;
@@ -226,4 +231,129 @@ export class SimulatorOracle implements DBOracle {
226
231
  public getDebugFunctionName(contractAddress: AztecAddress, selector: FunctionSelector): Promise<string> {
227
232
  return this.contractDataOracle.getDebugFunctionName(contractAddress, selector);
228
233
  }
234
+
235
+ /**
236
+ * Returns the full contents of your address book.
237
+ * This is used when calculating tags for incoming notes by deriving the shared secret, the contract-siloed tagging secret, and
238
+ * finally the index specified tag. We will then query the node with this tag for each address in the address book.
239
+ * @returns The full list of the users contact addresses.
240
+ */
241
+ public getContacts(): AztecAddress[] {
242
+ return this.db.getContactAddresses();
243
+ }
244
+
245
+ /**
246
+ * Returns the tagging secret for a given sender and recipient pair. For this to work, the ivpsk_m of the sender must be known.
247
+ * Includes the last known index used for tagging with this secret.
248
+ * @param contractAddress - The contract address to silo the secret for
249
+ * @param sender - The address sending the note
250
+ * @param recipient - The address receiving the note
251
+ * @returns A siloed tagging secret that can be used to tag notes.
252
+ */
253
+ public async getAppTaggingSecret(
254
+ contractAddress: AztecAddress,
255
+ sender: AztecAddress,
256
+ recipient: AztecAddress,
257
+ ): Promise<IndexedTaggingSecret> {
258
+ const directionalSecret = await this.#calculateDirectionalSecret(contractAddress, sender, recipient);
259
+ const [index] = await this.db.getTaggingSecretsIndexes([directionalSecret]);
260
+ return IndexedTaggingSecret.fromTaggingSecret(directionalSecret, index);
261
+ }
262
+
263
+ /**
264
+ * Increments the tagging secret for a given sender and recipient pair. For this to work, the ivpsk_m of the sender must be known.
265
+ * @param contractAddress - The contract address to silo the secret for
266
+ * @param sender - The address sending the note
267
+ * @param recipient - The address receiving the note
268
+ */
269
+ public async incrementAppTaggingSecret(
270
+ contractAddress: AztecAddress,
271
+ sender: AztecAddress,
272
+ recipient: AztecAddress,
273
+ ): Promise<void> {
274
+ const directionalSecret = await this.#calculateDirectionalSecret(contractAddress, sender, recipient);
275
+ await this.db.incrementTaggingSecretsIndexes([directionalSecret]);
276
+ }
277
+
278
+ async #calculateDirectionalSecret(contractAddress: AztecAddress, sender: AztecAddress, recipient: AztecAddress) {
279
+ const senderCompleteAddress = await this.getCompleteAddress(sender);
280
+ const senderIvsk = await this.keyStore.getMasterIncomingViewingSecretKey(sender);
281
+ const sharedSecret = computeTaggingSecret(senderCompleteAddress, senderIvsk, recipient);
282
+ // Silo the secret to the app so it can't be used to track other app's notes
283
+ const siloedSecret = poseidon2Hash([sharedSecret.x, sharedSecret.y, contractAddress]);
284
+ // Get the index of the secret, ensuring the directionality (sender -> recipient)
285
+ const directionalSecret = new TaggingSecret(siloedSecret, recipient);
286
+ return directionalSecret;
287
+ }
288
+
289
+ /**
290
+ * Returns the siloed tagging secrets for a given recipient and all the senders in the address book
291
+ * @param contractAddress - The contract address to silo the secret for
292
+ * @param recipient - The address receiving the notes
293
+ * @returns A list of siloed tagging secrets
294
+ */
295
+ public async getAppTaggingSecretsForSenders(
296
+ contractAddress: AztecAddress,
297
+ recipient: AztecAddress,
298
+ ): Promise<IndexedTaggingSecret[]> {
299
+ const recipientCompleteAddress = await this.getCompleteAddress(recipient);
300
+ const recipientIvsk = await this.keyStore.getMasterIncomingViewingSecretKey(recipient);
301
+
302
+ // We implicitly add the recipient as a contact, this helps us decrypt tags on notes that we send to ourselves (recipient = us, sender = us)
303
+ const contacts = [...this.db.getContactAddresses(), recipient];
304
+ const appTaggingSecrets = contacts.map(contact => {
305
+ const sharedSecret = computeTaggingSecret(recipientCompleteAddress, recipientIvsk, contact);
306
+ return poseidon2Hash([sharedSecret.x, sharedSecret.y, contractAddress]);
307
+ });
308
+ // Ensure the directionality (sender -> recipient)
309
+ const directionalSecrets = appTaggingSecrets.map(secret => new TaggingSecret(secret, recipient));
310
+ const indexes = await this.db.getTaggingSecretsIndexes(directionalSecrets);
311
+ return directionalSecrets.map((directionalSecret, i) =>
312
+ IndexedTaggingSecret.fromTaggingSecret(directionalSecret, indexes[i]),
313
+ );
314
+ }
315
+
316
+ /**
317
+ * Synchronizes the logs tagged with the recipient's address and all the senders in the addressbook.
318
+ * Returns the unsynched logs and updates the indexes of the secrets used to tag them until there are no more logs to sync.
319
+ * @param contractAddress - The address of the contract that the logs are tagged for
320
+ * @param recipient - The address of the recipient
321
+ * @returns A list of encrypted logs tagged with the recipient's address
322
+ */
323
+ public async syncTaggedLogs(contractAddress: AztecAddress, recipient: AztecAddress): Promise<EncryptedL2NoteLog[]> {
324
+ // Ideally this algorithm would be implemented in noir, exposing its building blocks as oracles.
325
+ // However it is impossible at the moment due to the language not supporting nested slices.
326
+ // This nesting is necessary because for a given set of tags we don't
327
+ // know how many logs we will get back. Furthermore, these logs are of undetermined
328
+ // length, since we don't really know the note they correspond to until we decrypt them.
329
+
330
+ // 1. Get all the secrets for the recipient and sender pairs (#9365)
331
+ let appTaggingSecrets = await this.getAppTaggingSecretsForSenders(contractAddress, recipient);
332
+
333
+ const logs: EncryptedL2NoteLog[] = [];
334
+ while (appTaggingSecrets.length > 0) {
335
+ // 2. Compute tags using the secrets, recipient and index. Obtain logs for each tag (#9380)
336
+ const currentTags = appTaggingSecrets.map(({ secret, recipient, index }) =>
337
+ poseidon2Hash([secret, recipient, index]),
338
+ );
339
+ const logsByTags = await this.aztecNode.getLogsByTags(currentTags);
340
+ const newTaggingSecrets: IndexedTaggingSecret[] = [];
341
+ logsByTags.forEach((logsByTag, index) => {
342
+ // 3.1. Append logs to the list and increment the index for the tags that have logs (#9380)
343
+ if (logsByTag.length > 0) {
344
+ logs.push(...logsByTag);
345
+ // 3.2. Increment the index for the tags that have logs (#9380)
346
+ newTaggingSecrets.push(
347
+ new IndexedTaggingSecret(appTaggingSecrets[index].secret, recipient, appTaggingSecrets[index].index + 1),
348
+ );
349
+ }
350
+ });
351
+ // 4. Consolidate in db and replace initial appTaggingSecrets with the new ones (updated indexes)
352
+ await this.db.incrementTaggingSecretsIndexes(
353
+ newTaggingSecrets.map(secret => new TaggingSecret(secret.secret, recipient)),
354
+ );
355
+ appTaggingSecrets = newTaggingSecrets;
356
+ }
357
+ return logs;
358
+ }
229
359
  }
@@ -249,14 +249,14 @@ export class Synchronizer {
249
249
  * @param startingBlock - The block where to start scanning for notes for this accounts.
250
250
  * @returns A promise that resolves once the account is added to the Synchronizer.
251
251
  */
252
- public async addAccount(account: CompleteAddress, keyStore: KeyStore, startingBlock: number) {
252
+ public addAccount(account: CompleteAddress, keyStore: KeyStore, startingBlock: number) {
253
253
  const predicate = (x: NoteProcessor) => x.account.equals(account);
254
254
  const processor = this.noteProcessors.find(predicate) ?? this.noteProcessorsToCatchUp.find(predicate);
255
255
  if (processor) {
256
256
  return;
257
257
  }
258
258
 
259
- this.noteProcessorsToCatchUp.push(await NoteProcessor.create(account, keyStore, this.db, this.node, startingBlock));
259
+ this.noteProcessorsToCatchUp.push(NoteProcessor.create(account, keyStore, this.db, this.node, startingBlock));
260
260
  }
261
261
 
262
262
  /**
@@ -371,15 +371,15 @@ export class Synchronizer {
371
371
 
372
372
  async #removeNullifiedNotes(notes: IncomingNoteDao[]) {
373
373
  // now group the decoded incoming notes by public key
374
- const publicKeyToIncomingNotes: Map<PublicKey, IncomingNoteDao[]> = new Map();
374
+ const addressPointToIncomingNotes: Map<PublicKey, IncomingNoteDao[]> = new Map();
375
375
  for (const noteDao of notes) {
376
- const notesForPublicKey = publicKeyToIncomingNotes.get(noteDao.ivpkM) ?? [];
377
- notesForPublicKey.push(noteDao);
378
- publicKeyToIncomingNotes.set(noteDao.ivpkM, notesForPublicKey);
376
+ const notesForAddressPoint = addressPointToIncomingNotes.get(noteDao.addressPoint) ?? [];
377
+ notesForAddressPoint.push(noteDao);
378
+ addressPointToIncomingNotes.set(noteDao.addressPoint, notesForAddressPoint);
379
379
  }
380
380
 
381
381
  // now for each group, look for the nullifiers in the nullifier tree
382
- for (const [publicKey, notes] of publicKeyToIncomingNotes.entries()) {
382
+ for (const [publicKey, notes] of addressPointToIncomingNotes.entries()) {
383
383
  const nullifiers = notes.map(n => n.siloedNullifier);
384
384
  const relevantNullifiers: Fr[] = [];
385
385
  for (const nullifier of nullifiers) {
@@ -1,12 +0,0 @@
1
- import { L1NotePayload } from '@aztec/circuit-types';
2
- import { type Fr } from '@aztec/foundation/fields';
3
- import { type PxeDatabase } from '../../database/pxe_database.js';
4
- /**
5
- * Inserts publicly delivered nullable fields into the note payload.
6
- * @param db - PXE database used to fetch contract instance and artifact.
7
- * @param payload - Note payload to which nullable fields should be added.
8
- * @param nullableFields - List of nullable fields to be added to the note payload.
9
- * @returns Note payload with nullable fields added.
10
- */
11
- export declare function addNullableFieldsToPayload(db: PxeDatabase, payload: L1NotePayload, nullableFields: Fr[]): Promise<L1NotePayload>;
12
- //# sourceMappingURL=add_nullable_field_to_payload.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"add_nullable_field_to_payload.d.ts","sourceRoot":"","sources":["../../../src/note_processor/utils/add_nullable_field_to_payload.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAQ,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAAE,KAAK,EAAE,EAAE,MAAM,0BAA0B,CAAC;AAGnD,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAElE;;;;;;GAMG;AACH,wBAAsB,0BAA0B,CAC9C,EAAE,EAAE,WAAW,EACf,OAAO,EAAE,aAAa,EACtB,cAAc,EAAE,EAAE,EAAE,GACnB,OAAO,CAAC,aAAa,CAAC,CAiDxB"}