Skip to content

feat(pxe): resolve tagging secret source via wallet hook#24040

Open
nchamo wants to merge 17 commits into
merge-train/fairies-v5from
nchamo/f-699-pxewallet-get_delivery_privacy_preference-oracle
Open

feat(pxe): resolve tagging secret source via wallet hook#24040
nchamo wants to merge 17 commits into
merge-train/fairies-v5from
nchamo/f-699-pxewallet-get_delivery_privacy_preference-oracle

Conversation

@nchamo

@nchamo nchamo commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Motivation

There are now several ways to deliver a message between users, each with a different privacy trade-off. Delivery can rely on sender-recipient coordination (a previously established handshake, or a secret the two already share off-chain) and leak nothing. Or it can establish a non-interactive handshake on the fly, which works without coordination at the cost of publishing information about the recipient onchain. When no tagging secret has been established for a (sender, recipient) pair yet, someone has to decide how to source one. That decision belongs to the wallet, which owns the user's privacy stance and can ask the user if needed.

The change

aztec-nr gains a resolve_tagging_secret(sender, recipient, mode) oracle returning a TaggingSecretSource. It is consulted only when no tagging secret has been established for the (sender, recipient) pair yet; an established secret is reused without asking. The send flow will call it when resolving the tagging secret (F-698, not wired in this PR).

TaggingSecretSource carries the wallet's decision and any material the chosen derivation needs:

  • non_interactive_handshake — a secret the recipient can derive from the onchain handshake registry. Works without prior coordination, but publishes information about the recipient onchain.
  • shared_secret — a secret both parties already share off-chain (e.g. derived via Diffie-Hellman from each other's address keys). Leaves no onchain trace, but because nothing onchain proves the recipient knows it, it is only sound for unconstrained delivery.

Returning the source directly (rather than an earlier privacy-preference enum) lets the wallet express exactly which secret to use, including material PXE cannot derive on its own.

PXE answers the oracle through a new optional resolveTaggingSecret execution hook. We chose a hook over a static config value because the decision is per message. The request carries:

  • the executing contract (address and class id)
  • the message's sender
  • the message's recipient
  • the delivery mode (constrained or unconstrained)

This lets wallets apply per-application or per-recipient policies, or surface the decision to the user. The hook follows the existing precedent of authorizeUtilityCall, PXE's other wallet policy callback.

When no hook is configured PXE applies a privacy-safe default: unconstrained delivery uses an address-derived (Diffie-Hellman) shared secret, which leaves no onchain trace, while constrained delivery fails, because every safe constrained source needs a wallet (a non-interactive handshake would reveal the recipient onchain). Privacy is therefore never weakened without the wallet opting in.

TXE has no hooks, so tests configure the source through a cheatcode (unset by default, which exercises PXE's default):

env.set_tagging_secret_source(Option::some(TaggingSecretSource::shared_secret(secret)));

Docs: a new "Execution hooks" page documents the hook mechanism and both hooks, the note delivery page gains a "Tagging secret source" section, and debugging.md now links the hooks page instead of duplicating the authorization instructions.

Fixes F-699

@nchamo nchamo requested a review from nventuro as a code owner June 11, 2026 22:19
@nchamo nchamo self-assigned this Jun 11, 2026
@nchamo nchamo requested review from Thunkar and vezenovm June 12, 2026 02:36

@Thunkar Thunkar left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I like it!

Comment thread docs/docs-developers/docs/aztec-nr/framework-description/note_delivery.md Outdated
Comment thread docs/docs-developers/docs/aztec-nr/framework-description/note_delivery.md Outdated
Comment thread docs/docs-developers/docs/aztec-nr/framework-description/note_delivery.md Outdated
Comment thread docs/docs-developers/docs/aztec-nr/framework-description/note_delivery.md Outdated
Comment thread docs/docs-developers/docs/aztec-nr/framework-description/note_delivery.md Outdated
Comment thread noir-projects/aztec-nr/aztec/src/oracle/delivery_privacy_preference.nr Outdated
Comment thread noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr Outdated
Comment thread yarn-project/pxe/src/hooks/execution_hooks.ts Outdated
Comment thread yarn-project/txe/src/txe_session.ts Outdated
@nchamo nchamo requested a review from nventuro June 12, 2026 21:46
@nchamo nchamo changed the title feat(pxe): add get_delivery_privacy_preference oracle feat(pxe): resolve tagging secret source via wallet hook Jun 19, 2026
/// reused. Otherwise, the wallet must decide how to source it: this type is that decision, carrying any material the
/// chosen derivation needs.
#[derive(Eq, Serialize)]
pub struct TaggingSecretSource {

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.

This would be source and secret, correct? Because we do have secret here.

Also try to make it so public nr docs have their first paragraph always be a very short single line (e.g. 'a tagging shared secret and its source'), expanding in future paragraphs. since the first par is what the docsite renders on a preview (see e.g. how the 'modules' list here is fairly clean https://docs.aztec.network/aztec-nr-api/mainnet/noir_aztec/index.html, while this is messier https://docs.aztec.network/aztec-nr-api/mainnet/noir_aztec/oracle/notes/).

The same advice applies to all fns in the impl below.

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 improved the docs, but I'm not sure what you mean by:

This would be source and secret, correct? Because we do have secret here.

Are you suggesting we rename TaggingSecretSource?


## Tagging secret source

Onchain delivery tags every message so the recipient can find it efficiently (see [note discovery](#note-discovery-and-the-sender) below). Computing a tag requires a secret shared between sender and recipient, and there is more than one way for the two parties to come to share it. When one has already been established for the pair, it is reused directly. Otherwise the wallet decides how to proceed, since it knows which secrets it holds and how it wants to reach the recipient.

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.

When one has already been established for the pair, it is reused directly

you mean when an onchain handshake exists, correct? not necessarily that any shared secret has already been used, rather that one has been registered (an ecdh handshake would not be registered)

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.

You are right, I made it clearer

The "sender" for note discovery is **not the contract calling `.deliver()`**. Instead, it's the **account contract** that initiated the transaction.

When your wallet submits a transaction, it tells PXE which address to use as the sender for tags (typically the originating account). This sender address is then used along with the recipient address to compute a shared secret (via [Diffie-Hellman key exchange](https://www.geeksforgeeks.org/computer-networks/diffie-hellman-key-exchange-and-perfect-forward-secrecy/)), which generates the tag that allows recipients to efficiently find their notes. Contracts can override the sender at message delivery via the `with_sender` builder method, e.g. `MessageDelivery::onchain_unconstrained().with_sender(address)`.
When your wallet submits a transaction, it tells PXE which address to use as the sender for tags (typically the originating account). The tag recipients use to find their notes is computed from a secret shared between the sender and recipient, and there is [more than one way to establish that secret](#tagging-secret-source), chosen by the wallet. Contracts can override the sender at message delivery via the `with_sender` builder method, which works for both constrained and unconstrained delivery, e.g. `MessageDelivery::onchain_constrained().with_sender(address)`.

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 more changes here (and the next file) so we can review most of the docs changes together. Otherwise, it will be very hard to review them incrementally

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