@@ -3,24 +3,27 @@ use types::{
33 constants::MAX_NULLIFIERS_PER_TX , side_effect::Ordered , utils::arrays:: find_first_index ,
44};
55
6- // This is mainly for ensuring that for any nullifier that links to a note hash,
7- // it is created _after_ the note hash.
8- // This is enforced for transient data when they are squashed in the reset circuit.
9- // But if a pair is not transient, their counters will be checked here.
10- // Why would we have a (nullifier, pending note) pair that is non-transient?
11- // When a pending note hash is non-revertible and its nullifier is revertible, we can't
12- // squash them, but we still need to perform this check on their counters.
13- // A nice side effect of this check is that it also makes sure all the transient data is squashed:
14- // In aztec-nr, if a contract is emitting a nullifier for a non-revertible note
15- // hash, or if it doesn't want to squash the note hash at all (to keep a full record
16- // of what had happened, for example), it could set the nullifier.note_hash to be
17- // the _siloed_ note hash (or not set it at all).
18- // When we run this function (in the tail), because the non-squashed note hashes
19- // are already siloed in the reset circuit, the nullifiers that map to non-transient
20- // note hashes will match up with those _siloed_ note hashes. But for nullifiers
21- // that should already have been squashed against a transient (not siloed) note
22- // hash, they won't be able to find a match.
23- pub fn validate_no_transient_data (previous_kernel_public_inputs : PrivateKernelCircuitPublicInputs ) {
6+ /// This is mainly for ensuring that for any nullifier that links to a note hash,
7+ /// it is created _after_ the note hash.
8+ /// This is enforced for transient data when they are squashed in the reset circuit.
9+ /// But if a pair is not transient, their counters will be checked here.
10+ /// Why would we have a (nullifier, pending note) pair that is non-transient?
11+ /// When a pending note hash is non-revertible and its nullifier is revertible, we can't
12+ /// squash them, but we still need to perform this check on their counters.
13+ ///
14+ /// A nice side effect of this check is that it also makes sure all the transient data is squashed:
15+ /// In aztec-nr, if a contract is emitting a nullifier for a non-revertible note
16+ /// hash, or if it doesn't want to squash the note hash at all (to keep a full record
17+ /// of what had happened, for example), it could set the nullifier.note_hash to be
18+ /// the _siloed_ note hash (or not set it at all).
19+ /// When we run this function (in the tail), because the non-squashed note hashes
20+ /// are already siloed in the reset circuit, the nullifiers that map to non-transient
21+ /// note hashes will match up with those _siloed_ note hashes. But for nullifiers
22+ /// that should already have been squashed against a transient (not siloed) note
23+ /// hash, they won't be able to find a match.
24+ pub fn validate_unsquashable_note_hash_nullifier_ordering (
25+ previous_kernel_public_inputs : PrivateKernelCircuitPublicInputs ,
26+ ) {
2427 // Safety: the below hints are constrained by the following methods. See private_kernel_inner for use.
2528 let note_hash_index_for_each_nullifier =
2629 unsafe { get_note_hash_index_for_each_nullifier (previous_kernel_public_inputs ) };
@@ -41,7 +44,18 @@ pub fn validate_no_transient_data(previous_kernel_public_inputs: PrivateKernelCi
4144 note_hash .counter () < nullifier .counter (),
4245 "Cannot link a note hash emitted after a nullifier" ,
4346 );
44- // No need to verify logs linked to a note hash are squashed.
47+ // Note: we don't need to assert that the contract_addresses of the note_hash and
48+ // nullifier match, because by this point (after the final reset circuit) they are all
49+ // siloed, and hence their contract_addresses will be 0.
50+
51+ // Note: A nullifier's note_hash field could reference a siloed note_hash from a
52+ // different contract. This is harmless: the note_hash field is discarded in
53+ // expose_to_public() and never reaches the rollup. Cross-contract squashing is
54+ // prevented by the reset circuit's contract_address check in
55+ // validate_squashable_note_hash_nullifier_pair. The only effect of the link here
56+ // is a counter-ordering check, which has no security implication.
57+
58+ // Note: No need to verify logs linked to a note hash are squashed.
4559 // When a note hash is squashed, all associated logs are guaranteed to be removed.
4660 // See reset/transient_data/transient_data_validator.nr for details.
4761 }
@@ -56,14 +70,13 @@ pub fn validate_no_transient_data(previous_kernel_public_inputs: PrivateKernelCi
5670// For each nullifier, this array gives the index of that note_hash in the new note_hashes array.
5771//
5872// note_hashes: [ C0, C1, C2, C3, C4, C5, C6, C7]
59- // nullifiers: [N(C5), N, N(C3), N, N, 0, 0, 0] <-- Notes C5 and C3 are transient notes .
73+ // nullifiers: [N(C5), N, N(C3), N, N, 0, 0, 0] <-- E.g. N(C5) is the nullifier pointing to C5 .
6074//
61- // this: [ 5, _, 3, _, _, _, _, _] <-- the index of the transient note for each nullifier
75+ // this: [ 5, _, 3, _, _, _, _, _] <-- the index of the linked note for each nullifier
6276//
6377unconstrained fn get_note_hash_index_for_each_nullifier (
6478 previous_kernel : PrivateKernelCircuitPublicInputs ,
6579) -> [u32 ; MAX_NULLIFIERS_PER_TX ] {
66- // Q: there's no "null" value, so how do we distinguish between "null" and transient note_hash index 0?
6780 let mut note_hash_index_for_each_nullifier = [0 ; MAX_NULLIFIERS_PER_TX ];
6881 let note_hashes = previous_kernel .end .note_hashes ;
6982 let nullifiers = previous_kernel .end .nullifiers ;
0 commit comments