Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: deprecate assert_contains_and_remove #2594

Merged
merged 9 commits into from
Oct 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 23 additions & 44 deletions docs/docs/dev_docs/contracts/syntax/storage.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
title: Storage
---

In an Aztec.nr contract, storage is to be defined as a single struct, that contains both public and private state variables.
In an Aztec.nr contract, storage is to be defined as a single struct, that contains both public and private state variables.

As their name indicates, public state variables can be read by anyone, while private state variables can only be read by their owner, or people whom the owner has shared the data with.

Expand Down Expand Up @@ -82,12 +82,12 @@ To define that a variable is public, it is wrapped in the `PublicState` struct,

#include_code public_state_struct /yarn-project/aztec-nr/aztec/src/state_vars/public_state.nr rust

The `PublicState` struct is generic over the variable type `T` and its serialized size `T_SERIALIZED_LEN`.
The `PublicState` struct is generic over the variable type `T` and its serialized size `T_SERIALIZED_LEN`.
:::info
Currently, the length of the types must be specified when declaring the storage struct but the intention is that this will be inferred in the future.
:::

The struct contains a `storage_slot` which, similar to Ethereum, is used to figure out *where* in storage the variable is located. Notice that while we don't have the exact same [state model](./../../../concepts/foundation/state_model.md) as EVM chains it will look similar from the contract developers point of view.
The struct contains a `storage_slot` which, similar to Ethereum, is used to figure out _where_ in storage the variable is located. Notice that while we don't have the exact same [state model](./../../../concepts/foundation/state_model.md) as EVM chains it will look similar from the contract developers point of view.

Beyond the struct, the `PublicState` also contains `serialization_methods`, which is a struct with methods that instruct the `PublicState` how to serialize and deserialize the variable.

Expand All @@ -101,17 +101,18 @@ The Aztec.nr library provides serialization methods for various common types. As

#include_code field_serialization /yarn-project/aztec-nr/aztec/src/types/type_serialization/field_serialization.nr rust


:::info
An example using a larger struct can be found in the [lending example](https://github.com/AztecProtocol/aztec-packages/tree/master/yarn-project/noir-contracts/src/contracts/lending_contract)'s use of an [`Asset`](https://github.com/AztecProtocol/aztec-packages/tree/master/yarn-project/noir-contracts/src/contracts/lending_contract/src/asset.nr).
:::

### `new`

When declaring the storage for `T` as a persistent public storage variable, we use the `PublicState::new()` constructor. As seen below, this takes the `storage_slot` and the `serialization_methods` as arguments along with the [`Context`](./context.mdx), which in this case is used to share interface with other structures.

#include_code public_state_struct_new /yarn-project/aztec-nr/aztec/src/state_vars/public_state.nr rust

#### Single value example

Say that we wish to add `admin` public state variable into our storage struct. In the struct we can add it as follows:

#include_code storage_admin /yarn-project/noir-contracts/src/contracts/token_contract/src/main.nr rust
Expand All @@ -121,14 +122,17 @@ And then when initializing it in the `Storage::init` function we can do it as fo
#include_code storage_admin_init /yarn-project/noir-contracts/src/contracts/token_contract/src/main.nr rust

In this case, specifying that we are dealing with a Field, and that it should be put at slot 1. This is just a single value, and would be similar to the following in solidity:

```solidity
address internal admin;
```

:::info
We know its verbose, and are working on making it less so.
:::

#### Mapping example

Say we want to have a group of `minters` that are able to mint assets in our contract, and we want them in public storage, because [access control in private is quite cumbersome](./../../../concepts/foundation/communication/public_private_calls.md#a-note-on-l2-access-control). In the `Storage` struct we can add it as follows:

#include_code storage_minters /yarn-project/noir-contracts/src/contracts/token_contract/src/main.nr rust
Expand All @@ -140,13 +144,14 @@ And then when initializing it in the `Storage::init` function we can do it as fo
In this case, specifying that we are dealing with a map of Fields, and that it should be put at slot 2.

This would be similar to the following in solidity:

```solidity
mapping(address => bool) internal minters;
```

### `read`

Now we have an idea of how to define storage, but storage is not really useful before we start using it, so how can we access it?
Now we have an idea of how to define storage, but storage is not really useful before we start using it, so how can we access it?

Reading data from storage is straightforward. On the `PublicState` structs we have a `read` method to read the value at the location in storage and using the specified deserialization method to deserialize it. Here is the function definition in the `public_state.nr` source:

Expand All @@ -158,17 +163,15 @@ For our `admin` example from earlier, this could be used as follows to check tha

#include_code read_admin /yarn-project/noir-contracts/src/contracts/token_contract/src/main.nr rust


#### Reading from our `minters` example

As we saw in the Map earlier, a very similar operation can be done to perform a lookup in a map.
As we saw in the Map earlier, a very similar operation can be done to perform a lookup in a map.

#include_code read_minter /yarn-project/noir-contracts/src/contracts/token_contract/src/main.nr rust


### `write`

We figured out how to read values, but how do we write them?
We figured out how to read values, but how do we write them?

Like reading, it is actually quite straight-forward. We have a `write` method on the `PublicState` struct that takes the value to write as an input and saves this in storage. It uses the serialization method defined earlier to serialize the value which inserts (possibly multiple) values into storage.

Expand All @@ -183,6 +186,7 @@ Like reading, it is actually quite straight-forward. We have a `write` method on
#include_code write_minter /yarn-project/noir-contracts/src/contracts/token_contract/src/main.nr rust

## Private State Variables

In contrast to public state, private state is persistent state that is **not** visible to the whole world. Depending on the logic of the smart contract, a private state variable's current value will only be known to one entity, or a closed group of entities.

The value of a private state variable can either be shared via an [encrypted log](./events.md#encrypted-events), or offchain via web2, or completely offline: it's up to the app developer.
Expand All @@ -201,7 +205,8 @@ These three structs abstract-away many of Aztec's protocol complexities, by prov
Note that an app can also choose to emit data via unencrypted log, or to define a note whose data is easy to figure out, then the information is technically not private and could be visible to anyone.
:::

### Notes
### Notes

Unlike public state variables, which can be arbitrary types, private state variables operate on `NoteType`.

Notes are the fundamental elements in the private world.
Expand All @@ -226,9 +231,6 @@ The interplay between a private state variable and its notes can be confusing. H
- To modify the "current value" of a `Set` state variable, is to [`insert`](#insert) new notes into the `Set`, or [`remove`](#remove) notes from that set.
- Interestingly, if a developer requires a private state to be modifiable by users who _aren't_ privy to the value of that state, a `Set` is a very useful type. The `insert` method allows new notes to be added to the `Set` without knowing any of the other notes in the set! (Like posting an envelope into a post box, you don't know what else is in there!).




## `Singleton<NoteType>`

Singleton is a private state variable that is unique in a way. When a Singleton is initialized, a note is created to represent its value. And the way to update the value is to destroy the current note, and create a new one with the updated value.
Expand Down Expand Up @@ -289,7 +291,7 @@ However, it's possible that at the time this function is called, the system hasn

ImmutableSingleton represents a unique private state variable that, as the name suggests, is immutable. Once initialized, its value cannot be altered.

#include_code struct /yarn-project/aztec-nr/aztec/src/state_vars/immutable_singleton.nr rust
#include_code struct /yarn-project/aztec-nr/aztec/src/state_vars/immutable_singleton.nr rust

### `new`

Expand All @@ -313,9 +315,9 @@ Once initialized, an ImmutableSingleton's value remains unchangeable. This metho

Similar to the `Singleton`, we can use the `get_note` method to read the value of an ImmutableSingleton.

#include_code get_note /yarn-project/aztec-nr/aztec/src/state_vars/immutable_singleton.nr rust
#include_code get_note /yarn-project/aztec-nr/aztec/src/state_vars/immutable_singleton.nr rust

Use this method to retrieve the value of an initialized ImmutableSingleton.
Use this method to retrieve the value of an initialized ImmutableSingleton.

#include_code get_note /yarn-project/noir-contracts/src/contracts/schnorr_account_contract/src/main.nr rust

Expand All @@ -327,7 +329,7 @@ This function will throw if the ImmutableSingleton hasn't been initialized.

Set is used for managing a collection of notes. All notes in a set are of the same `NoteType`. But whether these notes all belong to one entity, or are accessible and editable by different entities, is totally up to the developer. Due to our state model, the set is a collection of notes inserted into the data-tree, but notes are never removed from the tree itself, they are only nullified.

#include_code struct /yarn-project/aztec-nr/aztec/src/state_vars/set.nr rust
#include_code struct /yarn-project/aztec-nr/aztec/src/state_vars/set.nr rust

And can be added to the `Storage` struct as follows. Here adding a set for a custom note, the TransparentNote (useful for [public -> private communication](./functions.md#public---private)).

Expand Down Expand Up @@ -363,33 +365,9 @@ The usage is rather straight-forward and very similar to using the `insert` meth

#include_code insert_from_public /yarn-project/noir-contracts/src/contracts/token_contract/src/main.nr rust

### `assert_contains_and_remove`

This function is used to check existence of a note and then remove it without having read the note ahead of time. This can be useful for cases where the user is providing all the information needed, such as cases where the note was never emitted to the network and thereby available to the wallet.

#include_code assert_contains_and_remove /yarn-project/aztec-nr/aztec/src/state_vars/set.nr rust

<!---
@LHerskind
I don't see why this one is actually needed and could not be deprecated down the line.
Allow insertions into the rpc database and this one can be removed.
-->

### `assert_contains_and_remove_publicly_created`

Like above, this is used to ensure that the message exists in the data tree and then consume it. However, it differs slightly since there is currently a difference between notes that have been inserted from public and private execution. This means that you currently must use this function to consume and nullify a note that was created in a public function. This will be fixed in the future.

#include_code assert_contains_and_remove_publicly_created /yarn-project/aztec-nr/aztec/src/state_vars/set.nr rust

While this might look intimidating, the use of the function is rather easy, and is used in the following way:

#include_code assert_contains_and_remove_publicly_created /yarn-project/noir-contracts/src/contracts/token_contract/src/main.nr rust

The reason we are not reading this note ahead of time is that no [encrypted log](./events.md#encrypted-events) was emitted for this note, since it was created in public thereby making the encrypted log useless (everyone saw the content ahead of time).

### `remove`

Will remove a note from the set if it previously has been read from storage, e.g. you have fetched it through a `get_notes` call. This is useful when you want to remove a note that you have previously read from storage and do not have to read it again. If you recall from earlier, we are emitting a nullifier when reading values to make sure that they are up to date.
Will remove a note from the set if it previously has been read from storage, e.g. you have fetched it through a `get_notes` call. This is useful when you want to remove a note that you have previously read from storage and do not have to read it again. If you recall from earlier, we are emitting a nullifier when reading values to make sure that they are up to date.

#include_code remove /yarn-project/aztec-nr/aztec/src/state_vars/set.nr rust

Expand All @@ -403,15 +381,16 @@ This function returns the notes the account has access to:

#include_code get_notes /yarn-project/aztec-nr/aztec/src/state_vars/set.nr rust

Our kernel circuits are constrained to a maximum number of notes this function can return at a time. Check [here](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/aztec-nr/aztec/src/constants_gen.nr) and look for `MAX_READ_REQUESTS_PER_CALL` for the up-to-date number.
Our kernel circuits are constrained to a maximum number of notes this function can return at a time. Check [here](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/aztec-nr/aztec/src/constants_gen.nr) and look for `MAX_READ_REQUESTS_PER_CALL` for the up-to-date number.

Because of this limit, we should always consider using the second argument `NoteGetterOptions` to limit the number of notes we need to read and constrain in our programs. This is quite important as every extra call increases the time used to prove the program and we don't want to spend more time than necessary.

An example of such options is using the [filter_notes_min_sum](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/aztec-nr/value-note/src/filter.nr) to get "enough" notes to cover a given value. Essentially, this function will return just enough notes to cover the amount specified such that we don't need to read all our notes. For users with a lot of notes, this becomes increasingly important.
An example of such options is using the [filter_notes_min_sum](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/aztec-nr/value-note/src/filter.nr) to get "enough" notes to cover a given value. Essentially, this function will return just enough notes to cover the amount specified such that we don't need to read all our notes. For users with a lot of notes, this becomes increasingly important.

#include_code get_notes /yarn-project/aztec-nr/easy-private-state/src/easy_private_state.nr rust

### `view_notes`

Functionally similar to [`get_notes`](#get_notes), but executed unconstrained and can be used by the wallet to fetch notes for use by front-ends etc.

#include_code view_notes /yarn-project/aztec-nr/aztec/src/state_vars/set.nr rust
Expand Down
3 changes: 1 addition & 2 deletions docs/docs/dev_docs/getting_started/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ Note that the deployed contract address is exported, so we can use it as `$CONTR

Alice is set up as the contract admin and token minter in the `_initialize` function. Let's get Alice some private tokens.

We need to export the `SECRET` and `SECRET_HASH` values in order to privately mint tokens. Private tokens are claimable by anyone with the pre-image to a provided hash, see more about how the token contract works in the [token contract tutorial](../tutorials/writing_token_contract.md). Once the tokens have been minted, Alice can claim them with the `redeem_shield` function. After this, Alice should have 1000 tokens in their private balance.
We need to export the `SECRET` and `SECRET_HASH` values in order to privately mint tokens. Private tokens are claimable by anyone with the pre-image to a provided hash, see more about how the token contract works in the [token contract tutorial](../tutorials/writing_token_contract.md). After the tokens have been minted, the notes will have to added to the PXE to be consumed by private functions. Once added, Alice can claim them with the `redeem_shield` function. After this, Alice should have 1000 tokens in their private balance.

#include_code mint-private yarn-project/end-to-end/src/guides/up_quick_start.sh bash

Expand All @@ -117,4 +117,3 @@ Aztec's Layer 2 network is a fully programmable combined private/public ZK rollu
- Private Execution Environment (PXE) - Normally residing with the end client, this decrypts and stores a client's private state, executes simulations and submits transactions to the Aztec Node.
- [Aztec.js](./sandbox) - Aztec's client library for interacting with the PXE (think Ethers.js).
- [Aztec.nr](../contracts/main.md) - Aztec's smart contract framework

3 changes: 1 addition & 2 deletions docs/docs/dev_docs/tutorials/writing_token_contract.md
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,6 @@ Public functions are declared with the `#[aztec(public)]` macro above the functi

As described in the [execution contexts section above](#execution-contexts), public function logic and transaction information is transparent to the world. Public functions update public state, but can be used to prepare data to be used in a private context, as we will go over below (e.g. see the [shield](#shield) function).


Storage is referenced as `storage.variable`.

#### `set_admin`
Expand Down Expand Up @@ -375,7 +374,7 @@ Storage is referenced as `storage.variable`.

This private function enables an account to move tokens from a `TransparentNote` in the `pending_shields` mapping to any Aztec account as a `ValueNote` in private `balances`.

Going through the function logic, first storage is initialized. Then it gets the private balance for the recipient. A `TransparentNote` is created from the `amount` and `secret` and verified to exist storage in `pending_shields` with the `assert_contains_and_remove_publicly_created` method. If that is verified, the recipient's private balance is incremented using the `increment` helper function from the `value_note` [library](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/aztec-nr/value-note/src/utils.nr).
Going through the function logic, first the `secret_hash` is generated from the given secret. This ensures that only the entity possessing the secret can use it to redeem the note. Following this, a `TransparentNote` is retrieved from the set, using the provided amount and secret. The note is subsequently removed from the set, allowing it to be redeemed only once. The recipient's private balance is then increased using the `increment` helper function from the `value_note` [library](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/aztec-nr/value-note/src/utils.nr).

The function returns `1` to indicate successful execution.

Expand Down
Loading