Skip to content

feat(aztec-nr): add interactive handshake to the handshake registry - #24473

Merged
nchamo merged 3 commits into
merge-train/fairies-v5from
nchamo/interactive-handshakes
Jul 2, 2026
Merged

nchamo merged 3 commits into
merge-train/fairies-v5from
nchamo/interactive-handshakes

Conversation

@nchamo

@nchamo nchamo commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Motivation

Non-interactive handshakes are unilateral: the recipient never consents, and the announcement log reveals that the recipient was contacted. Some flows want the opposite trade-off: the recipient explicitly authorizes the handshake, and nothing is announced

The change

  • Adds HandshakeRegistry::interactive_handshake. It shares secret generation and note storage with the non-interactive flow, but requires the recipient to sign the freshly generated ephemeral key, and emits no announcement log.
  • The signature is fetched through the resolveCustomRequest hook. The response deserializes into a new RecipientSignature struct: the recipient's public keys, partial address, message-signing key point, and a schnorr signature.
  • Verification is in-circuit and needs no PXE registration of the recipient. The returned keys are bound to the recipient's address via AztecAddress::compute, the signing key is tied to the address-committed mspk_m_hash, and the signature is checked over [chain_id, version, registry, eph_pk.x] under a new domain separator. The signed message never includes the sender, so the recipient authorizes without learning who initiated the handshake.

@nchamo nchamo added ci-no-fail-fast Sets NO_FAIL_FAST in the CI so the run is not aborted on the first failure ci-draft Run CI on draft PRs. labels Jul 2, 2026
@nchamo nchamo self-assigned this Jul 2, 2026
@nchamo
nchamo marked this pull request as ready for review July 2, 2026 19:30
@nchamo
nchamo requested review from LeilaWang and nventuro as code owners July 2, 2026 19:30
// `interactive_handshake` must obtain the recipient's signature from a `resolveCustomRequest` resolver before
// storing anything. The TXE test environment configures no such resolver, so the call fails at the resolver hop.
#[test(should_fail_with = "no resolveCustomRequest hook is configured")]
unconstrained fn interactive_handshake_requires_a_resolver() {

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.

We currently can't test "green" scenarios since we can't mock an oracle inside private context. At the same time, we can't generate a signature from TXE accounts today

I think the best way to test the happy path would be with the e2e automatic test we are building

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.

Can we not mock regularly using OracleMock::mock("aztec_prv_callPrivateFunction")? Or do we need this to be exposed through TXE (I did not look deeply into this)? Just was looking at the unit tests added in constrained_delivery.nr.

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.

OracleMock only intercepts oracles fired from the test process itself. The constrained_delivery.nr tests are aztec-nr library unit tests, so the code under test runs in-process and its oracles are mockable. Here interactive_handshake runs inside TXE via env.call_private, so aztec_prv_resolveCustomRequest resolves in the TXE server (which throws without a hook) and never reaches the test-side mock. Happy path needs TXE-side resolver support (a signing resolver using the recipient account's mspk) or the e2e

@nchamo nchamo changed the title feat(aztec-nr): add interactive_handshake to the handshake registry feat(aztec-nr): add interactive handshake to the handshake registry Jul 2, 2026
@nchamo
nchamo requested a review from vezenovm July 2, 2026 20:00

@vezenovm vezenovm 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.

Overall looks good! Just some nits.

Comment thread docs/docs-developers/docs/foundational-topics/pxe/execution_hooks.md Outdated
/// If `recipient` is not a valid curve point. There are no upstream side effects in this call frame to
/// protect, and a fallback would insert a permanent note recording a handshake with an invalid recipient,
/// polluting registry state.
/// If `recipient` is not a valid curve point.

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.

nit: I felt the justification for the panic was good info, but I am also good with just cleaning it up.

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.

oh I see you moved this info to the tests now

/// Siloing by `self.msg_sender()` means a hostile contract cannot read another app's siloed secrets: whatever
/// arguments a caller passes, it only ever learns values siloed to its own address. Contracts should still call
/// [HandshakeRegistry::validate_handshake] when they need a constrained proof that supplied app-siloed secrets
/// [`HandshakeRegistry::validate_handshake`] when they need a constrained proof that supplied app-siloed secrets

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.

nit: I saw we added several back-ticks in our doc links. I don't think they are necessary for linking functionality

@nchamo nchamo Jul 2, 2026

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 link only shows on my IDE with the back-ticks. That why I assumed it was needed. Doesn't hurt though

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.

oh ok, ignore me then. Mine wasn't needing them but also works with the ticks.

// `interactive_handshake` must obtain the recipient's signature from a `resolveCustomRequest` resolver before
// storing anything. The TXE test environment configures no such resolver, so the call fails at the resolver hop.
#[test(should_fail_with = "no resolveCustomRequest hook is configured")]
unconstrained fn interactive_handshake_requires_a_resolver() {

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.

Can we not mock regularly using OracleMock::mock("aztec_prv_callPrivateFunction")? Or do we need this to be exposed through TXE (I did not look deeply into this)? Just was looking at the unit tests added in constrained_delivery.nr.

@nchamo
nchamo enabled auto-merge (squash) July 2, 2026 22:40
@nchamo
nchamo merged commit c86e84c into merge-train/fairies-v5 Jul 2, 2026
12 checks passed
@nchamo
nchamo deleted the nchamo/interactive-handshakes branch July 2, 2026 22:58
nchamo added a commit that referenced this pull request Jul 3, 2026
vezenovm added a commit that referenced this pull request Jul 6, 2026
…24511)

## Summary

- Renames the HandshakeRegistry's `get_handshakes` utility to
`get_non_interactive_handshakes`: it only returns handshakes discovered
from non-interactive announcement logs, so interactive handshakes
(#24473) never appear in it, and the name should say which kind it
returns.
- Sweeps the rename through the aztec-nr selector constant, the
registry's internal reader, the PXE default-authorization allowlist, and
tests, and documents the getter's privacy properties (any contract can
read a scope's discovered handshakes; ephemeral keys don't reveal the
shared secret).
- Re-pins the standard contracts, since the registry's artifact changes.

Stacked on #24483.

Fixes F-769

---------

Co-authored-by: Maxim Vezenov <mvezenov@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci-draft Run CI on draft PRs. ci-no-fail-fast Sets NO_FAIL_FAST in the CI so the run is not aborted on the first failure

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants