Skip to content

Commit

Permalink
feat: initial storage slots docs (#2842)
Browse files Browse the repository at this point in the history
Adds an initial explanation around storage slots, how they are computed
and used between private and public data.
  • Loading branch information
LHerskind authored Nov 6, 2023
1 parent c7490c5 commit e8bcd03
Show file tree
Hide file tree
Showing 15 changed files with 183 additions and 51 deletions.
2 changes: 1 addition & 1 deletion docs/docs/concepts/foundation/accounts/authwit.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ All of these issues have been discussed in the community for a while, and there

Adopting ERC20 for Aztec is not as simple as it might seem because of private state.

If you recall from [State model](./../state_model.md), private state is generally only known by its owner and those they have shared it with. Because it relies on secrets, private state might be "owned" by a contract, but it needs someone with knowledge of these secrets to actually spend it. You might see where this is going.
If you recall from [State model](./../state_model/main.md), private state is generally only known by its owner and those they have shared it with. Because it relies on secrets, private state might be "owned" by a contract, but it needs someone with knowledge of these secrets to actually spend it. You might see where this is going.

If we were to implement the `approve` with an allowance in private, you might know the allowance, but unless you also know about the individual notes that make up the user's balances, it would be of no use to you! It is private after all. To spend the user's funds you would need to know the decryption key, see [keys for more](../accounts/keys.md).

Expand Down
4 changes: 2 additions & 2 deletions docs/docs/concepts/foundation/main.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ As a layer 2 rollup on Ethereum, the Aztec network includes components that look

On this page we will introduce the high level network architecture for Aztec with an emphasis on the concepts that are core to understanding Aztec, including:

- [The state model](./state_model.md)
- [The state model](./state_model/main.md)
- [Accounts](./accounts/main.md)
- [Aztec Smart Contracts](./contracts.md)
- [Transactions](./transactions.md)
Expand All @@ -24,7 +24,7 @@ A user of the Aztec network will interact with the network through Aztec.js. Azt

### Private Execution Environment

The PXE provides a secure environment for the execution of sensitive operations, ensuring private information and decrypted data are not accessible to unauthorized applications. It hides the details of the [state model](./state_model.md) from end users, but the state model is important for Aztec developers to understand as it has implications for [private/public execution](./communication/public_private_calls.md) and [L1/L2 communication](./communication/cross_chain_calls.md). The PXE also includes the [ACIR Simulator](../advanced/acir_simulator.md) for private executions and the KeyStore for secure key management.
The PXE provides a secure environment for the execution of sensitive operations, ensuring private information and decrypted data are not accessible to unauthorized applications. It hides the details of the [state model](./state_model/main.md) from end users, but the state model is important for Aztec developers to understand as it has implications for [private/public execution](./communication/public_private_calls.md) and [L1/L2 communication](./communication/cross_chain_calls.md). The PXE also includes the [ACIR Simulator](../advanced/acir_simulator.md) for private executions and the KeyStore for secure key management.

Procedurally, the PXE sends results of private function execution and requests for public function executions to the [sequencer](./nodes_clients/sequencer.md), which will update the state of the rollup.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,4 @@ This is achieved with two main features:

## Further reading

Read more about how to leverage the Aztec state model in Aztec contracts [here](../../dev_docs/contracts/syntax/storage.md).
Read more about how to leverage the Aztec state model in Aztec contracts [here](../../../dev_docs/contracts/syntax/storage/main.md).
65 changes: 65 additions & 0 deletions docs/docs/concepts/foundation/state_model/storage_slots.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
---
title: Storage Slots
description: How are storage slots derived for public and private state
---

In Aztec private data and public data are stored in two trees, a public data tree and a note hashes tree.

These trees have in common that they store state for *all* accounts on the Aztec network directly as leaves. This is different from Ethereum, where a state trie contains smaller tries that hold the individual accounts' storage.

It also means that we need to be careful about how we allocate storage to ensure that they don't collide! We say that storage should be *siloed* to its contract. The exact way of siloing differs a little for public and private storage. Which we will see in the following sections.

## Public State Slots

As mentioned in [State Model](./main.md), Aztec public state behaves similarly to public state on Ethereum from the point of view of the developer. Behind the scenes however, the storage is managed differently. As mentioned, public state has just one large sparse tree in Aztec - so we silo slots of public data by hashing it together with its contract address.

The mental model is that we have a key-value store, where the siloed slot is the key, and the value is the data stored in that slot. You can think of the `real_storage_slot` identifying its position in the tree, and the `logical_storage_slot` identifying the position in the contract storage.

```rust
real_storage_slot = H(contract_address, logical_storage_slot)
```

The siloing is performed by the [Kernel circuits](../../advanced/circuits/kernels/main.md).

For structs and arrays, we are logically using a similar storage slot computation to ethereum, e.g., as a struct with 3 fields would be stored in 3 consecutive slots. However, because the "actual" storage slot is computed as a hash of the contract address and the logical storage slot, the actual storage slot is not consecutive.


## Private State Slots - Slots aren't real

Private storage is a different beast. As you might remember from [State Model](./main.md), private state is stored in encrypted logs and the corresponding private state commitments in append-only tree where each leaf is a commitment. Being append-only, means that leaves are never updated or deleted; instead a nullifier is emitted to signify that some note is no longer valid. A major reason we used this tree, is that lookups at a specific storage slot would leak information in the context of private state. If you could look up a specific address balance just by looking at the storage slot, even if encrypted you would be able to see it changing! That is not good privacy.

Following this. The storage slot as we know it doesn't really exists. The leaves of the note hashes tree are just commitments to content (think of it as a hash of its content).

Nevertheless, the concept of a storage slot is very useful when writing applications, since it allows us to reason about distinct and disjoint pieces of data. For example we can say that the balance of an account is stored in a specific slot and that the balance of another account is stored in another slot with the total supply stored in some third slot. By making sure that these slots are disjoint, we can be sure that the balances are not mixed up and that someone cannot use the total supply as their balance.

### But how?

If we include the storage slot, as part of the note whose commitment is stored in the note hashes tree, we can *logically link* all the notes that make up the storage slot. For the case of a balance, we can say that the balance is the sum of all the notes that have the same storage slot - in the same way that your physical \$ balance might be the sum of all the notes in your wallet.

Similarly to how we siloed the public storage slots, we can silo our private storage by hashing the logical storage slot together with the note content.

```rust
note_hash = H(...note_content);
commitment = H(logical_storage_slot, note_hash);
```

This siloing (there will be more) is done in the application circuit, since it is not necessary for security of the network (but only the application).
:::info
The private variable wrappers `Set` and `Singleton` in Aztec.nr include the `logical_storage_slot` in the commitments they compute, to make it easier for developers to write contracts without having to think about how to correctly handle storage slots.
:::

When reading the values for these notes, the application circuit can then constrain the values to only read notes with a specific logical storage slot.

To ensure that one contract cannot insert storage that other contracts would believe is theirs, we do a second siloing by hashing the `commitment` with the contract address.

```rust
siloed_commitment = H(contract_address, commitment);
```

By doing this address-siloing at the kernel circuit we *force* the inserted commitments to include and not lie about the `contract_address`.

:::info
To ensure that nullifiers don't collide across contracts we also force this contract siloing at the kernel level.
:::

For an example of this see [developer documentation storage slots](./../../../dev_docs/contracts/syntax/storage/storage_slots.md).
2 changes: 1 addition & 1 deletion docs/docs/dev_docs/contracts/layout.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
title: Structure
---

A contract is a collection of persistent [state variables](./syntax/storage.md), and [functions](./syntax/functions) which may manipulate these variables. Functions and state variables within a contract's scope are said to belong to that contract. A contract can only access and modify its own state. If a contract wishes to access or modify another contract's state, it must make a call to an external function of the other contract. For anything to happen on the Aztec network, an external function of a contract needs to be called.
A contract is a collection of persistent [state variables](./syntax/storage/main.md), and [functions](./syntax/functions) which may manipulate these variables. Functions and state variables within a contract's scope are said to belong to that contract. A contract can only access and modify its own state. If a contract wishes to access or modify another contract's state, it must make a call to an external function of the other contract. For anything to happen on the Aztec network, an external function of a contract needs to be called.

# Contract

Expand Down
4 changes: 1 addition & 3 deletions docs/docs/dev_docs/contracts/syntax/context.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,7 @@ On this page, you'll learn
- Differences between the private and public contexts, especially the unique features and variables in the public context

## Two context's one API

The `Aztec` blockchain contains two environments [public and private](../../../concepts/foundation/state_model.md).

The `Aztec` blockchain contains two environments [public and private](../../../concepts/foundation/state_model/main.md).
- Private, for private transactions taking place on user's devices.
- Public, for public transactions taking place on the network's sequencers.

Expand Down
2 changes: 1 addition & 1 deletion docs/docs/dev_docs/contracts/syntax/functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@ We achieve this by pushing return values to the execution context, which we then
**Making the contract's storage available**
#include_code storage-example-context /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/main.nr rust

When a [`Storage` struct](./storage.md) is declared within a contract, the `storage` keyword is made available. As shown in the macro expansion above, this calls the init function on the storage struct with the current function's context.
When a [`Storage` struct](./storage/main.md) is declared within a contract, the `storage` keyword is made available. As shown in the macro expansion above, this calls the init function on the storage struct with the current function's context.

Any state variables declared in the `Storage` struct can now be accessed as normal struct members.

Expand Down
2 changes: 1 addition & 1 deletion docs/docs/dev_docs/contracts/syntax/main.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ On top of [Noir's stdlib](https://noir-lang.org/standard_library/array_methods),

Aztec.nr contains abstractions which remove the need to understand the low-level Aztec protocol. Notably, it provides:

- Public and private [state variable types](./storage.md)
- Public and private [state variable types](./storage/main.md)
- Some pre-designed notes
- Functions for [emitting](./events.md) encrypted and unencrypted logs
- [Oracle functions](./functions.md#oracle-functions) for accessing:
Expand Down
Loading

0 comments on commit e8bcd03

Please sign in to comment.