@aztec/pxe 0.87.7 → 1.0.0-nightly.20250605

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 (44) hide show
  1. package/dest/contract_function_simulator/event_validation_request.d.ts +22 -0
  2. package/dest/contract_function_simulator/event_validation_request.d.ts.map +1 -0
  3. package/dest/contract_function_simulator/event_validation_request.js +43 -0
  4. package/dest/contract_function_simulator/execution_data_provider.d.ts +25 -28
  5. package/dest/contract_function_simulator/execution_data_provider.d.ts.map +1 -1
  6. package/dest/contract_function_simulator/note_validation_request.d.ts +21 -0
  7. package/dest/contract_function_simulator/note_validation_request.d.ts.map +1 -0
  8. package/dest/contract_function_simulator/note_validation_request.js +42 -0
  9. package/dest/contract_function_simulator/oracle/oracle.d.ts +3 -3
  10. package/dest/contract_function_simulator/oracle/oracle.d.ts.map +1 -1
  11. package/dest/contract_function_simulator/oracle/oracle.js +23 -19
  12. package/dest/contract_function_simulator/oracle/typed_oracle.d.ts +6 -6
  13. package/dest/contract_function_simulator/oracle/typed_oracle.d.ts.map +1 -1
  14. package/dest/contract_function_simulator/oracle/typed_oracle.js +7 -7
  15. package/dest/contract_function_simulator/oracle/utility_execution_oracle.d.ts +5 -6
  16. package/dest/contract_function_simulator/oracle/utility_execution_oracle.d.ts.map +1 -1
  17. package/dest/contract_function_simulator/oracle/utility_execution_oracle.js +8 -8
  18. package/dest/contract_function_simulator/pxe_oracle_interface.d.ts +7 -5
  19. package/dest/contract_function_simulator/pxe_oracle_interface.d.ts.map +1 -1
  20. package/dest/contract_function_simulator/pxe_oracle_interface.js +81 -32
  21. package/dest/private_kernel/hints/build_private_kernel_reset_private_inputs.d.ts.map +1 -1
  22. package/dest/private_kernel/hints/build_private_kernel_reset_private_inputs.js +30 -30
  23. package/dest/private_kernel/private_kernel_execution_prover.d.ts.map +1 -1
  24. package/dest/private_kernel/private_kernel_execution_prover.js +9 -7
  25. package/dest/pxe_service/pxe_service.d.ts.map +1 -1
  26. package/dest/pxe_service/pxe_service.js +10 -3
  27. package/dest/storage/capsule_data_provider/capsule_data_provider.d.ts +4 -2
  28. package/dest/storage/capsule_data_provider/capsule_data_provider.d.ts.map +1 -1
  29. package/dest/storage/capsule_data_provider/capsule_data_provider.js +46 -7
  30. package/dest/storage/note_data_provider/note_data_provider.d.ts.map +1 -1
  31. package/dest/storage/note_data_provider/note_data_provider.js +14 -14
  32. package/package.json +16 -16
  33. package/src/contract_function_simulator/event_validation_request.ts +53 -0
  34. package/src/contract_function_simulator/execution_data_provider.ts +29 -50
  35. package/src/contract_function_simulator/note_validation_request.ts +52 -0
  36. package/src/contract_function_simulator/oracle/oracle.ts +27 -50
  37. package/src/contract_function_simulator/oracle/typed_oracle.ts +18 -26
  38. package/src/contract_function_simulator/oracle/utility_execution_oracle.ts +15 -42
  39. package/src/contract_function_simulator/pxe_oracle_interface.ts +157 -51
  40. package/src/private_kernel/hints/build_private_kernel_reset_private_inputs.ts +35 -34
  41. package/src/private_kernel/private_kernel_execution_prover.ts +11 -10
  42. package/src/pxe_service/pxe_service.ts +10 -3
  43. package/src/storage/capsule_data_provider/capsule_data_provider.ts +56 -7
  44. package/src/storage/note_data_provider/note_data_provider.ts +22 -22
@@ -18,9 +18,10 @@ import type { KeyValidationRequest } from '@aztec/stdlib/kernel';
18
18
  import { computeAddressSecret, computeAppTaggingSecret } from '@aztec/stdlib/keys';
19
19
  import {
20
20
  IndexedTaggingSecret,
21
- LogWithTxData,
22
21
  PendingTaggedLog,
22
+ PrivateLogWithTxData,
23
23
  PublicLog,
24
+ PublicLogWithTxData,
24
25
  TxScopedL2Log,
25
26
  deriveEcdhSharedSecret,
26
27
  } from '@aztec/stdlib/logs';
@@ -40,6 +41,8 @@ import type { NoteDataProvider } from '../storage/note_data_provider/note_data_p
40
41
  import type { PrivateEventDataProvider } from '../storage/private_event_data_provider/private_event_data_provider.js';
41
42
  import type { SyncDataProvider } from '../storage/sync_data_provider/sync_data_provider.js';
42
43
  import type { TaggingDataProvider } from '../storage/tagging_data_provider/tagging_data_provider.js';
44
+ import { EventValidationRequest } from './event_validation_request.js';
45
+ import { NoteValidationRequest } from './note_validation_request.js';
43
46
  import type { ProxiedNode } from './proxied_node.js';
44
47
  import { WINDOW_HALF_SIZE, getIndexedTaggingSecretsForTheWindow, getInitialIndexesMap } from './tagging_utils.js';
45
48
 
@@ -384,7 +387,7 @@ export class PXEOracleInterface implements ExecutionDataProvider {
384
387
  });
385
388
 
386
389
  // We fetch the logs for the tags
387
- const possibleLogs = await this.aztecNode.getLogsByTags(currentTags);
390
+ const possibleLogs = await this.#getPrivateLogsByTags(currentTags);
388
391
 
389
392
  // We find the index of the last log in the window that is not empty
390
393
  const indexOfLastLog = possibleLogs.findLastIndex(possibleLog => possibleLog.length !== 0);
@@ -421,8 +424,8 @@ export class PXEOracleInterface implements ExecutionDataProvider {
421
424
  }
422
425
 
423
426
  /**
424
- * Synchronizes the logs tagged with scoped addresses and all the senders in the address book. Stores the found logs
425
- * in CapsuleArray ready for a later retrieval in Aztec.nr.
427
+ * Synchronizes the private logs tagged with scoped addresses and all the senders in the address book. Stores the found
428
+ * logs in CapsuleArray ready for a later retrieval in Aztec.nr.
426
429
  * @param contractAddress - The address of the contract that the logs are tagged for.
427
430
  * @param pendingTaggedLogArrayBaseSlot - The base slot of the pending tagged logs capsule array in which
428
431
  * found logs will be stored.
@@ -482,20 +485,14 @@ export class PXEOracleInterface implements ExecutionDataProvider {
482
485
  // a new set of secrets and windows to fetch logs for.
483
486
  const newLargestIndexMapForIteration: { [k: string]: number } = {};
484
487
 
485
- // Fetch the logs for the tags and iterate over them
486
- const logsByTags = await this.aztecNode.getLogsByTags(tagsForTheWholeWindow);
488
+ // Fetch the private logs for the tags and iterate over them
489
+ const logsByTags = await this.#getPrivateLogsByTags(tagsForTheWholeWindow);
487
490
 
488
491
  for (let logIndex = 0; logIndex < logsByTags.length; logIndex++) {
489
492
  const logsByTag = logsByTags[logIndex];
490
493
  if (logsByTag.length > 0) {
491
- // Discard public logs
492
- const filteredLogsByTag = logsByTag.filter(l => !l.isFromPublic);
493
- if (filteredLogsByTag.length < logsByTag.length) {
494
- this.log.warn(`Discarded ${logsByTag.filter(l => l.isFromPublic).length} public logs with matching tags`);
495
- }
496
-
497
494
  // We filter out the logs that are newer than the historical block number of the tx currently being constructed
498
- const filteredLogsByBlockNumber = filteredLogsByTag.filter(l => l.blockNumber <= maxBlockNumber);
495
+ const filteredLogsByBlockNumber = logsByTag.filter(l => l.blockNumber <= maxBlockNumber);
499
496
 
500
497
  // We store the logs in capsules (to later be obtained in Noir)
501
498
  await this.#storePendingTaggedLogs(
@@ -510,7 +507,7 @@ export class PXEOracleInterface implements ExecutionDataProvider {
510
507
  const secretCorrespondingToLog = secretsForTheWholeWindow[logIndex];
511
508
  const initialIndex = initialIndexesMap[secretCorrespondingToLog.appTaggingSecret.toString()];
512
509
 
513
- this.log.debug(`Found ${filteredLogsByTag.length} logs as recipient ${recipient}`, {
510
+ this.log.debug(`Found ${logsByTags.length} logs as recipient ${recipient}`, {
514
511
  recipient,
515
512
  secret: secretCorrespondingToLog.appTaggingSecret,
516
513
  contractName,
@@ -606,7 +603,55 @@ export class PXEOracleInterface implements ExecutionDataProvider {
606
603
  return this.capsuleDataProvider.appendToCapsuleArray(contractAddress, capsuleArrayBaseSlot, pendingTaggedLogs);
607
604
  }
608
605
 
609
- public async deliverNote(
606
+ public async validateEnqueuedNotesAndEvents(
607
+ contractAddress: AztecAddress,
608
+ noteValidationRequestsArrayBaseSlot: Fr,
609
+ eventValidationRequestsArrayBaseSlot: Fr,
610
+ ): Promise<void> {
611
+ // We read all note and event validation requests and process them all concurrently. This makes the process much
612
+ // faster as we don't need to wait for the network round-trip.
613
+ const noteValidationRequests = (
614
+ await this.capsuleDataProvider.readCapsuleArray(contractAddress, noteValidationRequestsArrayBaseSlot)
615
+ ).map(NoteValidationRequest.fromFields);
616
+
617
+ const eventValidationRequests = (
618
+ await this.capsuleDataProvider.readCapsuleArray(contractAddress, eventValidationRequestsArrayBaseSlot)
619
+ ).map(EventValidationRequest.fromFields);
620
+
621
+ const noteDeliveries = noteValidationRequests.map(request =>
622
+ this.deliverNote(
623
+ request.contractAddress,
624
+ request.storageSlot,
625
+ request.nonce,
626
+ request.content,
627
+ request.noteHash,
628
+ request.nullifier,
629
+ request.txHash,
630
+ request.recipient,
631
+ ),
632
+ );
633
+
634
+ const eventDeliveries = eventValidationRequests.map(request =>
635
+ this.deliverEvent(
636
+ request.contractAddress,
637
+ request.eventTypeId,
638
+ request.serializedEvent,
639
+ request.eventCommitment,
640
+ request.txHash,
641
+ request.logIndexInTx,
642
+ request.txIndexInBlock,
643
+ request.recipient,
644
+ ),
645
+ );
646
+
647
+ await Promise.all([...noteDeliveries, ...eventDeliveries]);
648
+
649
+ // Requests are cleared once we're done.
650
+ await this.capsuleDataProvider.resetCapsuleArray(contractAddress, noteValidationRequestsArrayBaseSlot, []);
651
+ await this.capsuleDataProvider.resetCapsuleArray(contractAddress, eventValidationRequestsArrayBaseSlot, []);
652
+ }
653
+
654
+ async deliverNote(
610
655
  contractAddress: AztecAddress,
611
656
  storageSlot: Fr,
612
657
  nonce: Fr,
@@ -633,6 +678,8 @@ export class PXEOracleInterface implements ExecutionDataProvider {
633
678
  // in time of the locally synced state.
634
679
  // Note that while this technically results in historical queries, we perform it at the latest locally synced block
635
680
  // number which *should* be recent enough to be available, even for non-archive nodes.
681
+ // Also note that the note should never be ahead of the synced block here since `fetchTaggedLogs` only processes
682
+ // logs up to the synced block making this only an additional safety check.
636
683
  const syncedBlockNumber = await this.syncDataProvider.getBlockNumber();
637
684
 
638
685
  // By computing siloed and unique note hashes ourselves we prevent contracts from interfering with the note storage
@@ -690,18 +737,58 @@ export class PXEOracleInterface implements ExecutionDataProvider {
690
737
  }
691
738
  }
692
739
 
693
- public async getLogByTag(tag: Fr): Promise<LogWithTxData | null> {
694
- const logs = await this.aztecNode.getLogsByTags([tag]);
740
+ async deliverEvent(
741
+ contractAddress: AztecAddress,
742
+ selector: EventSelector,
743
+ content: Fr[],
744
+ eventCommitment: Fr,
745
+ txHash: TxHash,
746
+ logIndexInTx: number,
747
+ txIndexInBlock: number,
748
+ recipient: AztecAddress,
749
+ ): Promise<void> {
750
+ // While using 'latest' block number would be fine for private events since they cannot be accessed from Aztec.nr
751
+ // (and thus we're less concerned about being ahead of the synced block), we use the synced block number to
752
+ // maintain consistent behavior in the PXE. Additionally, events should never be ahead of the synced block here
753
+ // since `fetchTaggedLogs` only processes logs up to the synced block.
754
+ const syncedBlockNumber = await this.syncDataProvider.getBlockNumber();
755
+
756
+ const siloedEventCommitment = await siloNullifier(contractAddress, eventCommitment);
757
+
758
+ const [nullifierIndex] = await this.aztecNode.findLeavesIndexes(syncedBlockNumber, MerkleTreeId.NULLIFIER_TREE, [
759
+ siloedEventCommitment,
760
+ ]);
761
+
762
+ if (nullifierIndex === undefined) {
763
+ throw new Error(
764
+ `Event commitment ${eventCommitment} (siloed as ${siloedEventCommitment}) is not present on the nullifier tree at block ${syncedBlockNumber} (from tx ${txHash})`,
765
+ );
766
+ }
767
+
768
+ return this.privateEventDataProvider.storePrivateEventLog(
769
+ contractAddress,
770
+ recipient,
771
+ selector,
772
+ content,
773
+ txHash,
774
+ logIndexInTx,
775
+ txIndexInBlock,
776
+ nullifierIndex.l2BlockNumber, // Block in which the event was emitted
777
+ );
778
+ }
779
+
780
+ public async getPublicLogByTag(tag: Fr, contractAddress: AztecAddress): Promise<PublicLogWithTxData | null> {
781
+ const logs = await this.#getPublicLogsByTagsFromContract([tag], contractAddress);
695
782
  const logsForTag = logs[0];
696
783
 
697
- this.log.debug(`Got ${logsForTag.length} logs for tag ${tag}`);
784
+ this.log.debug(`Got ${logsForTag.length} public logs for tag ${tag}`);
698
785
 
699
786
  if (logsForTag.length == 0) {
700
787
  return null;
701
788
  } else if (logsForTag.length > 1) {
702
789
  // TODO(#11627): handle this case
703
790
  throw new Error(
704
- `Got ${logsForTag.length} logs for tag ${tag}. getLogByTag currently only supports a single log per tag`,
791
+ `Got ${logsForTag.length} logs for tag ${tag} and contract ${contractAddress.toString()}. getPublicLogByTag currently only supports a single log per tag`,
705
792
  );
706
793
  }
707
794
 
@@ -715,11 +802,45 @@ export class PXEOracleInterface implements ExecutionDataProvider {
715
802
  throw new Error(`Unexpected: failed to retrieve tx effects for tx ${scopedLog.txHash} which is known to exist`);
716
803
  }
717
804
 
718
- const logContent = (scopedLog.isFromPublic ? [(scopedLog.log as PublicLog).contractAddress.toField()] : []).concat(
719
- scopedLog.log.getEmittedFields(),
805
+ return new PublicLogWithTxData(
806
+ scopedLog.log.getEmittedFieldsWithoutTag(),
807
+ scopedLog.txHash,
808
+ txEffect.data.noteHashes,
809
+ txEffect.data.nullifiers[0],
720
810
  );
811
+ }
721
812
 
722
- return new LogWithTxData(logContent, scopedLog.txHash, txEffect.data.noteHashes, txEffect.data.nullifiers[0]);
813
+ public async getPrivateLogByTag(siloedTag: Fr): Promise<PrivateLogWithTxData | null> {
814
+ const logs = await this.#getPrivateLogsByTags([siloedTag]);
815
+ const logsForTag = logs[0];
816
+
817
+ this.log.debug(`Got ${logsForTag.length} private logs for tag ${siloedTag}`);
818
+
819
+ if (logsForTag.length == 0) {
820
+ return null;
821
+ } else if (logsForTag.length > 1) {
822
+ // TODO(#11627): handle this case
823
+ throw new Error(
824
+ `Got ${logsForTag.length} logs for tag ${siloedTag}. getPrivateLogByTag currently only supports a single log per tag`,
825
+ );
826
+ }
827
+
828
+ const scopedLog = logsForTag[0];
829
+
830
+ // getLogsByTag doesn't have all of the information that we need (notably note hashes and the first nullifier), so
831
+ // we need to make a second call to the node for `getTxEffect`.
832
+ // TODO(#9789): bundle this information in the `getLogsByTag` call.
833
+ const txEffect = await this.aztecNode.getTxEffect(scopedLog.txHash);
834
+ if (txEffect == undefined) {
835
+ throw new Error(`Unexpected: failed to retrieve tx effects for tx ${scopedLog.txHash} which is known to exist`);
836
+ }
837
+
838
+ return new PrivateLogWithTxData(
839
+ scopedLog.log.getEmittedFieldsWithoutTag(),
840
+ scopedLog.txHash,
841
+ txEffect.data.noteHashes,
842
+ txEffect.data.nullifiers[0],
843
+ );
723
844
  }
724
845
 
725
846
  public async removeNullifiedNotes(contractAddress: AztecAddress) {
@@ -791,35 +912,20 @@ export class PXEOracleInterface implements ExecutionDataProvider {
791
912
  return deriveEcdhSharedSecret(addressSecret, ephPk);
792
913
  }
793
914
 
794
- async storePrivateEventLog(
795
- contractAddress: AztecAddress,
796
- recipient: AztecAddress,
797
- eventSelector: EventSelector,
798
- msgContent: Fr[],
799
- txHash: TxHash,
800
- logIndexInTx: number,
801
- txIndexInBlock: number,
802
- ): Promise<void> {
803
- const txReceipt = await this.aztecNode.getTxReceipt(txHash);
804
- const blockNumber = txReceipt.blockNumber;
805
- if (blockNumber === undefined) {
806
- throw new Error(`Block number is undefined for tx ${txHash} in storePrivateEventLog`);
807
- }
808
- const historicalBlockNumber = await this.syncDataProvider.getBlockNumber();
809
- if (blockNumber > historicalBlockNumber) {
810
- throw new Error(
811
- `Attempting to store private event log from a block newer than the historical block of the simulation. Log block number: ${blockNumber}, historical block number: ${historicalBlockNumber}`,
812
- );
813
- }
814
- return this.privateEventDataProvider.storePrivateEventLog(
815
- contractAddress,
816
- recipient,
817
- eventSelector,
818
- msgContent,
819
- txHash,
820
- logIndexInTx,
821
- txIndexInBlock,
822
- blockNumber,
915
+ // TODO(#12656): Make this a public function on the AztecNode interface and remove the original getLogsByTags. This
916
+ // was not done yet as we were unsure about the API and we didn't want to introduce a breaking change.
917
+ async #getPrivateLogsByTags(tags: Fr[]): Promise<TxScopedL2Log[][]> {
918
+ const allLogs = await this.aztecNode.getLogsByTags(tags);
919
+ return allLogs.map(logs => logs.filter(log => !log.isFromPublic));
920
+ }
921
+
922
+ // TODO(#12656): Make this a public function on the AztecNode interface and remove the original getLogsByTags. This
923
+ // was not done yet as we were unsure about the API and we didn't want to introduce a breaking change.
924
+ async #getPublicLogsByTagsFromContract(tags: Fr[], contractAddress: AztecAddress): Promise<TxScopedL2Log[][]> {
925
+ const allLogs = await this.aztecNode.getLogsByTags(tags);
926
+ const allPublicLogs = allLogs.map(logs => logs.filter(log => log.isFromPublic));
927
+ return allPublicLogs.map(logs =>
928
+ logs.filter(log => (log.log as PublicLog).contractAddress.equals(contractAddress)),
823
929
  );
824
930
  }
825
931
 
@@ -6,7 +6,6 @@ import {
6
6
  MAX_NULLIFIER_READ_REQUESTS_PER_TX,
7
7
  MAX_PRIVATE_LOGS_PER_TX,
8
8
  NULLIFIER_TREE_HEIGHT,
9
- VK_TREE_HEIGHT,
10
9
  } from '@aztec/constants';
11
10
  import { makeTuple } from '@aztec/foundation/array';
12
11
  import { padArrayEnd } from '@aztec/foundation/collection';
@@ -16,6 +15,7 @@ import { MembershipWitness } from '@aztec/foundation/trees';
16
15
  import { privateKernelResetDimensionsConfig } from '@aztec/noir-protocol-circuits-types/client';
17
16
  import {
18
17
  KeyValidationHint,
18
+ PaddedSideEffects,
19
19
  type PrivateCircuitPublicInputs,
20
20
  type PrivateKernelCircuitPublicInputs,
21
21
  PrivateKernelData,
@@ -42,6 +42,7 @@ import {
42
42
  privateKernelResetDimensionNames,
43
43
  } from '@aztec/stdlib/kernel';
44
44
  import { type PrivateCallExecutionResult, collectNested } from '@aztec/stdlib/tx';
45
+ import { VkData } from '@aztec/stdlib/vks';
45
46
 
46
47
  import type { PrivateKernelOracle } from '../private_kernel_oracle.js';
47
48
 
@@ -74,22 +75,17 @@ function getNullifierMembershipWitnessResolver(oracle: PrivateKernelOracle) {
74
75
 
75
76
  async function getMasterSecretKeysAndAppKeyGenerators(
76
77
  keyValidationRequests: Tuple<ScopedKeyValidationRequestAndGenerator, typeof MAX_KEY_VALIDATION_REQUESTS_PER_TX>,
78
+ numRequestsToVerify: number,
77
79
  oracle: PrivateKernelOracle,
78
80
  ) {
79
- const keysHints = [];
80
- for (let i = 0; i < keyValidationRequests.length; ++i) {
81
- const request = keyValidationRequests[i].request;
82
- if (request.isEmpty()) {
83
- break;
84
- }
85
- const secretKeys = await oracle.getMasterSecretKey(request.request.pkM);
86
- keysHints.push(new KeyValidationHint(secretKeys, i));
87
- }
88
- return padArrayEnd(
89
- keysHints,
90
- KeyValidationHint.nada(MAX_KEY_VALIDATION_REQUESTS_PER_TX),
91
- MAX_KEY_VALIDATION_REQUESTS_PER_TX,
81
+ const numRequests = countAccumulatedItems(keyValidationRequests);
82
+ const keysHints = await Promise.all(
83
+ keyValidationRequests.slice(0, Math.min(numRequests, numRequestsToVerify)).map(async ({ request }) => {
84
+ const secretKeys = await oracle.getMasterSecretKey(request.request.pkM);
85
+ return new KeyValidationHint(secretKeys);
86
+ }),
92
87
  );
88
+ return padArrayEnd(keysHints, KeyValidationHint.empty(), MAX_KEY_VALIDATION_REQUESTS_PER_TX);
93
89
  }
94
90
 
95
91
  export class PrivateKernelResetPrivateInputsBuilder {
@@ -166,26 +162,30 @@ export class PrivateKernelResetPrivateInputsBuilder {
166
162
  const previousVkMembershipWitness = await oracle.getVkMembershipWitness(
167
163
  this.previousKernelOutput.verificationKey.keyAsFields,
168
164
  );
169
- const previousKernelData = new PrivateKernelData(
170
- this.previousKernelOutput.publicInputs,
165
+ const vkData = new VkData(
171
166
  this.previousKernelOutput.verificationKey,
172
167
  Number(previousVkMembershipWitness.leafIndex),
173
- assertLength<Fr, typeof VK_TREE_HEIGHT>(previousVkMembershipWitness.siblingPath, VK_TREE_HEIGHT),
168
+ previousVkMembershipWitness.siblingPath,
174
169
  );
170
+ const previousKernelData = new PrivateKernelData(this.previousKernelOutput.publicInputs, vkData);
175
171
 
176
172
  this.reduceReadRequestStates(
177
173
  this.noteHashResetStates,
178
- dimensions.NOTE_HASH_PENDING_AMOUNT,
179
- dimensions.NOTE_HASH_SETTLED_AMOUNT,
174
+ dimensions.NOTE_HASH_PENDING_READ,
175
+ dimensions.NOTE_HASH_SETTLED_READ,
180
176
  );
181
177
  this.reduceReadRequestStates(
182
178
  this.nullifierResetStates,
183
- dimensions.NULLIFIER_PENDING_AMOUNT,
184
- dimensions.NULLIFIER_SETTLED_AMOUNT,
179
+ dimensions.NULLIFIER_PENDING_READ,
180
+ dimensions.NULLIFIER_SETTLED_READ,
185
181
  );
186
182
 
183
+ // TODO: Enable padding when we have a better idea what are the final amounts we should pad to.
184
+ const paddedSideEffects = PaddedSideEffects.empty();
185
+
187
186
  return new PrivateKernelResetCircuitPrivateInputs(
188
187
  previousKernelData,
188
+ paddedSideEffects,
189
189
  new PrivateKernelResetHints(
190
190
  await buildNoteHashReadRequestHintsFromResetStates(
191
191
  oracle,
@@ -201,6 +201,7 @@ export class PrivateKernelResetPrivateInputsBuilder {
201
201
  ),
202
202
  await getMasterSecretKeysAndAppKeyGenerators(
203
203
  this.previousKernel.validationRequests.scopedKeyValidationRequestsAndGenerators,
204
+ dimensions.KEY_VALIDATION,
204
205
  oracle,
205
206
  ),
206
207
  this.transientDataIndexHints,
@@ -266,19 +267,19 @@ export class PrivateKernelResetPrivateInputsBuilder {
266
267
 
267
268
  if (!this.nextIteration) {
268
269
  this.noteHashResetStates = resetStates;
269
- this.requestedDimensions.NOTE_HASH_PENDING_AMOUNT = numPendingReads;
270
- this.requestedDimensions.NOTE_HASH_SETTLED_AMOUNT = numSettledReads;
270
+ this.requestedDimensions.NOTE_HASH_PENDING_READ = numPendingReads;
271
+ this.requestedDimensions.NOTE_HASH_SETTLED_READ = numSettledReads;
271
272
  } else {
272
273
  // Pick only one dimension to reset if next iteration is not empty.
273
274
  if (numPendingReads > numSettledReads) {
274
- this.requestedDimensions.NOTE_HASH_PENDING_AMOUNT = numPendingReads;
275
+ this.requestedDimensions.NOTE_HASH_PENDING_READ = numPendingReads;
275
276
  this.noteHashResetStates.states = assertLength(
276
277
  resetStates.states.map(state => (state === ReadRequestState.PENDING ? state : ReadRequestState.NADA)),
277
278
  MAX_NOTE_HASH_READ_REQUESTS_PER_TX,
278
279
  );
279
280
  this.noteHashResetStates.pendingReadHints = resetStates.pendingReadHints;
280
281
  } else {
281
- this.requestedDimensions.NOTE_HASH_SETTLED_AMOUNT = numSettledReads;
282
+ this.requestedDimensions.NOTE_HASH_SETTLED_READ = numSettledReads;
282
283
  this.noteHashResetStates.states = assertLength(
283
284
  resetStates.states.map(state => (state === ReadRequestState.SETTLED ? state : ReadRequestState.NADA)),
284
285
  MAX_NOTE_HASH_READ_REQUESTS_PER_TX,
@@ -318,19 +319,19 @@ export class PrivateKernelResetPrivateInputsBuilder {
318
319
 
319
320
  if (!this.nextIteration) {
320
321
  this.nullifierResetStates = resetStates;
321
- this.requestedDimensions.NULLIFIER_PENDING_AMOUNT = numPendingReads;
322
- this.requestedDimensions.NULLIFIER_SETTLED_AMOUNT = numSettledReads;
322
+ this.requestedDimensions.NULLIFIER_PENDING_READ = numPendingReads;
323
+ this.requestedDimensions.NULLIFIER_SETTLED_READ = numSettledReads;
323
324
  } else {
324
325
  // Pick only one dimension to reset if next iteration is not empty.
325
326
  if (numPendingReads > numSettledReads) {
326
- this.requestedDimensions.NULLIFIER_PENDING_AMOUNT = numPendingReads;
327
+ this.requestedDimensions.NULLIFIER_PENDING_READ = numPendingReads;
327
328
  this.nullifierResetStates.states = assertLength(
328
329
  resetStates.states.map(state => (state === ReadRequestState.PENDING ? state : ReadRequestState.NADA)),
329
330
  MAX_NULLIFIER_READ_REQUESTS_PER_TX,
330
331
  );
331
332
  this.nullifierResetStates.pendingReadHints = resetStates.pendingReadHints;
332
333
  } else {
333
- this.requestedDimensions.NULLIFIER_SETTLED_AMOUNT = numSettledReads;
334
+ this.requestedDimensions.NULLIFIER_SETTLED_READ = numSettledReads;
334
335
  this.nullifierResetStates.states = assertLength(
335
336
  resetStates.states.map(state => (state === ReadRequestState.SETTLED ? state : ReadRequestState.NADA)),
336
337
  MAX_NULLIFIER_READ_REQUESTS_PER_TX,
@@ -353,7 +354,7 @@ export class PrivateKernelResetPrivateInputsBuilder {
353
354
  return false;
354
355
  }
355
356
 
356
- this.requestedDimensions.NULLIFIER_KEYS = numCurr;
357
+ this.requestedDimensions.KEY_VALIDATION = numCurr;
357
358
 
358
359
  return true;
359
360
  }
@@ -430,7 +431,7 @@ export class PrivateKernelResetPrivateInputsBuilder {
430
431
 
431
432
  this.numTransientData = numTransientData;
432
433
  this.transientDataIndexHints = transientDataIndexHints;
433
- this.requestedDimensions.TRANSIENT_DATA_AMOUNT = numTransientData;
434
+ this.requestedDimensions.TRANSIENT_DATA_SQUASHING = numTransientData;
434
435
 
435
436
  return numTransientData > 0;
436
437
  }
@@ -442,7 +443,7 @@ export class PrivateKernelResetPrivateInputsBuilder {
442
443
 
443
444
  const numNoteHashes = this.previousKernel.end.noteHashes.filter(n => !n.contractAddress.isZero()).length;
444
445
  const numToSilo = Math.max(0, numNoteHashes - this.numTransientData);
445
- this.requestedDimensions.NOTE_HASH_SILOING_AMOUNT = numToSilo;
446
+ this.requestedDimensions.NOTE_HASH_SILOING = numToSilo;
446
447
 
447
448
  return numToSilo > 0;
448
449
  }
@@ -458,7 +459,7 @@ export class PrivateKernelResetPrivateInputsBuilder {
458
459
  // The reset circuit checks that capped_size must be greater than or equal to all non-empty nullifiers.
459
460
  // Which includes the first nullifier, even though its contract address is always zero and doesn't need siloing.
460
461
  const cappedSize = numToSilo ? numToSilo + 1 : 0;
461
- this.requestedDimensions.NULLIFIER_SILOING_AMOUNT = cappedSize;
462
+ this.requestedDimensions.NULLIFIER_SILOING = cappedSize;
462
463
 
463
464
  return numToSilo > 0;
464
465
  }
@@ -478,7 +479,7 @@ export class PrivateKernelResetPrivateInputsBuilder {
478
479
  const numSquashedLogs = privateLogs.filter(l => squashedNoteHashCounters.includes(l.inner.noteHashCounter)).length;
479
480
 
480
481
  const numToSilo = numLogs - numSquashedLogs;
481
- this.requestedDimensions.PRIVATE_LOG_SILOING_AMOUNT = numToSilo;
482
+ this.requestedDimensions.PRIVATE_LOG_SILOING = numToSilo;
482
483
 
483
484
  return numToSilo > 0;
484
485
  }
@@ -1,8 +1,6 @@
1
- import { VK_TREE_HEIGHT } from '@aztec/constants';
2
1
  import { vkAsFieldsMegaHonk } from '@aztec/foundation/crypto';
3
2
  import { Fr } from '@aztec/foundation/fields';
4
3
  import { createLogger } from '@aztec/foundation/log';
5
- import { assertLength } from '@aztec/foundation/serialize';
6
4
  import { pushTestData } from '@aztec/foundation/testing';
7
5
  import { Timer } from '@aztec/foundation/timer';
8
6
  import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types/vk-tree';
@@ -12,6 +10,7 @@ import { computeContractAddressFromInstance } from '@aztec/stdlib/contract';
12
10
  import { hashVK } from '@aztec/stdlib/hash';
13
11
  import type { PrivateKernelProver } from '@aztec/stdlib/interfaces/client';
14
12
  import {
13
+ PaddedSideEffectAmounts,
15
14
  PrivateCallData,
16
15
  type PrivateExecutionStep,
17
16
  PrivateKernelCircuitPublicInputs,
@@ -33,7 +32,7 @@ import {
33
32
  collectNoteHashNullifierCounterMap,
34
33
  getFinalMinRevertibleSideEffectCounter,
35
34
  } from '@aztec/stdlib/tx';
36
- import { VerificationKeyAsFields, VerificationKeyData } from '@aztec/stdlib/vks';
35
+ import { VerificationKeyAsFields, VerificationKeyData, VkData } from '@aztec/stdlib/vks';
37
36
 
38
37
  import { PrivateKernelResetPrivateInputsBuilder } from './hints/build_private_kernel_reset_private_inputs.js';
39
38
  import type { PrivateKernelOracle } from './private_kernel_oracle.js';
@@ -195,12 +194,12 @@ export class PrivateKernelExecutionProver {
195
194
  const previousVkMembershipWitness = await this.oracle.getVkMembershipWitness(
196
195
  output.verificationKey.keyAsFields,
197
196
  );
198
- const previousKernelData = new PrivateKernelData(
199
- output.publicInputs,
197
+ const vkData = new VkData(
200
198
  output.verificationKey,
201
199
  Number(previousVkMembershipWitness.leafIndex),
202
- assertLength<Fr, typeof VK_TREE_HEIGHT>(previousVkMembershipWitness.siblingPath, VK_TREE_HEIGHT),
200
+ previousVkMembershipWitness.siblingPath,
203
201
  );
202
+ const previousKernelData = new PrivateKernelData(output.publicInputs, vkData);
204
203
  const proofInput = new PrivateKernelInnerCircuitPrivateInputs(previousKernelData, privateCallData);
205
204
 
206
205
  pushTestData('private-kernel-inputs-inner', proofInput);
@@ -261,18 +260,20 @@ export class PrivateKernelExecutionProver {
261
260
  }
262
261
  // Private tail.
263
262
  const previousVkMembershipWitness = await this.oracle.getVkMembershipWitness(output.verificationKey.keyAsFields);
264
- const previousKernelData = new PrivateKernelData(
265
- output.publicInputs,
263
+ const vkData = new VkData(
266
264
  output.verificationKey,
267
265
  Number(previousVkMembershipWitness.leafIndex),
268
- assertLength<Fr, typeof VK_TREE_HEIGHT>(previousVkMembershipWitness.siblingPath, VK_TREE_HEIGHT),
266
+ previousVkMembershipWitness.siblingPath,
269
267
  );
268
+ const previousKernelData = new PrivateKernelData(output.publicInputs, vkData);
270
269
 
271
270
  this.log.debug(
272
271
  `Calling private kernel tail with hwm ${previousKernelData.publicInputs.minRevertibleSideEffectCounter}`,
273
272
  );
274
273
 
275
- const privateInputs = new PrivateKernelTailCircuitPrivateInputs(previousKernelData);
274
+ // TODO: Enable padding when we have a better what are the final amounts we should pad to.
275
+ const paddedSideEffectAmounts = PaddedSideEffectAmounts.empty();
276
+ const privateInputs = new PrivateKernelTailCircuitPrivateInputs(previousKernelData, paddedSideEffectAmounts);
276
277
 
277
278
  pushTestData('private-kernel-inputs-ordering', privateInputs);
278
279
 
@@ -49,7 +49,7 @@ import type {
49
49
  } from '@aztec/stdlib/interfaces/client';
50
50
  import type { PrivateKernelExecutionProofOutput, PrivateKernelTailCircuitPublicInputs } from '@aztec/stdlib/kernel';
51
51
  import type { LogFilter } from '@aztec/stdlib/logs';
52
- import { getNonNullifiedL1ToL2MessageWitness } from '@aztec/stdlib/messaging';
52
+ import { computeL2ToL1MembershipWitness, getNonNullifiedL1ToL2MessageWitness } from '@aztec/stdlib/messaging';
53
53
  import { type NotesFilter, UniqueNote } from '@aztec/stdlib/note';
54
54
  import { MerkleTreeId } from '@aztec/stdlib/trees';
55
55
  import {
@@ -196,8 +196,15 @@ export class PXEService implements PXE {
196
196
  return this.node.isL1ToL2MessageSynced(l1ToL2Message);
197
197
  }
198
198
 
199
- public getL2ToL1MembershipWitness(blockNumber: number, l2Tol1Message: Fr): Promise<[bigint, SiblingPath<number>]> {
200
- return this.node.getL2ToL1MessageMembershipWitness(blockNumber, l2Tol1Message);
199
+ public async getL2ToL1MembershipWitness(
200
+ blockNumber: number,
201
+ l2Tol1Message: Fr,
202
+ ): Promise<[bigint, SiblingPath<number>]> {
203
+ const result = await computeL2ToL1MembershipWitness(this.node, blockNumber, l2Tol1Message);
204
+ if (!result) {
205
+ throw new Error(`L2 to L1 message not found in block ${blockNumber}`);
206
+ }
207
+ return [result.l2MessageIndex, result.siblingPath];
201
208
  }
202
209
 
203
210
  public getTxReceipt(txHash: TxHash): Promise<TxReceipt> {
@@ -75,26 +75,71 @@ export class CapsuleDataProvider implements DataProvider {
75
75
  * All operations are performed in a single transaction.
76
76
  * @param contractAddress - The contract address that owns the capsule array
77
77
  * @param baseSlot - The slot where the array length is stored
78
- * @param capsules - Array of capsule data to append
78
+ * @param content - Array of capsule data to append
79
79
  */
80
- appendToCapsuleArray(contractAddress: AztecAddress, baseSlot: Fr, capsules: Fr[][]): Promise<void> {
80
+ appendToCapsuleArray(contractAddress: AztecAddress, baseSlot: Fr, content: Fr[][]): Promise<void> {
81
81
  return this.#store.transactionAsync(async () => {
82
82
  // Load current length, defaulting to 0 if not found
83
83
  const lengthData = await this.loadCapsule(contractAddress, baseSlot);
84
- const currentLength = lengthData ? lengthData[0].toBigInt() : 0n;
84
+ const currentLength = lengthData ? lengthData[0].toNumber() : 0;
85
85
 
86
86
  // Store each capsule at consecutive slots after baseSlot + 1 + currentLength
87
- for (let i = 0; i < capsules.length; i++) {
88
- const nextSlot = baseSlot.add(new Fr(1)).add(new Fr(currentLength + BigInt(i)));
89
- await this.storeCapsule(contractAddress, nextSlot, capsules[i]);
87
+ for (let i = 0; i < content.length; i++) {
88
+ const nextSlot = arraySlot(baseSlot, currentLength + i);
89
+ await this.storeCapsule(contractAddress, nextSlot, content[i]);
90
90
  }
91
91
 
92
92
  // Update length to include all new capsules
93
- const newLength = currentLength + BigInt(capsules.length);
93
+ const newLength = currentLength + content.length;
94
94
  await this.storeCapsule(contractAddress, baseSlot, [new Fr(newLength)]);
95
95
  });
96
96
  }
97
97
 
98
+ readCapsuleArray(contractAddress: AztecAddress, baseSlot: Fr): Promise<Fr[][]> {
99
+ return this.#store.transactionAsync(async () => {
100
+ // Load length, defaulting to 0 if not found
101
+ const maybeLength = await this.loadCapsule(contractAddress, baseSlot);
102
+ const length = maybeLength ? maybeLength[0].toBigInt() : 0n;
103
+
104
+ const values: Fr[][] = [];
105
+
106
+ // Read each capsule at consecutive slots after baseSlot
107
+ for (let i = 0; i < length; i++) {
108
+ const currentValue = await this.loadCapsule(contractAddress, arraySlot(baseSlot, i));
109
+ if (currentValue == undefined) {
110
+ throw new Error(
111
+ `Expected non-empty value at capsule array in base slot ${baseSlot} at index ${i} for contract ${contractAddress}`,
112
+ );
113
+ }
114
+
115
+ values.push(currentValue);
116
+ }
117
+
118
+ return values;
119
+ });
120
+ }
121
+
122
+ resetCapsuleArray(contractAddress: AztecAddress, baseSlot: Fr, content: Fr[][]) {
123
+ return this.#store.transactionAsync(async () => {
124
+ // Load current length, defaulting to 0 if not found
125
+ const maybeLength = await this.loadCapsule(contractAddress, baseSlot);
126
+ const originalLength = maybeLength ? maybeLength[0].toNumber() : 0;
127
+
128
+ // Set the new length
129
+ await this.storeCapsule(contractAddress, baseSlot, [new Fr(content.length)]);
130
+
131
+ // Store the new content, possibly overwriting existing values
132
+ for (let i = 0; i < content.length; i++) {
133
+ await this.storeCapsule(contractAddress, arraySlot(baseSlot, i), content[i]);
134
+ }
135
+
136
+ // Clear any stragglers
137
+ for (let i = content.length; i < originalLength; i++) {
138
+ await this.deleteCapsule(contractAddress, arraySlot(baseSlot, i));
139
+ }
140
+ });
141
+ }
142
+
98
143
  public async getSize() {
99
144
  return (await toArray(this.#capsules.valuesAsync())).reduce(
100
145
  (sum, value) => sum + value.length * Fr.SIZE_IN_BYTES,
@@ -106,3 +151,7 @@ export class CapsuleDataProvider implements DataProvider {
106
151
  function dbSlotToKey(contractAddress: AztecAddress, slot: Fr): string {
107
152
  return `${contractAddress.toString()}:${slot.toString()}`;
108
153
  }
154
+
155
+ function arraySlot(baseSlot: Fr, index: number) {
156
+ return baseSlot.add(new Fr(1)).add(new Fr(index));
157
+ }