docs: partial notes as payment endpoints - #23564
Conversation
Add a concept page on using partial notes as a recipient's stable, unlinkable payment endpoint, and expand partial_notes.md to cover single-use semantics, public vs private completion, and a forward link to the new page. The new page walks through the design space for an Aztec naming service that resolves a name to a partial note rather than an address. Covers the completer choice (per-sender, shim contract, not the token), the distribution choice (offchain vs onchain), the recommended pattern (offchain distribution + shim + private completion), and an honest leakage analysis. Corrects a subtle misconception that partial-note single-use is enforced by the protocol. The validity commitment is checked at completion but not consumed, so reuse is mechanically possible. Reuse is unsafe because the completion log tag is derived from the commitment (privacy break) and the recipient's PXE only discovers the first completion (funds lost).
Address review feedback on the partial-notes payment-endpoints page: - Clarify that holding a commitment does not grant completion; only the designated completer can complete it. - Distinguish the partial-note primitive (which checks a completer-bound validity commitment) from the standard token's msg_sender == completer convention. - Reframe the stealth-address comparison: this pattern requires the recipient to pre-mint and refill a pool of commitments, rather than claiming non-interactive derivation is impossible.
# Conflicts: # docs/docs-words.txt
Rewrite the payment-endpoints page to assume the AIP-20 fungible token standard, with an explicit callout at the top. AIP-20 takes the completer as an argument to initialize_transfer_commitment and debits a separately authorized `from` account, so the example-token shim-routing workaround no longer applies: the recipient mints directly with any completer, and a relayer is only needed at completion time so unknown senders can pay. Update function names and flows to transfer_private_to_commitment / transfer_public_to_commitment. Also minor wording and formatting tweaks in partial_notes.md.
- partial_notes.md: add an AIP-20 assumption callout, a "The completer" section explaining why completion is gated by a committed completer (validity commitment as a capability, unbacked-note risk), map complete / complete_from_private to the AIP-20 entrypoints, and use consistent terminology (partial note in prose, partial_commitment in formulas). - partial_notes_as_payment_endpoints.md: tighten the completer-choice framing, scope the onchain-registry leak, and note the new not-yet-fully-constrained onchain_constrained delivery mode.
|
|
||
| They are also useful as **payment endpoints**: a recipient can mint a partial note ahead of time and share the commitment with prospective senders. Senders later complete the partial note to pay the recipient, who does not need to be online for the payment to land. See [partial notes as payment endpoints](./partial_notes_as_payment_endpoints.md) for the full design. | ||
|
|
||
| ## The completer |
There was a problem hiding this comment.
Can we add some details about what in the note struct is known at partial note vs completion of the full note (and therefore the direction of the funds as completer -> creator) if that makes sense
ciaranightingale
left a comment
There was a problem hiding this comment.
small improvements which when resolved is gtg
|
|
||
| :::note Assumed token standard | ||
| This page assumes the [AIP-20 fungible token standard](../../standards/aip-20.md), which exposes commitment-based transfers directly: the completer is an explicit argument to `initialize_transfer_commitment`, and completion debits a separately authorized account. Tokens that instead hardcode the completer to `msg_sender` (such as the example `token_contract` in aztec-packages) need a more roundabout flow that this page does not cover. | ||
| This page assumes the [AIP-20 fungible token standard](../../standards/aip-20.md), which exposes commitment-based transfers directly: the completer is an explicit argument to `initialize_transfer_commitment`, and completion debits a separately authorized account. The example `token_contract` in aztec-packages also supports both flows (the single-call `transfer_to_private` and the two-step `prepare_private_balance_increase` plus `finalize_transfer_to_private`), but its prepare step hardcodes the completer to the caller. A recipient who prepares a partial note there is the only party able to complete it, which rules out the third-party-sender flows this page describes. |
There was a problem hiding this comment.
im still not sure this is accurate - maybe Im wrong but I'm pretty sure a different completer is still possible in the token_contract - did you have a look?
There was a problem hiding this comment.
the token contract in aztec-packages hardcodes the msg_sender, whereas the wonderland token contract does not
| The trick is that the name never maps to an address at all. It maps to a pool of opaque commitments, and senders pay into a commitment. Concretely, for `alice.aztec`: | ||
|
|
||
| 1. **Alice's wallet creates partial notes ahead of time.** Each one is a commitment `H(alice_address, randomness_i)` with fresh randomness per note. Her address is inside the hash, blinded by the randomness, so the commitment reveals nothing about her. | ||
| 2. **The chain records a validity commitment.** Creating each partial note records `H(partial_commitment, completer)` in the nullifier tree. Also just a hash; no address is visible. |
There was a problem hiding this comment.
the term "validity commitment" is confusing - what is it? I'm confused again
There was a problem hiding this comment.
its literally just a recorded partial note in the note hash tree? where does the nullifier come in?
There was a problem hiding this comment.
the "validity commitment" binds the partial commitment to a completer, its a term used in the source code.
also the nullifier tree is used to store the commitment, so it can be checked in the future. i think it's confusing bc the "nullifier tree" is being used for something other than just storing a nullifier. i added a clarifying note later in the doc
- Clarify the validity commitment (what it is, nullifier-tree-as-existence-set) - Correct token_contract completer behavior: completer = msg_sender of prepare, private relayer flow still achievable - Use 'list' / 'partial note commitments' instead of 'pool' / 'opaque commitments'
Flakey Tests🤖 says: This CI run detected 1 tests that failed, but were tolerated due to a .test_patterns.yml entry. |
Summary
partial_notes_as_payment_endpoints.mdcovering how partial notes can back a stable name (e.g.alice.aztec) with a rotating supply of unlinkable, one-shot payment endpoints. Walks through the completer choice, the distribution choice (offchain vs. onchain registry), a recommended pattern (offchain distribution + shim contract as completer + private→private completion), and an honest leakage analysis.partial_notes.mdto cover single-use semantics, completion in public vs. private contexts, and a forward link to the new page.unlinkableandunlinkabilitytodocs-words.txtand fix a small pre-existing typo inpartial_notes.md.Context
Started from a Slack thread asking whether Aztec could support a naming service where resolving a name returns "something" other than a public address. The answer that fell out: a partial note. Each partial note is a commitment to be paid; senders complete it later. The recipient does not need to be online for the payment to land. Mike asked for docs reflecting this paradigm.
The new page deliberately stops short of shipping a reference contract. The shim contract sketch is illustrative, not compilable, because the public API decisions (whether to also act as fee payer, support for public→private routing, authwit ergonomics) need product input before being committed to docs.
What I verified against source
noir-projects/aztec-nr/uint-note/src/uint_note.nr— thepartial,complete, andcomplete_from_privatefunctions, plus the validity commitment semantics.noir-projects/noir-contracts/contracts/app/token_contract/src/main.nr:340-395—prepare_private_balance_increase,finalize_transfer_to_private,finalize_transfer_to_private_from_private, and the token's own warnings about partial-note reuse.noir-projects/aztec-nr/aztec/src/messages/discovery/partial_notes.nr:170— pending partial notes are removed after the first matching completion log, confirming "discovery fails on reuse."Codex (gpt-5.5) reviewed both the design plan and the draft and flagged the issues above before posting.
Test plan
yarn buildindocs/to verify the new page integrates with the Docusaurus build and the#include_codemacros resolve.sidebar_position: 2next to existingpartial_notes.md).