@aztec/pxe 0.66.0 → 0.67.1-devnet
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/bin/index.js +5 -6
- package/dest/config/index.d.ts +0 -9
- package/dest/config/index.d.ts.map +1 -1
- package/dest/config/index.js +1 -17
- package/dest/config/package_info.d.ts +5 -0
- package/dest/config/package_info.d.ts.map +1 -0
- package/dest/config/package_info.js +4 -0
- package/dest/contract_data_oracle/index.js +2 -2
- package/dest/database/incoming_note_dao.d.ts +1 -1
- package/dest/database/incoming_note_dao.d.ts.map +1 -1
- package/dest/database/incoming_note_dao.js +2 -2
- package/dest/database/kv_pxe_database.d.ts +10 -13
- package/dest/database/kv_pxe_database.d.ts.map +1 -1
- package/dest/database/kv_pxe_database.js +153 -230
- package/dest/database/outgoing_note_dao.js +2 -2
- package/dest/database/pxe_database.d.ts +7 -25
- 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 +18 -59
- package/dest/index.d.ts +2 -0
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +3 -1
- package/dest/kernel_oracle/index.d.ts.map +1 -1
- package/dest/kernel_oracle/index.js +4 -4
- package/dest/kernel_prover/hints/build_private_kernel_reset_private_inputs.js +2 -2
- package/dest/kernel_prover/index.d.ts +1 -0
- package/dest/kernel_prover/index.d.ts.map +1 -1
- package/dest/kernel_prover/index.js +2 -1
- package/dest/kernel_prover/kernel_prover.d.ts +1 -0
- package/dest/kernel_prover/kernel_prover.d.ts.map +1 -1
- package/dest/kernel_prover/kernel_prover.js +42 -5
- package/dest/kernel_prover/test/test_circuit_prover.d.ts +1 -2
- package/dest/kernel_prover/test/test_circuit_prover.d.ts.map +1 -1
- package/dest/kernel_prover/test/test_circuit_prover.js +7 -13
- package/dest/note_decryption_utils/add_public_values_to_payload.js +2 -2
- package/dest/note_decryption_utils/brute_force_note_info.d.ts +3 -3
- package/dest/note_decryption_utils/brute_force_note_info.d.ts.map +1 -1
- package/dest/note_decryption_utils/brute_force_note_info.js +8 -8
- package/dest/note_decryption_utils/produce_note_daos.d.ts +3 -6
- package/dest/note_decryption_utils/produce_note_daos.d.ts.map +1 -1
- package/dest/note_decryption_utils/produce_note_daos.js +5 -19
- package/dest/note_decryption_utils/produce_note_daos_for_key.d.ts +1 -1
- package/dest/note_decryption_utils/produce_note_daos_for_key.d.ts.map +1 -1
- package/dest/pxe_service/error_enriching.d.ts +3 -3
- package/dest/pxe_service/error_enriching.d.ts.map +1 -1
- package/dest/pxe_service/error_enriching.js +10 -10
- package/dest/pxe_service/index.d.ts +0 -1
- package/dest/pxe_service/index.d.ts.map +1 -1
- package/dest/pxe_service/index.js +1 -2
- package/dest/pxe_service/pxe_service.d.ts +2 -16
- package/dest/pxe_service/pxe_service.d.ts.map +1 -1
- package/dest/pxe_service/pxe_service.js +74 -96
- package/dest/pxe_service/test/pxe_test_suite.d.ts.map +1 -1
- package/dest/pxe_service/test/pxe_test_suite.js +1 -3
- package/dest/simulator/index.d.ts +1 -1
- package/dest/simulator/index.d.ts.map +1 -1
- package/dest/simulator/index.js +2 -2
- package/dest/simulator_oracle/index.d.ts +14 -11
- package/dest/simulator_oracle/index.d.ts.map +1 -1
- package/dest/simulator_oracle/index.js +170 -140
- package/dest/simulator_oracle/tagging_utils.d.ts +15 -0
- package/dest/simulator_oracle/tagging_utils.d.ts.map +1 -0
- package/dest/simulator_oracle/tagging_utils.js +23 -0
- package/dest/synchronizer/synchronizer.d.ts +10 -40
- package/dest/synchronizer/synchronizer.d.ts.map +1 -1
- package/dest/synchronizer/synchronizer.js +35 -69
- package/dest/{pxe_service → utils}/create_pxe_service.d.ts +1 -1
- package/dest/utils/create_pxe_service.d.ts.map +1 -0
- package/dest/utils/create_pxe_service.js +52 -0
- package/package.json +31 -19
- package/src/bin/index.ts +4 -5
- package/src/config/index.ts +0 -21
- package/src/config/package_info.ts +3 -0
- package/src/contract_data_oracle/index.ts +1 -1
- package/src/database/incoming_note_dao.ts +2 -2
- package/src/database/kv_pxe_database.ts +214 -309
- package/src/database/outgoing_note_dao.ts +1 -1
- package/src/database/pxe_database.ts +7 -28
- package/src/database/pxe_database_test_suite.ts +20 -75
- package/src/index.ts +2 -0
- package/src/kernel_oracle/index.ts +3 -3
- package/src/kernel_prover/hints/build_private_kernel_reset_private_inputs.ts +1 -1
- package/src/kernel_prover/index.ts +2 -0
- package/src/kernel_prover/kernel_prover.ts +72 -3
- package/src/kernel_prover/test/test_circuit_prover.ts +11 -25
- package/src/note_decryption_utils/add_public_values_to_payload.ts +1 -1
- package/src/note_decryption_utils/brute_force_note_info.ts +9 -9
- package/src/note_decryption_utils/produce_note_daos.ts +5 -48
- package/src/note_decryption_utils/produce_note_daos_for_key.ts +1 -1
- package/src/pxe_service/error_enriching.ts +14 -12
- package/src/pxe_service/index.ts +0 -1
- package/src/pxe_service/pxe_service.ts +127 -174
- package/src/pxe_service/test/pxe_test_suite.ts +0 -3
- package/src/simulator/index.ts +1 -1
- package/src/simulator_oracle/index.ts +201 -188
- package/src/simulator_oracle/tagging_utils.ts +28 -0
- package/src/synchronizer/synchronizer.ts +37 -77
- package/src/{pxe_service → utils}/create_pxe_service.ts +16 -13
- package/dest/pxe_service/create_pxe_service.d.ts.map +0 -1
- package/dest/pxe_service/create_pxe_service.js +0 -49
|
@@ -14,31 +14,31 @@ import {
|
|
|
14
14
|
} from '@aztec/circuit-types';
|
|
15
15
|
import {
|
|
16
16
|
type AztecAddress,
|
|
17
|
+
type BlockHeader,
|
|
17
18
|
type CompleteAddress,
|
|
18
19
|
type ContractInstance,
|
|
19
20
|
Fr,
|
|
20
21
|
type FunctionSelector,
|
|
21
|
-
type Header,
|
|
22
22
|
IndexedTaggingSecret,
|
|
23
23
|
type KeyValidationRequest,
|
|
24
24
|
type L1_TO_L2_MSG_TREE_HEIGHT,
|
|
25
25
|
PrivateLog,
|
|
26
26
|
computeAddressSecret,
|
|
27
|
-
|
|
27
|
+
computeTaggingSecretPoint,
|
|
28
28
|
} from '@aztec/circuits.js';
|
|
29
29
|
import { type FunctionArtifact, getFunctionArtifact } from '@aztec/foundation/abi';
|
|
30
30
|
import { poseidon2Hash } from '@aztec/foundation/crypto';
|
|
31
|
-
import {
|
|
32
|
-
import { createDebugLogger } from '@aztec/foundation/log';
|
|
31
|
+
import { createLogger } from '@aztec/foundation/log';
|
|
33
32
|
import { type KeyStore } from '@aztec/key-store';
|
|
34
|
-
import {
|
|
33
|
+
import { MessageLoadOracleInputs } from '@aztec/simulator/acvm';
|
|
34
|
+
import { type AcirSimulator, type DBOracle } from '@aztec/simulator/client';
|
|
35
35
|
|
|
36
36
|
import { type ContractDataOracle } from '../contract_data_oracle/index.js';
|
|
37
37
|
import { type IncomingNoteDao } from '../database/incoming_note_dao.js';
|
|
38
38
|
import { type PxeDatabase } from '../database/index.js';
|
|
39
|
-
import { type OutgoingNoteDao } from '../database/outgoing_note_dao.js';
|
|
40
39
|
import { produceNoteDaos } from '../note_decryption_utils/produce_note_daos.js';
|
|
41
40
|
import { getAcirSimulator } from '../simulator/index.js';
|
|
41
|
+
import { getIndexedTaggingSecretsForTheWindow, getInitialIndexesMap } from './tagging_utils.js';
|
|
42
42
|
|
|
43
43
|
/**
|
|
44
44
|
* A data oracle that provides information needed for simulating a transaction.
|
|
@@ -49,7 +49,7 @@ export class SimulatorOracle implements DBOracle {
|
|
|
49
49
|
private db: PxeDatabase,
|
|
50
50
|
private keyStore: KeyStore,
|
|
51
51
|
private aztecNode: AztecNode,
|
|
52
|
-
private log =
|
|
52
|
+
private log = createLogger('pxe:simulator_oracle'),
|
|
53
53
|
) {}
|
|
54
54
|
|
|
55
55
|
getKeyValidationRequest(pkMHash: Fr, contractAddress: AztecAddress): Promise<KeyValidationRequest> {
|
|
@@ -229,10 +229,10 @@ export class SimulatorOracle implements DBOracle {
|
|
|
229
229
|
* Retrieve the databases view of the Block Header object.
|
|
230
230
|
* This structure is fed into the circuits simulator and is used to prove against certain historical roots.
|
|
231
231
|
*
|
|
232
|
-
* @returns A Promise that resolves to a
|
|
232
|
+
* @returns A Promise that resolves to a BlockHeader object.
|
|
233
233
|
*/
|
|
234
|
-
|
|
235
|
-
return
|
|
234
|
+
getBlockHeader(): Promise<BlockHeader> {
|
|
235
|
+
return this.db.getBlockHeader();
|
|
236
236
|
}
|
|
237
237
|
|
|
238
238
|
/**
|
|
@@ -253,33 +253,33 @@ export class SimulatorOracle implements DBOracle {
|
|
|
253
253
|
* finally the index specified tag. We will then query the node with this tag for each address in the address book.
|
|
254
254
|
* @returns The full list of the users contact addresses.
|
|
255
255
|
*/
|
|
256
|
-
public getContacts(): AztecAddress[] {
|
|
256
|
+
public getContacts(): Promise<AztecAddress[]> {
|
|
257
257
|
return this.db.getContactAddresses();
|
|
258
258
|
}
|
|
259
259
|
|
|
260
260
|
/**
|
|
261
|
-
* Returns the tagging secret for a given sender and recipient pair. For this to work, the
|
|
261
|
+
* Returns the tagging secret for a given sender and recipient pair. For this to work, the ivsk_m of the sender must be known.
|
|
262
262
|
* Includes the next index to be used used for tagging with this secret.
|
|
263
263
|
* @param contractAddress - The contract address to silo the secret for
|
|
264
264
|
* @param sender - The address sending the note
|
|
265
265
|
* @param recipient - The address receiving the note
|
|
266
|
-
* @returns
|
|
266
|
+
* @returns An indexed tagging secret that can be used to tag notes.
|
|
267
267
|
*/
|
|
268
|
-
public async
|
|
268
|
+
public async getIndexedTaggingSecretAsSender(
|
|
269
269
|
contractAddress: AztecAddress,
|
|
270
270
|
sender: AztecAddress,
|
|
271
271
|
recipient: AztecAddress,
|
|
272
272
|
): Promise<IndexedTaggingSecret> {
|
|
273
273
|
await this.syncTaggedLogsAsSender(contractAddress, sender, recipient);
|
|
274
274
|
|
|
275
|
-
const
|
|
276
|
-
const [index] = await this.db.getTaggingSecretsIndexesAsSender([
|
|
275
|
+
const appTaggingSecret = await this.#calculateAppTaggingSecret(contractAddress, sender, recipient);
|
|
276
|
+
const [index] = await this.db.getTaggingSecretsIndexesAsSender([appTaggingSecret]);
|
|
277
277
|
|
|
278
|
-
return new IndexedTaggingSecret(
|
|
278
|
+
return new IndexedTaggingSecret(appTaggingSecret, index);
|
|
279
279
|
}
|
|
280
280
|
|
|
281
281
|
/**
|
|
282
|
-
* Increments the tagging secret for a given sender and recipient pair. For this to work, the
|
|
282
|
+
* Increments the tagging secret for a given sender and recipient pair. For this to work, the ivsk_m of the sender must be known.
|
|
283
283
|
* @param contractAddress - The contract address to silo the secret for
|
|
284
284
|
* @param sender - The address sending the note
|
|
285
285
|
* @param recipient - The address receiving the note
|
|
@@ -289,35 +289,39 @@ export class SimulatorOracle implements DBOracle {
|
|
|
289
289
|
sender: AztecAddress,
|
|
290
290
|
recipient: AztecAddress,
|
|
291
291
|
): Promise<void> {
|
|
292
|
-
const secret = await this.#
|
|
292
|
+
const secret = await this.#calculateAppTaggingSecret(contractAddress, sender, recipient);
|
|
293
293
|
const contractName = await this.contractDataOracle.getDebugContractName(contractAddress);
|
|
294
|
-
this.log.
|
|
295
|
-
|
|
296
|
-
|
|
294
|
+
this.log.debug(`Incrementing app tagging secret at ${contractName}(${contractAddress})`, {
|
|
295
|
+
secret,
|
|
296
|
+
sender,
|
|
297
|
+
recipient,
|
|
298
|
+
contractName,
|
|
299
|
+
contractAddress,
|
|
300
|
+
});
|
|
297
301
|
|
|
298
302
|
const [index] = await this.db.getTaggingSecretsIndexesAsSender([secret]);
|
|
299
303
|
await this.db.setTaggingSecretsIndexesAsSender([new IndexedTaggingSecret(secret, index + 1)]);
|
|
300
304
|
}
|
|
301
305
|
|
|
302
|
-
async #
|
|
306
|
+
async #calculateAppTaggingSecret(contractAddress: AztecAddress, sender: AztecAddress, recipient: AztecAddress) {
|
|
303
307
|
const senderCompleteAddress = await this.getCompleteAddress(sender);
|
|
304
308
|
const senderIvsk = await this.keyStore.getMasterIncomingViewingSecretKey(sender);
|
|
305
|
-
const
|
|
306
|
-
// Silo the secret
|
|
307
|
-
const
|
|
308
|
-
return
|
|
309
|
+
const secretPoint = computeTaggingSecretPoint(senderCompleteAddress, senderIvsk, recipient);
|
|
310
|
+
// Silo the secret so it can't be used to track other app's notes
|
|
311
|
+
const appSecret = poseidon2Hash([secretPoint.x, secretPoint.y, contractAddress]);
|
|
312
|
+
return appSecret;
|
|
309
313
|
}
|
|
310
314
|
|
|
311
315
|
/**
|
|
312
|
-
* Returns the
|
|
316
|
+
* Returns the indexed tagging secrets for a given recipient and all the senders in the address book
|
|
313
317
|
* This method should be exposed as an oracle call to allow aztec.nr to perform the orchestration
|
|
314
318
|
* of the syncTaggedLogs and processTaggedLogs methods. However, it is not possible to do so at the moment,
|
|
315
319
|
* so we're keeping it private for now.
|
|
316
320
|
* @param contractAddress - The contract address to silo the secret for
|
|
317
321
|
* @param recipient - The address receiving the notes
|
|
318
|
-
* @returns A list of
|
|
322
|
+
* @returns A list of indexed tagging secrets
|
|
319
323
|
*/
|
|
320
|
-
async #
|
|
324
|
+
async #getIndexedTaggingSecretsForContacts(
|
|
321
325
|
contractAddress: AztecAddress,
|
|
322
326
|
recipient: AztecAddress,
|
|
323
327
|
): Promise<IndexedTaggingSecret[]> {
|
|
@@ -325,11 +329,11 @@ export class SimulatorOracle implements DBOracle {
|
|
|
325
329
|
const recipientIvsk = await this.keyStore.getMasterIncomingViewingSecretKey(recipient);
|
|
326
330
|
|
|
327
331
|
// We implicitly add all PXE accounts as contacts, this helps us decrypt tags on notes that we send to ourselves (recipient = us, sender = us)
|
|
328
|
-
const contacts = [...this.db.getContactAddresses(), ...(await this.keyStore.getAccounts())].filter(
|
|
332
|
+
const contacts = [...(await this.db.getContactAddresses()), ...(await this.keyStore.getAccounts())].filter(
|
|
329
333
|
(address, index, self) => index === self.findIndex(otherAddress => otherAddress.equals(address)),
|
|
330
334
|
);
|
|
331
335
|
const appTaggingSecrets = contacts.map(contact => {
|
|
332
|
-
const sharedSecret =
|
|
336
|
+
const sharedSecret = computeTaggingSecretPoint(recipientCompleteAddress, recipientIvsk, contact);
|
|
333
337
|
return poseidon2Hash([sharedSecret.x, sharedSecret.y, contractAddress]);
|
|
334
338
|
});
|
|
335
339
|
const indexes = await this.db.getTaggingSecretsIndexesAsRecipient(appTaggingSecrets);
|
|
@@ -348,7 +352,7 @@ export class SimulatorOracle implements DBOracle {
|
|
|
348
352
|
sender: AztecAddress,
|
|
349
353
|
recipient: AztecAddress,
|
|
350
354
|
): Promise<void> {
|
|
351
|
-
const appTaggingSecret = await this.#
|
|
355
|
+
const appTaggingSecret = await this.#calculateAppTaggingSecret(contractAddress, sender, recipient);
|
|
352
356
|
let [currentIndex] = await this.db.getTaggingSecretsIndexesAsSender([appTaggingSecret]);
|
|
353
357
|
|
|
354
358
|
const INDEX_OFFSET = 10;
|
|
@@ -395,14 +399,19 @@ export class SimulatorOracle implements DBOracle {
|
|
|
395
399
|
await this.db.setTaggingSecretsIndexesAsSender([new IndexedTaggingSecret(appTaggingSecret, newIndex)]);
|
|
396
400
|
|
|
397
401
|
const contractName = await this.contractDataOracle.getDebugContractName(contractAddress);
|
|
398
|
-
this.log.debug(
|
|
399
|
-
|
|
400
|
-
|
|
402
|
+
this.log.debug(`Syncing logs for sender ${sender} at contract ${contractName}(${contractAddress})`, {
|
|
403
|
+
sender,
|
|
404
|
+
secret: appTaggingSecret,
|
|
405
|
+
index: currentIndex,
|
|
406
|
+
contractName,
|
|
407
|
+
contractAddress,
|
|
408
|
+
});
|
|
401
409
|
}
|
|
402
410
|
|
|
403
411
|
/**
|
|
404
|
-
* Synchronizes the logs tagged with scoped addresses and all the senders in the
|
|
405
|
-
* Returns the unsynched logs and updates the indexes of the secrets used to tag them until there are no more logs
|
|
412
|
+
* Synchronizes the logs tagged with scoped addresses and all the senders in the address book.
|
|
413
|
+
* Returns the unsynched logs and updates the indexes of the secrets used to tag them until there are no more logs
|
|
414
|
+
* to sync.
|
|
406
415
|
* @param contractAddress - The address of the contract that the logs are tagged for
|
|
407
416
|
* @param recipient - The address of the recipient
|
|
408
417
|
* @returns A list of encrypted logs tagged with the recipient's address
|
|
@@ -412,117 +421,140 @@ export class SimulatorOracle implements DBOracle {
|
|
|
412
421
|
maxBlockNumber: number,
|
|
413
422
|
scopes?: AztecAddress[],
|
|
414
423
|
): Promise<Map<string, TxScopedL2Log[]>> {
|
|
424
|
+
// Half the size of the window we slide over the tagging secret indexes.
|
|
425
|
+
const WINDOW_HALF_SIZE = 10;
|
|
426
|
+
|
|
427
|
+
// Ideally this algorithm would be implemented in noir, exposing its building blocks as oracles.
|
|
428
|
+
// However it is impossible at the moment due to the language not supporting nested slices.
|
|
429
|
+
// This nesting is necessary because for a given set of tags we don't
|
|
430
|
+
// know how many logs we will get back. Furthermore, these logs are of undetermined
|
|
431
|
+
// length, since we don't really know the note they correspond to until we decrypt them.
|
|
432
|
+
|
|
415
433
|
const recipients = scopes ? scopes : await this.keyStore.getAccounts();
|
|
416
|
-
|
|
434
|
+
// A map of logs going from recipient address to logs. Note that the logs might have been processed before
|
|
435
|
+
// due to us having a sliding window that "looks back" for logs as well. (We look back as there is no guarantee
|
|
436
|
+
// that a logs will be received ordered by a given tax index and that the tags won't be reused).
|
|
437
|
+
const logsMap = new Map<string, TxScopedL2Log[]>();
|
|
417
438
|
const contractName = await this.contractDataOracle.getDebugContractName(contractAddress);
|
|
418
439
|
for (const recipient of recipients) {
|
|
419
|
-
const
|
|
420
|
-
|
|
421
|
-
//
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
//
|
|
425
|
-
|
|
426
|
-
//
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
//
|
|
430
|
-
//
|
|
431
|
-
//
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
};
|
|
443
|
-
const searchState = appTaggingSecrets.reduce<SearchState>(
|
|
444
|
-
(acc, appTaggingSecret) => ({
|
|
445
|
-
// Start looking for logs before the stored index
|
|
446
|
-
currentTagggingSecrets: acc.currentTagggingSecrets.concat([
|
|
447
|
-
new IndexedTaggingSecret(appTaggingSecret.secret, Math.max(0, appTaggingSecret.index - INDEX_OFFSET)),
|
|
448
|
-
]),
|
|
449
|
-
// Keep looking for logs beyond the stored index
|
|
450
|
-
maxIndexesToCheck: {
|
|
451
|
-
...acc.maxIndexesToCheck,
|
|
452
|
-
...{ [appTaggingSecret.secret.toString()]: appTaggingSecret.index + INDEX_OFFSET },
|
|
453
|
-
},
|
|
454
|
-
// Keeps track of the secrets we have to increment in the database
|
|
455
|
-
secretsToIncrement: {},
|
|
456
|
-
// Store the initial set of indexes for the secrets
|
|
457
|
-
initialSecretIndexes: {
|
|
458
|
-
...acc.initialSecretIndexes,
|
|
459
|
-
...{ [appTaggingSecret.secret.toString()]: appTaggingSecret.index },
|
|
460
|
-
},
|
|
461
|
-
}),
|
|
462
|
-
{ currentTagggingSecrets: [], maxIndexesToCheck: {}, secretsToIncrement: {}, initialSecretIndexes: {} },
|
|
463
|
-
);
|
|
440
|
+
const logsForRecipient: TxScopedL2Log[] = [];
|
|
441
|
+
|
|
442
|
+
// Get all the secrets for the recipient and sender pairs (#9365)
|
|
443
|
+
const secrets = await this.#getIndexedTaggingSecretsForContacts(contractAddress, recipient);
|
|
444
|
+
|
|
445
|
+
// We fetch logs for a window of indexes in a range:
|
|
446
|
+
// <latest_log_index - WINDOW_HALF_SIZE, latest_log_index + WINDOW_HALF_SIZE>.
|
|
447
|
+
//
|
|
448
|
+
// We use this window approach because it could happen that a sender might have messed up and inadvertently
|
|
449
|
+
// incremented their index without us getting any logs (for example, in case of a revert). If we stopped looking
|
|
450
|
+
// for logs the first time we don't receive any logs for a tag, we might never receive anything from that sender again.
|
|
451
|
+
// Also there's a possibility that we have advanced our index, but the sender has reused it, so we might have missed
|
|
452
|
+
// some logs. For these reasons, we have to look both back and ahead of the stored index.
|
|
453
|
+
let secretsAndWindows = secrets.map(secret => {
|
|
454
|
+
return {
|
|
455
|
+
appTaggingSecret: secret.appTaggingSecret,
|
|
456
|
+
leftMostIndex: Math.max(0, secret.index - WINDOW_HALF_SIZE),
|
|
457
|
+
rightMostIndex: secret.index + WINDOW_HALF_SIZE,
|
|
458
|
+
};
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
// As we iterate we store the largest index we have seen for a given secret to later on store it in the db.
|
|
462
|
+
const newLargestIndexMapToStore: { [k: string]: number } = {};
|
|
464
463
|
|
|
465
|
-
|
|
466
|
-
const
|
|
464
|
+
// The initial/unmodified indexes of the secrets stored in a key-value map where key is the app tagging secret.
|
|
465
|
+
const initialIndexesMap = getInitialIndexesMap(secrets);
|
|
467
466
|
|
|
468
|
-
while (
|
|
469
|
-
|
|
470
|
-
const
|
|
471
|
-
|
|
467
|
+
while (secretsAndWindows.length > 0) {
|
|
468
|
+
const secretsForTheWholeWindow = getIndexedTaggingSecretsForTheWindow(secretsAndWindows);
|
|
469
|
+
const tagsForTheWholeWindow = secretsForTheWholeWindow.map(secret =>
|
|
470
|
+
secret.computeSiloedTag(recipient, contractAddress),
|
|
472
471
|
);
|
|
473
|
-
|
|
474
|
-
|
|
472
|
+
|
|
473
|
+
// We store the new largest indexes we find in the iteration in the following map to later on construct
|
|
474
|
+
// a new set of secrets and windows to fetch logs for.
|
|
475
|
+
const newLargestIndexMapForIteration: { [k: string]: number } = {};
|
|
476
|
+
|
|
477
|
+
// Fetch the logs for the tags and iterate over them
|
|
478
|
+
const logsByTags = await this.aztecNode.getLogsByTags(tagsForTheWholeWindow);
|
|
479
|
+
|
|
475
480
|
logsByTags.forEach((logsByTag, logIndex) => {
|
|
476
|
-
const { secret: currentSecret, index: currentIndex } = currentTagggingSecrets[logIndex];
|
|
477
|
-
const currentSecretAsStr = currentSecret.toString();
|
|
478
|
-
this.log.debug(
|
|
479
|
-
`Syncing logs for recipient ${recipient}, secret ${currentSecretAsStr}:${currentIndex} at contract: ${contractName}(${contractAddress})`,
|
|
480
|
-
);
|
|
481
|
-
// 3.1. Append logs to the list and increment the index for the tags that have logs (#9380)
|
|
482
481
|
if (logsByTag.length > 0) {
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
);
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
482
|
+
// The logs for the given tag exist so we store them for later processing
|
|
483
|
+
logsForRecipient.push(...logsByTag);
|
|
484
|
+
|
|
485
|
+
// We retrieve the indexed tagging secret corresponding to the log as I need that to evaluate whether
|
|
486
|
+
// a new largest index have been found.
|
|
487
|
+
const secretCorrespondingToLog = secretsForTheWholeWindow[logIndex];
|
|
488
|
+
const initialIndex = initialIndexesMap[secretCorrespondingToLog.appTaggingSecret.toString()];
|
|
489
|
+
|
|
490
|
+
this.log.debug(`Found ${logsByTag.length} logs as recipient ${recipient}`, {
|
|
491
|
+
recipient,
|
|
492
|
+
secret: secretCorrespondingToLog.appTaggingSecret,
|
|
493
|
+
contractName,
|
|
494
|
+
contractAddress,
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
if (
|
|
498
|
+
secretCorrespondingToLog.index >= initialIndex &&
|
|
499
|
+
(newLargestIndexMapForIteration[secretCorrespondingToLog.appTaggingSecret.toString()] === undefined ||
|
|
500
|
+
secretCorrespondingToLog.index >=
|
|
501
|
+
newLargestIndexMapForIteration[secretCorrespondingToLog.appTaggingSecret.toString()])
|
|
502
|
+
) {
|
|
503
|
+
// We have found a new largest index so we store it for later processing (storing it in the db + fetching
|
|
504
|
+
// the difference of the window sets of current and the next iteration)
|
|
505
|
+
newLargestIndexMapForIteration[secretCorrespondingToLog.appTaggingSecret.toString()] =
|
|
506
|
+
secretCorrespondingToLog.index + 1;
|
|
507
|
+
|
|
508
|
+
this.log.debug(
|
|
509
|
+
`Incrementing index to ${
|
|
510
|
+
secretCorrespondingToLog.index + 1
|
|
511
|
+
} at contract ${contractName}(${contractAddress})`,
|
|
512
|
+
);
|
|
498
513
|
}
|
|
499
514
|
}
|
|
500
|
-
// 3.4 Keep increasing the index (inside the window) temporarily for the tags that have no logs
|
|
501
|
-
// There's a chance the sender missed some and we want to catch up
|
|
502
|
-
if (currentIndex < maxIndexesToCheck[currentSecretAsStr]) {
|
|
503
|
-
const newTaggingSecret = new IndexedTaggingSecret(currentSecret, currentIndex + 1);
|
|
504
|
-
newTaggingSecrets.push(newTaggingSecret);
|
|
505
|
-
}
|
|
506
515
|
});
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
516
|
+
|
|
517
|
+
// Now based on the new largest indexes we found, we will construct a new secrets and windows set to fetch logs
|
|
518
|
+
// for. Note that it's very unlikely that a new log from the current window would appear between the iterations
|
|
519
|
+
// so we fetch the logs only for the difference of the window sets.
|
|
520
|
+
const newSecretsAndWindows = [];
|
|
521
|
+
for (const [appTaggingSecret, newIndex] of Object.entries(newLargestIndexMapForIteration)) {
|
|
522
|
+
const secret = secrets.find(secret => secret.appTaggingSecret.toString() === appTaggingSecret);
|
|
523
|
+
if (secret) {
|
|
524
|
+
newSecretsAndWindows.push({
|
|
525
|
+
appTaggingSecret: secret.appTaggingSecret,
|
|
526
|
+
// We set the left most index to the new index to avoid fetching the same logs again
|
|
527
|
+
leftMostIndex: newIndex,
|
|
528
|
+
rightMostIndex: newIndex + WINDOW_HALF_SIZE,
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
// We store the new largest index in the map to later store it in the db.
|
|
532
|
+
newLargestIndexMapToStore[appTaggingSecret] = newIndex;
|
|
533
|
+
} else {
|
|
534
|
+
throw new Error(
|
|
535
|
+
`Secret not found for appTaggingSecret ${appTaggingSecret}. This is a bug as it should never happen!`,
|
|
536
|
+
);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
// Now we set the new secrets and windows and proceed to the next iteration.
|
|
541
|
+
secretsAndWindows = newSecretsAndWindows;
|
|
513
542
|
}
|
|
514
543
|
|
|
515
|
-
|
|
544
|
+
// We filter the logs by block number and store them in the map.
|
|
545
|
+
logsMap.set(
|
|
516
546
|
recipient.toString(),
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
547
|
+
logsForRecipient.filter(log => log.blockNumber <= maxBlockNumber),
|
|
548
|
+
);
|
|
549
|
+
|
|
550
|
+
// At this point we have processed all the logs for the recipient so we store the new largest indexes in the db.
|
|
551
|
+
await this.db.setTaggingSecretsIndexesAsRecipient(
|
|
552
|
+
Object.entries(newLargestIndexMapToStore).map(
|
|
553
|
+
([appTaggingSecret, index]) => new IndexedTaggingSecret(Fr.fromHexString(appTaggingSecret), index),
|
|
522
554
|
),
|
|
523
555
|
);
|
|
524
556
|
}
|
|
525
|
-
return
|
|
557
|
+
return logsMap;
|
|
526
558
|
}
|
|
527
559
|
|
|
528
560
|
/**
|
|
@@ -538,14 +570,11 @@ export class SimulatorOracle implements DBOracle {
|
|
|
538
570
|
recipientCompleteAddress.publicKeys.masterIncomingViewingPublicKey,
|
|
539
571
|
);
|
|
540
572
|
const addressSecret = computeAddressSecret(recipientCompleteAddress.getPreaddress(), ivskM);
|
|
541
|
-
|
|
542
|
-
recipientCompleteAddress.publicKeys.masterOutgoingViewingPublicKey,
|
|
543
|
-
);
|
|
573
|
+
|
|
544
574
|
// Since we could have notes with the same index for different txs, we need
|
|
545
575
|
// to keep track of them scoping by txHash
|
|
546
576
|
const excludedIndices: Map<string, Set<number>> = new Map();
|
|
547
577
|
const incomingNotes: IncomingNoteDao[] = [];
|
|
548
|
-
const outgoingNotes: OutgoingNoteDao[] = [];
|
|
549
578
|
|
|
550
579
|
const txEffectsCache = new Map<string, InBlock<TxEffect> | undefined>();
|
|
551
580
|
|
|
@@ -553,21 +582,9 @@ export class SimulatorOracle implements DBOracle {
|
|
|
553
582
|
const incomingNotePayload = scopedLog.isFromPublic
|
|
554
583
|
? L1NotePayload.decryptAsIncomingFromPublic(scopedLog.logData, addressSecret)
|
|
555
584
|
: L1NotePayload.decryptAsIncoming(PrivateLog.fromBuffer(scopedLog.logData), addressSecret);
|
|
556
|
-
const outgoingNotePayload = scopedLog.isFromPublic
|
|
557
|
-
? L1NotePayload.decryptAsOutgoingFromPublic(scopedLog.logData, ovskM)
|
|
558
|
-
: L1NotePayload.decryptAsOutgoing(PrivateLog.fromBuffer(scopedLog.logData), ovskM);
|
|
559
|
-
|
|
560
|
-
if (incomingNotePayload || outgoingNotePayload) {
|
|
561
|
-
if (incomingNotePayload && outgoingNotePayload && !incomingNotePayload.equals(outgoingNotePayload)) {
|
|
562
|
-
this.log.warn(
|
|
563
|
-
`Incoming and outgoing note payloads do not match. Incoming: ${tryJsonStringify(
|
|
564
|
-
incomingNotePayload,
|
|
565
|
-
)}, Outgoing: ${tryJsonStringify(outgoingNotePayload)}`,
|
|
566
|
-
);
|
|
567
|
-
continue;
|
|
568
|
-
}
|
|
569
585
|
|
|
570
|
-
|
|
586
|
+
if (incomingNotePayload) {
|
|
587
|
+
const payload = incomingNotePayload;
|
|
571
588
|
|
|
572
589
|
const txEffect =
|
|
573
590
|
txEffectsCache.get(scopedLog.txHash.toString()) ?? (await this.aztecNode.getTxEffect(scopedLog.txHash));
|
|
@@ -582,14 +599,13 @@ export class SimulatorOracle implements DBOracle {
|
|
|
582
599
|
if (!excludedIndices.has(scopedLog.txHash.toString())) {
|
|
583
600
|
excludedIndices.set(scopedLog.txHash.toString(), new Set());
|
|
584
601
|
}
|
|
585
|
-
const { incomingNote
|
|
602
|
+
const { incomingNote } = await produceNoteDaos(
|
|
586
603
|
// I don't like this at all, but we need a simulator to run `computeNoteHashAndOptionallyANullifier`. This generates
|
|
587
604
|
// a chicken-and-egg problem due to this oracle requiring a simulator, which in turn requires this oracle. Furthermore, since jest doesn't allow
|
|
588
605
|
// mocking ESM exports, we have to pollute the method even more by providing a simulator parameter so tests can inject a fake one.
|
|
589
606
|
simulator ?? getAcirSimulator(this.db, this.aztecNode, this.keyStore, this.contractDataOracle),
|
|
590
607
|
this.db,
|
|
591
608
|
incomingNotePayload ? recipient.toAddressPoint() : undefined,
|
|
592
|
-
outgoingNotePayload ? recipientCompleteAddress.publicKeys.masterOutgoingViewingPublicKey : undefined,
|
|
593
609
|
payload!,
|
|
594
610
|
txEffect.data.txHash,
|
|
595
611
|
txEffect.l2BlockNumber,
|
|
@@ -603,12 +619,9 @@ export class SimulatorOracle implements DBOracle {
|
|
|
603
619
|
if (incomingNote) {
|
|
604
620
|
incomingNotes.push(incomingNote);
|
|
605
621
|
}
|
|
606
|
-
if (outgoingNote) {
|
|
607
|
-
outgoingNotes.push(outgoingNote);
|
|
608
|
-
}
|
|
609
622
|
}
|
|
610
623
|
}
|
|
611
|
-
return { incomingNotes
|
|
624
|
+
return { incomingNotes };
|
|
612
625
|
}
|
|
613
626
|
|
|
614
627
|
/**
|
|
@@ -621,41 +634,41 @@ export class SimulatorOracle implements DBOracle {
|
|
|
621
634
|
recipient: AztecAddress,
|
|
622
635
|
simulator?: AcirSimulator,
|
|
623
636
|
): Promise<void> {
|
|
624
|
-
const { incomingNotes
|
|
625
|
-
if (incomingNotes.length
|
|
626
|
-
await this.db.addNotes(incomingNotes,
|
|
637
|
+
const { incomingNotes } = await this.#decryptTaggedLogs(logs, recipient, simulator);
|
|
638
|
+
if (incomingNotes.length) {
|
|
639
|
+
await this.db.addNotes(incomingNotes, recipient);
|
|
627
640
|
incomingNotes.forEach(noteDao => {
|
|
628
|
-
this.log.verbose(
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
);
|
|
641
|
+
this.log.verbose(`Added incoming note for contract ${noteDao.contractAddress} at slot ${noteDao.storageSlot}`, {
|
|
642
|
+
contract: noteDao.contractAddress,
|
|
643
|
+
slot: noteDao.storageSlot,
|
|
644
|
+
nullifier: noteDao.siloedNullifier.toString(),
|
|
645
|
+
});
|
|
633
646
|
});
|
|
634
|
-
|
|
635
|
-
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
public async removeNullifiedNotes(contractAddress: AztecAddress) {
|
|
651
|
+
for (const recipient of await this.keyStore.getAccounts()) {
|
|
652
|
+
const currentNotesForRecipient = await this.db.getIncomingNotes({ contractAddress, owner: recipient });
|
|
653
|
+
const nullifiersToCheck = currentNotesForRecipient.map(note => note.siloedNullifier);
|
|
654
|
+
const nullifierIndexes = await this.aztecNode.findNullifiersIndexesWithBlock('latest', nullifiersToCheck);
|
|
655
|
+
|
|
656
|
+
const foundNullifiers = nullifiersToCheck
|
|
657
|
+
.map((nullifier, i) => {
|
|
658
|
+
if (nullifierIndexes[i] !== undefined) {
|
|
659
|
+
return { ...nullifierIndexes[i], ...{ data: nullifier } } as InBlock<Fr>;
|
|
660
|
+
}
|
|
661
|
+
})
|
|
662
|
+
.filter(nullifier => nullifier !== undefined) as InBlock<Fr>[];
|
|
663
|
+
|
|
664
|
+
const nullifiedNotes = await this.db.removeNullifiedNotes(foundNullifiers, recipient.toAddressPoint());
|
|
665
|
+
nullifiedNotes.forEach(noteDao => {
|
|
666
|
+
this.log.verbose(`Removed note for contract ${noteDao.contractAddress} at slot ${noteDao.storageSlot}`, {
|
|
667
|
+
contract: noteDao.contractAddress,
|
|
668
|
+
slot: noteDao.storageSlot,
|
|
669
|
+
nullifier: noteDao.siloedNullifier.toString(),
|
|
670
|
+
});
|
|
636
671
|
});
|
|
637
672
|
}
|
|
638
|
-
const nullifiedNotes: IncomingNoteDao[] = [];
|
|
639
|
-
const currentNotesForRecipient = await this.db.getIncomingNotes({ owner: recipient });
|
|
640
|
-
const nullifiersToCheck = currentNotesForRecipient.map(note => note.siloedNullifier);
|
|
641
|
-
const currentBlockNumber = await this.getBlockNumber();
|
|
642
|
-
const nullifierIndexes = await this.aztecNode.findNullifiersIndexesWithBlock(currentBlockNumber, nullifiersToCheck);
|
|
643
|
-
|
|
644
|
-
const foundNullifiers = nullifiersToCheck
|
|
645
|
-
.map((nullifier, i) => {
|
|
646
|
-
if (nullifierIndexes[i] !== undefined) {
|
|
647
|
-
return { ...nullifierIndexes[i], ...{ data: nullifier } } as InBlock<Fr>;
|
|
648
|
-
}
|
|
649
|
-
})
|
|
650
|
-
.filter(nullifier => nullifier !== undefined) as InBlock<Fr>[];
|
|
651
|
-
|
|
652
|
-
await this.db.removeNullifiedNotes(foundNullifiers, recipient.toAddressPoint());
|
|
653
|
-
nullifiedNotes.forEach(noteDao => {
|
|
654
|
-
this.log.verbose(
|
|
655
|
-
`Removed note for contract ${noteDao.contractAddress} at slot ${
|
|
656
|
-
noteDao.storageSlot
|
|
657
|
-
} with nullifier ${noteDao.siloedNullifier.toString()}`,
|
|
658
|
-
);
|
|
659
|
-
});
|
|
660
673
|
}
|
|
661
674
|
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type Fr, IndexedTaggingSecret } from '@aztec/circuits.js';
|
|
2
|
+
|
|
3
|
+
export function getIndexedTaggingSecretsForTheWindow(
|
|
4
|
+
secretsAndWindows: { appTaggingSecret: Fr; leftMostIndex: number; rightMostIndex: number }[],
|
|
5
|
+
): IndexedTaggingSecret[] {
|
|
6
|
+
const secrets: IndexedTaggingSecret[] = [];
|
|
7
|
+
for (const secretAndWindow of secretsAndWindows) {
|
|
8
|
+
for (let i = secretAndWindow.leftMostIndex; i <= secretAndWindow.rightMostIndex; i++) {
|
|
9
|
+
secrets.push(new IndexedTaggingSecret(secretAndWindow.appTaggingSecret, i));
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
return secrets;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Creates a map from app tagging secret to initial index.
|
|
17
|
+
* @param indexedTaggingSecrets - The indexed tagging secrets to get the initial indexes from.
|
|
18
|
+
* @returns The map from app tagging secret to initial index.
|
|
19
|
+
*/
|
|
20
|
+
export function getInitialIndexesMap(indexedTaggingSecrets: IndexedTaggingSecret[]): { [k: string]: number } {
|
|
21
|
+
const initialIndexes: { [k: string]: number } = {};
|
|
22
|
+
|
|
23
|
+
for (const indexedTaggingSecret of indexedTaggingSecrets) {
|
|
24
|
+
initialIndexes[indexedTaggingSecret.appTaggingSecret.toString()] = indexedTaggingSecret.index;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return initialIndexes;
|
|
28
|
+
}
|