@aztec/pxe 0.61.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.
- package/dest/database/deferred_note_dao.d.ts +2 -2
- package/dest/database/deferred_note_dao.d.ts.map +1 -1
- package/dest/database/deferred_note_dao.js +2 -2
- package/dest/database/incoming_note_dao.d.ts +3 -3
- package/dest/database/incoming_note_dao.d.ts.map +1 -1
- package/dest/database/incoming_note_dao.js +6 -6
- package/dest/database/kv_pxe_database.d.ts +8 -5
- package/dest/database/kv_pxe_database.d.ts.map +1 -1
- package/dest/database/kv_pxe_database.js +63 -44
- package/dest/database/outgoing_note_dao.d.ts +1 -1
- package/dest/database/outgoing_note_dao.js +2 -2
- package/dest/database/pxe_database.d.ts +33 -3
- package/dest/database/pxe_database.d.ts.map +1 -1
- package/dest/database/pxe_database_test_suite.d.ts.map +1 -1
- package/dest/database/pxe_database_test_suite.js +11 -11
- package/dest/index.d.ts +1 -1
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +1 -1
- package/dest/note_processor/note_processor.d.ts.map +1 -1
- package/dest/note_processor/note_processor.js +9 -8
- package/dest/note_processor/utils/produce_note_daos.d.ts +2 -2
- package/dest/note_processor/utils/produce_note_daos.d.ts.map +1 -1
- package/dest/note_processor/utils/produce_note_daos.js +7 -7
- package/dest/pxe_http/pxe_http_server.d.ts.map +1 -1
- package/dest/pxe_http/pxe_http_server.js +23 -21
- package/dest/pxe_service/pxe_service.d.ts +15 -7
- package/dest/pxe_service/pxe_service.d.ts.map +1 -1
- package/dest/pxe_service/pxe_service.js +42 -39
- package/dest/pxe_service/test/pxe_test_suite.d.ts.map +1 -1
- package/dest/pxe_service/test/pxe_test_suite.js +2 -32
- package/dest/simulator_oracle/index.d.ts +24 -1
- package/dest/simulator_oracle/index.d.ts.map +1 -1
- package/dest/simulator_oracle/index.js +81 -18
- package/dest/synchronizer/synchronizer.js +6 -6
- package/package.json +16 -14
- package/src/database/deferred_note_dao.ts +1 -1
- package/src/database/incoming_note_dao.ts +4 -4
- package/src/database/kv_pxe_database.ts +72 -46
- package/src/database/outgoing_note_dao.ts +2 -2
- package/src/database/pxe_database.ts +36 -2
- package/src/database/pxe_database_test_suite.ts +12 -20
- package/src/index.ts +1 -1
- package/src/note_processor/note_processor.ts +12 -9
- package/src/note_processor/utils/produce_note_daos.ts +6 -6
- package/src/pxe_http/pxe_http_server.ts +22 -19
- package/src/pxe_service/pxe_service.ts +50 -44
- package/src/pxe_service/test/pxe_test_suite.ts +1 -53
- package/src/simulator_oracle/index.ts +93 -14
- package/src/synchronizer/synchronizer.ts +5 -5
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
type AuthWitness,
|
|
3
3
|
type AztecNode,
|
|
4
|
-
|
|
4
|
+
EventMetadata,
|
|
5
5
|
EventType,
|
|
6
6
|
type ExtendedNote,
|
|
7
7
|
type FunctionCall,
|
|
@@ -44,11 +44,13 @@ import {
|
|
|
44
44
|
computeAddressSecret,
|
|
45
45
|
computeContractAddressFromInstance,
|
|
46
46
|
computeContractClassId,
|
|
47
|
+
computePoint,
|
|
47
48
|
getContractClassFromArtifact,
|
|
48
49
|
} from '@aztec/circuits.js';
|
|
49
50
|
import { computeNoteHashNonce, siloNullifier } from '@aztec/circuits.js/hash';
|
|
50
51
|
import {
|
|
51
52
|
type AbiDecoded,
|
|
53
|
+
type AbiType,
|
|
52
54
|
type ContractArtifact,
|
|
53
55
|
EventSelector,
|
|
54
56
|
FunctionSelector,
|
|
@@ -124,19 +126,18 @@ export class PXEService implements PXE {
|
|
|
124
126
|
|
|
125
127
|
private async restoreNoteProcessors() {
|
|
126
128
|
const accounts = await this.keyStore.getAccounts();
|
|
127
|
-
const
|
|
128
|
-
const publicKeysSet = new Set(publicKeys.map(k => k.toString()));
|
|
129
|
+
const accountsSet = new Set(accounts.map(k => k.toString()));
|
|
129
130
|
|
|
130
131
|
const registeredAddresses = await this.db.getCompleteAddresses();
|
|
131
132
|
|
|
132
133
|
let count = 0;
|
|
133
|
-
for (const
|
|
134
|
-
if (!
|
|
134
|
+
for (const completeAddress of registeredAddresses) {
|
|
135
|
+
if (!accountsSet.has(completeAddress.address.toString())) {
|
|
135
136
|
continue;
|
|
136
137
|
}
|
|
137
138
|
|
|
138
139
|
count++;
|
|
139
|
-
this.synchronizer.addAccount(
|
|
140
|
+
this.synchronizer.addAccount(completeAddress, this.keyStore, this.config.l2StartingBlock);
|
|
140
141
|
}
|
|
141
142
|
|
|
142
143
|
if (count > 0) {
|
|
@@ -204,47 +205,56 @@ export class PXEService implements PXE {
|
|
|
204
205
|
return accountCompleteAddress;
|
|
205
206
|
}
|
|
206
207
|
|
|
207
|
-
public async
|
|
208
|
-
// Get complete addresses of both the recipients and the accounts
|
|
209
|
-
const completeAddresses = await this.db.getCompleteAddresses();
|
|
210
|
-
// Filter out the addresses not corresponding to accounts
|
|
208
|
+
public async registerContact(address: AztecAddress): Promise<AztecAddress> {
|
|
211
209
|
const accounts = await this.keyStore.getAccounts();
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
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;
|
|
215
224
|
}
|
|
216
225
|
|
|
217
|
-
public
|
|
218
|
-
const
|
|
219
|
-
|
|
220
|
-
return Promise.resolve(
|
|
226
|
+
public getContacts(): Promise<AztecAddress[]> {
|
|
227
|
+
const contacts = this.db.getContactAddresses();
|
|
228
|
+
|
|
229
|
+
return Promise.resolve(contacts);
|
|
221
230
|
}
|
|
222
231
|
|
|
223
|
-
public async
|
|
224
|
-
const
|
|
232
|
+
public async removeContact(address: AztecAddress): Promise<void> {
|
|
233
|
+
const wasRemoved = await this.db.removeContactAddress(address);
|
|
225
234
|
|
|
226
|
-
if (
|
|
227
|
-
this.log.info(`
|
|
235
|
+
if (wasRemoved) {
|
|
236
|
+
this.log.info(`Removed contact:\n ${address.toString()}`);
|
|
228
237
|
} else {
|
|
229
|
-
this.log.info(`
|
|
238
|
+
this.log.info(`Contact:\n "${address.toString()}"\n not in address book.`);
|
|
230
239
|
}
|
|
240
|
+
|
|
241
|
+
return Promise.resolve();
|
|
231
242
|
}
|
|
232
243
|
|
|
233
|
-
public async
|
|
244
|
+
public async getRegisteredAccounts(): Promise<CompleteAddress[]> {
|
|
234
245
|
// Get complete addresses of both the recipients and the accounts
|
|
235
246
|
const completeAddresses = await this.db.getCompleteAddresses();
|
|
236
|
-
// Filter out the addresses corresponding to accounts
|
|
247
|
+
// Filter out the addresses not corresponding to accounts
|
|
237
248
|
const accounts = await this.keyStore.getAccounts();
|
|
238
|
-
|
|
239
|
-
|
|
249
|
+
return completeAddresses.filter(completeAddress =>
|
|
250
|
+
accounts.find(address => address.equals(completeAddress.address)),
|
|
240
251
|
);
|
|
241
|
-
return recipients;
|
|
242
252
|
}
|
|
243
253
|
|
|
244
|
-
public async
|
|
245
|
-
const result = await this.
|
|
246
|
-
const
|
|
247
|
-
return Promise.resolve(
|
|
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);
|
|
248
258
|
}
|
|
249
259
|
|
|
250
260
|
public async registerContractClass(artifact: ContractArtifact): Promise<void> {
|
|
@@ -306,11 +316,11 @@ export class PXEService implements PXE {
|
|
|
306
316
|
const extendedNotes = noteDaos.map(async dao => {
|
|
307
317
|
let owner = filter.owner;
|
|
308
318
|
if (owner === undefined) {
|
|
309
|
-
const completeAddresses = (await this.db.getCompleteAddresses()).find(
|
|
310
|
-
address.
|
|
319
|
+
const completeAddresses = (await this.db.getCompleteAddresses()).find(completeAddress =>
|
|
320
|
+
computePoint(completeAddress.address).equals(dao.addressPoint),
|
|
311
321
|
);
|
|
312
322
|
if (completeAddresses === undefined) {
|
|
313
|
-
throw new Error(`Cannot find complete address for
|
|
323
|
+
throw new Error(`Cannot find complete address for addressPoint ${dao.addressPoint.toString()}`);
|
|
314
324
|
}
|
|
315
325
|
owner = completeAddresses.address;
|
|
316
326
|
}
|
|
@@ -405,7 +415,7 @@ export class PXEService implements PXE {
|
|
|
405
415
|
noteHash,
|
|
406
416
|
siloedNullifier,
|
|
407
417
|
index,
|
|
408
|
-
owner.
|
|
418
|
+
computePoint(owner.address),
|
|
409
419
|
),
|
|
410
420
|
scope,
|
|
411
421
|
);
|
|
@@ -413,11 +423,6 @@ export class PXEService implements PXE {
|
|
|
413
423
|
}
|
|
414
424
|
|
|
415
425
|
public async addNullifiedNote(note: ExtendedNote) {
|
|
416
|
-
const owner = await this.db.getCompleteAddress(note.owner);
|
|
417
|
-
if (!owner) {
|
|
418
|
-
throw new Error(`Unknown account: ${note.owner.toString()}`);
|
|
419
|
-
}
|
|
420
|
-
|
|
421
426
|
const nonces = await this.#getNoteNonces(note);
|
|
422
427
|
if (nonces.length === 0) {
|
|
423
428
|
throw new Error(`Cannot find the note in tx: ${note.txHash}.`);
|
|
@@ -453,7 +458,7 @@ export class PXEService implements PXE {
|
|
|
453
458
|
noteHash,
|
|
454
459
|
Fr.ZERO, // We are not able to derive
|
|
455
460
|
index,
|
|
456
|
-
owner
|
|
461
|
+
computePoint(note.owner),
|
|
457
462
|
),
|
|
458
463
|
);
|
|
459
464
|
}
|
|
@@ -830,24 +835,25 @@ export class PXEService implements PXE {
|
|
|
830
835
|
|
|
831
836
|
public getEvents<T>(
|
|
832
837
|
type: EventType.Encrypted,
|
|
833
|
-
|
|
838
|
+
event: { eventSelector: EventSelector; abiType: AbiType; fieldNames: string[] },
|
|
834
839
|
from: number,
|
|
835
840
|
limit: number,
|
|
836
841
|
vpks: Point[],
|
|
837
842
|
): Promise<T[]>;
|
|
838
843
|
public getEvents<T>(
|
|
839
844
|
type: EventType.Unencrypted,
|
|
840
|
-
|
|
845
|
+
event: { eventSelector: EventSelector; abiType: AbiType; fieldNames: string[] },
|
|
841
846
|
from: number,
|
|
842
847
|
limit: number,
|
|
843
848
|
): Promise<T[]>;
|
|
844
849
|
public getEvents<T>(
|
|
845
850
|
type: EventType,
|
|
846
|
-
|
|
851
|
+
event: { eventSelector: EventSelector; abiType: AbiType; fieldNames: string[] },
|
|
847
852
|
from: number,
|
|
848
853
|
limit: number,
|
|
849
854
|
vpks: Point[] = [],
|
|
850
855
|
): Promise<T[]> {
|
|
856
|
+
const eventMetadata = new EventMetadata<T>(type, event);
|
|
851
857
|
if (type.includes(EventType.Encrypted)) {
|
|
852
858
|
return this.getEncryptedEvents(from, limit, eventMetadata, vpks);
|
|
853
859
|
}
|
|
@@ -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,
|
|
@@ -17,6 +18,7 @@ import {
|
|
|
17
18
|
IndexedTaggingSecret,
|
|
18
19
|
type KeyValidationRequest,
|
|
19
20
|
type L1_TO_L2_MSG_TREE_HEIGHT,
|
|
21
|
+
TaggingSecret,
|
|
20
22
|
computeTaggingSecret,
|
|
21
23
|
} from '@aztec/circuits.js';
|
|
22
24
|
import { type FunctionArtifact, getFunctionArtifact } from '@aztec/foundation/abi';
|
|
@@ -49,7 +51,7 @@ export class SimulatorOracle implements DBOracle {
|
|
|
49
51
|
if (!completeAddress) {
|
|
50
52
|
throw new Error(
|
|
51
53
|
`No public key registered for address ${account}.
|
|
52
|
-
Register it by calling pxe.
|
|
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`,
|
|
53
55
|
);
|
|
54
56
|
}
|
|
55
57
|
return completeAddress;
|
|
@@ -230,6 +232,16 @@ export class SimulatorOracle implements DBOracle {
|
|
|
230
232
|
return this.contractDataOracle.getDebugFunctionName(contractAddress, selector);
|
|
231
233
|
}
|
|
232
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
|
+
|
|
233
245
|
/**
|
|
234
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.
|
|
235
247
|
* Includes the last known index used for tagging with this secret.
|
|
@@ -243,13 +255,35 @@ export class SimulatorOracle implements DBOracle {
|
|
|
243
255
|
sender: AztecAddress,
|
|
244
256
|
recipient: AztecAddress,
|
|
245
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) {
|
|
246
279
|
const senderCompleteAddress = await this.getCompleteAddress(sender);
|
|
247
280
|
const senderIvsk = await this.keyStore.getMasterIncomingViewingSecretKey(sender);
|
|
248
281
|
const sharedSecret = computeTaggingSecret(senderCompleteAddress, senderIvsk, recipient);
|
|
249
282
|
// Silo the secret to the app so it can't be used to track other app's notes
|
|
250
|
-
const
|
|
251
|
-
|
|
252
|
-
|
|
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;
|
|
253
287
|
}
|
|
254
288
|
|
|
255
289
|
/**
|
|
@@ -263,18 +297,63 @@ export class SimulatorOracle implements DBOracle {
|
|
|
263
297
|
recipient: AztecAddress,
|
|
264
298
|
): Promise<IndexedTaggingSecret[]> {
|
|
265
299
|
const recipientCompleteAddress = await this.getCompleteAddress(recipient);
|
|
266
|
-
const completeAddresses = await this.db.getCompleteAddresses();
|
|
267
|
-
// Filter out the addresses corresponding to accounts
|
|
268
|
-
const accounts = await this.keyStore.getAccounts();
|
|
269
|
-
const senders = completeAddresses.filter(
|
|
270
|
-
completeAddress => !accounts.find(account => account.equals(completeAddress.address)),
|
|
271
|
-
);
|
|
272
300
|
const recipientIvsk = await this.keyStore.getMasterIncomingViewingSecretKey(recipient);
|
|
273
|
-
|
|
274
|
-
|
|
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);
|
|
275
306
|
return poseidon2Hash([sharedSecret.x, sharedSecret.y, contractAddress]);
|
|
276
307
|
});
|
|
277
|
-
|
|
278
|
-
|
|
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;
|
|
279
358
|
}
|
|
280
359
|
}
|
|
@@ -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
|
|
374
|
+
const addressPointToIncomingNotes: Map<PublicKey, IncomingNoteDao[]> = new Map();
|
|
375
375
|
for (const noteDao of notes) {
|
|
376
|
-
const
|
|
377
|
-
|
|
378
|
-
|
|
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
|
|
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) {
|