|
2 | 2 | title: Partial Notes |
3 | 3 | sidebar_position: 1 |
4 | 4 | tags: [Developers, Contracts, Notes] |
5 | | -description: "Learn how partial notes enable private-to-public value transfers when data depends on onchain state." |
| 5 | +description: How partial notes work and how they can be used. |
6 | 6 | --- |
7 | 7 |
|
8 | 8 | import Image from "@theme/IdealImage"; |
9 | 9 |
|
10 | | -Partial notes are notes created with incomplete data during private execution, which are completed later with additional information that becomes available during public execution. |
| 10 | +## What are Partial Notes? |
11 | 11 |
|
12 | | -## Prerequisites |
| 12 | +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. |
13 | 13 |
|
14 | | -- Understanding of [notes and private state](../how_to_implement_custom_notes.md) |
15 | | -- Familiarity with [private and public function execution](../../../foundational-topics/call_types.md) |
| 14 | +Let's say, for example, we have a `UintNote`: |
16 | 15 |
|
17 | | -## Overview |
| 16 | +#include_code uint_note_def /noir-projects/aztec-nr/uint-note/src/uint_note.nr rust |
18 | 17 |
|
19 | | -Consider a `UintNote`: |
| 18 | +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. |
20 | 19 |
|
21 | | -#include_code uint_note_def noir-projects/aztec-nr/uint-note/src/uint_note.nr rust |
22 | | - |
23 | | -The struct only contains the `value` field. Additional fields (`owner`, `randomness`, `storage_slot`) are passed as parameters during note hash computation. |
24 | | - |
25 | | -When creating a note in private, the `owner` and `storage_slot` are known, but the `value` may not be (e.g., it depends on onchain state). A **partial note** commits to the private fields first, then is _completed_ by adding the `value` field during public execution. |
| 20 | +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`, `randomness`, and `storage_slot`, and then the note is *"completed"* to create a full note by later adding the `value` field, usually during public execution. |
26 | 21 |
|
27 | 22 | <Image img={require("@site/static/img/partial-notes.png")} /> |
28 | 23 |
|
29 | 24 | ## Use Cases |
30 | 25 |
|
31 | | -Partial notes are useful when part of the note depends on dynamic, public onchain data unavailable during private execution: |
| 26 | +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: |
32 | 27 |
|
33 | 28 | - AMM swap prices |
34 | 29 | - Current gas prices |
35 | 30 | - Time-dependent interest accrual |
36 | 31 |
|
37 | | -## Two-Phase Commitment Process |
38 | | - |
39 | | -All notes in Aztec use the partial note format internally. This ensures identical note hashes regardless of whether notes were created complete (all fields known in private) or as partial notes (completed later in public). |
| 32 | +## Implementation |
40 | 33 |
|
41 | | -### Phase 1: Partial Commitment (Private Execution) |
| 34 | +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. |
42 | 35 |
|
43 | | -The private fields (`owner`, `randomness`, `storage_slot`) are committed during private execution, creating a `PartialUintNote`: |
| 36 | +### Note Structure Example |
44 | 37 |
|
45 | | -#include_code partial_uint_note_def noir-projects/aztec-nr/uint-note/src/uint_note.nr rust |
| 38 | +The `UintNote` struct contains only the `value` field: |
46 | 39 |
|
47 | | -The commitment is computed as: |
| 40 | +#include_code uint_note_def /noir-projects/aztec-nr/uint-note/src/uint_note.nr rust |
48 | 41 |
|
49 | | -#include_code compute_partial_commitment noir-projects/aztec-nr/uint-note/src/uint_note.nr rust |
| 42 | +### Two-Phase Commitment Process |
50 | 43 |
|
51 | | -This produces: `partial_commitment = H(owner, storage_slot, randomness)` |
| 44 | +**Phase 1: Partial Commitment (Private Execution)** |
52 | 45 |
|
53 | | -### Phase 2: Note Completion (Public Execution) |
| 46 | +The private fields (`owner`, `randomness`, and `storage_slot`) are committed during local, private execution: |
54 | 47 |
|
55 | | -The note is completed by hashing the partial commitment with the public value: |
56 | | - |
57 | | -#include_code compute_complete_note_hash noir-projects/aztec-nr/uint-note/src/uint_note.nr rust |
| 48 | +#include_code compute_partial_commitment /noir-projects/aztec-nr/uint-note/src/uint_note.nr rust |
58 | 49 |
|
59 | | -The resulting note hash is: `H(partial_commitment, value)` |
| 50 | +This creates a partial note commitment: |
60 | 51 |
|
61 | | -### Complete Notes Use the Same Format |
| 52 | +``` |
| 53 | +partial_commitment = H(owner, storage_slot, randomness) |
| 54 | +``` |
62 | 55 |
|
63 | | -When a note is created with all fields known, it still follows the same two-phase process internally: |
| 56 | +**Phase 2: Note Completion (Public Execution)** |
64 | 57 |
|
65 | | -#include_code compute_note_hash noir-projects/aztec-nr/uint-note/src/uint_note.nr rust |
| 58 | +The note is completed by hashing the partial commitment with the public value: |
66 | 59 |
|
67 | | -This ensures notes with identical field values produce identical note hashes, regardless of whether they were created as partial or complete notes. |
| 60 | +#include_code compute_complete_note_hash /noir-projects/aztec-nr/uint-note/src/uint_note.nr rust |
68 | 61 |
|
69 | | -## Using Partial Notes |
| 62 | +The resulting structure is a nested commitment: |
70 | 63 |
|
71 | | -The typical workflow involves two function calls. The [Token contract](https://github.com/AztecProtocol/aztec-packages/tree/#include_aztec_version/noir-projects/noir-contracts/contracts/app/token_contract/src/main.nr) demonstrates this pattern: |
| 64 | +``` |
| 65 | +note_hash = H(H(owner, storage_slot, randomness), value) |
| 66 | + = H(partial_commitment, value) |
| 67 | +``` |
72 | 68 |
|
73 | | -**1. Private function**: Create the partial note using `UintNote::partial()`: |
| 69 | +## Universal Note Format |
74 | 70 |
|
75 | | -#include_code prepare_private_balance_increase noir-projects/noir-contracts/contracts/app/token_contract/src/main.nr rust |
| 71 | +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. |
76 | 72 |
|
77 | | -**2. Public function**: Complete the note with the now-known value: |
| 73 | +When a note is created with all fields known (including `owner`, `storage_slot`, `randomness`, and `value`): |
78 | 74 |
|
79 | | -#include_code finalize_transfer_to_private noir-projects/noir-contracts/contracts/app/token_contract/src/main.nr rust |
| 75 | +1. A partial commitment is computed from the private fields (`owner`, `storage_slot`, `randomness`) |
| 76 | +2. The partial commitment is immediately completed with the `value` field |
80 | 77 |
|
81 | | -The `completer` parameter ensures only the authorized address can finalize the note, preventing front-running attacks. |
| 78 | +#include_code compute_note_hash /noir-projects/aztec-nr/uint-note/src/uint_note.nr rust |
82 | 79 |
|
83 | | -## Example: AMM Contract |
| 80 | +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. |
84 | 81 |
|
85 | | -The [AMM contract](https://github.com/AztecProtocol/aztec-packages/tree/next/noir-projects/noir-contracts/contracts/app/amm_contract) uses partial notes for token swaps. Since the exchange rate is only known onchain, a partial note is created for the recipient in private, then completed during public execution once the output amount is calculated. |
| 82 | +<Image img={require("@site/static/img/shrek.jpeg")} /> |
86 | 83 |
|
87 | | -## Next Steps |
| 84 | +## Partial Notes in Practice |
88 | 85 |
|
89 | | -- [Implement custom notes](../how_to_implement_custom_notes.md) - Learn about note structure and lifecycle |
90 | | -- [Private and public execution](../../../foundational-topics/call_types.md) - Understand the execution model |
91 | | -- [Token contract tutorial](../../../tutorials/contract_tutorials/token_contract.md) - See partial notes in a complete example |
| 86 | +To understand how to use partial notes in practice, [this AMM contract](https://github.com/AztecProtocol/aztec-packages/tree/next/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. |
0 commit comments