Skip to content

feat(aztec-nr): Initial handshake registry contract with non interactive handshake function#22854

Merged
vezenovm merged 38 commits into
merge-train/fairiesfrom
mv/f-586-handshake-registry-non-interactive-handshake-function
May 6, 2026
Merged

feat(aztec-nr): Initial handshake registry contract with non interactive handshake function#22854
vezenovm merged 38 commits into
merge-train/fairiesfrom
mv/f-586-handshake-registry-non-interactive-handshake-function

Conversation

@vezenovm

@vezenovm vezenovm commented Apr 29, 2026

Copy link
Copy Markdown
Contributor

Fixes https://linear.app/aztec-labs/issue/F-586/handshake-registry-non-interactive-handshake-function

Flow:

  • non_interactive_handshake(recipient) retrieves sender_for_tags from PXE via get_sender_for_tags()
  • Sender stores registry note: HandshakeNote { secret_hash, handshake_type, recipient }
  • Recipient-discoverable log emission, log contains 1-field [eph_pk.x]
  • Invalid recipient fails
    • No upstream side effects to protect
    • Fallback would pollute state with notes recording handshakes to invalid non-curve points
  • New utility get_handshake_for(sender_for_tags, recipient, handshake_type) -> Option<HandshakeNote>
  • Various tests that cover the cases listed above

@vezenovm vezenovm changed the title Mv/f 586 handshake registry non interactive handshake function feat(aztec-nr): Initial handshake registry contract with non interactive handshake function Apr 29, 2026
@vezenovm vezenovm requested a review from nchamo April 29, 2026 15:30
/// Stored in [`crate::HandshakeRegistry`]'s `handshakes` set, keyed by sender. Holds the **hash** of the master shared
/// secret rather than the secret itself: apps that later prove existence of this note which avoids ever exposing the master secret to app circuits.
///
/// The note is non-nullifiable. The registry currently never removes it.

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.

The note is nullifiable, i.e. its nullifier is defined (and even computed during discovery). It's just that the registry never does nullify it.

Comment thread noir-projects/aztec-nr/aztec/src/keys/ecdh_shared_secret.nr Outdated

let s_app = env.call_private(sender, registry.non_interactive_handshake(recipient));

let notes = env.execute_utility(registry.get_handshakes_for_sender(sender));

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.

Given how we'll use this registry, perhaps it'd make more sense to write a helper function such as

fn get_constr_deli_shared_secret(context: &mut PrivateContext, recipient: AztecAddress) -> Field {
   ... // do utility call to registry.get_note_hint(recipient)
   context.assert_note_exists(note_hint, registry_address);
   assert_eq(recipient, note.recipient); // also handshake type
   let key_val_req = key_validation_request_oracle(note.secret_hash);
   context.push_key_validation_req(key_val_req);
  key_val_req.app_siloed_key
}

and then test the invocation of that. The registry won't really be called the way we're doing it in this test (which is why the msg.sender issues are not surfaced).

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.

I tested the utility function but didn't go forward with the rest of the key validation checking because we still need the oracle to store the raw secret.

vezenovm and others added 5 commits April 29, 2026 14:40
…hake_registry_contract/src/main.nr

Co-authored-by: Nicolás Venturo <nicolas.venturo@gmail.com>
…interactive-handshake-function' into mv/f-586-handshake-registry-non-interactive-handshake-function
@vezenovm vezenovm requested a review from nventuro April 30, 2026 15:47

@nchamo nchamo left a comment

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.

Amazing work!

Left some comments and questions, but really like how it's turning up

/// TODO: This utility takes `sender_for_tags` as an explicit parameter for now. The ideal interface is get_handshake(recipient, handshake_type).
/// We could add a `aztec_utl_getSenderForTags` oracle so the utility can retrieve the identity itself.
#[external("utility")]
unconstrained fn get_handshake(

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'm curious about the next step. I understand that the sender will always be able to call this to get the note, and then check that handshake actually happened

But what about the recipient? What will they do when gets the secret in the private log? Not saying we need to tackle it now, just asking out loud

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.

The recipient should be able to recover the master secret S using eph_pk.x (the scalar that was generated with the recipient_point). I was under the impression that after this handshake, the sender would then call an app and generate a tagging shared secret with tagging_shared_secret = hash(S, contract_address, CONSTR_TAG_DOMAIN_SEP). The recipient can then use S to find the tagging_shared_secret to ultimately recover the tag. Is this correct @nventuro?

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 also discussed this with him. So we need to figure out how, but the recipient will discover the log, get the secret and store it somewhere, so that they know to search for messages with that tag afterwards

Comment thread noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr Outdated

@nchamo nchamo left a comment

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.

Answering some comments

/// TODO: This utility takes `sender_for_tags` as an explicit parameter for now. The ideal interface is get_handshake(recipient, handshake_type).
/// We could add a `aztec_utl_getSenderForTags` oracle so the utility can retrieve the identity itself.
#[external("utility")]
unconstrained fn get_handshake(

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 also discussed this with him. So we need to figure out how, but the recipient will discover the log, get the secret and store it somewhere, so that they know to search for messages with that tag afterwards

vezenovm and others added 7 commits May 5, 2026 15:52
…hake_registry_contract/src/main.nr

Co-authored-by: Nicolas Chamo <nicolas@chamo.com.ar>
…hake_registry_contract/src/main.nr

Co-authored-by: Nicolas Chamo <nicolas@chamo.com.ar>
…hake_registry_contract/src/main.nr

Co-authored-by: Nicolas Chamo <nicolas@chamo.com.ar>
@vezenovm vezenovm requested a review from nchamo May 5, 2026 20:28

@nchamo nchamo left a comment

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.

LGTM!

@vezenovm vezenovm enabled auto-merge (squash) May 6, 2026 14:53
@vezenovm vezenovm merged commit e67f943 into merge-train/fairies May 6, 2026
14 checks passed
@vezenovm vezenovm deleted the mv/f-586-handshake-registry-non-interactive-handshake-function branch May 6, 2026 15:14
AztecBot pushed a commit that referenced this pull request May 7, 2026
## Summary 

Extends the `aztec_txe_getLastTxEffects` oracle to also surface the last
tx's private logs, so contract tests can fetch them and assert on
tags/payloads without a dedicated `get_logs_by_tag` oracle. As discussed
in
#22854 (comment).

Added type aliases in `txe_oracles.nr` for readability.

Once this lands, #22854 will replace its `private_logs_by_tag` queries
with `get_last_tx_effects`.
@AztecBot

AztecBot commented May 7, 2026

Copy link
Copy Markdown
Collaborator

❌ Failed to cherry-pick to v4-next due to conflicts. (🤖) View backport run.

AztecBot pushed a commit that referenced this pull request May 8, 2026
…ive handshake function (#22854)

cherry-pick of e67f943 with unresolved conflicts (markers preserved in history)
AztecBot added a commit that referenced this pull request May 8, 2026
Drop incoming entries that don't exist on v4-next (DOM_SEP__MERKLE_HASH,
DOM_SEP__NULLIFIER_MERKLE, DOM_SEP__PARTIAL_NOTE_COMMITMENT,
DOM_SEP__PUBLIC_DATA_MERKLE) while keeping the two new ones added by
#22854 (DOM_SEP__HANDSHAKE_SECRET_HASH,
DOM_SEP__NON_INTERACTIVE_HANDSHAKE_LOG_TAG). HashedValueTester sized
<58, 51> = v4-next baseline <56, 49> + 2 new dom seps.
vezenovm added a commit that referenced this pull request May 8, 2026
Cherry-picks #22854 (`feat(aztec-nr): Initial handshake registry
contract with non interactive handshake function`) to
`backport-to-v4-next-staging`.

## Conflict resolution

The only conflict was in
`noir-projects/noir-protocol-circuits/crates/types/src/constants_tests.nr`.
`next` has accumulated a number of new merkle/partial-note dom seps
(`DOM_SEP__MERKLE_HASH`, `DOM_SEP__NULLIFIER_MERKLE`,
`DOM_SEP__PARTIAL_NOTE_COMMITMENT`, `DOM_SEP__PUBLIC_DATA_MERKLE`) that
don't exist on `v4-next`, so the upstream version of this file imports
symbols that aren't defined here.

Resolution: drop the imports/assertions for those non-existent
constants, but keep the two new ones added by this PR
(`DOM_SEP__HANDSHAKE_SECRET_HASH`,
`DOM_SEP__NON_INTERACTIVE_HANDSHAKE_LOG_TAG`). `HashedValueTester` sized
`<58, 51>` = v4-next baseline `<56, 49>` plus the two new dom seps.

Validated:
- Imports list matches the symbols actually referenced inside
`hashed_values_match_derived` (no orphan imports / no missing imports).
- Assertion counts inside the test match the tester generics: `48`
`assert_dom_sep_matches_derived` + `3`
`assert_blob_prefixes_match_derived` = 51 u32 values; plus `1`
protocol-circuit value + `6` aztec-nr values = 58 total field values.
- All aztec-nr APIs the new contract uses (`derive_ecdh_shared_secret`,
`generate_positive_ephemeral_key_pair`, `set_sender_for_tags`,
`compute_log_tag`, `MessageDelivery.ONCHAIN_UNCONSTRAINED`, `Owned`,
`emit_private_log_vec_unsafe`, `to_address_point`,
`compute_siloed_private_log_first_field`,
`poseidon2_hash_with_separator`) exist on `v4-next` — the contract uses
the `aztec::protocol` re-export of `protocol_types`.

## Commit structure

Per the backport convention, history is preserved as:
1. Cherry-pick with conflicts (markers in tree, recorded in history).
2. Conflict resolution commit (this PR's tip).

No separate build-fixes commit was needed — the conflict was
metadata-only.

## Build verification

The container does not ship a prebuilt `nargo`, and Docker is
unavailable, so `noir-contracts/bootstrap.sh` cannot run locally.
Relying on CI for end-to-end build validation.

Original PR: #22854

ClaudeBox log: https://claudebox.work/s/096af9ddd4f770c8?run=4
AztecBot added a commit that referenced this pull request May 9, 2026
BEGIN_COMMIT_OVERRIDE
fix(aztec-up): Aztec installer does not shadow user installed binaries
on PATH (#22902)
fix: include sqlite binary in its npm package (#23039)
chore: backport #23041 (add sendMessagesAs to wallet api schemas) to
v4-next (#23081)
chore: backport DeployMethod refactor (#22985) to v4-next (#23029)
refactor(pxe): deduplicate tx hash lookups in MessageContextService
(#23075)
refactor(pxe): batch tagged private log queries across all secrets
(#23048)
refactor(pxe): batch log RPC calls in LogService.fetchLogsByTag (#23088)
feat(aztec-nr): Initial handshake registry contract with non interactive
handshake function (#22854)
fix: add Tag.random() helper required by backported #23088 tests
(#23094)
chore: backport: fix(aztec-up): installer does not shadow user binaries
on PATH (#22902) (#23060)
chore: backport handshake registry contract (#22854) to v4-next (#23063)
feat: deploy method refactor 2 (#23033)
refactor(pxe): skip redundant getBlock RPC when querying at anchor block
(#23100)
port(v4-next): feat(docs): autogenerate node JSON-RPC API reference
(#22543) (#23046)
chore: backport feat: deploy method refactor 2 (#23033) to v4-next
(#23103)
port(v4-next): feat(ci): Snapshots for aztec-nr contract compilation
failures and nargo expand (#23061) (#23104)
feat(txe): allow authorizing cross-contract utility calls in nr tests
(#23064)
END_COMMIT_OVERRIDE
vezenovm added a commit that referenced this pull request May 15, 2026
… delivery (#23278)

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

---------

Co-authored-by: Nicolas Chamo <nicolas@chamo.com.ar>
rangozd pushed a commit to rangozd/aztec-packages that referenced this pull request May 16, 2026
BEGIN_COMMIT_OVERRIDE
refactor: `getPackageVersion` fn cleanup (AztecProtocol#22941)
fix(ci): skip acceptance test for canary -commit. tags (AztecProtocol#22951)
fix: closing db, correct stub side effects (AztecProtocol#22939)
feat: wallet-sdk heartbeat (AztecProtocol#22948)
chore: pxe db schema compatibility test (AztecProtocol#22872)
feat: stamping aztec version into contract artifacts (AztecProtocol#22550)
fix: add aztecVersion to PXE schema_tests fixture (AztecProtocol#22960)
feat(pxe): deduplicate class ID verification per contract (AztecProtocol#22966)
chore: add noirfmt.toml to noir-contracts and run nargo fmt (AztecProtocol#22971)
feat(aztec-nr): Initial handshake registry contract with non interactive
handshake function (AztecProtocol#22854)
chore: merge next into merge-train/fairies (AztecProtocol#22991)
fix(aztec-up): Aztec installer does not shadow user installed binaries
on PATH (AztecProtocol#22902)
test(ci): drop e2e_kernelless_simulation from backwards-compat e2e
(AztecProtocol#23005)
feat(ci): notify #team-fairies when backwards-compat e2e fails on
nightly (AztecProtocol#23006)
chore: merge next into merge-train/fairies (AztecProtocol#23021)
fix: better DeployMethod (AztecProtocol#22985)
refactor(pxe): batch RPC calls for note and event validation (AztecProtocol#22988)
END_COMMIT_OVERRIDE
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants