Skip to content

Commit

Permalink
merge
Browse files Browse the repository at this point in the history
  • Loading branch information
alexghr committed Oct 2, 2023
2 parents fd2f7c0 + 1073bcd commit 3691b70
Show file tree
Hide file tree
Showing 74 changed files with 965 additions and 713 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ For the sake of cross-chain messages, this means inserting and nullifying L1 $\r

### Messages

While a message could theoretically be arbitrary long, we want to limit the cost of the insertion on L1 as much as possible. Therefore, we allow the users to send 32 bytes of "content" between L1 and L2. If 32 suffices, no packing required. If the 32 is too "small" for the message directly, the sender should simply pass along a `sha256(content)` instead of the content directly. The content can then either be emitted as an event on L2 or kept by the sender, who should then be the only entity that can "unpack" the message.
While a message could theoretically be arbitrary long, we want to limit the cost of the insertion on L1 as much as possible. Therefore, we allow the users to send 32 bytes of "content" between L1 and L2. If 32 suffices, no packing required. If the 32 is too "small" for the message directly, the sender should simply pass along a `sha256(content)` instead of the content directly (note that this hash should fit in a field element which is ~254 bits. More info on this below). The content can then either be emitted as an event on L2 or kept by the sender, who should then be the only entity that can "unpack" the message.
In this manner, there is some way to "unpack" the content on the receiving domain.

The message that is passed along, require the `sender/recipient` pair to be communicated as well (we need to know who should receive the message and be able to check). By having the pending messages be a contract on L1, we can ensure that the `sender = msg.sender` and let only `content` and `recipient` be provided by the caller. Summing up, we can use the struct's seen below, and only store the commitment (`sha256(LxToLyMsg)`) on chain or in the trees, this way, we need only update a single storage slot per message.
Expand Down
14 changes: 14 additions & 0 deletions docs/docs/dev_docs/contracts/portals/main.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,20 @@ Computing the `content` must be done manually in its current form, as we are sti

#include_code claim_public /yarn-project/noir-contracts/src/contracts/token_bridge_contract/src/main.nr rust

:::info
The `content_hash` is a sha256 truncated to a field element (~ 254 bits). In Aztec-nr, you can use our `sha256_to_field()` to do a sha256 hash which fits in one field element:

#include_code mint_public_content_hash_nr /yarn-project/noir-contracts/src/contracts/token_bridge_contract/src/util.nr rust

In solidity, you can use our `Hash.sha256ToField()` method:

#include_code content_hash_sol_import l1-contracts/test/portals/TokenPortal.sol solidity

#include_code deposit_public l1-contracts/test/portals/TokenPortal.sol solidity

The `secret_hash` uses the pederson hash which fits in a field element. You can use the utility method `computeMessageSecretHash()`in `@aztec/aztec.js` npm package to generate a secret and its corresponding hash.
:::

After the transaction has been mined, the message is consumed, a nullifier is emitted and the tokens have been minted on Aztec and are ready for claiming.

Since the message consumption is emitting a nullifier the same message cannot be consumed again. The index in the message tree is used as part of the nullifier computation, ensuring that the same content and secret being inserted will be distinct messages that can each be consumed. Without the index in the nullifier, it would be possible to perform a kind of attack known as `Faerie Gold` attacks where two seemingly good messages are inserted, but only one of them can be consumed later.
Expand Down
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

Loading

0 comments on commit 3691b70

Please sign in to comment.