Skip to content

Conversation

@ciaranightingale
Copy link
Collaborator

No description provided.

@netlify
Copy link

netlify bot commented Nov 11, 2025

Deploy Preview for aztec-docs-2 ready!

Name Link
🔨 Latest commit 639da73
🔍 Latest deploy log https://app.netlify.com/projects/aztec-docs-2/deploys/693067fad580600009a5582d
😎 Deploy Preview https://deploy-preview-11--aztec-docs-2.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

```

In Aztec.nr we instead define a `struct` (link to noir structs) that holds all state variables. we call this struct **the storage struct**, and it is identified by having the `#[storage]` macro (link to storage api ref) applied to it.
In Aztec.nr, we define a [`struct`](https://noir-lang.org/docs/noir/concepts/data_types/structs) that holds _all_ state variables. This struct is called **the storage struct**, and it is identified by having the `#[storage]` macro applied to it.
Copy link
Contributor

Choose a reason for hiding this comment

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

(link to storage api ref)

This and other removed link suggestions would be quite valuable I think. I know we don't yet have the docs for them, but if we already know the structure of the docsite we can at least link to these empty pages. I worry if we just remove these notes we'll forget to add them back.

```

The storage struct can have any name, but it is typically just called `Storage`. this struct must also have a generic type called C or Context - this is unfortunate boilerplate (link to api ref where we explain _why_ this must exist and what it means - which we don't think is something users need to know about, but some might be curious).
The storage struct can have _any_ name, but it is _typically_ named `Storage`. This struct must also have a generic type called `C` or `Context` - this is an unfortunate boilerplate parameter that provides execution mode information.
Copy link
Contributor

Choose a reason for hiding this comment

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

Looking at our test suite I just realized we don't allow for any name other than Storage 🙃

- For public state variables, storage slots are related to slots in the public data tree
- For private state variables, storage slots are metadata that gets included in the note hash

The purpose of slots is the same for both domains: they keep the values of different state values _separate_ so that they do not interfere with one another.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
The purpose of slots is the same for both domains: they keep the values of different state values _separate_ so that they do not interfere with one another.
The purpose of slots is the same for both domains: they keep the values of different state variables _separate_ so that they do not interfere with one another.

This was a typo of mine in the original text.

The purpose of slots is the same for both domains: they keep the values of different state values _separate_ so that they do not interfere with one another.

For example, a `Map<AztecAddress, PublicMutable<UintNote>>` can be accessed with an address to obtain a `PublicMutable` that corresponds to it. This is exactly equivalent to a Solidity `mapping (address => uint)`.
Storage slots are a low-level detail that developers don't typically need to concern themselves with. They are automatically allocated to each state variable by Aztec.nr.
Copy link
Contributor

Choose a reason for hiding this comment

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

My original comment included something about it being dangerous to manually assign/use storage slots, I think we should keep it.

`PublicMutable` is the simplest kind of public state variable: a value that can be read and written. It is essentially the same as a non-`immutable` or `constant` Solidity state variable.

because they reside in the network's public storage tree (link to foundational concepts), they can only be written to by public contract functions. it is possible to read _past_ values of a public state variable in a private contract function, but the current values of the network's public state tree are not accessible in those. this means that most public state variables cannot be read from a private function - though there's exceptions.
It **cannot be read or written to privately**, but it is possible to call private functions that enqueue a public call in which a `PublicMutable` is accessed. For example, a voting contract may allow private submission of votes which then enqueue a public call in which the vote count, represented as a `PublicMutable<u128>`, is incremented. This would let anyone see how many votes have been cast, while preserving the privacy of the account that cast the vote.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
It **cannot be read or written to privately**, but it is possible to call private functions that enqueue a public call in which a `PublicMutable` is accessed. For example, a voting contract may allow private submission of votes which then enqueue a public call in which the vote count, represented as a `PublicMutable<u128>`, is incremented. This would let anyone see how many votes have been cast, while preserving the privacy of the account that cast the vote.
It **cannot be read or written to privately**, but it is possible to have private functions enqueue a public call in which a `PublicMutable` is accessed. For example, a voting contract may allow private submission of votes which then enqueue a public call in which the vote count, represented as a `PublicMutable<u128>`, is incremented. This would let anyone see how many votes have been cast, while preserving the privacy of the account that cast the vote.

recipients learning about notes created for them is known as 'note discovery', which is a process aztecnr handles efficiently automatically. it does mean however that when a note is created, a _message_ with the content of note is created and needs to be delivered to a recipient via one of multiple means (link to messages).
Most often, nullifiers are used to mark a note as being spent, which prevents note double spends. The nullifier is typically computed as a **hash of the note contents concatenated with a private key of the note's owner**. These values are **immutable**, and only the owner knows their private keys, ensuring both determinism and secrecy.

##### Note Lifecycle
Copy link
Contributor

Choose a reason for hiding this comment

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

I do not agree with the removal of this section. Yes, it is long, and yes, it delays the explanation of state variables (which is what this article is about after all), but it is quite critical and it introduces lots of important concepts.

If anything, we could have a different page blather on about notes, nullifiers, lifecycle, delivery, etc, remove that entire section from this page, and just go 'if you don't know what we mean by notes and nullifiers, go read this thing first and then come back here'.

- `MessageDelivery`: Either `CONSTRAINED_ONCHAIN` (verified in the circuit) or `UNCONSTRAINED_ONCHAIN` (cheaper but less secure)

- insertion: the transaction is sent to the network and gets included in a block. the note hash is inserted into the note hash tree - this is visible to the entire network, but the content of the note remains private. (link to protocol details - note hash tree insertion and tx effects)
- **`.discard()`**: Explicitly discards the note without emitting it (useful when you're reading but not sending)
Copy link
Contributor

Choose a reason for hiding this comment

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

dicard is rarely, if ever, used. If reading a private mutable you still need to deliver the replacement note, failure to do so will lock you out of access to the state variable forever.

#### Accessing the Note

- reading: while executing private contract function, the recipient fetches the note's content and metadata from their private database and shows that its hash exists in the note hash tree as part of the zero-knowledge proof.
After calling `.emit()` or `.discard()`, you can access the underlying note using the `.note` property:
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd not go crazy on this section since NoteEmission is about to change in its naming (for the better).

| `PrivateImmutable` | no | no | no | Fixed configuration, one-way actions (e.g. initialization settings for a proposal) |
| `PrivateSet` | yes | yes | yes | Aggregated state others can add to, e.g. token balance (set of amount notes), nft collections (set of nft ids) |

### How aztec-nr Abstracts Private State Variables
Copy link
Contributor

Choose a reason for hiding this comment

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

Do you find this entire bit useless?

Comment on lines +356 to +364
To update the value of a `PrivateMutable`, we can use the `replace` method:

```rust
#[external("private")]
fn update_settings(new_value: u8) {
let owner = self.msg_sender();
self.storage.user_settings.at(owner).replace(|_| SettingsNote::new(new_value, owner)).emit(owner, MessageDelivery.CONSTRAINED_ONCHAIN);
}
```
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd maybe also include an example with e.g. a callcount to better motivate replace.

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