-
Notifications
You must be signed in to change notification settings - Fork 607
feat(aztec-nr): Helper to validate the app-siloed secret and index for constrained tagging #23569
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 18 commits
98b9784
27e5a5e
381858d
fff3541
3202ab1
49bfefc
d00136d
76c025e
75554b1
9a451ef
14d22ca
bfb03bf
aa3d1e6
df3f982
36d355d
3bad347
f4b6e86
652d92d
f686408
37c3d0e
0afe779
a45d68a
0ff7c94
f7ef01f
3e89f16
dd3de61
21f5920
9f44ac2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,101 @@ | ||||||
| //! Sender-side helpers for constrained message delivery. | ||||||
|
|
||||||
| use crate::context::{NullifierExistenceRequest, PrivateContext}; | ||||||
| use crate::oracle::{call_utility_function::call_utility_function, notes::get_next_constrained_index}; | ||||||
|
|
||||||
| use crate::protocol::{ | ||||||
| abis::function_selector::FunctionSelector, | ||||||
| address::AztecAddress, | ||||||
| constants::DOM_SEP__CONSTRAINED_MSG_NULLIFIER, | ||||||
| hash::poseidon2_hash_with_separator, | ||||||
| traits::{Deserialize, ToField}, | ||||||
| }; | ||||||
|
|
||||||
| /// Resolves the app-siloed shared secret and next per-secret index for a constrained send. | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Warning, boss-man likes the first line to be 10 words max |
||||||
| /// | ||||||
| /// Wraps the registry calls needed by every constrained-delivery app: query the handshake registry for an | ||||||
| /// existing app-siloed secret, bootstrap a fresh handshake if there isn't one, and constrain the | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| /// oracle-supplied secret. The returned `(secret, index)` pair is the input for the caller's tag derivation | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't this be an index?
Suggested change
|
||||||
| /// and nullifier emission. | ||||||
| /// | ||||||
| /// ## Implementation Details | ||||||
| /// 1. Calls `HandshakeRegistry::get_app_siloed_secret` offchain (untrusted hint). | ||||||
| /// 2. On `None`, calls `HandshakeRegistry::non_interactive_handshake(sender, recipient)` to bootstrap and | ||||||
| /// returns `(secret, 0)`. The constrained return value of the private call is the source of truth for the | ||||||
| /// secret. | ||||||
| /// 3. On `Some(secret)`, reads the per-secret index hint via [`get_next_constrained_index`] (also untrusted), | ||||||
| /// then constrains: | ||||||
| /// - `index == 0`: calls `HandshakeRegistry::validate_handshake` to bind the secret to the registry's | ||||||
| /// current note. `PrivateMutable::get_note` inside `validate_handshake` nullifies the current handshake | ||||||
| /// note and emits a fresh one, so this branch already produces a per-call nullifier on the registry | ||||||
| /// note. | ||||||
| /// - `index > 0`: asserts the prior nullifier | ||||||
| /// `poseidon2_hash_with_separator([sender, recipient, secret, index - 1], DOM_SEP__CONSTRAINED_MSG_NULLIFIER)` | ||||||
| /// exists in the app's pending set / nullifier tree. The chain transitively constrains back to the | ||||||
| /// index-0 `validate_handshake`. The caller is expected to emit the matching nullifier alongside each | ||||||
| /// constrained send so subsequent calls can prove the chain. | ||||||
| pub fn calculate_secret_and_index( | ||||||
| context: &mut PrivateContext, | ||||||
| registry: AztecAddress, | ||||||
| sender: AztecAddress, | ||||||
| recipient: AztecAddress, | ||||||
| ) -> (Field, u32) { | ||||||
| let caller = context.this_address(); | ||||||
|
|
||||||
| // Safety: the returned `Option<Field>` is untrusted and is constrained below before being returned to the | ||||||
| // caller, either by performing a new handshake or by constraining the prior handshake's existence. | ||||||
|
Comment on lines
+47
to
+48
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What about something like this? |
||||||
| let maybe_secret: Option<Field> = unsafe { | ||||||
| let selector = comptime { | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we somehow avoid the selector and to something like |
||||||
| FunctionSelector::from_signature("get_app_siloed_secret((Field),(Field),(Field))") | ||||||
| }; | ||||||
| let returns = call_utility_function( | ||||||
| registry, | ||||||
| selector, | ||||||
| [sender.to_field(), recipient.to_field(), caller.to_field()], | ||||||
| ); | ||||||
| Deserialize::deserialize(returns) | ||||||
| }; | ||||||
|
|
||||||
| if maybe_secret.is_none() { | ||||||
| // Bootstrap: no handshake exists yet. The registry inserts a fresh note and returns the app-siloed | ||||||
| // secret to the caller. The constrained return is the source of truth for the secret, so no separate | ||||||
| // `validate_handshake` is needed. | ||||||
| // TODO(F-660): dispatch to `perform_handshake(sender, recipient, handshake_type)` once interactive | ||||||
| // handshakes are supported. | ||||||
| let selector = comptime { | ||||||
| FunctionSelector::from_signature("non_interactive_handshake((Field),(Field))") | ||||||
| }; | ||||||
| let secret: Field = context | ||||||
| .call_private_function(registry, selector, [sender.to_field(), recipient.to_field()]) | ||||||
| .get_preimage(); | ||||||
| (secret, 0) | ||||||
| } else { | ||||||
| let secret = maybe_secret.unwrap_unchecked(); | ||||||
|
|
||||||
| // Safety: the index is untrusted; the anchor branches below ensure the returned `(secret, index)` pair | ||||||
| // is bound to a real registry handshake before it leaves the helper. | ||||||
| let index = unsafe { get_next_constrained_index(secret) }; | ||||||
|
|
||||||
| if index == 0 { | ||||||
| let selector = comptime { | ||||||
| FunctionSelector::from_signature("validate_handshake((Field),(Field),Field)") | ||||||
| }; | ||||||
| let _ = context.call_private_function( | ||||||
| registry, | ||||||
| selector, | ||||||
| [sender.to_field(), recipient.to_field(), secret], | ||||||
| ); | ||||||
|
Comment on lines
+90
to
+97
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same with selector |
||||||
| } else { | ||||||
| let prev_nullifier = poseidon2_hash_with_separator( | ||||||
| [sender.to_field(), recipient.to_field(), secret, (index - 1) as Field], | ||||||
| DOM_SEP__CONSTRAINED_MSG_NULLIFIER, | ||||||
| ); | ||||||
|
Comment on lines
+99
to
+102
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We could probably have a helper function in this file for this, since we'll need to re-use it when we actually send a message |
||||||
| context.assert_nullifier_exists(NullifierExistenceRequest::for_pending( | ||||||
| prev_nullifier, | ||||||
| caller, | ||||||
| )); | ||||||
| } | ||||||
|
|
||||||
| (secret, index) | ||||||
| } | ||||||
| } | ||||||
Uh oh!
There was an error while loading. Please reload this page.