Skip to content

feat(aztec-nr): V2 handshake registry for non interactive constrained delivery#23278

Merged
vezenovm merged 13 commits into
merge-train/fairiesfrom
mv/v2-handshake-registry-non-interactive
May 15, 2026
Merged

feat(aztec-nr): V2 handshake registry for non interactive constrained delivery#23278
vezenovm merged 13 commits into
merge-train/fairiesfrom
mv/v2-handshake-registry-non-interactive

Conversation

@vezenovm
Copy link
Copy Markdown
Contributor

@vezenovm vezenovm commented May 14, 2026

Fixes https://linear.app/aztec-labs/issue/F-586/handshake-registry-non-interactive-handshake-function (updates work from #22854)

Updated the handshake registry following the new spec in https://www.notion.so/aztecnetwork/Plan-Onchain-constrained-delivery-34fa1f6b0e358063b64ecc25b768c359

  • We store the raw handshake secret in the Handshake note
  • The raw secret never leaves the registry. The registry app-siloes the secret against the msg_sender and returns it from the both the handshake and its utility method for fetching the siloed secret.
  • The registry provides a validation method to check the app siloed secret is for a valid handshake secret
  • Various additional tests. Mainly making sure we reject invalid handshakes and that we never expose the raw handshake secret
  • Linked directly to https://linear.app/aztec-labs/issue/F-653/route-handshake-log-through-do-private-message-delivery-to-preserve to implement as a follow-up

I decided to just update the handshake registry directly rather than duplicating it with the old one. I felt if we ever wanted to go back to the old spec, we have the git history which we can reference for the old contract.

Note packing bug was revealed with this work: https://linear.app/aztec-labs/issue/F-665/note-properties-generates-incorrect-selectors-for-custom-packed-fields
I attempted to fitler notes after calling get_notes which through testing revealed itself as the incorrect way to filter notes. This should be made easier as it is a foot-gun for developers: https://linear.app/aztec-labs/issue/F-666/get-notes-and-view-notes-make-it-easy-to-filter-after-pagination

@vezenovm vezenovm requested a review from nchamo May 14, 2026 15:01
@vezenovm vezenovm changed the base branch from next to merge-train/fairies May 14, 2026 15:02
env.call_private(sender, registry.validate_handshake(sender, recipient, zero_padded_secret));
}

// Regression for the unfiltered-page bug: insert 16 unrelated handshakes (different recipients) before
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This and the get_app_siloed_secret test variant are regressions for https://linear.app/aztec-labs/issue/F-666/get-notes-and-view-notes-make-it-easy-to-filter-after-pagination. I originally did not use .select on the NoteGetterOptions and filtered the result of get_notes in the utility function which caused these tests to fail. I decided to leave these tests in for now as protection. But I felt aztec-nr should make it impossible for devs to run into such a foot-gun.

Copy link
Copy Markdown
Contributor

@nchamo nchamo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small initial review, will continue but wanted to report early findings

Comment thread noir-projects/aztec-nr/aztec/src/note/note_getter/test.nr
@nchamo nchamo self-requested a review May 14, 2026 18:13
…eNote, Context>, Context>, Context> and additional test
Copy link
Copy Markdown
Contributor

@nchamo nchamo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I need to go, but I wanted to share all my thoughts so far

/// polluting registry state.
#[external("private")]
fn non_interactive_handshake(sender: AztecAddress, recipient: AztecAddress) {
fn non_interactive_handshake(sender: AztecAddress, recipient: AztecAddress) -> Field {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I went through the rabbit hole. I was concerned that an attacker could front-run a handshake, which would cause our transaction to fail (it would fail since a both txs would emit the initialization nullifier). But in PrivateMutable, the initialization nullifier requires the owner's nullifier hiding key, which an attacker won't have. So you can only specify a sender that you have the keys for, which is exactly what we want: flexibility + security

On the other side, with our previous storage, anyone could have called this, since PrivateSet has no check like that. So iterating through all of them would have been very expensive

Not sure if this is 100% correct, since I'm very tired 😅 . So could we add a check to verify? I would like to test that:

  • An account can't set the handshake for another sender
  • An account can set the handshake for another sender, if the sender is added as additional scopes

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added non_interactive_handshake_rejects_other_sender and non_interactive_handshake_accepts_other_sender_in_additional_scopes.

Comment on lines +21 to +22
/// The raw ECDH shared-secret point `S = eph_sk * recipient_address_point`. Only this module can read it for
/// siloing, and it is never returned by an external function.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only this module can read it for siloing, and it is never returned by an external function.

Nice, really like this approach!

vezenovm and others added 2 commits May 14, 2026 16:42
…hake_registry_contract/src/main.nr

Co-authored-by: Nicolas Chamo <nicolas@chamo.com.ar>
@vezenovm vezenovm requested a review from nchamo May 15, 2026 15:12
Copy link
Copy Markdown
Contributor

@nchamo nchamo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work, we are almost there!

/// Returning the latest (rather than the first) lets a sender re-initiate by issuing a fresh handshake; e.g.
/// if the previous secret was leaked.
/// Apps that receive an `app_siloed_secret` from an untrusted source (e.g. a PXE oracle) call this once to
/// bind that secret to the registry's stored handshake. Subsequent uses can rely on the prior-nullifier chain
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"bind" feels off. I think we should stick to "validate" (and maybe rephrase the rest of the sentence with it)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rephrased

/// Returning the latest (rather than the first) lets a sender re-initiate by issuing a fresh handshake; e.g.
/// if the previous secret was leaked.
/// Apps that receive an `app_siloed_secret` from an untrusted source (e.g. a PXE oracle) call this once to
/// bind that secret to the registry's stored handshake. Subsequent uses can rely on the prior-nullifier chain
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would remove the entire "Subsequent uses can rely on the prior-nullifier chain..." text. The registry doesn't know about nullifier chains, nor should it. It just stores handshakes, and validates secrets

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Comment thread noir-projects/aztec-nr/aztec/src/keys/ecdh_shared_secret.nr Outdated
@vezenovm vezenovm requested a review from nchamo May 15, 2026 18:41
Copy link
Copy Markdown
Contributor

@nchamo nchamo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work!

I'm really happy with our approach, I think this is going to be a really cool feature with minimal changes needed 🎉

@vezenovm vezenovm enabled auto-merge (squash) May 15, 2026 18:52
@AztecBot
Copy link
Copy Markdown
Collaborator

Flakey Tests

🤖 says: This CI run detected 1 tests that failed, but were tolerated due to a .test_patterns.yml entry.

\033FLAKED\033 (8;;http://ci.aztec-labs.com/a24769b1003e89f9�a24769b1003e89f98;;�):  yarn-project/end-to-end/scripts/run_test.sh simple src/e2e_epochs/epochs_mbps.parallel.test.ts "builds multiple blocks per slot with L2 to L1 messages" (338s) (code: 0) group:e2e-p2p-epoch-flakes

@vezenovm vezenovm merged commit d3cf01b into merge-train/fairies May 15, 2026
14 checks passed
@vezenovm vezenovm deleted the mv/v2-handshake-registry-non-interactive branch May 15, 2026 19:11
rangozd pushed a commit to rangozd/aztec-packages that referenced this pull request May 16, 2026
BEGIN_COMMIT_OVERRIDE
fix(aztec-up): fall back to no timeout when /usr/bin/timeout absent
(macOS) (AztecProtocol#23310)
chore: reduce compat e2e timeout (AztecProtocol#23318)
feat(aztec-nr): V2 handshake registry for non interactive constrained
delivery (AztecProtocol#23278)
chore(aztec-nr): Public internal/utility methods self constructor
(AztecProtocol#23115)
END_COMMIT_OVERRIDE
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants