@@ -7,8 +7,8 @@ use crate::oracle::{call_utility_function::call_utility_function, notes::get_nex
77use crate::protocol:: {
88 abis::function_selector::FunctionSelector ,
99 address::AztecAddress ,
10- constants::DOM_SEP__CONSTRAINED_MSG_NULLIFIER ,
11- hash:: poseidon2_hash_with_separator ,
10+ constants ::{ DOM_SEP__CONSTRAINED_MSG_LOG_TAG , DOM_SEP__CONSTRAINED_MSG_NULLIFIER } ,
11+ hash ::{ compute_log_tag , poseidon2_hash , poseidon2_hash_with_separator } ,
1212 traits ::{Deserialize , ToField },
1313};
1414
@@ -32,7 +32,8 @@ use crate::protocol::{
3232/// note.
3333/// - `index > 0`: asserts the prior nullifier
3434/// `poseidon2_hash_with_separator([sender, recipient, secret, index - 1], DOM_SEP__CONSTRAINED_MSG_NULLIFIER)`
35- /// exists via [`compute_nullifier_existence_request`](crate::nullifier::utils::compute_nullifier_existence_request).
35+ /// exists via
36+ /// [`compute_nullifier_existence_request`](crate::nullifier::utils::compute_nullifier_existence_request).
3637/// The chain transitively constrains back to the index-0 `validate_handshake`. The caller is expected
3738/// to emit the matching nullifier alongside each constrained send so subsequent calls can prove the
3839/// chain.
@@ -47,9 +48,7 @@ pub fn calculate_secret_and_index(
4748 // Safety: the returned `Option<Field>` is untrusted and is constrained below before being returned to the
4849 // caller, either by performing a new handshake or by constraining the prior handshake's existence.
4950 let maybe_secret : Option <Field > = unsafe {
50- let selector = comptime {
51- FunctionSelector ::from_signature ("get_app_siloed_secret((Field),(Field),(Field))" )
52- };
51+ let selector = comptime { FunctionSelector ::from_signature ("get_app_siloed_secret((Field),(Field),(Field))" ) };
5352 let returns = call_utility_function (
5453 registry ,
5554 selector ,
@@ -64,12 +63,9 @@ pub fn calculate_secret_and_index(
6463 // `validate_handshake` is needed.
6564 // TODO(F-660): dispatch to `perform_handshake(sender, recipient, handshake_type)` once interactive
6665 // handshakes are supported.
67- let selector = comptime {
68- FunctionSelector ::from_signature ("non_interactive_handshake((Field),(Field))" )
69- };
70- let secret : Field = context
71- .call_private_function (registry , selector , [sender .to_field (), recipient .to_field ()])
72- .get_preimage ();
66+ let selector = comptime { FunctionSelector ::from_signature ("non_interactive_handshake((Field),(Field))" ) };
67+ let secret : Field =
68+ context .call_private_function (registry , selector , [sender .to_field (), recipient .to_field ()]).get_preimage ();
7369 (secret , 0 )
7470 } else {
7571 let secret = maybe_secret .unwrap_unchecked ();
@@ -79,23 +75,51 @@ pub fn calculate_secret_and_index(
7975 let index = unsafe { get_next_constrained_index (secret ) };
8076
8177 if index == 0 {
82- let selector = comptime {
83- FunctionSelector ::from_signature ("validate_handshake((Field),(Field),Field)" )
84- };
85- let _ = context .call_private_function (
86- registry ,
87- selector ,
88- [sender .to_field (), recipient .to_field (), secret ],
89- );
78+ let selector = comptime { FunctionSelector ::from_signature ("validate_handshake((Field),(Field),Field)" ) };
79+ let _ =
80+ context .call_private_function (registry , selector , [sender .to_field (), recipient .to_field (), secret ]);
9081 } else {
9182 let prev_nullifier = poseidon2_hash_with_separator (
9283 [sender .to_field (), recipient .to_field (), secret , (index - 1 ) as Field ],
9384 DOM_SEP__CONSTRAINED_MSG_NULLIFIER ,
9485 );
95- // TODO(F-670): exercise the index > 0 path end-to-end once send_constrained_msg emits nullifiers.
9686 context .assert_nullifier_exists (compute_nullifier_existence_request (prev_nullifier , caller ));
9787 }
9888
9989 (secret , index )
10090 }
10191}
92+
93+ /// Computes the constrained-delivery discovery log tag for an `(app_siloed_secret, index)` pair.
94+ ///
95+ /// Collapses the secret and index into a single raw tag and domain-separates it with
96+ /// `DOM_SEP__CONSTRAINED_MSG_LOG_TAG`. This mirrors the recipient-side derivation in the PXE (`siloedTagFor`): the
97+ /// recipient recomputes the same tag to discover the message, and the protocol silos the emitted log's first field by
98+ /// the emitting contract address, completing the match.
99+ pub fn compute_constrained_log_tag (app_siloed_secret : Field , index : u32 ) -> Field {
100+ compute_log_tag (poseidon2_hash ([app_siloed_secret , index as Field ]), DOM_SEP__CONSTRAINED_MSG_LOG_TAG )
101+ }
102+
103+ /// Emits the per-send chain nullifier for a constrained message and returns its discovery log tag.
104+ ///
105+ /// Used by [`crate::messages::delivery::do_private_message_delivery`] on the constrained-delivery path. It wraps
106+ /// [`calculate_secret_and_index`] to resolve the `(app_siloed_secret, index)` pair, then:
107+ /// 1. Emits the chain nullifier. A subsequent send at `index + 1` proves this nullifier exists, transitively constraining
108+ /// the chain back to the `index == 0` handshake validation in [`calculate_secret_and_index`].
109+ /// 2. Returns the log tag. The recipient recomputes the same tag from the handshake secret to discover the message.
110+ pub (crate ) fn emit_nullifier_and_compute_constrained_tag (
111+ context : &mut PrivateContext ,
112+ registry : AztecAddress ,
113+ sender : AztecAddress ,
114+ recipient : AztecAddress ,
115+ ) -> Field {
116+ let (secret , index ) = calculate_secret_and_index (context , registry , sender , recipient );
117+
118+ let nullifier = poseidon2_hash_with_separator (
119+ [sender .to_field (), recipient .to_field (), secret , index as Field ],
120+ DOM_SEP__CONSTRAINED_MSG_NULLIFIER ,
121+ );
122+ context .push_nullifier_unsafe (nullifier );
123+
124+ compute_constrained_log_tag (secret , index )
125+ }
0 commit comments