Skip to content

Commit 8155b94

Browse files
author
AztecBot
committed
chore: sync public-v5-next with upstream v5-next
2 parents 89a3575 + 9448c85 commit 8155b94

8 files changed

Lines changed: 304 additions & 106 deletions

File tree

docs/docs-developers/ai_tooling.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,13 @@ For Claude Code, create a `CLAUDE.md` file in your project root. For Codex, crea
2222
```markdown
2323
# Aztec Project
2424

25-
## Critical: Use `aztec` CLI, not `nargo` directly
25+
## Critical: Use the `aztec` CLI, not `nargo` or `bb` directly
2626

27-
This is an Aztec smart contract project. Always use the `aztec` CLI wrapper instead of calling `nargo` directly:
27+
This is an Aztec smart contract project. Always use the `aztec` CLI wrapper instead of calling `nargo` or `bb` (the Barretenberg prover) directly:
2828

2929
- **Compile**: `aztec compile` (NOT `nargo compile`). Using `nargo compile` alone produces incomplete artifacts.
3030
- **Test**: `aztec test` (NOT `nargo test`).
31+
- **Prove**: NEVER call `bb` directly. Proof generation is handled for you by the PXE through the `aztec` CLI and `aztec.js`. There is no contract-development workflow that runs `bb` by hand.
3132
- **Other nargo commands** like `aztec-nargo fmt` and `aztec-nargo doc` are fine to use directly. The Aztec installer exposes the bundled `nargo` as `aztec-nargo`; bare `nargo` resolves to your own install (if any), not the bundled one.
3233

3334
## Error Handling
@@ -57,7 +58,7 @@ This prevents the two most common AI mistakes: using `nargo compile`/`nargo test
5758

5859
### Why this matters
5960

60-
LLMs have extensive training data for `nargo` (the standalone Noir compiler) but limited exposure to the `aztec` CLI wrapper. Without explicit instructions, they default to `nargo compile`, which produces artifacts missing the AVM transpilation step.
61+
LLMs have extensive training data for `nargo` (the standalone Noir compiler) and `bb` (the Barretenberg prover CLI) but limited exposure to the `aztec` CLI wrapper. Without explicit instructions, they default to `nargo compile` (which produces artifacts missing the AVM transpilation step) or reach for `bb` to generate proofs. In an Aztec project, compilation and proving both go through the `aztec` tooling.
6162

6263
## MCP servers
6364

Lines changed: 67 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
11
---
2-
title: Partial Notes
2+
title: Partial notes
33
sidebar_position: 1
44
tags: [Developers, Contracts, Notes]
5-
description: How partial notes work and how they can be used.
6-
references: ["noir-projects/aztec-nr/uint-note/src/uint_note.nr", "noir-projects/noir-contracts/contracts/app/token_contract/src/main.nr"]
5+
description: How partial notes work, how they are completed, and how they enable use cases like AMM swaps and payment endpoints.
6+
references: ["noir-projects/aztec-nr/uint-note/src/uint_note.nr"]
77
---
88

99
import Image from "@theme/IdealImage";
1010

11-
## What are Partial Notes?
11+
:::note Token standard
12+
Where this page refers to a concrete token, it assumes the [AIP-20 fungible token standard](../../standards/aip-20.md). The partial-note primitive (`UintNote` / `PartialUintNote`) is token-agnostic; AIP-20 is one standard built on top of it.
13+
:::
14+
15+
## What are partial notes?
1216

1317
Partial notes are notes created with incomplete data, usually during private execution, which can be completed with additional information that becomes available later, usually during public execution.
1418

@@ -18,31 +22,77 @@ Let's say, for example, we have a `UintNote`:
1822

1923
The `UintNote` struct itself only contains the `value` field. Additional fields including `owner`, `randomness`, and `storage_slot` are passed as parameters during note hash computation.
2024

21-
When creating the note locally during private execution, the `owner` and `storage_slot` are known, but the `value` potentially is not (e.g., it depends on some onchain dynamic variable). First, a **partial note** can be created during private execution that commits to the `owner` and `randomness`, and then the note is *"completed"* to create a full note by later adding the `storage_slot` and `value` fields, usually during public execution.
25+
When creating the note locally during private execution, the `owner` and `storage_slot` are known, but the `value` potentially is not (e.g., it depends on some onchain dynamic variable). First, a **partial note** can be created during private execution that commits to the `owner` and `randomness`, and then the note is _"completed"_ to create a full note by later adding the `storage_slot` and `value` fields, usually during public execution.
2226

2327
<Image img={require("@site/static/img/partial-notes.png")} />
2428

25-
## Use Cases
29+
## Use cases
2630

27-
Partial notes are useful when a e.g., part of the note struct is a value that depends on dynamic, public onchain data that isn't available during private execution, such as:
31+
Partial notes are useful when part of the note struct is a value that depends on dynamic, public onchain data that isn't available during private execution, such as:
2832

2933
- AMM swap prices
3034
- Current gas prices
3135
- Time-dependent interest accrual
3236

37+
They are also useful as **payment endpoints**: a recipient can create a partial note ahead of time and share the commitment with prospective senders. Senders later complete the partial note to pay the recipient, with no action needed from the recipient at payment time. See [partial notes as payment endpoints](./partial_notes_as_payment_endpoints.md) for the full design.
38+
39+
## The completer
40+
41+
A partial note is finalized by a later completion step that supplies the public fields (`storage_slot` and `value`). At that point its private preimage (`owner` and `randomness`) is not re-derived or re-checked, and the partial note itself is just a `Field` that can be copied and shared freely. If anyone holding it could complete it, they could insert a note with an arbitrary, unbacked `value` into the note hash tree, or complete the note at the wrong time or with the wrong values.
42+
43+
To prevent this, the creator designates a **completer** at creation time. During the constrained private execution that creates the partial note, the contract records a validity commitment `H(partial_commitment, completer)` in the nullifier tree. Completion recomputes this commitment and asserts it exists, using its presence as proof that a legitimate, constrained execution created the partial note and authorized this specific completer to supply the public values and finalize it.
44+
45+
With AIP-20, the completer is chosen explicitly when calling `initialize_transfer_commitment`; completion (`transfer_private_to_commitment` / `transfer_public_to_commitment`) binds the completer to the caller's `msg_sender` and debits a separately authorized `from` account.
46+
47+
For `UintNote`, the fields split cleanly across the two phases:
48+
49+
| Field | Fixed at | How |
50+
| -------------- | ------------------------------- | -------------------------------------------------------------------------------------------- |
51+
| `owner` | Partial note creation (private) | Committed in `partial_commitment = H(owner, randomness)` |
52+
| `randomness` | Partial note creation (private) | Same commitment; fresh per note, blinds the owner |
53+
| `completer` | Partial note creation (private) | Bound in the validity commitment `H(partial_commitment, completer)`; not part of the note hash |
54+
| `storage_slot` | Completion (public or private) | Hashed into `note_hash = H(storage_slot, partial_commitment, value)` |
55+
| `value` | Completion (public or private) | Same hash; supplied by the completer's call |
56+
57+
(`storage_slot` is typically known during private execution too; it is just not bound into the note hash until completion.)
58+
59+
The creator fixes who gets paid (`owner`) and who may finalize (`completer`); the completer later fixes how much (`value`). Funds therefore flow from the completing side to the note's owner: in AIP-20, completion debits the authorized `from` account (the completer itself, or a payer who authorized it) and credits the `owner` chosen by the creator.
60+
61+
## Single-use semantics
62+
63+
Each partial note is intended to be completed exactly once. The protocol does not enforce this directly: completion checks that a validity commitment exists in the nullifier tree but does not consume it, so a partial note can technically be completed more than once. However, reuse is unsafe for two independent reasons:
64+
65+
1. **Privacy.** The completion log is tagged by `H(partial_commitment)`. Two completions of the same partial note emit logs with the same tag, which publicly links those completions as paying the same recipient.
66+
2. **Discovery.** The recipient's Private eXecution Environment (PXE) treats the partial note as pending until the first matching completion log is found. After the first match, the pending entry is removed. A second completion against the same commitment may not be discovered by the recipient's wallet, so the funds are effectively lost.
67+
68+
This is why an AIP-20 commitment should be completed only once. A second `transfer_private_to_commitment` (or `transfer_public_to_commitment`) against the same commitment is not found by the recipient's log processing on the second pass, so the amount is most likely lost.
69+
70+
The takeaway: treat each partial note as a one-shot object. To accept multiple payments, create multiple partial notes.
71+
72+
## Completion in public and private contexts
73+
74+
`PartialUintNote` supports completion in two contexts:
75+
76+
- `complete` runs in a public function (AIP-20's `transfer_public_to_commitment`). The storage slot and value are emitted in a public log tagged by the partial note's commitment. Anyone observing the chain learns the amount.
77+
- `complete_from_private` runs in a private function (AIP-20's `transfer_private_to_commitment`). The same storage slot and value are emitted in a private log with the same tag. The payload is plaintext, but it is only discoverable by a party that can derive the tag, and the tag derives from the partial note's commitment.
78+
79+
For private→private completion, the privacy of the amount depends on whether the partial note's commitment itself is held secret. If the commitment is published publicly (e.g., in an onchain registry), anyone can derive the tag and read the amount from the private log payload. If the commitment is shared only with prospective senders, the amount stays hidden from outside observers.
80+
81+
One additional protocol constraint: `complete_from_private` requires the validity commitment to be settled in a prior transaction. A partial note cannot be both created and completed in the same private transaction. The public completion path has no such restriction.
82+
3383
## Implementation
3484

3585
All notes in Aztec use the partial note format internally. This ensures that notes produce identical note hashes regardless of whether they were created as complete notes (with all fields known in private) or as partial notes (completed later in public). By having all notes follow the same two-phase hash commitment process, the protocol maintains consistency and allows notes created through different flows to behave identically.
3686

37-
### Note Structure Example
87+
### Note structure example
3888

3989
The `UintNote` struct contains only the `value` field:
4090

4191
#include_code uint_note_def /noir-projects/aztec-nr/uint-note/src/uint_note.nr rust
4292

43-
### Two-Phase Commitment Process
93+
### Two-phase commitment process
4494

45-
**Phase 1: Partial Commitment (Private Execution)**
95+
**Phase 1: partial commitment (private execution)**
4696

4797
The private fields (`owner` and `randomness`) are committed during local, private execution:
4898

@@ -54,7 +104,7 @@ This creates a partial note commitment:
54104
partial_commitment = H(owner, randomness)
55105
```
56106

57-
**Phase 2: Note Completion (Public Execution)**
107+
**Phase 2: note completion (public execution)**
58108

59109
The note is completed by hashing the partial commitment with the public value:
60110

@@ -63,11 +113,11 @@ The note is completed by hashing the partial commitment with the public value:
63113
The resulting structure is a nested commitment:
64114

65115
```
66-
note_hash = H(H(owner, randomness), storage_slot, value)
67-
= H(partial_commitment, storage_slot, value)
116+
note_hash = H(storage_slot, H(owner, randomness), value)
117+
= H(storage_slot, partial_commitment, value)
68118
```
69119

70-
## Universal Note Format
120+
## Universal note format
71121

72122
All notes in Aztec use the partial note format internally, even when all data is known during private execution. This ensures consistent note hash computation regardless of how the note was created.
73123

@@ -80,7 +130,8 @@ When a note is created with all fields known (including `owner`, `storage_slot`,
80130

81131
This two-step process ensures that notes with identical field values produce identical note hashes, regardless of whether they were created as partial notes or complete notes.
82132

83-
84-
## Partial Notes in Practice
133+
## Partial notes in practice
85134

86135
To understand how to use partial notes in practice, [this AMM contract](https://github.com/AztecProtocol/aztec-packages/tree/#include_aztec_version/noir-projects/noir-contracts/contracts/app/amm_contract) uses partial notes to initiate and complete the swap of `token1` to `token2`. Since the exchange rate is onchain, it cannot be known ahead of time while executing in private so a full note cannot be created. Instead, a partial note is created for the `owner` swapping the tokens. This partial note is then completed during public execution once the exchange rate can be read.
136+
137+
For a different application of the same primitive, where the partial note represents an offer to be paid rather than a deferred DeFi settlement, see [partial notes as payment endpoints](./partial_notes_as_payment_endpoints.md).

0 commit comments

Comments
 (0)