@aztec/pxe 0.67.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.
Files changed (29) hide show
  1. package/dest/database/kv_pxe_database.d.ts.map +1 -1
  2. package/dest/database/kv_pxe_database.js +2 -2
  3. package/dest/kernel_oracle/index.js +2 -2
  4. package/dest/kernel_prover/hints/build_private_kernel_reset_private_inputs.js +2 -2
  5. package/dest/kernel_prover/kernel_prover.d.ts.map +1 -1
  6. package/dest/kernel_prover/kernel_prover.js +5 -2
  7. package/dest/kernel_prover/test/test_circuit_prover.d.ts +1 -2
  8. package/dest/kernel_prover/test/test_circuit_prover.d.ts.map +1 -1
  9. package/dest/kernel_prover/test/test_circuit_prover.js +4 -10
  10. package/dest/pxe_service/pxe_service.d.ts.map +1 -1
  11. package/dest/pxe_service/pxe_service.js +7 -4
  12. package/dest/simulator_oracle/index.d.ts +7 -5
  13. package/dest/simulator_oracle/index.d.ts.map +1 -1
  14. package/dest/simulator_oracle/index.js +131 -118
  15. package/dest/simulator_oracle/tagging_utils.d.ts +15 -0
  16. package/dest/simulator_oracle/tagging_utils.d.ts.map +1 -0
  17. package/dest/simulator_oracle/tagging_utils.js +23 -0
  18. package/dest/utils/create_pxe_service.d.ts.map +1 -1
  19. package/dest/utils/create_pxe_service.js +8 -5
  20. package/package.json +14 -14
  21. package/src/database/kv_pxe_database.ts +3 -1
  22. package/src/kernel_oracle/index.ts +1 -1
  23. package/src/kernel_prover/hints/build_private_kernel_reset_private_inputs.ts +1 -1
  24. package/src/kernel_prover/kernel_prover.ts +11 -1
  25. package/src/kernel_prover/test/test_circuit_prover.ts +6 -22
  26. package/src/pxe_service/pxe_service.ts +6 -7
  27. package/src/simulator_oracle/index.ts +163 -143
  28. package/src/simulator_oracle/tagging_utils.ts +28 -0
  29. package/src/utils/create_pxe_service.ts +7 -4
@@ -37,7 +37,7 @@ import {
37
37
  import { makeTuple } from '@aztec/foundation/array';
38
38
  import { padArrayEnd } from '@aztec/foundation/collection';
39
39
  import { type Tuple, assertLength } from '@aztec/foundation/serialize';
40
- import { privateKernelResetDimensionsConfig } from '@aztec/noir-protocol-circuits-types';
40
+ import { privateKernelResetDimensionsConfig } from '@aztec/noir-protocol-circuits-types/client';
41
41
 
42
42
  import { type ProvingDataOracle } from '../proving_data_oracle.js';
43
43
 
@@ -31,7 +31,8 @@ import { vkAsFieldsMegaHonk } from '@aztec/foundation/crypto';
31
31
  import { createLogger } from '@aztec/foundation/log';
32
32
  import { assertLength } from '@aztec/foundation/serialize';
33
33
  import { pushTestData } from '@aztec/foundation/testing';
34
- import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types';
34
+ import { Timer } from '@aztec/foundation/timer';
35
+ import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types/client';
35
36
  import {
36
37
  getProtocolContractSiblingPath,
37
38
  isProtocolContract,
@@ -118,6 +119,8 @@ export class KernelProver {
118
119
  profile: boolean = false,
119
120
  dryRun: boolean = false,
120
121
  ): Promise<PrivateKernelSimulateOutput<PrivateKernelTailCircuitPublicInputs>> {
122
+ const timer = new Timer();
123
+
121
124
  const isPrivateOnlyTx = this.isPrivateOnly(executionResult);
122
125
 
123
126
  const executionStack = [executionResult];
@@ -197,7 +200,9 @@ export class KernelProver {
197
200
  privateCallData,
198
201
  isPrivateOnlyTx,
199
202
  );
203
+
200
204
  pushTestData('private-kernel-inputs-init', proofInput);
205
+
201
206
  output = await this.proofCreator.simulateProofInit(proofInput);
202
207
 
203
208
  acirs.push(output.bytecode);
@@ -214,7 +219,9 @@ export class KernelProver {
214
219
  assertLength<Fr, typeof VK_TREE_HEIGHT>(previousVkMembershipWitness.siblingPath, VK_TREE_HEIGHT),
215
220
  );
216
221
  const proofInput = new PrivateKernelInnerCircuitPrivateInputs(previousKernelData, privateCallData);
222
+
217
223
  pushTestData('private-kernel-inputs-inner', proofInput);
224
+
218
225
  output = await this.proofCreator.simulateProofInner(proofInput);
219
226
 
220
227
  acirs.push(output.bytecode);
@@ -267,6 +274,7 @@ export class KernelProver {
267
274
  const privateInputs = new PrivateKernelTailCircuitPrivateInputs(previousKernelData);
268
275
 
269
276
  pushTestData('private-kernel-inputs-ordering', privateInputs);
277
+
270
278
  const tailOutput = await this.proofCreator.simulateProofTail(privateInputs);
271
279
  if (tailOutput.publicInputs.forPublic) {
272
280
  const privateLogs = privateInputs.previousKernel.publicInputs.end.privateLogs;
@@ -282,6 +290,8 @@ export class KernelProver {
282
290
  tailOutput.profileResult = { gateCounts };
283
291
  }
284
292
 
293
+ this.log.info(`Witness generation took ${timer.ms()}ms`);
294
+
285
295
  // TODO(#7368) how do we 'bincode' encode these inputs?
286
296
  if (!dryRun) {
287
297
  const ivcProof = await this.proofCreator.createClientIvcProof(acirs, witnessStack);
@@ -1,11 +1,6 @@
1
- import {
2
- type AppCircuitSimulateOutput,
3
- type PrivateKernelProver,
4
- type PrivateKernelSimulateOutput,
5
- } from '@aztec/circuit-types';
1
+ import { type PrivateKernelProver, type PrivateKernelSimulateOutput } from '@aztec/circuit-types';
6
2
  import type { CircuitSimulationStats } from '@aztec/circuit-types/stats';
7
3
  import {
8
- CLIENT_IVC_VERIFICATION_KEY_LENGTH_IN_FIELDS,
9
4
  ClientIvcProof,
10
5
  type PrivateKernelCircuitPublicInputs,
11
6
  type PrivateKernelInitCircuitPrivateInputs,
@@ -13,13 +8,12 @@ import {
13
8
  type PrivateKernelResetCircuitPrivateInputs,
14
9
  type PrivateKernelTailCircuitPrivateInputs,
15
10
  type PrivateKernelTailCircuitPublicInputs,
16
- VerificationKeyAsFields,
17
11
  } from '@aztec/circuits.js';
18
12
  import { createLogger } from '@aztec/foundation/log';
19
13
  import { elapsed } from '@aztec/foundation/timer';
20
14
  import {
21
- type ProtocolArtifact,
22
- ProtocolCircuitVks,
15
+ ClientCircuitVks,
16
+ type ClientProtocolArtifact,
23
17
  executeInit,
24
18
  executeInner,
25
19
  executeReset,
@@ -27,7 +21,7 @@ import {
27
21
  executeTailForPublic,
28
22
  getPrivateKernelResetArtifactName,
29
23
  maxPrivateKernelResetDimensions,
30
- } from '@aztec/noir-protocol-circuits-types';
24
+ } from '@aztec/noir-protocol-circuits-types/client';
31
25
 
32
26
  import { type WitnessMap } from '@noir-lang/types';
33
27
 
@@ -114,22 +108,12 @@ export class TestPrivateKernelProver implements PrivateKernelProver {
114
108
  return Promise.resolve(0);
115
109
  }
116
110
 
117
- computeAppCircuitVerificationKey(
118
- _bytecode: Buffer,
119
- _appCircuitName?: string | undefined,
120
- ): Promise<AppCircuitSimulateOutput> {
121
- const appCircuitProofOutput: AppCircuitSimulateOutput = {
122
- verificationKey: VerificationKeyAsFields.makeEmpty(CLIENT_IVC_VERIFICATION_KEY_LENGTH_IN_FIELDS),
123
- };
124
- return Promise.resolve(appCircuitProofOutput);
125
- }
126
-
127
111
  private makeEmptyKernelSimulateOutput<
128
112
  PublicInputsType extends PrivateKernelTailCircuitPublicInputs | PrivateKernelCircuitPublicInputs,
129
- >(publicInputs: PublicInputsType, circuitType: ProtocolArtifact) {
113
+ >(publicInputs: PublicInputsType, circuitType: ClientProtocolArtifact) {
130
114
  const kernelProofOutput: PrivateKernelSimulateOutput<PublicInputsType> = {
131
115
  publicInputs,
132
- verificationKey: ProtocolCircuitVks[circuitType].keyAsFields,
116
+ verificationKey: ClientCircuitVks[circuitType].keyAsFields,
133
117
  outputWitness: new Map(),
134
118
  bytecode: Buffer.from([]),
135
119
  };
@@ -56,13 +56,11 @@ import {
56
56
  } from '@aztec/foundation/abi';
57
57
  import { Fr, type Point } from '@aztec/foundation/fields';
58
58
  import { type Logger, createLogger } from '@aztec/foundation/log';
59
+ import { Timer } from '@aztec/foundation/timer';
59
60
  import { type KeyStore } from '@aztec/key-store';
60
61
  import { type L2TipsStore } from '@aztec/kv-store/stores';
61
- import {
62
- ProtocolContractAddress,
63
- getCanonicalProtocolContract,
64
- protocolContractNames,
65
- } from '@aztec/protocol-contracts';
62
+ import { ProtocolContractAddress, protocolContractNames } from '@aztec/protocol-contracts';
63
+ import { getCanonicalProtocolContract } from '@aztec/protocol-contracts/bundle';
66
64
  import { type AcirSimulator } from '@aztec/simulator/client';
67
65
 
68
66
  import { inspect } from 'util';
@@ -494,10 +492,11 @@ export class PXEService implements PXE {
494
492
  version: txRequest.txContext.version,
495
493
  authWitnesses: txRequest.authWitnesses.map(w => w.requestHash),
496
494
  };
497
- this.log.verbose(
495
+ this.log.info(
498
496
  `Simulating transaction execution request to ${txRequest.functionSelector} at ${txRequest.origin}`,
499
497
  txInfo,
500
498
  );
499
+ const timer = new Timer();
501
500
  await this.synchronizer.sync();
502
501
  const privateExecutionResult = await this.#executePrivate(txRequest, msgSender, scopes);
503
502
 
@@ -526,7 +525,7 @@ export class PXEService implements PXE {
526
525
  }
527
526
  }
528
527
 
529
- this.log.info(`Simulation completed for ${simulatedTx.tryGetTxHash()}`, {
528
+ this.log.info(`Simulation completed for ${simulatedTx.tryGetTxHash()} in ${timer.ms()}ms`, {
530
529
  txHash: simulatedTx.tryGetTxHash(),
531
530
  ...txInfo,
532
531
  ...(profileResult ? { gateCounts: profileResult.gateCounts } : {}),
@@ -24,7 +24,7 @@ import {
24
24
  type L1_TO_L2_MSG_TREE_HEIGHT,
25
25
  PrivateLog,
26
26
  computeAddressSecret,
27
- computeTaggingSecret,
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';
@@ -38,6 +38,7 @@ import { type IncomingNoteDao } from '../database/incoming_note_dao.js';
38
38
  import { type PxeDatabase } from '../database/index.js';
39
39
  import { produceNoteDaos } from '../note_decryption_utils/produce_note_daos.js';
40
40
  import { getAcirSimulator } from '../simulator/index.js';
41
+ import { getIndexedTaggingSecretsForTheWindow, getInitialIndexesMap } from './tagging_utils.js';
41
42
 
42
43
  /**
43
44
  * A data oracle that provides information needed for simulating a transaction.
@@ -257,28 +258,28 @@ export class SimulatorOracle implements DBOracle {
257
258
  }
258
259
 
259
260
  /**
260
- * Returns the tagging secret for a given sender and recipient pair. For this to work, the ivpsk_m of the sender must be known.
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.
261
262
  * Includes the next index to be used used for tagging with this secret.
262
263
  * @param contractAddress - The contract address to silo the secret for
263
264
  * @param sender - The address sending the note
264
265
  * @param recipient - The address receiving the note
265
- * @returns A siloed tagging secret that can be used to tag notes.
266
+ * @returns An indexed tagging secret that can be used to tag notes.
266
267
  */
267
- public async getAppTaggingSecretAsSender(
268
+ public async getIndexedTaggingSecretAsSender(
268
269
  contractAddress: AztecAddress,
269
270
  sender: AztecAddress,
270
271
  recipient: AztecAddress,
271
272
  ): Promise<IndexedTaggingSecret> {
272
273
  await this.syncTaggedLogsAsSender(contractAddress, sender, recipient);
273
274
 
274
- const secret = await this.#calculateTaggingSecret(contractAddress, sender, recipient);
275
- const [index] = await this.db.getTaggingSecretsIndexesAsSender([secret]);
275
+ const appTaggingSecret = await this.#calculateAppTaggingSecret(contractAddress, sender, recipient);
276
+ const [index] = await this.db.getTaggingSecretsIndexesAsSender([appTaggingSecret]);
276
277
 
277
- return new IndexedTaggingSecret(secret, index);
278
+ return new IndexedTaggingSecret(appTaggingSecret, index);
278
279
  }
279
280
 
280
281
  /**
281
- * Increments the tagging secret for a given sender and recipient pair. For this to work, the ivpsk_m of the sender must be known.
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.
282
283
  * @param contractAddress - The contract address to silo the secret for
283
284
  * @param sender - The address sending the note
284
285
  * @param recipient - The address receiving the note
@@ -288,7 +289,7 @@ export class SimulatorOracle implements DBOracle {
288
289
  sender: AztecAddress,
289
290
  recipient: AztecAddress,
290
291
  ): Promise<void> {
291
- const secret = await this.#calculateTaggingSecret(contractAddress, sender, recipient);
292
+ const secret = await this.#calculateAppTaggingSecret(contractAddress, sender, recipient);
292
293
  const contractName = await this.contractDataOracle.getDebugContractName(contractAddress);
293
294
  this.log.debug(`Incrementing app tagging secret at ${contractName}(${contractAddress})`, {
294
295
  secret,
@@ -302,25 +303,25 @@ export class SimulatorOracle implements DBOracle {
302
303
  await this.db.setTaggingSecretsIndexesAsSender([new IndexedTaggingSecret(secret, index + 1)]);
303
304
  }
304
305
 
305
- async #calculateTaggingSecret(contractAddress: AztecAddress, sender: AztecAddress, recipient: AztecAddress) {
306
+ async #calculateAppTaggingSecret(contractAddress: AztecAddress, sender: AztecAddress, recipient: AztecAddress) {
306
307
  const senderCompleteAddress = await this.getCompleteAddress(sender);
307
308
  const senderIvsk = await this.keyStore.getMasterIncomingViewingSecretKey(sender);
308
- const sharedSecret = computeTaggingSecret(senderCompleteAddress, senderIvsk, recipient);
309
- // Silo the secret to the app so it can't be used to track other app's notes
310
- const siloedSecret = poseidon2Hash([sharedSecret.x, sharedSecret.y, contractAddress]);
311
- return siloedSecret;
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;
312
313
  }
313
314
 
314
315
  /**
315
- * Returns the siloed tagging secrets for a given recipient and all the senders in the address book
316
+ * Returns the indexed tagging secrets for a given recipient and all the senders in the address book
316
317
  * This method should be exposed as an oracle call to allow aztec.nr to perform the orchestration
317
318
  * of the syncTaggedLogs and processTaggedLogs methods. However, it is not possible to do so at the moment,
318
319
  * so we're keeping it private for now.
319
320
  * @param contractAddress - The contract address to silo the secret for
320
321
  * @param recipient - The address receiving the notes
321
- * @returns A list of siloed tagging secrets
322
+ * @returns A list of indexed tagging secrets
322
323
  */
323
- async #getAppTaggingSecretsForContacts(
324
+ async #getIndexedTaggingSecretsForContacts(
324
325
  contractAddress: AztecAddress,
325
326
  recipient: AztecAddress,
326
327
  ): Promise<IndexedTaggingSecret[]> {
@@ -332,7 +333,7 @@ export class SimulatorOracle implements DBOracle {
332
333
  (address, index, self) => index === self.findIndex(otherAddress => otherAddress.equals(address)),
333
334
  );
334
335
  const appTaggingSecrets = contacts.map(contact => {
335
- const sharedSecret = computeTaggingSecret(recipientCompleteAddress, recipientIvsk, contact);
336
+ const sharedSecret = computeTaggingSecretPoint(recipientCompleteAddress, recipientIvsk, contact);
336
337
  return poseidon2Hash([sharedSecret.x, sharedSecret.y, contractAddress]);
337
338
  });
338
339
  const indexes = await this.db.getTaggingSecretsIndexesAsRecipient(appTaggingSecrets);
@@ -351,7 +352,7 @@ export class SimulatorOracle implements DBOracle {
351
352
  sender: AztecAddress,
352
353
  recipient: AztecAddress,
353
354
  ): Promise<void> {
354
- const appTaggingSecret = await this.#calculateTaggingSecret(contractAddress, sender, recipient);
355
+ const appTaggingSecret = await this.#calculateAppTaggingSecret(contractAddress, sender, recipient);
355
356
  let [currentIndex] = await this.db.getTaggingSecretsIndexesAsSender([appTaggingSecret]);
356
357
 
357
358
  const INDEX_OFFSET = 10;
@@ -409,7 +410,8 @@ export class SimulatorOracle implements DBOracle {
409
410
 
410
411
  /**
411
412
  * Synchronizes the logs tagged with scoped addresses and all the senders in the address book.
412
- * Returns the unsynched logs and updates the indexes of the secrets used to tag them until there are no more logs to sync.
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.
413
415
  * @param contractAddress - The address of the contract that the logs are tagged for
414
416
  * @param recipient - The address of the recipient
415
417
  * @returns A list of encrypted logs tagged with the recipient's address
@@ -419,125 +421,140 @@ export class SimulatorOracle implements DBOracle {
419
421
  maxBlockNumber: number,
420
422
  scopes?: AztecAddress[],
421
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
+
422
433
  const recipients = scopes ? scopes : await this.keyStore.getAccounts();
423
- const result = new Map<string, TxScopedL2Log[]>();
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[]>();
424
438
  const contractName = await this.contractDataOracle.getDebugContractName(contractAddress);
425
439
  for (const recipient of recipients) {
426
- const logs: TxScopedL2Log[] = [];
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
-
433
- // 1. Get all the secrets for the recipient and sender pairs (#9365)
434
- const appTaggingSecrets = await this.#getAppTaggingSecretsForContacts(contractAddress, recipient);
435
-
436
- // 1.1 Set up a sliding window with an offset. Chances are the sender might have messed up
437
- // and inadvertently incremented their index without use getting any logs (for example, in case
438
- // of a revert). If we stopped looking for logs the first time
439
- // we receive 0 for a tag, we might never receive anything from that sender again.
440
- // Also there's a possibility that we have advanced our index, but the sender has reused it, so
441
- // we might have missed some logs. For these reasons, we have to look both back and ahead of the
442
- // stored index
443
- const INDEX_OFFSET = 10;
444
- type SearchState = {
445
- currentTagggingSecrets: IndexedTaggingSecret[];
446
- maxIndexesToCheck: { [k: string]: number };
447
- initialSecretIndexes: { [k: string]: number };
448
- secretsToIncrement: { [k: string]: number };
449
- };
450
- const searchState = appTaggingSecrets.reduce<SearchState>(
451
- (acc, appTaggingSecret) => ({
452
- // Start looking for logs before the stored index
453
- currentTagggingSecrets: acc.currentTagggingSecrets.concat([
454
- new IndexedTaggingSecret(appTaggingSecret.secret, Math.max(0, appTaggingSecret.index - INDEX_OFFSET)),
455
- ]),
456
- // Keep looking for logs beyond the stored index
457
- maxIndexesToCheck: {
458
- ...acc.maxIndexesToCheck,
459
- ...{ [appTaggingSecret.secret.toString()]: appTaggingSecret.index + INDEX_OFFSET },
460
- },
461
- // Keeps track of the secrets we have to increment in the database
462
- secretsToIncrement: {},
463
- // Store the initial set of indexes for the secrets
464
- initialSecretIndexes: {
465
- ...acc.initialSecretIndexes,
466
- ...{ [appTaggingSecret.secret.toString()]: appTaggingSecret.index },
467
- },
468
- }),
469
- { currentTagggingSecrets: [], maxIndexesToCheck: {}, secretsToIncrement: {}, initialSecretIndexes: {} },
470
- );
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 } = {};
471
463
 
472
- let { currentTagggingSecrets } = searchState;
473
- const { maxIndexesToCheck, secretsToIncrement, initialSecretIndexes } = searchState;
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);
474
466
 
475
- while (currentTagggingSecrets.length > 0) {
476
- // 2. Compute tags using the secrets, recipient and index. Obtain logs for each tag (#9380)
477
- const currentTags = currentTagggingSecrets.map(taggingSecret =>
478
- taggingSecret.computeSiloedTag(recipient, contractAddress),
467
+ while (secretsAndWindows.length > 0) {
468
+ const secretsForTheWholeWindow = getIndexedTaggingSecretsForTheWindow(secretsAndWindows);
469
+ const tagsForTheWholeWindow = secretsForTheWholeWindow.map(secret =>
470
+ secret.computeSiloedTag(recipient, contractAddress),
479
471
  );
480
- const logsByTags = await this.aztecNode.getLogsByTags(currentTags);
481
- const newTaggingSecrets: IndexedTaggingSecret[] = [];
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
+
482
480
  logsByTags.forEach((logsByTag, logIndex) => {
483
- const { secret: currentSecret, index: currentIndex } = currentTagggingSecrets[logIndex];
484
- const currentSecretAsStr = currentSecret.toString();
485
- this.log.debug(`Syncing logs for recipient ${recipient} at contract ${contractName}(${contractAddress})`, {
486
- recipient,
487
- secret: currentSecret,
488
- index: currentIndex,
489
- contractName,
490
- contractAddress,
491
- });
492
- // 3.1. Append logs to the list and increment the index for the tags that have logs (#9380)
493
481
  if (logsByTag.length > 0) {
494
- const newIndex = currentIndex + 1;
495
- this.log.debug(
496
- `Found ${logsByTag.length} logs as recipient ${recipient}. Incrementing index to ${newIndex} at contract ${contractName}(${contractAddress})`,
497
- {
498
- recipient,
499
- secret: currentSecret,
500
- newIndex,
501
- contractName,
502
- contractAddress,
503
- },
504
- );
505
- logs.push(...logsByTag);
506
-
507
- if (currentIndex >= initialSecretIndexes[currentSecretAsStr]) {
508
- // 3.2. Increment the index for the tags that have logs, provided they're higher than the one
509
- // we have stored in the db (#9380)
510
- secretsToIncrement[currentSecretAsStr] = newIndex;
511
- // 3.3. Slide the window forwards if we have found logs beyond the initial index
512
- maxIndexesToCheck[currentSecretAsStr] = currentIndex + INDEX_OFFSET;
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
+ );
513
513
  }
514
514
  }
515
- // 3.4 Keep increasing the index (inside the window) temporarily for the tags that have no logs
516
- // There's a chance the sender missed some and we want to catch up
517
- if (currentIndex < maxIndexesToCheck[currentSecretAsStr]) {
518
- const newTaggingSecret = new IndexedTaggingSecret(currentSecret, currentIndex + 1);
519
- newTaggingSecrets.push(newTaggingSecret);
520
- }
521
515
  });
522
- await this.db.setTaggingSecretsIndexesAsRecipient(
523
- Object.keys(secretsToIncrement).map(
524
- secret => new IndexedTaggingSecret(Fr.fromHexString(secret), secretsToIncrement[secret]),
525
- ),
526
- );
527
- currentTagggingSecrets = newTaggingSecrets;
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;
528
542
  }
529
543
 
530
- result.set(
544
+ // We filter the logs by block number and store them in the map.
545
+ logsMap.set(
531
546
  recipient.toString(),
532
- // Remove logs with a block number higher than the max block number
533
- // Duplicates are likely to happen due to the sliding window, so we also filter them out
534
- logs.filter(
535
- (log, index, self) =>
536
- log.blockNumber <= maxBlockNumber && index === self.findIndex(otherLog => otherLog.equals(log)),
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),
537
554
  ),
538
555
  );
539
556
  }
540
- return result;
557
+ return logsMap;
541
558
  }
542
559
 
543
560
  /**
@@ -628,27 +645,30 @@ export class SimulatorOracle implements DBOracle {
628
645
  });
629
646
  });
630
647
  }
631
- const nullifiedNotes: IncomingNoteDao[] = [];
632
- const currentNotesForRecipient = await this.db.getIncomingNotes({ owner: recipient });
633
- const nullifiersToCheck = currentNotesForRecipient.map(note => note.siloedNullifier);
634
- const currentBlockNumber = await this.getBlockNumber();
635
- const nullifierIndexes = await this.aztecNode.findNullifiersIndexesWithBlock(currentBlockNumber, nullifiersToCheck);
636
-
637
- const foundNullifiers = nullifiersToCheck
638
- .map((nullifier, i) => {
639
- if (nullifierIndexes[i] !== undefined) {
640
- return { ...nullifierIndexes[i], ...{ data: nullifier } } as InBlock<Fr>;
641
- }
642
- })
643
- .filter(nullifier => nullifier !== undefined) as InBlock<Fr>[];
644
-
645
- await this.db.removeNullifiedNotes(foundNullifiers, recipient.toAddressPoint());
646
- nullifiedNotes.forEach(noteDao => {
647
- this.log.verbose(`Removed note for contract ${noteDao.contractAddress} at slot ${noteDao.storageSlot}`, {
648
- contract: noteDao.contractAddress,
649
- slot: noteDao.storageSlot,
650
- nullifier: noteDao.siloedNullifier.toString(),
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
+ });
651
671
  });
652
- });
672
+ }
653
673
  }
654
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
+ }
@@ -1,4 +1,5 @@
1
1
  import { BBNativePrivateKernelProver } from '@aztec/bb-prover';
2
+ import { BBWasmPrivateKernelProver } from '@aztec/bb-prover/wasm';
2
3
  import { type AztecNode, type PrivateKernelProver } from '@aztec/circuit-types';
3
4
  import { randomBytes } from '@aztec/foundation/crypto';
4
5
  import { createLogger } from '@aztec/foundation/log';
@@ -59,9 +60,11 @@ function createProver(config: PXEServiceConfig, logSuffix?: string) {
59
60
 
60
61
  // (@PhilWindle) Temporary validation until WASM is implemented
61
62
  if (!config.bbBinaryPath || !config.bbWorkingDirectory) {
62
- throw new Error(`Prover must be configured with binary path and working directory`);
63
+ return new BBWasmPrivateKernelProver(16);
64
+ } else {
65
+ const bbConfig = config as Required<Pick<PXEServiceConfig, 'bbBinaryPath' | 'bbWorkingDirectory'>> &
66
+ PXEServiceConfig;
67
+ const log = createLogger('pxe:bb-native-prover' + (logSuffix ? `:${logSuffix}` : ''));
68
+ return BBNativePrivateKernelProver.new({ bbSkipCleanup: false, ...bbConfig }, log);
63
69
  }
64
- const bbConfig = config as Required<Pick<PXEServiceConfig, 'bbBinaryPath' | 'bbWorkingDirectory'>> & PXEServiceConfig;
65
- const log = createLogger('pxe:bb-native-prover' + (logSuffix ? `:${logSuffix}` : ''));
66
- return BBNativePrivateKernelProver.new({ bbSkipCleanup: false, ...bbConfig }, log);
67
70
  }