@aztec/pxe 0.61.0 → 0.63.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/config/index.d.ts +2 -3
- package/dest/config/index.d.ts.map +1 -1
- package/dest/config/index.js +4 -5
- package/dest/contract_data_oracle/index.d.ts +1 -0
- package/dest/contract_data_oracle/index.d.ts.map +1 -1
- package/dest/contract_data_oracle/index.js +5 -1
- package/dest/database/incoming_note_dao.d.ts +4 -4
- 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 +10 -14
- package/dest/database/kv_pxe_database.d.ts.map +1 -1
- package/dest/database/kv_pxe_database.js +82 -94
- package/dest/database/outgoing_note_dao.d.ts +2 -2
- package/dest/database/outgoing_note_dao.d.ts.map +1 -1
- package/dest/database/outgoing_note_dao.js +2 -2
- package/dest/database/pxe_database.d.ts +42 -20
- 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/kernel_oracle/index.d.ts +3 -3
- package/dest/kernel_oracle/index.d.ts.map +1 -1
- package/dest/kernel_prover/hints/build_private_kernel_reset_private_inputs.d.ts.map +1 -1
- package/dest/kernel_prover/hints/build_private_kernel_reset_private_inputs.js +12 -4
- package/dest/kernel_prover/kernel_prover.d.ts +3 -1
- package/dest/kernel_prover/kernel_prover.d.ts.map +1 -1
- package/dest/kernel_prover/kernel_prover.js +39 -7
- package/dest/kernel_prover/test/test_circuit_prover.d.ts +1 -0
- package/dest/kernel_prover/test/test_circuit_prover.d.ts.map +1 -1
- package/dest/kernel_prover/test/test_circuit_prover.js +5 -1
- package/dest/{note_processor/utils → note_decryption_utils}/add_public_values_to_payload.d.ts +1 -1
- package/dest/note_decryption_utils/add_public_values_to_payload.d.ts.map +1 -0
- package/dest/{note_processor/utils → note_decryption_utils}/add_public_values_to_payload.js +1 -1
- package/dest/note_decryption_utils/brute_force_note_info.d.ts.map +1 -0
- package/dest/{note_processor/utils → note_decryption_utils}/brute_force_note_info.js +1 -1
- package/dest/note_decryption_utils/index.d.ts.map +1 -0
- package/dest/{note_processor/utils → note_decryption_utils}/index.js +1 -1
- package/dest/{note_processor/utils → note_decryption_utils}/produce_note_daos.d.ts +6 -9
- package/dest/note_decryption_utils/produce_note_daos.d.ts.map +1 -0
- package/dest/note_decryption_utils/produce_note_daos.js +47 -0
- package/dest/note_decryption_utils/produce_note_daos_for_key.d.ts +8 -0
- package/dest/note_decryption_utils/produce_note_daos_for_key.d.ts.map +1 -0
- package/dest/note_decryption_utils/produce_note_daos_for_key.js +17 -0
- package/dest/pxe_http/pxe_http_server.d.ts +1 -2
- package/dest/pxe_http/pxe_http_server.d.ts.map +1 -1
- package/dest/pxe_http/pxe_http_server.js +5 -49
- package/dest/pxe_service/create_pxe_service.d.ts.map +1 -1
- package/dest/pxe_service/create_pxe_service.js +7 -4
- package/dest/pxe_service/error_enriching.d.ts.map +1 -1
- package/dest/pxe_service/error_enriching.js +7 -6
- package/dest/pxe_service/pxe_service.d.ts +13 -19
- package/dest/pxe_service/pxe_service.d.ts.map +1 -1
- package/dest/pxe_service/pxe_service.js +79 -79
- package/dest/pxe_service/test/pxe_test_suite.d.ts.map +1 -1
- package/dest/pxe_service/test/pxe_test_suite.js +5 -34
- package/dest/simulator_oracle/index.d.ts +32 -10
- package/dest/simulator_oracle/index.d.ts.map +1 -1
- package/dest/simulator_oracle/index.js +223 -30
- package/dest/synchronizer/synchronizer.d.ts +0 -48
- package/dest/synchronizer/synchronizer.d.ts.map +1 -1
- package/dest/synchronizer/synchronizer.js +3 -201
- package/package.json +16 -14
- package/src/config/index.ts +4 -7
- package/src/contract_data_oracle/index.ts +5 -0
- package/src/database/incoming_note_dao.ts +5 -5
- package/src/database/kv_pxe_database.ts +95 -106
- package/src/database/outgoing_note_dao.ts +3 -3
- package/src/database/pxe_database.ts +47 -22
- package/src/database/pxe_database_test_suite.ts +12 -20
- package/src/index.ts +1 -1
- package/src/kernel_prover/hints/build_private_kernel_reset_private_inputs.ts +13 -3
- package/src/kernel_prover/kernel_prover.ts +49 -5
- package/src/kernel_prover/test/test_circuit_prover.ts +8 -4
- package/src/{note_processor/utils → note_decryption_utils}/add_public_values_to_payload.ts +1 -1
- package/src/{note_processor/utils → note_decryption_utils}/produce_note_daos.ts +12 -22
- package/src/{note_processor/utils → note_decryption_utils}/produce_note_daos_for_key.ts +6 -15
- package/src/pxe_http/pxe_http_server.ts +5 -81
- package/src/pxe_service/create_pxe_service.ts +9 -3
- package/src/pxe_service/error_enriching.ts +12 -5
- package/src/pxe_service/pxe_service.ts +102 -113
- package/src/pxe_service/test/pxe_test_suite.ts +7 -55
- package/src/simulator_oracle/index.ts +322 -23
- package/src/synchronizer/synchronizer.ts +3 -253
- package/dest/database/deferred_note_dao.d.ts +0 -40
- package/dest/database/deferred_note_dao.d.ts.map +0 -1
- package/dest/database/deferred_note_dao.js +0 -38
- package/dest/note_processor/index.d.ts +0 -2
- package/dest/note_processor/index.d.ts.map +0 -1
- package/dest/note_processor/index.js +0 -2
- package/dest/note_processor/note_processor.d.ts +0 -83
- package/dest/note_processor/note_processor.d.ts.map +0 -1
- package/dest/note_processor/note_processor.js +0 -230
- package/dest/note_processor/utils/add_public_values_to_payload.d.ts.map +0 -1
- package/dest/note_processor/utils/brute_force_note_info.d.ts.map +0 -1
- package/dest/note_processor/utils/index.d.ts.map +0 -1
- package/dest/note_processor/utils/produce_note_daos.d.ts.map +0 -1
- package/dest/note_processor/utils/produce_note_daos.js +0 -51
- package/dest/note_processor/utils/produce_note_daos_for_key.d.ts +0 -9
- package/dest/note_processor/utils/produce_note_daos_for_key.d.ts.map +0 -1
- package/dest/note_processor/utils/produce_note_daos_for_key.js +0 -26
- package/src/database/deferred_note_dao.ts +0 -47
- package/src/note_processor/index.ts +0 -1
- package/src/note_processor/note_processor.ts +0 -355
- /package/dest/{note_processor/utils → note_decryption_utils}/brute_force_note_info.d.ts +0 -0
- /package/dest/{note_processor/utils → note_decryption_utils}/index.d.ts +0 -0
- /package/src/{note_processor/utils → note_decryption_utils}/brute_force_note_info.ts +0 -0
- /package/src/{note_processor/utils → note_decryption_utils}/index.ts +0 -0
|
@@ -1,32 +1,42 @@
|
|
|
1
1
|
import {
|
|
2
2
|
type AztecNode,
|
|
3
|
+
L1NotePayload,
|
|
3
4
|
type L2Block,
|
|
5
|
+
type L2BlockNumber,
|
|
4
6
|
MerkleTreeId,
|
|
5
7
|
type NoteStatus,
|
|
6
8
|
type NullifierMembershipWitness,
|
|
7
9
|
type PublicDataWitness,
|
|
10
|
+
type TxEffect,
|
|
11
|
+
type TxScopedL2Log,
|
|
8
12
|
getNonNullifiedL1ToL2MessageWitness,
|
|
9
13
|
} from '@aztec/circuit-types';
|
|
10
14
|
import {
|
|
11
15
|
type AztecAddress,
|
|
12
16
|
type CompleteAddress,
|
|
13
17
|
type ContractInstance,
|
|
14
|
-
|
|
18
|
+
Fr,
|
|
15
19
|
type FunctionSelector,
|
|
16
20
|
type Header,
|
|
17
21
|
IndexedTaggingSecret,
|
|
18
22
|
type KeyValidationRequest,
|
|
19
23
|
type L1_TO_L2_MSG_TREE_HEIGHT,
|
|
24
|
+
computeAddressSecret,
|
|
25
|
+
computePoint,
|
|
20
26
|
computeTaggingSecret,
|
|
21
27
|
} from '@aztec/circuits.js';
|
|
22
28
|
import { type FunctionArtifact, getFunctionArtifact } from '@aztec/foundation/abi';
|
|
23
29
|
import { poseidon2Hash } from '@aztec/foundation/crypto';
|
|
24
30
|
import { createDebugLogger } from '@aztec/foundation/log';
|
|
25
31
|
import { type KeyStore } from '@aztec/key-store';
|
|
26
|
-
import { type DBOracle, MessageLoadOracleInputs } from '@aztec/simulator';
|
|
32
|
+
import { type AcirSimulator, type DBOracle, MessageLoadOracleInputs } from '@aztec/simulator';
|
|
27
33
|
|
|
28
34
|
import { type ContractDataOracle } from '../contract_data_oracle/index.js';
|
|
35
|
+
import { type IncomingNoteDao } from '../database/incoming_note_dao.js';
|
|
29
36
|
import { type PxeDatabase } from '../database/index.js';
|
|
37
|
+
import { type OutgoingNoteDao } from '../database/outgoing_note_dao.js';
|
|
38
|
+
import { produceNoteDaos } from '../note_decryption_utils/produce_note_daos.js';
|
|
39
|
+
import { getAcirSimulator } from '../simulator/index.js';
|
|
30
40
|
|
|
31
41
|
/**
|
|
32
42
|
* A data oracle that provides information needed for simulating a transaction.
|
|
@@ -49,7 +59,7 @@ export class SimulatorOracle implements DBOracle {
|
|
|
49
59
|
if (!completeAddress) {
|
|
50
60
|
throw new Error(
|
|
51
61
|
`No public key registered for address ${account}.
|
|
52
|
-
Register it by calling pxe.
|
|
62
|
+
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
63
|
);
|
|
54
64
|
}
|
|
55
65
|
return completeAddress;
|
|
@@ -151,7 +161,7 @@ export class SimulatorOracle implements DBOracle {
|
|
|
151
161
|
* @returns - The index of the commitment. Undefined if it does not exist in the tree.
|
|
152
162
|
*/
|
|
153
163
|
async getCommitmentIndex(commitment: Fr) {
|
|
154
|
-
return await this.
|
|
164
|
+
return await this.findLeafIndex('latest', MerkleTreeId.NOTE_HASH_TREE, commitment);
|
|
155
165
|
}
|
|
156
166
|
|
|
157
167
|
// We need this in public as part of the EXISTS calls - but isn't used in private
|
|
@@ -160,11 +170,16 @@ export class SimulatorOracle implements DBOracle {
|
|
|
160
170
|
}
|
|
161
171
|
|
|
162
172
|
async getNullifierIndex(nullifier: Fr) {
|
|
163
|
-
return await this.
|
|
173
|
+
return await this.findLeafIndex('latest', MerkleTreeId.NULLIFIER_TREE, nullifier);
|
|
164
174
|
}
|
|
165
175
|
|
|
166
|
-
public async findLeafIndex(
|
|
167
|
-
|
|
176
|
+
public async findLeafIndex(
|
|
177
|
+
blockNumber: L2BlockNumber,
|
|
178
|
+
treeId: MerkleTreeId,
|
|
179
|
+
leafValue: Fr,
|
|
180
|
+
): Promise<bigint | undefined> {
|
|
181
|
+
const [leafIndex] = await this.aztecNode.findLeavesIndexes(blockNumber, treeId, [leafValue]);
|
|
182
|
+
return leafIndex;
|
|
168
183
|
}
|
|
169
184
|
|
|
170
185
|
public async getSiblingPath(blockNumber: number, treeId: MerkleTreeId, leafIndex: bigint): Promise<Fr[]> {
|
|
@@ -230,51 +245,335 @@ export class SimulatorOracle implements DBOracle {
|
|
|
230
245
|
return this.contractDataOracle.getDebugFunctionName(contractAddress, selector);
|
|
231
246
|
}
|
|
232
247
|
|
|
248
|
+
/**
|
|
249
|
+
* Returns the full contents of your address book.
|
|
250
|
+
* This is used when calculating tags for incoming notes by deriving the shared secret, the contract-siloed tagging secret, and
|
|
251
|
+
* finally the index specified tag. We will then query the node with this tag for each address in the address book.
|
|
252
|
+
* @returns The full list of the users contact addresses.
|
|
253
|
+
*/
|
|
254
|
+
public getContacts(): AztecAddress[] {
|
|
255
|
+
return this.db.getContactAddresses();
|
|
256
|
+
}
|
|
257
|
+
|
|
233
258
|
/**
|
|
234
259
|
* 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
|
-
* Includes the
|
|
260
|
+
* Includes the next index to be used used for tagging with this secret.
|
|
236
261
|
* @param contractAddress - The contract address to silo the secret for
|
|
237
262
|
* @param sender - The address sending the note
|
|
238
263
|
* @param recipient - The address receiving the note
|
|
239
264
|
* @returns A siloed tagging secret that can be used to tag notes.
|
|
240
265
|
*/
|
|
241
|
-
public async
|
|
266
|
+
public async getAppTaggingSecretAsSender(
|
|
242
267
|
contractAddress: AztecAddress,
|
|
243
268
|
sender: AztecAddress,
|
|
244
269
|
recipient: AztecAddress,
|
|
245
270
|
): Promise<IndexedTaggingSecret> {
|
|
271
|
+
const secret = await this.#calculateTaggingSecret(contractAddress, sender, recipient);
|
|
272
|
+
const [index] = await this.db.getTaggingSecretsIndexesAsSender([secret]);
|
|
273
|
+
return new IndexedTaggingSecret(secret, index);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Increments the tagging secret for a given sender and recipient pair. For this to work, the ivpsk_m of the sender must be known.
|
|
278
|
+
* @param contractAddress - The contract address to silo the secret for
|
|
279
|
+
* @param sender - The address sending the note
|
|
280
|
+
* @param recipient - The address receiving the note
|
|
281
|
+
*/
|
|
282
|
+
public async incrementAppTaggingSecretIndexAsSender(
|
|
283
|
+
contractAddress: AztecAddress,
|
|
284
|
+
sender: AztecAddress,
|
|
285
|
+
recipient: AztecAddress,
|
|
286
|
+
): Promise<void> {
|
|
287
|
+
const secret = await this.#calculateTaggingSecret(contractAddress, sender, recipient);
|
|
288
|
+
const contractName = await this.contractDataOracle.getDebugContractName(contractAddress);
|
|
289
|
+
this.log.verbose(
|
|
290
|
+
`Incrementing secret ${secret} as sender ${sender} for recipient: ${recipient} at contract: ${contractName}(${contractAddress})`,
|
|
291
|
+
);
|
|
292
|
+
await this.db.incrementTaggingSecretsIndexesAsSender([secret]);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
async #calculateTaggingSecret(contractAddress: AztecAddress, sender: AztecAddress, recipient: AztecAddress) {
|
|
246
296
|
const senderCompleteAddress = await this.getCompleteAddress(sender);
|
|
247
297
|
const senderIvsk = await this.keyStore.getMasterIncomingViewingSecretKey(sender);
|
|
248
298
|
const sharedSecret = computeTaggingSecret(senderCompleteAddress, senderIvsk, recipient);
|
|
249
299
|
// Silo the secret to the app so it can't be used to track other app's notes
|
|
250
|
-
const
|
|
251
|
-
|
|
252
|
-
return new IndexedTaggingSecret(secret, index);
|
|
300
|
+
const siloedSecret = poseidon2Hash([sharedSecret.x, sharedSecret.y, contractAddress]);
|
|
301
|
+
return siloedSecret;
|
|
253
302
|
}
|
|
254
303
|
|
|
255
304
|
/**
|
|
256
305
|
* Returns the siloed tagging secrets for a given recipient and all the senders in the address book
|
|
306
|
+
* This method should be exposed as an oracle call to allow aztec.nr to perform the orchestration
|
|
307
|
+
* of the syncTaggedLogs and processTaggedLogs methods. However, it is not possible to do so at the moment,
|
|
308
|
+
* so we're keeping it private for now.
|
|
257
309
|
* @param contractAddress - The contract address to silo the secret for
|
|
258
310
|
* @param recipient - The address receiving the notes
|
|
259
311
|
* @returns A list of siloed tagging secrets
|
|
260
312
|
*/
|
|
261
|
-
|
|
313
|
+
async #getAppTaggingSecretsForContacts(
|
|
262
314
|
contractAddress: AztecAddress,
|
|
263
315
|
recipient: AztecAddress,
|
|
264
316
|
): Promise<IndexedTaggingSecret[]> {
|
|
265
317
|
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
318
|
const recipientIvsk = await this.keyStore.getMasterIncomingViewingSecretKey(recipient);
|
|
273
|
-
|
|
274
|
-
|
|
319
|
+
|
|
320
|
+
// We implicitly add all PXE accounts as contacts, this helps us decrypt tags on notes that we send to ourselves (recipient = us, sender = us)
|
|
321
|
+
const contacts = [...this.db.getContactAddresses(), ...(await this.keyStore.getAccounts())].filter(
|
|
322
|
+
(address, index, self) => index === self.findIndex(otherAddress => otherAddress.equals(address)),
|
|
323
|
+
);
|
|
324
|
+
const appTaggingSecrets = contacts.map(contact => {
|
|
325
|
+
const sharedSecret = computeTaggingSecret(recipientCompleteAddress, recipientIvsk, contact);
|
|
275
326
|
return poseidon2Hash([sharedSecret.x, sharedSecret.y, contractAddress]);
|
|
276
327
|
});
|
|
277
|
-
const indexes = await this.db.
|
|
278
|
-
return
|
|
328
|
+
const indexes = await this.db.getTaggingSecretsIndexesAsRecipient(appTaggingSecrets);
|
|
329
|
+
return appTaggingSecrets.map((secret, i) => new IndexedTaggingSecret(secret, indexes[i]));
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Synchronizes the logs tagged with scoped addresses and all the senders in the addressbook.
|
|
334
|
+
* Returns the unsynched logs and updates the indexes of the secrets used to tag them until there are no more logs to sync.
|
|
335
|
+
* @param contractAddress - The address of the contract that the logs are tagged for
|
|
336
|
+
* @param recipient - The address of the recipient
|
|
337
|
+
* @returns A list of encrypted logs tagged with the recipient's address
|
|
338
|
+
*/
|
|
339
|
+
public async syncTaggedLogs(
|
|
340
|
+
contractAddress: AztecAddress,
|
|
341
|
+
maxBlockNumber: number,
|
|
342
|
+
scopes?: AztecAddress[],
|
|
343
|
+
): Promise<Map<string, TxScopedL2Log[]>> {
|
|
344
|
+
const recipients = scopes ? scopes : await this.keyStore.getAccounts();
|
|
345
|
+
const result = new Map<string, TxScopedL2Log[]>();
|
|
346
|
+
const contractName = await this.contractDataOracle.getDebugContractName(contractAddress);
|
|
347
|
+
for (const recipient of recipients) {
|
|
348
|
+
const logs: TxScopedL2Log[] = [];
|
|
349
|
+
// Ideally this algorithm would be implemented in noir, exposing its building blocks as oracles.
|
|
350
|
+
// However it is impossible at the moment due to the language not supporting nested slices.
|
|
351
|
+
// This nesting is necessary because for a given set of tags we don't
|
|
352
|
+
// know how many logs we will get back. Furthermore, these logs are of undetermined
|
|
353
|
+
// length, since we don't really know the note they correspond to until we decrypt them.
|
|
354
|
+
|
|
355
|
+
// 1. Get all the secrets for the recipient and sender pairs (#9365)
|
|
356
|
+
const appTaggingSecrets = await this.#getAppTaggingSecretsForContacts(contractAddress, recipient);
|
|
357
|
+
|
|
358
|
+
// 1.1 Set up a sliding window with an offset. Chances are the sender might have messed up
|
|
359
|
+
// and inadvertedly incremented their index without use getting any logs (for example, in case
|
|
360
|
+
// of a revert). If we stopped looking for logs the first time
|
|
361
|
+
// we receive 0 for a tag, we might never receive anything from that sender again.
|
|
362
|
+
// Also there's a possibility that we have advanced our index, but the sender has reused it, so
|
|
363
|
+
// we might have missed some logs. For these reasons, we have to look both back and ahead of the
|
|
364
|
+
// stored index
|
|
365
|
+
const INDEX_OFFSET = 10;
|
|
366
|
+
type SearchState = {
|
|
367
|
+
currentTagggingSecrets: IndexedTaggingSecret[];
|
|
368
|
+
maxIndexesToCheck: { [k: string]: number };
|
|
369
|
+
initialSecretIndexes: { [k: string]: number };
|
|
370
|
+
secretsToIncrement: { [k: string]: number };
|
|
371
|
+
};
|
|
372
|
+
const searchState = appTaggingSecrets.reduce<SearchState>(
|
|
373
|
+
(acc, appTaggingSecret) => ({
|
|
374
|
+
// Start looking for logs before the stored index
|
|
375
|
+
currentTagggingSecrets: acc.currentTagggingSecrets.concat([
|
|
376
|
+
new IndexedTaggingSecret(appTaggingSecret.secret, Math.max(0, appTaggingSecret.index - INDEX_OFFSET)),
|
|
377
|
+
]),
|
|
378
|
+
// Keep looking for logs beyond the stored index
|
|
379
|
+
maxIndexesToCheck: {
|
|
380
|
+
...acc.maxIndexesToCheck,
|
|
381
|
+
...{ [appTaggingSecret.secret.toString()]: appTaggingSecret.index + INDEX_OFFSET },
|
|
382
|
+
},
|
|
383
|
+
// Keeps track of the secrets we have to increment in the database
|
|
384
|
+
secretsToIncrement: {},
|
|
385
|
+
// Store the initial set of indexes for the secrets
|
|
386
|
+
initialSecretIndexes: {
|
|
387
|
+
...acc.initialSecretIndexes,
|
|
388
|
+
...{ [appTaggingSecret.secret.toString()]: appTaggingSecret.index },
|
|
389
|
+
},
|
|
390
|
+
}),
|
|
391
|
+
{ currentTagggingSecrets: [], maxIndexesToCheck: {}, secretsToIncrement: {}, initialSecretIndexes: {} },
|
|
392
|
+
);
|
|
393
|
+
|
|
394
|
+
let { currentTagggingSecrets } = searchState;
|
|
395
|
+
const { maxIndexesToCheck, secretsToIncrement, initialSecretIndexes } = searchState;
|
|
396
|
+
|
|
397
|
+
while (currentTagggingSecrets.length > 0) {
|
|
398
|
+
// 2. Compute tags using the secrets, recipient and index. Obtain logs for each tag (#9380)
|
|
399
|
+
const currentTags = currentTagggingSecrets.map(taggingSecret => taggingSecret.computeTag(recipient));
|
|
400
|
+
const logsByTags = await this.aztecNode.getLogsByTags(currentTags);
|
|
401
|
+
const newTaggingSecrets: IndexedTaggingSecret[] = [];
|
|
402
|
+
logsByTags.forEach((logsByTag, logIndex) => {
|
|
403
|
+
const { secret: currentSecret, index: currentIndex } = currentTagggingSecrets[logIndex];
|
|
404
|
+
const currentSecretAsStr = currentSecret.toString();
|
|
405
|
+
this.log.debug(
|
|
406
|
+
`Syncing logs for recipient ${recipient}, secret ${currentSecretAsStr}:${currentIndex} at contract: ${contractName}(${contractAddress})`,
|
|
407
|
+
);
|
|
408
|
+
// 3.1. Append logs to the list and increment the index for the tags that have logs (#9380)
|
|
409
|
+
if (logsByTag.length > 0) {
|
|
410
|
+
this.log.verbose(
|
|
411
|
+
`Found ${
|
|
412
|
+
logsByTag.length
|
|
413
|
+
} logs for secret ${currentSecretAsStr} as recipient ${recipient}. Incrementing index to ${
|
|
414
|
+
currentIndex + 1
|
|
415
|
+
} at contract: ${contractName}(${contractAddress})`,
|
|
416
|
+
);
|
|
417
|
+
logs.push(...logsByTag);
|
|
418
|
+
|
|
419
|
+
if (currentIndex >= initialSecretIndexes[currentSecretAsStr]) {
|
|
420
|
+
// 3.2. Increment the index for the tags that have logs, provided they're higher than the one
|
|
421
|
+
// we have stored in the db (#9380)
|
|
422
|
+
secretsToIncrement[currentSecretAsStr] = currentIndex + 1;
|
|
423
|
+
// 3.3. Slide the window forwards if we have found logs beyond the initial index
|
|
424
|
+
maxIndexesToCheck[currentSecretAsStr] = currentIndex + INDEX_OFFSET;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
// 3.4 Keep increasing the index (inside the window) temporarily for the tags that have no logs
|
|
428
|
+
// There's a chance the sender missed some and we want to catch up
|
|
429
|
+
if (currentIndex < maxIndexesToCheck[currentSecretAsStr]) {
|
|
430
|
+
const newTaggingSecret = new IndexedTaggingSecret(currentSecret, currentIndex + 1);
|
|
431
|
+
newTaggingSecrets.push(newTaggingSecret);
|
|
432
|
+
}
|
|
433
|
+
});
|
|
434
|
+
await this.db.setTaggingSecretsIndexesAsRecipient(
|
|
435
|
+
Object.keys(secretsToIncrement).map(
|
|
436
|
+
secret => new IndexedTaggingSecret(Fr.fromString(secret), secretsToIncrement[secret]),
|
|
437
|
+
),
|
|
438
|
+
);
|
|
439
|
+
currentTagggingSecrets = newTaggingSecrets;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
result.set(
|
|
443
|
+
recipient.toString(),
|
|
444
|
+
logs.filter(log => log.blockNumber <= maxBlockNumber),
|
|
445
|
+
);
|
|
446
|
+
}
|
|
447
|
+
return result;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* Decrypts logs tagged for a recipient and returns them.
|
|
452
|
+
* @param scopedLogs - The logs to decrypt.
|
|
453
|
+
* @param recipient - The recipient of the logs.
|
|
454
|
+
* @param simulator - The simulator to use for decryption.
|
|
455
|
+
* @returns The decrypted notes.
|
|
456
|
+
*/
|
|
457
|
+
async #decryptTaggedLogs(scopedLogs: TxScopedL2Log[], recipient: AztecAddress, simulator?: AcirSimulator) {
|
|
458
|
+
const recipientCompleteAddress = await this.getCompleteAddress(recipient);
|
|
459
|
+
const ivskM = await this.keyStore.getMasterSecretKey(
|
|
460
|
+
recipientCompleteAddress.publicKeys.masterIncomingViewingPublicKey,
|
|
461
|
+
);
|
|
462
|
+
const addressSecret = computeAddressSecret(recipientCompleteAddress.getPreaddress(), ivskM);
|
|
463
|
+
const ovskM = await this.keyStore.getMasterSecretKey(
|
|
464
|
+
recipientCompleteAddress.publicKeys.masterOutgoingViewingPublicKey,
|
|
465
|
+
);
|
|
466
|
+
// Since we could have notes with the same index for different txs, we need
|
|
467
|
+
// to keep track of them scoping by txHash
|
|
468
|
+
const excludedIndices: Map<string, Set<number>> = new Map();
|
|
469
|
+
const incomingNotes: IncomingNoteDao[] = [];
|
|
470
|
+
const outgoingNotes: OutgoingNoteDao[] = [];
|
|
471
|
+
|
|
472
|
+
const txEffectsCache = new Map<string, TxEffect | undefined>();
|
|
473
|
+
|
|
474
|
+
for (const scopedLog of scopedLogs) {
|
|
475
|
+
const incomingNotePayload = L1NotePayload.decryptAsIncoming(
|
|
476
|
+
scopedLog.logData,
|
|
477
|
+
addressSecret,
|
|
478
|
+
scopedLog.isFromPublic,
|
|
479
|
+
);
|
|
480
|
+
const outgoingNotePayload = L1NotePayload.decryptAsOutgoing(scopedLog.logData, ovskM, scopedLog.isFromPublic);
|
|
481
|
+
|
|
482
|
+
if (incomingNotePayload || outgoingNotePayload) {
|
|
483
|
+
if (incomingNotePayload && outgoingNotePayload && !incomingNotePayload.equals(outgoingNotePayload)) {
|
|
484
|
+
this.log.warn(
|
|
485
|
+
`Incoming and outgoing note payloads do not match. Incoming: ${JSON.stringify(
|
|
486
|
+
incomingNotePayload,
|
|
487
|
+
)}, Outgoing: ${JSON.stringify(outgoingNotePayload)}`,
|
|
488
|
+
);
|
|
489
|
+
continue;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
const payload = incomingNotePayload || outgoingNotePayload;
|
|
493
|
+
|
|
494
|
+
const txEffect =
|
|
495
|
+
txEffectsCache.get(scopedLog.txHash.toString()) ?? (await this.aztecNode.getTxEffect(scopedLog.txHash));
|
|
496
|
+
|
|
497
|
+
if (!txEffect) {
|
|
498
|
+
this.log.warn(`No tx effect found for ${scopedLog.txHash} while decrypting tagged logs`);
|
|
499
|
+
continue;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
txEffectsCache.set(scopedLog.txHash.toString(), txEffect);
|
|
503
|
+
|
|
504
|
+
if (!excludedIndices.has(scopedLog.txHash.toString())) {
|
|
505
|
+
excludedIndices.set(scopedLog.txHash.toString(), new Set());
|
|
506
|
+
}
|
|
507
|
+
const { incomingNote, outgoingNote } = await produceNoteDaos(
|
|
508
|
+
// I don't like this at all, but we need a simulator to run `computeNoteHashAndOptionallyANullifier`. This generates
|
|
509
|
+
// a chicken-and-egg problem due to this oracle requiring a simulator, which in turn requires this oracle. Furthermore, since jest doesn't allow
|
|
510
|
+
// mocking ESM exports, we have to pollute the method even more by providing a simulator parameter so tests can inject a fake one.
|
|
511
|
+
simulator ?? getAcirSimulator(this.db, this.aztecNode, this.keyStore, this.contractDataOracle),
|
|
512
|
+
this.db,
|
|
513
|
+
incomingNotePayload ? computePoint(recipient) : undefined,
|
|
514
|
+
outgoingNotePayload ? recipientCompleteAddress.publicKeys.masterOutgoingViewingPublicKey : undefined,
|
|
515
|
+
payload!,
|
|
516
|
+
txEffect.txHash,
|
|
517
|
+
txEffect.noteHashes,
|
|
518
|
+
scopedLog.dataStartIndexForTx,
|
|
519
|
+
excludedIndices.get(scopedLog.txHash.toString())!,
|
|
520
|
+
this.log,
|
|
521
|
+
);
|
|
522
|
+
|
|
523
|
+
if (incomingNote) {
|
|
524
|
+
incomingNotes.push(incomingNote);
|
|
525
|
+
}
|
|
526
|
+
if (outgoingNote) {
|
|
527
|
+
outgoingNotes.push(outgoingNote);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
return { incomingNotes, outgoingNotes };
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
/**
|
|
535
|
+
* Processes the tagged logs returned by syncTaggedLogs by decrypting them and storing them in the database.
|
|
536
|
+
* @param logs - The logs to process.
|
|
537
|
+
* @param recipient - The recipient of the logs.
|
|
538
|
+
*/
|
|
539
|
+
public async processTaggedLogs(
|
|
540
|
+
logs: TxScopedL2Log[],
|
|
541
|
+
recipient: AztecAddress,
|
|
542
|
+
simulator?: AcirSimulator,
|
|
543
|
+
): Promise<void> {
|
|
544
|
+
const { incomingNotes, outgoingNotes } = await this.#decryptTaggedLogs(logs, recipient, simulator);
|
|
545
|
+
if (incomingNotes.length || outgoingNotes.length) {
|
|
546
|
+
await this.db.addNotes(incomingNotes, outgoingNotes, recipient);
|
|
547
|
+
incomingNotes.forEach(noteDao => {
|
|
548
|
+
this.log.verbose(
|
|
549
|
+
`Added incoming note for contract ${noteDao.contractAddress} at slot ${
|
|
550
|
+
noteDao.storageSlot
|
|
551
|
+
} with nullifier ${noteDao.siloedNullifier.toString()}`,
|
|
552
|
+
);
|
|
553
|
+
});
|
|
554
|
+
outgoingNotes.forEach(noteDao => {
|
|
555
|
+
this.log.verbose(`Added outgoing note for contract ${noteDao.contractAddress} at slot ${noteDao.storageSlot}`);
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
const nullifiedNotes: IncomingNoteDao[] = [];
|
|
559
|
+
const currentNotesForRecipient = await this.db.getIncomingNotes({ owner: recipient });
|
|
560
|
+
const nullifierIndexes = await this.aztecNode.findLeavesIndexes(
|
|
561
|
+
'latest',
|
|
562
|
+
MerkleTreeId.NULLIFIER_TREE,
|
|
563
|
+
currentNotesForRecipient.map(note => note.siloedNullifier),
|
|
564
|
+
);
|
|
565
|
+
|
|
566
|
+
const foundNullifiers = currentNotesForRecipient
|
|
567
|
+
.filter((_, i) => nullifierIndexes[i] !== undefined)
|
|
568
|
+
.map(note => note.siloedNullifier);
|
|
569
|
+
|
|
570
|
+
await this.db.removeNullifiedNotes(foundNullifiers, computePoint(recipient));
|
|
571
|
+
nullifiedNotes.forEach(noteDao => {
|
|
572
|
+
this.log.verbose(
|
|
573
|
+
`Removed note for contract ${noteDao.contractAddress} at slot ${
|
|
574
|
+
noteDao.storageSlot
|
|
575
|
+
} with nullifier ${noteDao.siloedNullifier.toString()}`,
|
|
576
|
+
);
|
|
577
|
+
});
|
|
279
578
|
}
|
|
280
579
|
}
|