From ec40a2858cca49a59b10e9e3e3df407ef94ae1c4 Mon Sep 17 00:00:00 2001 From: Dan Lee Date: Fri, 29 Sep 2023 17:50:34 +0100 Subject: [PATCH 01/10] add storage slot to docs --- .../docs/dev_docs/contracts/syntax/storage.md | 83 ++++++++++++------- 1 file changed, 51 insertions(+), 32 deletions(-) diff --git a/docs/docs/dev_docs/contracts/syntax/storage.md b/docs/docs/dev_docs/contracts/syntax/storage.md index 4bac605a736b..6a13703fd36a 100644 --- a/docs/docs/dev_docs/contracts/syntax/storage.md +++ b/docs/docs/dev_docs/contracts/syntax/storage.md @@ -2,14 +2,14 @@ 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 contained in a single struct containining 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. +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 decrypted data/note viewing key with). -As mentioned earlier in the foundational concepts ([state model](./../../../concepts/foundation/state_model.md) and [private/public execution](./../../../concepts/foundation/communication/public_private_calls.md)) private state follows a UTXO model. Where note pre-images are only known to those able to decrypt them. +Public state follows the ethereum style account model, where each contract has its own key-value datastore. Private state follows a UTXO model, where note contents (pre-images) are only known by the sender and those able to decrypt them - see ([state model](./../../../concepts/foundation/state_model.md) and [private/public execution](./../../../concepts/foundation/communication/public_private_calls.md)) for more background. :::info -The struct **must** be called `Storage` for the Aztec.nr library to properly handle it (will be fixed in the future to support more flexibility). +The struct **must** be called `Storage` for the Aztec.nr library to properly handle it (this will be relaxed in the future). ::: ```rust @@ -20,10 +20,10 @@ struct Storage { ``` :::info -If your storage includes private state variables it must include a `compute_note_hash_and_nullifier` function to allow the RPC to process encrypted events, see [encrypted events](./events.md#processing-encrypted-events) for more. +If your contract storage includes private state variables, it must include a `compute_note_hash_and_nullifier` function to allow the RPC to process encrypted events. See [encrypted events](./events.md#processing-encrypted-events) for more. ::: -Since Aztec.nr is written in Noir, which on its own is state-less, we need to specify how the storage struct should be initialized to read and write data correctly. This is done by specifying an `init` function that is run in functions that rely on reading or altering the state variables. This `init` function should declare the storage struct with an actual instantiation defining how variables are accessed and manipulated. The function MUST be called `init` for the Aztec.nr library to properly handle it (will be fixed in the future to support more flexibility). +Since Aztec.nr is written in Noir, which on its own is state-less, we need to specify how the storage struct should be initialized to read and write data correctly. This is done by specifying an `init` function that is run in functions that rely on reading or altering the state variables. This `init` function must declare the storage struct with an instantiation defining how variables are accessed and manipulated. The function MUST be called `init` for the Aztec.nr library to properly handle it (this will be relaxed in the future). ```rust impl Storage { @@ -39,12 +39,28 @@ impl Storage { If you have defined a `Storage` struct following this naming scheme, then it will be made available to you through the reserved `storage` keyword within your contract functions. :::warning Using slot `0` is not supported! -No storage values should be initialized at slot `0`. This is a known issue that will be fixed in the future. +No storage values should be initialized at slot `0` - storage slots begin at `1`. This is a known issue that will be fixed in the future. ::: +## Storage Slots + +Aztec stores state in storage slots - each contract's storage is implemented internally as a single key-value mapping of `Map`. For now, storage slot values must be explicitly assigned inside the `Storage` struct. Except for `Map` types, storage is in contiguous blocks, so you must calculate how many slots each public variable requires, and increment the next variable's slot by this amount. + +In definition your contract storage, `Map`s can be treated as occupying only 1 storage slot (its "base_slot"), because the actual values are stored in derived slots calculated as `pedersen_hash(base_slot, key)`. + +For private variables, the storage slot is used to link UTXO notes through their `note header` attribute. This allows the contract to determine which notes are relevant for that state variable. + +We currently do not support any "packing" type optimizations as in the EVM. + ## Map -A `map` is a state variable that "maps" a key to a value. In Aztec.nr, keys are `Field`s (or values that are convertible to Fields) and values can be any type - even other maps. The map is a struct defined as follows: +A `map` is a state variable that "maps" a key to a value. + +:::info +In Aztec.nr, keys are always `Field`s (or types that can be serialized as Fields) and values can be any type - even other maps. +::: + +The map is a struct defined as follows: #include_code map /yarn-project/aztec-nr/aztec/src/state_vars/map.nr rust @@ -82,12 +98,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. @@ -101,34 +117,38 @@ 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: + +Say that we wish to add `admin` public state variable into our storage struct. In the struct we can define it as: #include_code storage_admin /yarn-project/noir-contracts/src/contracts/token_contract/src/main.nr rust -And then when initializing it in the `Storage::init` function we can do it as follows: +And then when initializing it in the `Storage::init` function we can do: #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: +We have specified that we are storing a `Field` that should be placed in storage slot `1`. This is just a single value, and is 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 @@ -140,13 +160,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: @@ -158,17 +179,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. @@ -183,6 +202,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. @@ -201,7 +221,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. @@ -226,9 +247,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` 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. @@ -289,7 +307,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` @@ -313,9 +331,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 @@ -327,7 +345,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)). @@ -371,13 +389,13 @@ This function is used to check existence of a note and then remove it without ha ### `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. +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 @@ -389,7 +407,7 @@ The reason we are not reading this note ahead of time is that no [encrypted log] ### `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 @@ -403,15 +421,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 From 65fcfb7f06debdccacb619bec78fbddbe33de8c7 Mon Sep 17 00:00:00 2001 From: Dan Lee Date: Fri, 29 Sep 2023 17:59:36 +0100 Subject: [PATCH 02/10] mention private notes need 1 slot only --- docs/docs/dev_docs/contracts/syntax/storage.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/docs/dev_docs/contracts/syntax/storage.md b/docs/docs/dev_docs/contracts/syntax/storage.md index 6a13703fd36a..949ba1d12b82 100644 --- a/docs/docs/dev_docs/contracts/syntax/storage.md +++ b/docs/docs/dev_docs/contracts/syntax/storage.md @@ -44,11 +44,13 @@ No storage values should be initialized at slot `0` - storage slots begin at `1` ## Storage Slots -Aztec stores state in storage slots - each contract's storage is implemented internally as a single key-value mapping of `Map`. For now, storage slot values must be explicitly assigned inside the `Storage` struct. Except for `Map` types, storage is in contiguous blocks, so you must calculate how many slots each public variable requires, and increment the next variable's slot by this amount. +Internally, Aztec keeps contract state in a storage slot key value store of type `Map`, stored as a merkle tree(?). For now, storage slot positions for each variable must be explicitly assigned inside the `Storage impl`. Except for `Map` types, storage is in contiguous blocks, so you must calculate how many slots each public variable requires, and increment the next variable's slot by this amount. -In definition your contract storage, `Map`s can be treated as occupying only 1 storage slot (its "base_slot"), because the actual values are stored in derived slots calculated as `pedersen_hash(base_slot, key)`. +In defining contract storage, `Map`s can be treated as occupying only 1 storage slot (its "base_slot"), because the actual values are stored in derived slots calculated as `pedersen_hash(base_slot, key)`. -For private variables, the storage slot is used to link UTXO notes through their `note header` attribute. This allows the contract to determine which notes are relevant for that state variable. +:::info +Private variables only require one single slot, because all notes are linked to contract state through a single `storage slot` attribute in their header. +::: We currently do not support any "packing" type optimizations as in the EVM. From 991571c794dea0886d8166f9052a201982c36437 Mon Sep 17 00:00:00 2001 From: Dan Lee Date: Mon, 2 Oct 2023 11:10:19 +0100 Subject: [PATCH 03/10] update to wording, not 100% on the merkle tree explanation --- docs/docs/dev_docs/contracts/syntax/storage.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/docs/dev_docs/contracts/syntax/storage.md b/docs/docs/dev_docs/contracts/syntax/storage.md index 949ba1d12b82..1533e3dba532 100644 --- a/docs/docs/dev_docs/contracts/syntax/storage.md +++ b/docs/docs/dev_docs/contracts/syntax/storage.md @@ -44,7 +44,7 @@ No storage values should be initialized at slot `0` - storage slots begin at `1` ## Storage Slots -Internally, Aztec keeps contract state in a storage slot key value store of type `Map`, stored as a merkle tree(?). For now, storage slot positions for each variable must be explicitly assigned inside the `Storage impl`. Except for `Map` types, storage is in contiguous blocks, so you must calculate how many slots each public variable requires, and increment the next variable's slot by this amount. +Internally, Aztec keeps contract state in a storage slot key value store of type `Map`, stored as a (contract specific) merkle tree. For now, storage slot positions for each variable must be explicitly assigned inside the `Storage impl`. Except for `Map` types, storage is in contiguous blocks, so you must calculate how many slots each public variable requires, and increment the next variable's slot by this amount. In defining contract storage, `Map`s can be treated as occupying only 1 storage slot (its "base_slot"), because the actual values are stored in derived slots calculated as `pedersen_hash(base_slot, key)`. @@ -52,7 +52,9 @@ In defining contract storage, `Map`s can be treated as occupying only 1 storage Private variables only require one single slot, because all notes are linked to contract state through a single `storage slot` attribute in their header. ::: -We currently do not support any "packing" type optimizations as in the EVM. +We currently do not support any "packing" type optimizations as in most EVM languages. + +Contract global state is aggregated as a single large merkle tree, with each contract's storate tree merkle root stored as the leaf in this tree, taking the contract address as the leaf index. ## Map From e41670383a1e150493eeef807fdd369bf1a251c3 Mon Sep 17 00:00:00 2001 From: Dan Lee Date: Mon, 2 Oct 2023 11:32:39 +0100 Subject: [PATCH 04/10] fix explanation on storage tree --- docs/docs/dev_docs/contracts/syntax/storage.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/docs/dev_docs/contracts/syntax/storage.md b/docs/docs/dev_docs/contracts/syntax/storage.md index 1533e3dba532..6b0e26f3392e 100644 --- a/docs/docs/dev_docs/contracts/syntax/storage.md +++ b/docs/docs/dev_docs/contracts/syntax/storage.md @@ -44,9 +44,9 @@ No storage values should be initialized at slot `0` - storage slots begin at `1` ## Storage Slots -Internally, Aztec keeps contract state in a storage slot key value store of type `Map`, stored as a (contract specific) merkle tree. For now, storage slot positions for each variable must be explicitly assigned inside the `Storage impl`. Except for `Map` types, storage is in contiguous blocks, so you must calculate how many slots each public variable requires, and increment the next variable's slot by this amount. +Each contract's state is represented with a storage slot key value store of type `Map`. For now, storage slot positions for each variable must be explicitly assigned inside the `Storage impl`. Except for `Map` types, storage is in contiguous blocks, so you must calculate how many slots each public variable requires, and increment the next variable's slot by this amount. -In defining contract storage, `Map`s can be treated as occupying only 1 storage slot (its "base_slot"), because the actual values are stored in derived slots calculated as `pedersen_hash(base_slot, key)`. +When assigning contract storage slots, `Map`s are treated as occupying only 1 storage slot (its "base_slot"), because the actual values are stored in derived slots calculated as `map_value_storage_slot = pedersen_hash(base_slot, key)`. :::info Private variables only require one single slot, because all notes are linked to contract state through a single `storage slot` attribute in their header. @@ -54,7 +54,9 @@ Private variables only require one single slot, because all notes are linked to We currently do not support any "packing" type optimizations as in most EVM languages. -Contract global state is aggregated as a single large merkle tree, with each contract's storate tree merkle root stored as the leaf in this tree, taking the contract address as the leaf index. +Storage in Aztec is actually implemented as a single global merkle tree, with each contract's internal storage slot combined with its contract address to generate its position in the global tree as `global_storage_slot = pedersen_hash(contract_storage_slot, contract_address)`. + +Note: The choice of hash function is subject to change in later versions. ## Map From 1f1cb310ca7a60b377dfc111b159b6b111620a74 Mon Sep 17 00:00:00 2001 From: Dan Lee <142251406+dan-aztec@users.noreply.github.com> Date: Mon, 2 Oct 2023 09:16:05 -0700 Subject: [PATCH 05/10] Update storage.md emptyline --- docs/docs/dev_docs/contracts/syntax/storage.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/docs/dev_docs/contracts/syntax/storage.md b/docs/docs/dev_docs/contracts/syntax/storage.md index c03c58e44501..e7bea7902948 100644 --- a/docs/docs/dev_docs/contracts/syntax/storage.md +++ b/docs/docs/dev_docs/contracts/syntax/storage.md @@ -389,7 +389,6 @@ 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 - ### `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. From 19e24c7830398f585a6b4d17b7fe893f5a4fa862 Mon Sep 17 00:00:00 2001 From: Dan Lee <142251406+dan-aztec@users.noreply.github.com> Date: Tue, 3 Oct 2023 04:31:33 -0700 Subject: [PATCH 06/10] Update docs/docs/dev_docs/contracts/syntax/storage.md Co-authored-by: Maddiaa <47148561+Maddiaa0@users.noreply.github.com> --- docs/docs/dev_docs/contracts/syntax/storage.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/dev_docs/contracts/syntax/storage.md b/docs/docs/dev_docs/contracts/syntax/storage.md index e7bea7902948..ca664f09353f 100644 --- a/docs/docs/dev_docs/contracts/syntax/storage.md +++ b/docs/docs/dev_docs/contracts/syntax/storage.md @@ -2,7 +2,7 @@ title: Storage --- -In an Aztec.nr contract, storage is contained in a single struct that containins both public and private state variables. +In an Aztec.nr contract, storage is contained in a single struct that contains both public and private state variables. 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 decrypted data/note viewing key with). From c4501cc719edffbc6a6a602270e0bfabf4ecf680 Mon Sep 17 00:00:00 2001 From: Dan Lee <142251406+dan-aztec@users.noreply.github.com> Date: Tue, 3 Oct 2023 04:31:43 -0700 Subject: [PATCH 07/10] Update docs/docs/dev_docs/contracts/syntax/storage.md Co-authored-by: Maddiaa <47148561+Maddiaa0@users.noreply.github.com> --- docs/docs/dev_docs/contracts/syntax/storage.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/dev_docs/contracts/syntax/storage.md b/docs/docs/dev_docs/contracts/syntax/storage.md index ca664f09353f..ed2827e22a96 100644 --- a/docs/docs/dev_docs/contracts/syntax/storage.md +++ b/docs/docs/dev_docs/contracts/syntax/storage.md @@ -54,7 +54,7 @@ Private variables only require one single slot, because all notes are linked to We currently do not support any "packing" type optimizations as in most EVM languages. -Storage in Aztec is actually implemented as a single global merkle tree, with each contract's internal storage slot combined with its contract address to generate its position in the global tree as `global_storage_slot = pedersen_hash(contract_storage_slot, contract_address)`. +Storage in Aztec is implemented as a single global merkle tree, with each contract's internal storage slot combined with its contract address to generate its position in the global tree as `global_storage_slot = pedersen_hash(contract_storage_slot, contract_address)`. Note: The choice of hash function is subject to change in later versions. From 4f314ef04da5edcfac9cfdfd10f893285ebb9235 Mon Sep 17 00:00:00 2001 From: Dan Lee Date: Tue, 3 Oct 2023 16:42:34 +0100 Subject: [PATCH 08/10] fix storage slot explanation --- docs/docs/dev_docs/contracts/syntax/storage.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/docs/dev_docs/contracts/syntax/storage.md b/docs/docs/dev_docs/contracts/syntax/storage.md index ed2827e22a96..10cc36b1bdb1 100644 --- a/docs/docs/dev_docs/contracts/syntax/storage.md +++ b/docs/docs/dev_docs/contracts/syntax/storage.md @@ -44,18 +44,20 @@ No storage values should be initialized at slot `0` - storage slots begin at `1` ## Storage Slots -Each contract's state is represented with a storage slot key value store of type `Map`. For now, storage slot positions for each variable must be explicitly assigned inside the `Storage impl`. Except for `Map` types, storage is in contiguous blocks, so you must calculate how many slots each public variable requires, and increment the next variable's slot by this amount. +Public state in Aztec is implemented as a single global merkle tree of depth 254, with each contract's internal storage slot combined with its contract address to generate its position in the global tree as `global_storage_slot = pedersen_hash(contract_storage_slot, contract_address)`. -When assigning contract storage slots, `Map`s are treated as occupying only 1 storage slot (its "base_slot"), because the actual values are stored in derived slots calculated as `map_value_storage_slot = pedersen_hash(base_slot, key)`. +A contract's state is represented by mapping its own storage slots to each variable. For now, storage slot positions for each variable must be explicitly assigned inside the `Storage impl`. Although variables can be arrays or structs that are stored internally as contiguous blocks, each variable in the storage definition takes just 1 block, so in practice you can increment the storage slot by 1 each time you add another variable, whether it is public or private. + +When assigning contract storage slots, `Map`s are also treated as occupying only 1 storage slot (its "base_slot"), because the actual values in the global state tree are stored in derived slots calculated as `map_value_storage_slot = pedersen_hash(base_slot, key)`. + +Private state is stored in a separate UTXO tree (a second nullifier tree tracks which notes have been spent - each note's nullifier is calculated deterministically from its contents.) A contract's private variables may be associated with 0, 1, or multiple notes in the UTXO tree. +The position of each note in the tree does not matter - the relationship to contract state is contained through the note header. Each private variable's storage slot is contained in a contract's bytecode, but they do not appear at all in the global storage tree. :::info -Private variables only require one single slot, because all notes are linked to contract state through a single `storage slot` attribute in their header. -::: +Private variables only require one single slot, because all notes are linked their contract variable through the `storage slot` attribute in their note header (which also contains the contract address and nonce). ::: We currently do not support any "packing" type optimizations as in most EVM languages. -Storage in Aztec is implemented as a single global merkle tree, with each contract's internal storage slot combined with its contract address to generate its position in the global tree as `global_storage_slot = pedersen_hash(contract_storage_slot, contract_address)`. - Note: The choice of hash function is subject to change in later versions. ## Map @@ -135,7 +137,6 @@ When declaring the storage for `T` as a persistent public storage variable, we u #### Single value example - Say that we wish to add `admin` public state variable into our storage struct. In the struct we can define it as: #include_code storage_admin /yarn-project/noir-contracts/src/contracts/token_contract/src/main.nr rust @@ -146,7 +147,6 @@ And then when initializing it in the `Storage::init` function we can do: We have specified that we are storing a `Field` that should be placed in storage slot `1`. This is just a single value, and is similar to the following in solidity: - ```solidity address internal admin; ``` From 8e0058139fab6c23843e33d4b8e4bd1094de794e Mon Sep 17 00:00:00 2001 From: Dan Lee Date: Tue, 3 Oct 2023 17:04:50 +0100 Subject: [PATCH 09/10] more fixes --- docs/docs/dev_docs/contracts/syntax/storage.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/docs/dev_docs/contracts/syntax/storage.md b/docs/docs/dev_docs/contracts/syntax/storage.md index 10cc36b1bdb1..497b43a898bc 100644 --- a/docs/docs/dev_docs/contracts/syntax/storage.md +++ b/docs/docs/dev_docs/contracts/syntax/storage.md @@ -46,12 +46,12 @@ No storage values should be initialized at slot `0` - storage slots begin at `1` Public state in Aztec is implemented as a single global merkle tree of depth 254, with each contract's internal storage slot combined with its contract address to generate its position in the global tree as `global_storage_slot = pedersen_hash(contract_storage_slot, contract_address)`. -A contract's state is represented by mapping its own storage slots to each variable. For now, storage slot positions for each variable must be explicitly assigned inside the `Storage impl`. Although variables can be arrays or structs that are stored internally as contiguous blocks, each variable in the storage definition takes just 1 block, so in practice you can increment the storage slot by 1 each time you add another variable, whether it is public or private. +A contract's state is represented by mapping its own storage slots to each variable. For now, storage slot positions for each variable must be explicitly assigned inside the `Storage impl`. Although variables can be arrays or structs that are stored internally as contiguous blocks, each variable in the storage definition takes just 1 block, so you can increment the storage slot by 1 each time you add another variable, whether it is public or private. When assigning contract storage slots, `Map`s are also treated as occupying only 1 storage slot (its "base_slot"), because the actual values in the global state tree are stored in derived slots calculated as `map_value_storage_slot = pedersen_hash(base_slot, key)`. -Private state is stored in a separate UTXO tree (a second nullifier tree tracks which notes have been spent - each note's nullifier is calculated deterministically from its contents.) A contract's private variables may be associated with 0, 1, or multiple notes in the UTXO tree. -The position of each note in the tree does not matter - the relationship to contract state is contained through the note header. Each private variable's storage slot is contained in a contract's bytecode, but they do not appear at all in the global storage tree. +Private state is stored in a separate UTXO tree, but each private variable is still assigned a storage slot to track the meaning of the note. Each private variable's storage slot is contained in a contract's bytecode, but they do not appear at all in the global storage tree. Each contract private variable can be associated with 0, 1, or multiple notes in the UTXO tree (and some of those may have already been spent, if their nullifier is already present in the nullifier tree). +The position of each note in the UTXO tree does not matter - the relationship to contract state is contained entirely in its note header. :::info Private variables only require one single slot, because all notes are linked their contract variable through the `storage slot` attribute in their note header (which also contains the contract address and nonce). ::: From ac3a5dc3a14f52754e67e52e242094b8000c9708 Mon Sep 17 00:00:00 2001 From: Dan Lee Date: Tue, 3 Oct 2023 17:05:48 +0100 Subject: [PATCH 10/10] small wording --- docs/docs/dev_docs/contracts/syntax/storage.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/docs/dev_docs/contracts/syntax/storage.md b/docs/docs/dev_docs/contracts/syntax/storage.md index 497b43a898bc..0db4a321275d 100644 --- a/docs/docs/dev_docs/contracts/syntax/storage.md +++ b/docs/docs/dev_docs/contracts/syntax/storage.md @@ -56,9 +56,9 @@ The position of each note in the UTXO tree does not matter - the relationship to :::info Private variables only require one single slot, because all notes are linked their contract variable through the `storage slot` attribute in their note header (which also contains the contract address and nonce). ::: -We currently do not support any "packing" type optimizations as in most EVM languages. +We currently do not support any "bit packing" type optimizations as in most EVM languages. -Note: The choice of hash function is subject to change in later versions. +Note: The choice of hash function for global slot position is subject to change in later versions. ## Map