@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.
- package/dest/contract_function_simulator/event_validation_request.d.ts +22 -0
- package/dest/contract_function_simulator/event_validation_request.d.ts.map +1 -0
- package/dest/contract_function_simulator/event_validation_request.js +43 -0
- package/dest/contract_function_simulator/execution_data_provider.d.ts +25 -28
- package/dest/contract_function_simulator/execution_data_provider.d.ts.map +1 -1
- package/dest/contract_function_simulator/note_validation_request.d.ts +21 -0
- package/dest/contract_function_simulator/note_validation_request.d.ts.map +1 -0
- package/dest/contract_function_simulator/note_validation_request.js +42 -0
- package/dest/contract_function_simulator/oracle/oracle.d.ts +3 -3
- package/dest/contract_function_simulator/oracle/oracle.d.ts.map +1 -1
- package/dest/contract_function_simulator/oracle/oracle.js +23 -19
- package/dest/contract_function_simulator/oracle/typed_oracle.d.ts +6 -6
- package/dest/contract_function_simulator/oracle/typed_oracle.d.ts.map +1 -1
- package/dest/contract_function_simulator/oracle/typed_oracle.js +7 -7
- package/dest/contract_function_simulator/oracle/utility_execution_oracle.d.ts +5 -6
- package/dest/contract_function_simulator/oracle/utility_execution_oracle.d.ts.map +1 -1
- package/dest/contract_function_simulator/oracle/utility_execution_oracle.js +8 -8
- package/dest/contract_function_simulator/pxe_oracle_interface.d.ts +7 -5
- package/dest/contract_function_simulator/pxe_oracle_interface.d.ts.map +1 -1
- package/dest/contract_function_simulator/pxe_oracle_interface.js +81 -32
- package/dest/private_kernel/hints/build_private_kernel_reset_private_inputs.d.ts.map +1 -1
- package/dest/private_kernel/hints/build_private_kernel_reset_private_inputs.js +30 -30
- package/dest/private_kernel/private_kernel_execution_prover.d.ts.map +1 -1
- package/dest/private_kernel/private_kernel_execution_prover.js +9 -7
- package/dest/pxe_service/pxe_service.d.ts.map +1 -1
- package/dest/pxe_service/pxe_service.js +10 -3
- package/dest/storage/capsule_data_provider/capsule_data_provider.d.ts +4 -2
- package/dest/storage/capsule_data_provider/capsule_data_provider.d.ts.map +1 -1
- package/dest/storage/capsule_data_provider/capsule_data_provider.js +46 -7
- package/dest/storage/note_data_provider/note_data_provider.d.ts.map +1 -1
- package/dest/storage/note_data_provider/note_data_provider.js +14 -14
- package/package.json +16 -16
- package/src/contract_function_simulator/event_validation_request.ts +53 -0
- package/src/contract_function_simulator/execution_data_provider.ts +29 -50
- package/src/contract_function_simulator/note_validation_request.ts +52 -0
- package/src/contract_function_simulator/oracle/oracle.ts +27 -50
- package/src/contract_function_simulator/oracle/typed_oracle.ts +18 -26
- package/src/contract_function_simulator/oracle/utility_execution_oracle.ts +15 -42
- package/src/contract_function_simulator/pxe_oracle_interface.ts +157 -51
- package/src/private_kernel/hints/build_private_kernel_reset_private_inputs.ts +35 -34
- package/src/private_kernel/private_kernel_execution_prover.ts +11 -10
- package/src/pxe_service/pxe_service.ts +10 -3
- package/src/storage/capsule_data_provider/capsule_data_provider.ts +56 -7
- 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
|
|
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
|
|
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
|
|
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 =
|
|
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 ${
|
|
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
|
|
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
|
-
|
|
694
|
-
|
|
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}.
|
|
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
|
-
|
|
719
|
-
scopedLog.log.
|
|
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
|
-
|
|
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
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
const
|
|
805
|
-
|
|
806
|
-
|
|
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
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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
|
|
170
|
-
this.previousKernelOutput.publicInputs,
|
|
165
|
+
const vkData = new VkData(
|
|
171
166
|
this.previousKernelOutput.verificationKey,
|
|
172
167
|
Number(previousVkMembershipWitness.leafIndex),
|
|
173
|
-
|
|
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.
|
|
179
|
-
dimensions.
|
|
174
|
+
dimensions.NOTE_HASH_PENDING_READ,
|
|
175
|
+
dimensions.NOTE_HASH_SETTLED_READ,
|
|
180
176
|
);
|
|
181
177
|
this.reduceReadRequestStates(
|
|
182
178
|
this.nullifierResetStates,
|
|
183
|
-
dimensions.
|
|
184
|
-
dimensions.
|
|
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.
|
|
270
|
-
this.requestedDimensions.
|
|
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.
|
|
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.
|
|
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.
|
|
322
|
-
this.requestedDimensions.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
199
|
-
output.publicInputs,
|
|
197
|
+
const vkData = new VkData(
|
|
200
198
|
output.verificationKey,
|
|
201
199
|
Number(previousVkMembershipWitness.leafIndex),
|
|
202
|
-
|
|
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
|
|
265
|
-
output.publicInputs,
|
|
263
|
+
const vkData = new VkData(
|
|
266
264
|
output.verificationKey,
|
|
267
265
|
Number(previousVkMembershipWitness.leafIndex),
|
|
268
|
-
|
|
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
|
-
|
|
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(
|
|
200
|
-
|
|
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
|
|
78
|
+
* @param content - Array of capsule data to append
|
|
79
79
|
*/
|
|
80
|
-
appendToCapsuleArray(contractAddress: AztecAddress, baseSlot: Fr,
|
|
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].
|
|
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 <
|
|
88
|
-
const nextSlot = baseSlot
|
|
89
|
-
await this.storeCapsule(contractAddress, nextSlot,
|
|
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 +
|
|
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
|
+
}
|