From 584fb1b5e0fe993eb28eed649967fca6cf46f4ba Mon Sep 17 00:00:00 2001 From: Leila Wang Date: Tue, 22 Aug 2023 11:56:31 +0000 Subject: [PATCH 1/7] Docs for storage and state variables. --- docs/docs/dev_docs/contracts/functions.md | 10 +- .../dev_docs/contracts/state_variables.md | 219 ++++++++++++++++-- docs/docs/dev_docs/contracts/storage.md | 14 +- docs/docs/dev_docs/contracts/syntax.md | 12 +- yarn-project/noir-contracts/.gitignore | 2 +- .../docs_example_contract/Nargo.toml | 8 + .../docs_example_contract/src/actions.nr | 176 ++++++++++++++ .../docs_example_contract/src/main.nr | 199 ++++++++++++++++ .../docs_example_contract/src/options.nr | 66 ++++++ .../docs_example_contract/src/storage.nr | 50 ++++ .../src/storage/cards.nr | 9 + .../src/storage/game_rules.nr | 9 + .../src/storage/legendary_card.nr | 9 + .../src/storage/locked.nr | 11 + .../src/storage/profiles.nr | 12 + .../src/storage/queen.nr | 29 +++ .../docs_example_contract/src/types.nr | 4 + .../docs_example_contract/src/types/card.nr | 101 ++++++++ .../src/types/profile.nr | 88 +++++++ .../docs_example_contract/src/types/queen.nr | 6 + .../docs_example_contract/src/types/rules.nr | 84 +++++++ .../private_token_contract/src/main.nr | 2 - .../private_token_contract/src/storage.nr | 4 - .../public_token_contract/src/storage.nr | 5 +- .../noir-aztec/src/note/note_interface.nr | 2 + 25 files changed, 1090 insertions(+), 41 deletions(-) create mode 100644 yarn-project/noir-contracts/src/contracts/docs_example_contract/Nargo.toml create mode 100644 yarn-project/noir-contracts/src/contracts/docs_example_contract/src/actions.nr create mode 100644 yarn-project/noir-contracts/src/contracts/docs_example_contract/src/main.nr create mode 100644 yarn-project/noir-contracts/src/contracts/docs_example_contract/src/options.nr create mode 100644 yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage.nr create mode 100644 yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage/cards.nr create mode 100644 yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage/game_rules.nr create mode 100644 yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage/legendary_card.nr create mode 100644 yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage/locked.nr create mode 100644 yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage/profiles.nr create mode 100644 yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage/queen.nr create mode 100644 yarn-project/noir-contracts/src/contracts/docs_example_contract/src/types.nr create mode 100644 yarn-project/noir-contracts/src/contracts/docs_example_contract/src/types/card.nr create mode 100644 yarn-project/noir-contracts/src/contracts/docs_example_contract/src/types/profile.nr create mode 100644 yarn-project/noir-contracts/src/contracts/docs_example_contract/src/types/queen.nr create mode 100644 yarn-project/noir-contracts/src/contracts/docs_example_contract/src/types/rules.nr diff --git a/docs/docs/dev_docs/contracts/functions.md b/docs/docs/dev_docs/contracts/functions.md index e76052ffeb1..97993921d44 100644 --- a/docs/docs/dev_docs/contracts/functions.md +++ b/docs/docs/dev_docs/contracts/functions.md @@ -4,16 +4,20 @@ - A special `constructor` function MUST be declared within a contract's scope. - A constructor doesn't have a name, because its purpose is clear: to initialise state. -- In Aztec terminology, a constructor is always a 'private function' (i.e. it cannot be an `open` function). +- In Aztec terminology, a constructor is always a 'secret function' (i.e. it cannot be an `open` function). - A constructor behaves almost identically to any other function. It's just important for Aztec to be able to identify this function as special: it may only be called once, and will not be deployed as part of the contract. ## secret functions +#include_code functions-SecretFunction /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/main.nr rust + ## `open` functions -## `unconstrained` functions +#include_code functions-OpenFunction /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/main.nr rust +## `unconstrained` functions +#include_code functions-UncontrainedFunction /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/main.nr rust # Calling functions @@ -47,4 +51,4 @@ E.g. `get()` ## Delegatecall -Talk a about the dangers of delegatecall too! \ No newline at end of file +Talk a about the dangers of delegatecall too! diff --git a/docs/docs/dev_docs/contracts/state_variables.md b/docs/docs/dev_docs/contracts/state_variables.md index bf7f46723ed..7a0584d02dd 100644 --- a/docs/docs/dev_docs/contracts/state_variables.md +++ b/docs/docs/dev_docs/contracts/state_variables.md @@ -1,35 +1,220 @@ # State Variables -## `PublicState` +State variables come in two flavours: [**public** state](#publicstatet-t_serialised_len) and [**private** state](#private-state-variables). -Public state is persistent state which is _publicly visible_, by anyone in the world. +## `PublicState` -For developers coming from other blockchain ecosystems (such as Ethereum) this will be a familiar concept, because there, _all_ state is _publicly visible_. +Public state is persistent state that is _publicly visible_ to anyone in the world. -Aztec public state follows an account-based model. That is, each state occupies a leaf in an account-based merkle tree; the _public state tree_ (LINK). See here (LINK) for more of the technical details. +For developers coming from other blockchain ecosystems (such as Ethereum), this will be a familiar concept, because there, _all_ state is _publicly visible_. -The `PublicState` struct, provides a wrapper around conventional Noir types `T`, allowing such types to be written-to and read-from the public state tree. +Aztec public state follows an account-based model. That is, each state occupies a leaf in an account-based merkle tree: the _public state tree_ (LINK). See here (LINK) for more of the technical details. -#include_code PublicState /yarn-project/noir-contracts/src/contracts/public_token_contract/src/storage.nr rust +The `PublicState` struct serves as a wrapper around conventional Noir types `T`, allowing these types to be written to and read from the public state tree. -:::danger TODO -Examples which: -- initialise a `PublicState` by itself (without being wrapped in a `Map`) -- initialise a `PublicState` where `T` is a custom struct. -::: +The Aztec stdlib provides serialization methods for some common types. Check [here](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/noir-libs/noir-aztec/src/types/type_serialisation) for the complete list. +### `::new` -## `Map` +In the following example, we define a public state with a boolean value. + +#include_code state_vars-PublicState /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage/locked.nr rust + +### Custom value + +It's possible to create a public state for any types. For example, to create a public state for the following struct: + +#include_code state_vars-CustomStruct /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/types/queen.nr rust + +First, define how to serialise and deserialise the struct. And then initialise the PublicState with it: + +#include_code state_vars-PublicStateCustomStruct /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage/queen.nr rust + +### `.write` + +We can pass the associated type directly to a public state. It knows how to serialise the given value to store in the public state tree. + +#include_code state_vars-PublicStateWrite /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/actions.nr rust +#include_code state_vars-PublicStateWriteCustom /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/actions.nr rust + +### `.read` + +Reading a value from a public state is straightforward: +#include_code state_vars-PublicStateRead /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/actions.nr rust -## Private State +It returns the type the public state was declared with. The above example returns a boolean. And the following example returns a custom struct. -### UTXO trees +#include_code state_vars-PublicStateReadCustom /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/actions.nr rust + +## Private State Variables + +There are 3 different types of private state variables: + +- [Singleton](#singletonnotetype) +- [ImmutableSingleton](#immutablesingletonnotetype) +- [Set](#setnotetype) + +In contrast to public state variables, private state variables are only visible to specific relevant parties. The value of a private state variable can either be shared via (log)[INSERT_LINK_HERE] or completely offline. + +Note that an app can choose to emit the data via unencrypted log, or define a note whose data is easy to figure out, then the information is technically not private and could be visible to anyone. ### Notes -### Custom 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. + +A note should confine to the following interface: + +#include_code NoteInterface /yarn-project/noir-libs/noir-aztec/src/note/note_interface.nr rust + +## `Singleton` + +Singleton is a private state variable that is unique in a way. When a singleton is initialised, 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. + +### `::new` + +Here we define a singleton for storing a Card note: + +#include_code state_vars-Singleton /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage/legendary_card.nr rust + +### `.initialise` + +The initial value of a singleton is set via calling `initialise`: + +#include_code state_vars-SingletonInit /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/actions.nr rust + +When this function is called, a nullifier of the storage slot is created, preventing this singleton from being initialised again. + +### `.replace` + +To modify the value of a singleton, we will create a note (a Card in the following example) that has the new data, and replace the current note with it: + +#include_code state_vars-SingletonReplace /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/actions.nr rust + +This function will destroy the old note under the hood. If two people are trying to modify the singleton at the same time, only one will succeed. + +### `.get_note` + +This function allows us to get the note of a singleton: + +#include_code state_vars-SingletonGet /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/actions.nr rust + +However, it's possible that at the time this function is called, the system hasn't synched to the block where the latest note was created. Or a malicious user might feed an old state to this function, tricking the proving system into thinking that the value hasn't changed. To avoid an attack around it, this function will destroy the current note, and replace it with a duplicated note that has the same fields. Because the nullifier of the latest note will be emitted, if two people are trying to use this function against the same note, only one will succeed. + +## `ImmutableSingleton` + +Immutable singleton is unique and, as the name suggests, immutable. Once it has been initialised, its value can't be changed anymore. + +### `::new` + +#include_code state_vars-ImmutableSingleton /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage/game_rules.nr rust + +### `.initialise` + +#include_code state_vars-ImmutableSingletonInit /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/actions.nr rust + +### `.get_note` + +#include_code state_vars-ImmutableSingletonGet /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/actions.nr rust + +Different to singleton, calling the `get_note` function on an immutable singleton to read the value doesn't destroy the current note behind the scene. Which means this function can be called simultaneously. + +## `Set` + +Set is used for managing a collection of notes. All notes in a set should be the same type. But whether they belong to one single account, or are accessible by different entities, is totally up to the developer. + +### `::new` + +In the following example, we define a set of cards: + +#include_code state_vars-Set /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage/cards.nr rust + +### `.insert` + +We can call `insert` for as many times as we need to add new notes to a set: + +#include_code state_vars-SetInsert /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/actions.nr rust + +### `.remove` + +We can also remove a note from a set: + +#include_code state_vars-SetRemove /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/actions.nr rust + +Note that the transaction won't fail if the note we are removing does not exist in the set. As a best practice, we should fetch the notes by calling [`get_notes`](#get_notes), which does a membership check under the hood to make sure the notes exist, and then feed the returned notes to the `remove` function. + +### `.get_notes` + +This function returns the notes the account has access to: + +#include_code state_vars-SetGet /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/actions.nr rust + +There's a limit on the maxinum number of notes this function can return at a time. Check [here](INSERT_LINK_HERE) 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 target the notes we need, and to reduce the time required to recursively call this function. + +### NoteGetterOptions + +`NoteGetterOptions` encapsulates a set of configurable options for retrieving notes from a database: + +#include_code NoteGetterOptions /yarn-project/noir-libs/noir-aztec/src/note/note_getter_options.nr rust + +For example, the following configs let us find the cards that belong to `account_address`. The returned cards are sorted by their points in descending order, and the first `offset` cards with the highest points are skipped. + +#include_code state_vars-NoteGetterOptionsSelectSortOffset /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/options.nr rust + +The first value of `.select` and `.sort` is the index of a field in a note type. For the note type `Card` that has the following fields: + +#include_code state_vars-NoteCard /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/types/card.nr rust + +The indices are: 0 for `points`, 1 for `secret`, and 2 for `owner`. + +In the previous example, + +`.select(2, account_address)` matches the 2nd field of `Card`, which is `owner`, and returns the cards whose `owner` field equals `account_address`. + +`.sort(0, SortOrder.DESC)` sorts the 0th field of `Card`, which is `points`, in descending order. + +There can be as many conditions as the number of fields a note type has. The following example finds cards whose fields match the three given values: + +#include_code state_vars-NoteGetterOptionsMultiSelects /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/options.nr rust + +While `selects` lets us find notes with specific values, `filter` lets us find notes in a more dynamic way. The function below picks the cards whose points are at least `min_points`: + +#include_code state_vars-OptionFilter /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/options.nr rust + +We can use it as a filter to further reduce the number of the final notes: + +#include_code state_vars-NoteGetterOptionsFilter /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/options.nr rust + +One thing to remember is, `filter` will be applied on the notes after they are picked from the database. Therefor, it's possible that the actual notes we end up getting are fewer than the limit. + +The limit is `MAX_READ_REQUESTS_PER_CALL` by default. But we can set it to any value "smaller" than that: + +#include_code state_vars-NoteGetterOptionsPickOne /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/options.nr rust + +The process of applying the options to get the final notes is not constrained. It's necessary to always check the returned notes even when some conditions have been specified in the options. + +#include_code state_vars-check_return_notes /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/main.nr rust + +## `Map` + +`Map` is a state variable that maps a field to another state variable, which can be [`PublicState`](#publicstatet-t_serialised_len), all the [private state variables](#private-state-variables), and even another Map. + +### `::new` + +The following map uses singleton as its value: + +#include_code state_vars-MapSingleton /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage/profiles.nr rust + +### `.at` + +The only api of a map is `.at`. It returns the underlying type that occupies a specific storage slot, which is generated by the field passed to `.at`. + +#include_code state_vars-MapAtSingletonInit /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/actions.nr rust -### `UTXO` +#include_code state_vars-MapAtSingletonGet /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/actions.nr rust -### `UTXOSet` \ No newline at end of file +In both code snippets, `state_var.at(account)` returns a singleton that is linked to the requested account. diff --git a/docs/docs/dev_docs/contracts/storage.md b/docs/docs/dev_docs/contracts/storage.md index 71a407f228c..56f93b63c0b 100644 --- a/docs/docs/dev_docs/contracts/storage.md +++ b/docs/docs/dev_docs/contracts/storage.md @@ -4,10 +4,16 @@ State variables must be declared inside a struct. (This enables us to declare types composed of nested generics in Noir - see [types](./types.md)). -By way of example, we could define a private state variable `balances`, mapping user addresses to their token balances: +We could define any kinds of state variables in the Storage struct: -#include_code storage-declaration /yarn-project/noir-contracts/src/contracts/private_token_contract/src/storage.nr rust +#include_code storage-declaration /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage.nr rust -#include_code storage-import /yarn-project/noir-contracts/src/contracts/private_token_contract/src/main.nr rust +See [State Variables](./state_variables.md) for how to initialise them. -State variables come in two flavours: **public** state and **private** state. . \ No newline at end of file +Using Storage in a contract is like using any other variables. First, import them to the `main.nr` file: + +#include_code storage-import /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/main.nr rust + +For each function that needs access to the storage, initialise the storage inside the function, and call the state variables in it: + +#include_code storage-init /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/main.nr rust diff --git a/docs/docs/dev_docs/contracts/syntax.md b/docs/docs/dev_docs/contracts/syntax.md index 288a46cb380..132c5f2b780 100644 --- a/docs/docs/dev_docs/contracts/syntax.md +++ b/docs/docs/dev_docs/contracts/syntax.md @@ -4,15 +4,15 @@ ## Aztec stdlib -On top of ['Vanialla Noir's' stdlib](https://noir-lang.org/standard_library/array_methods), we provide an [Aztec stdlib](https://github.com/AztecProtocol/aztec-packages/tree/master/yarn-project/noir-libs) for writing Noir Contracts. The Aztec stdlib contains abstractions which remove the need to understand the low-level Aztec protocol. Notably, it provides: -- Public and private [state variable types](./types.md) +On top of ['Vanilla Noir's' stdlib](https://noir-lang.org/standard_library/array_methods), we provide an [Aztec stdlib](https://github.com/AztecProtocol/aztec-packages/tree/master/yarn-project/noir-libs) for writing Noir Contracts. The Aztec stdlib contains abstractions which remove the need to understand the low-level Aztec protocol. Notably, it provides: + +- Public and private [state variable types](./types.md). - Ready-made notes. -- Functions for [emitting](./events.md) encrypted and unencrypted logs +- Functions for [emitting](./events.md) encrypted and unencrypted logs. - [Oracle functions](./functions.md#oracle-calls) for accessing: - private state - secrets -- Functions for communicating with Ethereum L1 - +- Functions for communicating with Ethereum L1. To import the Aztec stdlib into your Noir Contract project, simply include it as a dependency: @@ -20,4 +20,4 @@ To import the Aztec stdlib into your Noir Contract project, simply include it as https://github.com/AztecProtocol/aztec-packages/issues/1335 ::: -#include_code importing-aztec /yarn-project/noir-contracts/src/contracts/private_token_contract/Nargo.toml toml \ No newline at end of file +#include_code importing-aztec /yarn-project/noir-contracts/src/contracts/private_token_contract/Nargo.toml toml diff --git a/yarn-project/noir-contracts/.gitignore b/yarn-project/noir-contracts/.gitignore index 203ceefb7ac..983cade75af 100644 --- a/yarn-project/noir-contracts/.gitignore +++ b/yarn-project/noir-contracts/.gitignore @@ -1,6 +1,6 @@ artifacts/ target/ proofs/ -types/ +/src/types/ Prover.toml Verifier.toml \ No newline at end of file diff --git a/yarn-project/noir-contracts/src/contracts/docs_example_contract/Nargo.toml b/yarn-project/noir-contracts/src/contracts/docs_example_contract/Nargo.toml new file mode 100644 index 00000000000..add81444e03 --- /dev/null +++ b/yarn-project/noir-contracts/src/contracts/docs_example_contract/Nargo.toml @@ -0,0 +1,8 @@ +[package] +name = "docs_example_contract" +authors = [""] +compiler_version = "0.1" +type = "contract" + +[dependencies] +aztec = { path = "../../../../noir-libs/noir-aztec" } \ No newline at end of file diff --git a/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/actions.nr b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/actions.nr new file mode 100644 index 00000000000..a7103f2f78f --- /dev/null +++ b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/actions.nr @@ -0,0 +1,176 @@ +use dep::std::option::Option; +use dep::aztec::constants_gen::MAX_READ_REQUESTS_PER_CALL; +use dep::aztec::context::{ + PrivateContext, +}; +use dep::aztec::note::note_getter_options::NoteGetterOptions; +use dep::aztec::state_vars::{ + immutable_singleton::ImmutableSingleton, + map::Map, + public_state::PublicState, + set::Set, + singleton::Singleton, +}; +use dep::aztec::types::type_serialisation::bool_serialisation::{ + BOOL_SERIALISED_LEN, +}; + +use crate::storage::{ + queen::QUEEN_SERIALISED_LEN, +}; +use crate::types::{ + card::{Card, CARD_LEN}, + profile::{Profile, PROFILE_LEN}, + queen::Queen, + rules::{Rules, RULES_LEN}, +}; + +// docs:start:state_vars-PublicStateRead +fn is_locked(state_var: PublicState) -> bool { + state_var.read() +} +// docs:end:state_vars-PublicStateRead + +// docs:start:state_vars-PublicStateWrite +fn lock(state_var: PublicState) { + state_var.write(true); +} +// docs:end:state_vars-PublicStateWrite + +fn unlock(state_var: PublicState) { + state_var.write(false); +} + +// docs:start:state_vars-PublicStateReadCustom +fn get_current_queen(state_var: PublicState) -> Queen { + state_var.read() +} +// docs:end:state_vars-PublicStateReadCustom + +fn can_replace_queen( + state_var: PublicState, + new_queen: Queen, + ) -> bool { + let current_queen = get_current_queen(state_var); + new_queen.points > current_queen.points +} + +// docs:start:state_vars-PublicStateWriteCustom +fn replace_queen( + state_var: PublicState, + new_queen: Queen, +) { + state_var.write(new_queen); +} +// docs:end:state_vars-PublicStateWriteCustom + +// docs:start:state_vars-SingletonInit +fn init_legendary_card( + context: &mut PrivateContext, + state_var: Singleton, + card: &mut Card, +) { + state_var.initialise(context, card); +} +// docs:end:state_vars-SingletonInit + +// docs:start:state_vars-SingletonReplace +fn update_legendary_card( + context: &mut PrivateContext, + state_var: Singleton, + card: &mut Card, +) { + state_var.replace(context, card); +} +// docs:end:state_vars-SingletonReplace + +// docs:start:state_vars-SingletonGet +fn get_legendary_card( + context: &mut PrivateContext, + state_var: Singleton, +) -> Card { + state_var.get_note(context) +} +// docs:end:state_vars-SingletonGet + +// docs:start:state_vars-ImmutableSingletonInit +fn init_game_rules( + context: &mut PrivateContext, + state_var: ImmutableSingleton, + rules: &mut Rules, +) { + state_var.initialise(context, rules); +} +// docs:end:state_vars-ImmutableSingletonInit + +// docs:start:state_vars-ImmutableSingletonGet +fn is_valid_card( + context: &mut PrivateContext, + state_var: ImmutableSingleton, + card: Card, +) -> bool { + let rules = state_var.get_note(context); + card.points >= rules.min_points & card.points <= rules.max_points +} +// docs:end:state_vars-ImmutableSingletonGet + +// docs:start:state_vars-SetInsert +fn add_new_card( + context: &mut PrivateContext, + state_var: Set, + card: &mut Card, + ) { + state_var.insert(context, card); +} +// docs:end:state_vars-SetInsert + +// docs:start:state_vars-SetRemove +fn remove_card( + context: &mut PrivateContext, + state_var: Set, + card: Card, +) { + state_var.remove(context, card); +} +// docs:end:state_vars-SetRemove + +// docs:start:state_vars-SetGet +fn get_cards( + context: &mut PrivateContext, + state_var: Set, + options: NoteGetterOptions, +) -> [Option; MAX_READ_REQUESTS_PER_CALL] { + state_var.get_notes(context, options) +} +// docs:end:state_vars-SetGet + +// docs:start:state_vars-SetContains +fn assert_contains_card( + context: &mut PrivateContext, + state_var: Set, + card: Card, +) { + state_var.assert_contains_and_remove(context, card); +} +// docs:end:state_vars-SetContains + +// docs:start:state_vars-MapAtSingletonInit +fn add_new_profile( + context: &mut PrivateContext, + state_var: Map>, + account: Field, + profile: &mut Profile, +) { + state_var.at(account).initialise(context, profile); +} +// docs:end:state_vars-MapAtSingletonInit + +// docs:start:state_vars-MapAtSingletonGet +fn get_profile( + context: &mut PrivateContext, + state_var: Map>, + account: Field, +) -> Profile { + state_var.at(account).get_note(context) +} +// docs:end:state_vars-MapAtSingletonGet \ No newline at end of file diff --git a/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/main.nr b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/main.nr new file mode 100644 index 00000000000..00cc7345589 --- /dev/null +++ b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/main.nr @@ -0,0 +1,199 @@ +mod actions; +mod options; +mod storage; +mod types; + +contract DocsExample { + use dep::aztec::abi; + use dep::aztec::abi::{PrivateCircuitPublicInputs, PrivateContextInputs, PublicContextInputs}; + use dep::aztec::constants_gen::MAX_READ_REQUESTS_PER_CALL; + use dep::aztec::context::{ + PrivateContext, + PublicContext, + }; + + use crate::actions; + use crate::options::{create_get_account_cards_options, create_get_account_largest_card_options}; + // docs:start:storage-import + use crate::storage::Storage; + // docs:end:storage-import + use crate::types::{ + card::Card, + queen::Queen, + rules::Rules, + }; + + global REPLACE_QUEEN_FUNCTION_SELECTOR = 11111111; + global GET_POINTS_OF_COMMON_CARD_FUNCTION_SELECTOR = 11111111; + + fn constructor( + inputs: PrivateContextInputs, + min_points: u8, + max_points: u8, + legendary_card_secret: Field, + ) -> distinct pub PrivateCircuitPublicInputs { + let mut context = PrivateContext::new(inputs, abi::hash_args([ + min_points as Field, + max_points as Field, + legendary_card_secret, + ])); + + let storage = Storage::init(); + + let mut game_rules = Rules::new(min_points, max_points); + actions::init_game_rules(&mut context, storage.game_rules, &mut game_rules); + + let mut legendary_card = Card::new(0, legendary_card_secret, 0); + actions::init_legendary_card(&mut context, storage.legendary_card, &mut legendary_card); + + context.finish() + } + + // docs:start:storage-init + open fn lock(inputs: PublicContextInputs) -> pub abi::PublicCircuitPublicInputs { + let context = PublicContext::new(inputs, 0); + + // highlight-next-line:storage-init + let storage = Storage::init(); + storage.locked.write(true); + + context.finish() + } + // docs:end:storage-init + + // docs:start:functions-OpenFunction + open fn unlock(inputs: PublicContextInputs) -> pub abi::PublicCircuitPublicInputs { + let context = PublicContext::new(inputs, 0); + + let storage = Storage::init(); + actions::unlock(storage.locked); + + context.finish() + } + // docs:end:functions-OpenFunction + + open fn replace_queen( + inputs: PublicContextInputs, + account: Field, + points: u8, + ) -> pub abi::PublicCircuitPublicInputs { + let context = PublicContext::new(inputs, abi::hash_args([account, points as Field])); + let storage = Storage::init(); + + let new_queen = Queen { account, points }; + + assert(actions::can_replace_queen(storage.queen, new_queen)); + + actions::replace_queen(storage.queen, new_queen); + + context.finish() + } + + // docs:start:functions-SecretFunction + fn add_common_cards( + inputs: PrivateContextInputs, + secrets: [Field; 4], + ) -> distinct pub PrivateCircuitPublicInputs { + let mut context = PrivateContext::new(inputs, abi::hash_args(secrets)); + let storage = Storage::init(); + + for i in 0..secrets.len() as u8 { + let mut card = Card::new(0, secrets[i], 0); + actions::add_new_card(&mut context, storage.cards, &mut card); + } + + context.finish() + } + // docs:end:functions-SecretFunction + + fn update_legendary_card( + inputs: PrivateContextInputs, + new_points: u8, + new_secret: Field, + ) -> distinct pub PrivateCircuitPublicInputs { + let mut context = PrivateContext::new(inputs, abi::hash_args([new_secret])); + let storage = Storage::init(); + + let owner = inputs.call_context.msg_sender; + let mut updated_card = Card::new(new_points, new_secret, owner); + + assert(actions::is_valid_card(&mut context, storage.game_rules, updated_card)); + + actions::update_legendary_card(&mut context, storage.legendary_card, &mut updated_card); + + context.finish() + } + + fn become_queen(inputs: PrivateContextInputs) -> distinct pub PrivateCircuitPublicInputs { + let mut context = PrivateContext::new(inputs, 0); + let storage = Storage::init(); + + let legendary_card = actions::get_legendary_card(&mut context, storage.legendary_card); + + let owner = legendary_card.owner; + let result = context.call_private_function( + inputs.call_context.storage_contract_address, + GET_POINTS_OF_COMMON_CARD_FUNCTION_SELECTOR, + [owner, 0] + ); + let total_points = legendary_card.points + result[0] as u8; + + context.call_public_function( + inputs.call_context.storage_contract_address, + REPLACE_QUEEN_FUNCTION_SELECTOR, + [owner, total_points as Field] + ); + + context.finish() + } + + fn get_points_of_common_cards( + inputs: PrivateContextInputs, + account: Field, + offset: u32, + ) -> distinct pub PrivateCircuitPublicInputs { + let mut context = PrivateContext::new(inputs, abi::hash_args([account, offset as Field])); + let storage = Storage::init(); + + let mut total_points = 0; + let options = create_get_account_cards_options(account, offset); + let cards = actions::get_cards(&mut context, storage.cards, options); + for i in 0..MAX_READ_REQUESTS_PER_CALL { + if (cards[i].is_some()) { + let card = cards[i].unwrap_unchecked(); + assert(card.owner == account); + total_points += card.points; + } + } + + context.return_values.push(total_points as Field); + + context.finish() + } + + // docs:start:state_vars-check_return_notes + fn discard_largest_card( + inputs: PrivateContextInputs, + ) -> distinct pub PrivateCircuitPublicInputs { + let mut context = PrivateContext::new(inputs, 0); + let storage = Storage::init(); + + let account = context.msg_sender(); + let options = create_get_account_largest_card_options(account); + let card = actions::get_cards(&mut context, storage.cards, options)[0].unwrap_unchecked(); + // highlight-next-line:state_vars-check_return_notes + assert(card.owner == account); + + actions::remove_card(&mut context, storage.cards, card); + + context.finish() + } + // docs:end:state_vars-check_return_notes + + // docs:start:functions-UncontrainedFunction + unconstrained fn get_total_points(account: Field) -> u8 { + // + 0 + } + // docs:end:functions-UncontrainedFunction +} diff --git a/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/options.nr b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/options.nr new file mode 100644 index 00000000000..2b31a632ef3 --- /dev/null +++ b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/options.nr @@ -0,0 +1,66 @@ +use dep::std::option::Option; +use dep::aztec::constants_gen::MAX_READ_REQUESTS_PER_CALL; +use dep::aztec::note::note_getter_options::{NoteGetterOptions, Sort, SortOrder}; +use crate::types::card::{Card, CARD_LEN}; + +// docs:start:state_vars-NoteGetterOptionsSelectSortOffset +fn create_get_account_cards_options( + account_address: Field, + offset: u32, +) -> NoteGetterOptions { + NoteGetterOptions::new() + .select(2, account_address) + .sort(0, SortOrder.DESC) + .set_offset(offset) +} +// docs:end:state_vars-NoteGetterOptionsSelectSortOffset + +// docs:start:state_vars-NoteGetterOptionsMultiSelects +fn create_get_exact_card_options( + points: u8, + secret: Field, + account_address: Field, +) -> NoteGetterOptions { + NoteGetterOptions::new() + .select(0, points as Field) + .select(1, secret) + .select(2, account_address) +} +// docs:end:state_vars-NoteGetterOptionsMultiSelects + +// docs:start:state_vars-OptionFilter +fn filter_min_points( + cards: [Option; MAX_READ_REQUESTS_PER_CALL], + min_points: u8, +) -> [Option; MAX_READ_REQUESTS_PER_CALL] { + let mut selected_cards = [Option::none(); MAX_READ_REQUESTS_PER_CALL]; + let mut num_selected = 0; + for i in 0..cards.len() { + if cards[i].is_some() & cards[i].unwrap_unchecked().points >= min_points { + selected_cards[num_selected] = cards[i]; + num_selected += 1; + } + } + selected_cards +} +// docs:end:state_vars-OptionFilter + +// docs:start:state_vars-NoteGetterOptionsFilter +fn create_get_account_cards_with_min_points_options( + account_address: Field, + min_points: u8, +) -> NoteGetterOptions { + NoteGetterOptions::with_filter(filter_min_points, min_points) + .select(2, account_address) + .sort(0, SortOrder.ASC) +} +// docs:end:state_vars-NoteGetterOptionsFilter + +// docs:start:state_vars-NoteGetterOptionsPickOne +fn create_get_account_largest_card_options(account_address: Field) -> NoteGetterOptions { + NoteGetterOptions::new() + .select(2, account_address) + .sort(0, SortOrder.DESC) + .set_limit(1) +} +// docs:end:state_vars-NoteGetterOptionsPickOne \ No newline at end of file diff --git a/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage.nr b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage.nr new file mode 100644 index 00000000000..0a0cce65b11 --- /dev/null +++ b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage.nr @@ -0,0 +1,50 @@ +mod cards; +mod game_rules; +mod legendary_card; +mod locked; +mod profiles; +mod queen; + +use dep::aztec::state_vars::{ + immutable_singleton::ImmutableSingleton, + map::Map, + public_state::PublicState, + set::Set, + singleton::Singleton, +}; +use dep::aztec::types::type_serialisation::bool_serialisation::{ + BOOL_SERIALISED_LEN, +}; + +use crate::types::{ + card::{Card, CARD_LEN}, + profile::{Profile, PROFILE_LEN}, + queen::Queen, + rules::{Rules, RULES_LEN}, +}; + +use queen::QUEEN_SERIALISED_LEN; + +// docs:start:storage-declaration +struct Storage { + locked: PublicState, + queen: PublicState, + game_rules: ImmutableSingleton, + legendary_card: Singleton, + cards: Set, + profiles: Map>, +} + +impl Storage { + fn init() -> Self { + Storage { + locked: locked::init(1), + queen: queen::init(2), + game_rules: game_rules::init(3), + legendary_card: legendary_card::init(4), + cards: cards::init(5), + profiles: profiles::init(6), + } + } +} +// docs:end:storage-declaration \ No newline at end of file diff --git a/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage/cards.nr b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage/cards.nr new file mode 100644 index 00000000000..62f5a517f25 --- /dev/null +++ b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage/cards.nr @@ -0,0 +1,9 @@ +// docs:start:state_vars-Set +use dep::aztec::state_vars::set::Set; + +use crate::types::card::{Card, CardMethods, CARD_LEN}; + +fn init(storage_slot: Field) -> Set { + Set::new(storage_slot, CardMethods) +} +// docs:end:state_vars-Set \ No newline at end of file diff --git a/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage/game_rules.nr b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage/game_rules.nr new file mode 100644 index 00000000000..f037aa0ad4e --- /dev/null +++ b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage/game_rules.nr @@ -0,0 +1,9 @@ +// docs:start:state_vars-ImmutableSingleton +use dep::aztec::state_vars::immutable_singleton::ImmutableSingleton; + +use crate::types::rules::{Rules, RulesMethods, RULES_LEN}; + +fn init(storage_slot: Field) -> ImmutableSingleton { + ImmutableSingleton::new(storage_slot, RulesMethods) +} +// docs:end:state_vars-ImmutableSingleton \ No newline at end of file diff --git a/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage/legendary_card.nr b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage/legendary_card.nr new file mode 100644 index 00000000000..dc85dd8220e --- /dev/null +++ b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage/legendary_card.nr @@ -0,0 +1,9 @@ +// docs:start:state_vars-Singleton +use dep::aztec::state_vars::singleton::Singleton; + +use crate::types::card::{Card, CardMethods, CARD_LEN}; + +fn init(storage_slot: Field) -> Singleton { + Singleton::new(storage_slot, CardMethods) +} +// docs:end:state_vars-Singleton \ No newline at end of file diff --git a/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage/locked.nr b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage/locked.nr new file mode 100644 index 00000000000..efe4b1fbdaf --- /dev/null +++ b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage/locked.nr @@ -0,0 +1,11 @@ +// docs:start:state_vars-PublicState +use dep::aztec::state_vars::public_state::PublicState; +use dep::aztec::types::type_serialisation::bool_serialisation::{ + BoolSerialisationMethods, + BOOL_SERIALISED_LEN, +}; + +fn init(storage_slot: Field) -> PublicState { + PublicState::new(storage_slot, BoolSerialisationMethods) +} +// docs:end:state_vars-PublicState \ No newline at end of file diff --git a/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage/profiles.nr b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage/profiles.nr new file mode 100644 index 00000000000..635bf1ab558 --- /dev/null +++ b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage/profiles.nr @@ -0,0 +1,12 @@ +// docs:start:state_vars-MapSingleton +use dep::aztec::state_vars::{ + map::Map, + singleton::Singleton, +}; + +use crate::types::profile::{Profile, ProfileMethods, PROFILE_LEN}; + +fn init(storage_slot: Field) -> Map> { + Map::new(storage_slot, |slot| Singleton::new(slot, ProfileMethods)) +} +// docs:end:state_vars-MapSingleton \ No newline at end of file diff --git a/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage/queen.nr b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage/queen.nr new file mode 100644 index 00000000000..8d22dc26a38 --- /dev/null +++ b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage/queen.nr @@ -0,0 +1,29 @@ +use dep::aztec::state_vars::public_state::PublicState; +use dep::aztec::types::type_serialisation::TypeSerialisationInterface; + +use crate::types::queen::Queen; + +// docs:start:state_vars-PublicStateCustomStruct +global QUEEN_SERIALISED_LEN: Field = 2; + +fn deserialise(fields: [Field; QUEEN_SERIALISED_LEN]) -> Queen { + Queen { + account: fields[0], + points: fields[1] as u8, + } +} + +fn serialise(queen: Queen) -> [Field; QUEEN_SERIALISED_LEN] { + [queen.account, queen.points as Field] +} + +global QueenSerialisationMethods = TypeSerialisationInterface { + deserialise, + serialise, +}; + +fn init(storage_slot: Field) -> PublicState { + // highlight-next-line:state_vars-PublicStateCustomStruct + PublicState::new(storage_slot, QueenSerialisationMethods) +} +// docs:end:state_vars-PublicStateCustomStruct \ No newline at end of file diff --git a/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/types.nr b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/types.nr new file mode 100644 index 00000000000..655418e559c --- /dev/null +++ b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/types.nr @@ -0,0 +1,4 @@ +mod card; +mod profile; +mod queen; +mod rules; \ No newline at end of file diff --git a/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/types/card.nr b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/types/card.nr new file mode 100644 index 00000000000..cbf1ccada80 --- /dev/null +++ b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/types/card.nr @@ -0,0 +1,101 @@ +use dep::aztec::note::{ + note_header::NoteHeader, + note_interface::NoteInterface, + utils::compute_note_hash_for_read_or_nullify, +}; +use dep::aztec::oracle::{ + get_secret_key::get_secret_key, + get_public_key::get_public_key, +}; + +global CARD_LEN: Field = 3; + +// docs:start:state_vars-NoteCard +struct Card { + points: u8, + secret: Field, + owner: Field, + header: NoteHeader, +} +// docs:end:state_vars-NoteCard + +impl Card { + fn new(points: u8, secret: Field, owner: Field) -> Self { + Card { + points, + secret, + owner, + header: NoteHeader::empty(), + } + } + + fn serialise(self) -> [Field; CARD_LEN] { + [self.points as Field, self.secret, self.owner] + } + + fn deserialise(preimage: [Field; CARD_LEN]) -> Self { + Card { + points: preimage[0] as u8, + secret: preimage[1], + owner: preimage[2], + header: NoteHeader::empty(), + } + } + + fn compute_note_hash(self) -> Field { + dep::std::hash::pedersen([ + self.points as Field, + self.secret, + self.owner, + ])[0] + } + + fn compute_nullifier(self) -> Field { + let note_hash_for_nullify = compute_note_hash_for_read_or_nullify(CardMethods, self); + let owner_nullifying_public_key = get_public_key(self.owner); + // TODO: get_secret_key should just accept an address + // TODO! + let secret = get_secret_key(owner_nullifying_public_key); + dep::std::hash::pedersen([ + note_hash_for_nullify, + secret, + ])[0] + } + + fn set_header(&mut self, header: NoteHeader) { + self.header = header; + } +} + +fn deserialise(preimage: [Field; CARD_LEN]) -> Card { + Card::deserialise(preimage) +} + +fn serialise(note: Card) -> [Field; CARD_LEN] { + note.serialise() +} + +fn compute_note_hash(note: Card) -> Field { + note.compute_note_hash() +} + +fn compute_nullifier(note: Card) -> Field { + note.compute_nullifier() +} + +fn get_header(note: Card) -> NoteHeader { + note.header +} + +fn set_header(note: &mut Card, header: NoteHeader) { + note.set_header(header) +} + +global CardMethods = NoteInterface { + deserialise, + serialise, + compute_note_hash, + compute_nullifier, + get_header, + set_header, +}; diff --git a/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/types/profile.nr b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/types/profile.nr new file mode 100644 index 00000000000..a0e702ab799 --- /dev/null +++ b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/types/profile.nr @@ -0,0 +1,88 @@ +use dep::aztec::note::{ + note_header::NoteHeader, + note_interface::NoteInterface, + utils::compute_note_hash_for_read_or_nullify, +}; +use dep::aztec::oracle::{ + get_secret_key::get_secret_key, + get_public_key::get_public_key, +}; + +global PROFILE_LEN: Field = 2; + +struct Profile { + avatar: Field, + xp: Field, + header: NoteHeader, +} + +impl Profile { + fn new(avatar: Field, xp: Field) -> Self { + Profile { + avatar, + xp, + header: NoteHeader::empty(), + } + } + + fn serialise(self) -> [Field; PROFILE_LEN] { + [self.avatar, self.xp] + } + + fn deserialise(preimage: [Field; PROFILE_LEN]) -> Self { + Profile { + avatar: preimage[1], + xp: preimage[0], + header: NoteHeader::empty(), + } + } + + fn compute_note_hash(self) -> Field { + dep::std::hash::pedersen([ + self.avatar, + self.xp, + ])[0] + } + + fn compute_nullifier(_self: Self) -> Field { + assert(false); // Not allowed. + 0 + } + + fn set_header(&mut self, header: NoteHeader) { + self.header = header; + } +} + +fn deserialise(preimage: [Field; PROFILE_LEN]) -> Profile { + Profile::deserialise(preimage) +} + +fn serialise(note: Profile) -> [Field; PROFILE_LEN] { + note.serialise() +} + +fn compute_note_hash(note: Profile) -> Field { + note.compute_note_hash() +} + +fn compute_nullifier(note: Profile) -> Field { + note.compute_nullifier() +} + +fn get_header(note: Profile) -> NoteHeader { + note.header +} + +fn set_header(note: &mut Profile, header: NoteHeader) { + note.set_header(header) +} + +global ProfileMethods = NoteInterface { + deserialise, + serialise, + compute_note_hash, + compute_nullifier, + get_header, + set_header, +}; diff --git a/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/types/queen.nr b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/types/queen.nr new file mode 100644 index 00000000000..383a092269d --- /dev/null +++ b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/types/queen.nr @@ -0,0 +1,6 @@ +// docs:start:state_vars-CustomStruct +struct Queen { + account: Field, + points: u8, +} +// docs:end:state_vars-CustomStruct \ No newline at end of file diff --git a/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/types/rules.nr b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/types/rules.nr new file mode 100644 index 00000000000..4c9565e5383 --- /dev/null +++ b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/types/rules.nr @@ -0,0 +1,84 @@ +use dep::aztec::note::{ + note_header::NoteHeader, + note_interface::NoteInterface, + utils::compute_note_hash_for_read_or_nullify, +}; + +global RULES_LEN: Field = 2; + +struct Rules { + min_points: u8, + max_points: u8, + header: NoteHeader, +} + +impl Rules { + fn new(min_points: u8, max_points: u8) -> Self { + Rules { + min_points, + max_points, + header: NoteHeader::empty(), + } + } + + fn serialise(self) -> [Field; RULES_LEN] { + [self.min_points as Field, self.max_points as Field] + } + + fn deserialise(preimage: [Field; RULES_LEN]) -> Self { + Rules { + min_points: preimage[0] as u8, + max_points: preimage[1] as u8, + header: NoteHeader::empty(), + } + } + + fn compute_note_hash(self) -> Field { + dep::std::hash::pedersen([ + self.min_points as Field, + self.max_points as Field, + ])[0] + } + + fn compute_nullifier(_self: Self) -> Field { + // Not used + 0 + } + + fn set_header(&mut self, header: NoteHeader) { + self.header = header; + } +} + +fn deserialise(preimage: [Field; RULES_LEN]) -> Rules { + Rules::deserialise(preimage) +} + +fn serialise(note: Rules) -> [Field; RULES_LEN] { + note.serialise() +} + +fn compute_note_hash(note: Rules) -> Field { + note.compute_note_hash() +} + +fn compute_nullifier(note: Rules) -> Field { + note.compute_nullifier() +} + +fn get_header(note: Rules) -> NoteHeader { + note.header +} + +fn set_header(note: &mut Rules, header: NoteHeader) { + note.set_header(header) +} + +global RulesMethods = NoteInterface { + deserialise, + serialise, + compute_note_hash, + compute_nullifier, + get_header, + set_header, +}; diff --git a/yarn-project/noir-contracts/src/contracts/private_token_contract/src/main.nr b/yarn-project/noir-contracts/src/contracts/private_token_contract/src/main.nr index 514377b95a8..f83de75a3d1 100644 --- a/yarn-project/noir-contracts/src/contracts/private_token_contract/src/main.nr +++ b/yarn-project/noir-contracts/src/contracts/private_token_contract/src/main.nr @@ -17,9 +17,7 @@ contract PrivateToken { }; use dep::aztec::log::emit_unencrypted_log; - // docs:start:storage-import use crate::storage::Storage; - // docs:end:storage-import // docs:start:constructor // Constructs the contract and sets `initial_supply` which is fully owned by `owner`. diff --git a/yarn-project/noir-contracts/src/contracts/private_token_contract/src/storage.nr b/yarn-project/noir-contracts/src/contracts/private_token_contract/src/storage.nr index 52fcd691490..36e5fc8e9f4 100644 --- a/yarn-project/noir-contracts/src/contracts/private_token_contract/src/storage.nr +++ b/yarn-project/noir-contracts/src/contracts/private_token_contract/src/storage.nr @@ -8,14 +8,11 @@ use dep::value_note::value_note::{ VALUE_NOTE_LEN, }; -// docs:start:storage-declaration -// highlight-next-line:storage-declaration struct Storage { // maps an aztec address to its balance balances: Map>, } -// highlight-next-line:storage-declaration impl Storage { fn init() -> Self { Storage { @@ -23,4 +20,3 @@ impl Storage { } } } -// docs:end:storage-declaration diff --git a/yarn-project/noir-contracts/src/contracts/public_token_contract/src/storage.nr b/yarn-project/noir-contracts/src/contracts/public_token_contract/src/storage.nr index 9513325c9fa..ef39a851b2e 100644 --- a/yarn-project/noir-contracts/src/contracts/public_token_contract/src/storage.nr +++ b/yarn-project/noir-contracts/src/contracts/public_token_contract/src/storage.nr @@ -1,4 +1,3 @@ -// docs:start:PublicState use dep::aztec::state_vars::{ map::Map, // highlight-start:PublicState @@ -12,7 +11,6 @@ use dep::aztec::types::type_serialisation::field_serialisation::{ }; struct Storage { - // highlight-next-line:PublicState balances: Map>, } @@ -22,5 +20,4 @@ impl Storage { balances: Map::new(1, |slot| PublicState::new(slot, FieldSerialisationMethods)), } } -} -// docs:end:PublicState \ No newline at end of file +} \ No newline at end of file diff --git a/yarn-project/noir-libs/noir-aztec/src/note/note_interface.nr b/yarn-project/noir-libs/noir-aztec/src/note/note_interface.nr index fd662fc3b47..84d171fd5ba 100644 --- a/yarn-project/noir-libs/noir-aztec/src/note/note_interface.nr +++ b/yarn-project/noir-libs/noir-aztec/src/note/note_interface.nr @@ -1,5 +1,6 @@ use crate::note::note_header::NoteHeader; +// docs:start:NoteInterface struct NoteInterface { deserialise: fn ([Field; N]) -> Note, @@ -13,3 +14,4 @@ struct NoteInterface { set_header: fn (&mut Note, NoteHeader) -> (), } +// docs:end:NoteInterface \ No newline at end of file From 12fb07046000537eb3a02f876f383e48a1e9d62e Mon Sep 17 00:00:00 2001 From: Leila Wang Date: Fri, 25 Aug 2023 18:14:29 +0000 Subject: [PATCH 2/7] Updated docs. Co-authored-by: Michael Connor --- docs/docs/dev_docs/contracts/functions.md | 6 +- .../dev_docs/contracts/state_variables.md | 90 ++++++++++++++----- docs/docs/dev_docs/contracts/storage.md | 2 +- .../docs_example_contract/src/main.nr | 4 +- .../docs_example_contract/src/options.nr | 8 +- 5 files changed, 79 insertions(+), 31 deletions(-) diff --git a/docs/docs/dev_docs/contracts/functions.md b/docs/docs/dev_docs/contracts/functions.md index 97993921d44..ceddbeb1186 100644 --- a/docs/docs/dev_docs/contracts/functions.md +++ b/docs/docs/dev_docs/contracts/functions.md @@ -4,15 +4,19 @@ - A special `constructor` function MUST be declared within a contract's scope. - A constructor doesn't have a name, because its purpose is clear: to initialise state. -- In Aztec terminology, a constructor is always a 'secret function' (i.e. it cannot be an `open` function). +- In Aztec terminology, a constructor is always a 'private function' (i.e. it cannot be an `open` function). - A constructor behaves almost identically to any other function. It's just important for Aztec to be able to identify this function as special: it may only be called once, and will not be deployed as part of the contract. ## secret functions +> a.k.a. "private" functions + #include_code functions-SecretFunction /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/main.nr rust ## `open` functions +> a.k.a. "public" functions + #include_code functions-OpenFunction /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/main.nr rust ## `unconstrained` functions diff --git a/docs/docs/dev_docs/contracts/state_variables.md b/docs/docs/dev_docs/contracts/state_variables.md index 7a0584d02dd..3954e7e8e88 100644 --- a/docs/docs/dev_docs/contracts/state_variables.md +++ b/docs/docs/dev_docs/contracts/state_variables.md @@ -16,11 +16,13 @@ The Aztec stdlib provides serialization methods for some common types. Check [he ### `::new` -In the following example, we define a public state with a boolean value. +To declare a type `T` as a persistent, public state variable, use the `PublicState::new()` constructor. + +In the following example, we define a public state with a boolean type: #include_code state_vars-PublicState /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage/locked.nr rust -### Custom value +### Custom types It's possible to create a public state for any types. For example, to create a public state for the following struct: @@ -30,45 +32,77 @@ First, define how to serialise and deserialise the struct. And then initialise t #include_code state_vars-PublicStateCustomStruct /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage/queen.nr rust +### `.read` + +Reading the currently-stored value of a public state variable is straightforward: + +#include_code state_vars-PublicStateRead /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/actions.nr rust + +It returns the type the public state was declared with. The above example returns a boolean. And the following example returns a custom struct. + +#include_code state_vars-PublicStateReadCustom /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/actions.nr rust + ### `.write` -We can pass the associated type directly to a public state. It knows how to serialise the given value to store in the public state tree. +The currently-stored value of a private state variable can be overwritten with `.write()`. + +Due to the way public states are [declared](#new), a public state knows how to serialise a given value and store it in the protocol's public state tree. + +We can pass the associated type directly to the `write()` method: #include_code state_vars-PublicStateWrite /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/actions.nr rust #include_code state_vars-PublicStateWriteCustom /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/actions.nr rust -### `.read` +#### Writing before calling -Reading a value from a public state is straightforward: +**Important note:** +Before making a call to an external function, it is important to remember to `.write` state variables that have been edited, so as to persist their new values. This is particularly important if the call to the external function might result in re-entrancy into your contract, later in the transaction. If state variables aren't written before making such an external call, then upon re-entrancy, the 'current values' of your state variables will equal the values as at the start of the original function call. -#include_code state_vars-PublicStateRead /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/actions.nr rust +E.g. -It returns the type the public state was declared with. The above example returns a boolean. And the following example returns a custom struct. +## Private State Variables -#include_code state_vars-PublicStateReadCustom /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/actions.nr rust +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. -## Private State Variables +The value of a private state variable can either be shared via an [encrypted log](INSERT_LINK_HERE), or offchain via web2, or completely offline: it's up to the app developer. + +Aztec private state follows a utxo-based model. That is, a private state's current value is represented as one or many [notes](#notes). Each note is stored as an individual leaf in a utxo-based merkle tree: the [_private state tree_](INSERT_LINK_HERE). -There are 3 different types of private state variables: +To greatly simplify the experience of writing private state, Aztec.nr provides three different types of private state variable: - [Singleton](#singletonnotetype) - [ImmutableSingleton](#immutablesingletonnotetype) - [Set](#setnotetype) -In contrast to public state variables, private state variables are only visible to specific relevant parties. The value of a private state variable can either be shared via (log)[INSERT_LINK_HERE] or completely offline. +These three structs abstract-away many of Aztec's protocol complexities, by providing intuitive methods to modify notes in the utxo tree in a privacy-preserving way. -Note that an app can choose to emit the data via unencrypted log, or define a note whose data is easy to figure out, then the information is technically not private and could be visible to anyone. +> 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 -Unlike public state variables, which can be arbitrary types. Private state variables operate on `NoteType`. +Unlike public state variables, which can be arbitrary types, private state variables operate on `NoteType`. Notes are the fundamental elements in the private world. -A note should confine to the following interface: +A note should conform to the following interface: #include_code NoteInterface /yarn-project/noir-libs/noir-aztec/src/note/note_interface.nr rust +The interplay between a private state variable and its notes can be confusing. Here's a summary to aid intuition: + +- A private state variable (of type `Singleton`, `ImmutableSingleton` or `Set`) may be declared in [Storage](./storage.md). +- Every note contains (as a 'header') the contract address and storage slot of the state variable to which it "belongs". A note is said to "belong" to a private state if the storage slot of the private state matches the storage slot contained in the note's header. + - Management of this 'header' is abstracted-away from developers who use the `ImmutableSingleton`, `Singleton` and `Set` types. +- A private state variable is colloquially said to "point" to one or many notes (depending on the type), if those note(s) all "belong" to that private state, and those note(s) haven't-yet been nullified. +- An `ImmutableSingleton` will point to _one_ note over the lifetime of the contract. ("One", hence "Singleton"). This note is a struct of information that is persisted forever. +- A `Singleton` may point to _one_ note at a time. ("One", hence "Singleton"). But since it's not "immutable", the note that it points to may be [replaced](#replace) by functions of the contract, over time. The "current value" of a `Singleton` is interpreted as the one note which has not-yet been nullified. The act of 'replacing' a Singleton's note is how a `Singleton` state may be modified by functions. + - `Singleton` is a useful type when declaring a private state which may only ever be modified by those who are privy to the current value of that state. +- A `Set` may point to _multiple_ notes at a time. The "current value" of a private state variable of type `Set` is some 'accumulation' of all not-yet nullified notes which "belong" to the `Set`. + - The term "some accumulation" is intentionally vague. The interpretation of the "current value" of a `Set` must be expressed by the smart contract developer. A common use case for a `Set` is to represent the sum of a collection of values (in which case 'accumulation' is 'summation'). + - Think of a ZCash balance (or even a Bitcoin balance). The "current value" of a user's ZCash balance is the sum of all unspent (not-yet nullified) notes belonging to that user. + - 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 initialised, 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. @@ -87,13 +121,17 @@ The initial value of a singleton is set via calling `initialise`: When this function is called, a nullifier of the storage slot is created, preventing this singleton from being initialised again. +Unlike public states, which have a default initial value of `0` (or many zeros, in the case of a struct, array or map), a private state (of type `Singleton`, `ImmutableSingleton` or `Set`) does not have a default initial value. The `initialise` method (or `insert`, in the case of a `Set`) must be called. + ### `.replace` -To modify the value of a singleton, we will create a note (a Card in the following example) that has the new data, and replace the current note with it: +The 'current value' of a `Singleton` state variable may be overwritten via the `.replace` method. + +To modify the 'current value' of a singleton, we may create a new note (a Card in the following example) containing some new data, and replace the current note with it: #include_code state_vars-SingletonReplace /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/actions.nr rust -This function will destroy the old note under the hood. If two people are trying to modify the singleton at the same time, only one will succeed. +This function will destroy the old note under the hood. If two people are trying to modify the singleton at the same time, only one will succeed. Developers should put in place appropriate access controls to avoid race conditions (unless a race is intended!). ### `.get_note` @@ -119,11 +157,11 @@ Immutable singleton is unique and, as the name suggests, immutable. Once it has #include_code state_vars-ImmutableSingletonGet /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/actions.nr rust -Different to singleton, calling the `get_note` function on an immutable singleton to read the value doesn't destroy the current note behind the scene. Which means this function can be called simultaneously. +Unlike a [`singleton`](#get_note-1), when you call the `get_note` function on an immutable singleton to read the value, the current note is not destroyed in the background. This means that multiple accounts can call this function simultaneously. ## `Set` -Set is used for managing a collection of notes. All notes in a set should be the same type. But whether they belong to one single account, or are accessible by different entities, is totally up to the developer. +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. ### `::new` @@ -133,7 +171,7 @@ In the following example, we define a set of cards: ### `.insert` -We can call `insert` for as many times as we need to add new notes to a set: +We can call `insert` for as many times as we need to add new notes to a `Set`. A `Set` is unbounded in size. #include_code state_vars-SetInsert /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/actions.nr rust @@ -157,11 +195,13 @@ Because of this limit, we should always consider using the second argument `Note ### NoteGetterOptions -`NoteGetterOptions` encapsulates a set of configurable options for retrieving notes from a database: +`NoteGetterOptions` encapsulates a set of configurable options for filtering and retrieving a selection of notes from a database: #include_code NoteGetterOptions /yarn-project/noir-libs/noir-aztec/src/note/note_getter_options.nr rust -For example, the following configs let us find the cards that belong to `account_address`. The returned cards are sorted by their points in descending order, and the first `offset` cards with the highest points are skipped. +Developers can design instances of `NoteGetterOptions`, to determine how notes should be filtered and returned to the functions of their smart contracts. + +For example, the following function outputs an instance of `NoteGetterOptions`, which has been configured to find the cards that belong to `account_address`. The returned cards are sorted by their points in descending order, and the first `offset` cards with the highest points are skipped. #include_code state_vars-NoteGetterOptionsSelectSortOffset /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/options.nr rust @@ -201,14 +241,18 @@ The process of applying the options to get the final notes is not constrained. I ## `Map` -`Map` is a state variable that maps a field to another state variable, which can be [`PublicState`](#publicstatet-t_serialised_len), all the [private state variables](#private-state-variables), and even another Map. +`Map` is a state variable that maps a `Field` to another state variable, which can be [`PublicState`](#publicstatet-t_serialised_len), all the [private state variables](#private-state-variables), and even another Map. + +> `Map` can map from `Field` or any native Noir type which is convertible to `Field`. ### `::new` -The following map uses singleton as its value: +The following declares a mapping from a `Field` to a `Singleton`: #include_code state_vars-MapSingleton /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage/profiles.nr rust +The second argument `|slot| Singleton::new(slot, ProfileMethods)` is a Noir closure function. It teaches this instance of `Map` how to create a new instance of a `Singleton` whenever the `.at` method is called to access a state variable at a particular mapping key. The `slot` argument will be derived when `.at` is called, based on the lookup key provided. + ### `.at` The only api of a map is `.at`. It returns the underlying type that occupies a specific storage slot, which is generated by the field passed to `.at`. diff --git a/docs/docs/dev_docs/contracts/storage.md b/docs/docs/dev_docs/contracts/storage.md index 56f93b63c0b..73bf120c164 100644 --- a/docs/docs/dev_docs/contracts/storage.md +++ b/docs/docs/dev_docs/contracts/storage.md @@ -10,7 +10,7 @@ We could define any kinds of state variables in the Storage struct: See [State Variables](./state_variables.md) for how to initialise them. -Using Storage in a contract is like using any other variables. First, import them to the `main.nr` file: +Using Storage in a contract is like using any other struct in Noir. First, import the struct into the contract's `main.nr` file: #include_code storage-import /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/main.nr rust diff --git a/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/main.nr b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/main.nr index 00cc7345589..ae369757246 100644 --- a/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/main.nr +++ b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/main.nr @@ -13,7 +13,7 @@ contract DocsExample { }; use crate::actions; - use crate::options::{create_get_account_cards_options, create_get_account_largest_card_options}; + use crate::options::{create_account_card_getter_options, create_get_account_largest_card_options}; // docs:start:storage-import use crate::storage::Storage; // docs:end:storage-import @@ -156,7 +156,7 @@ contract DocsExample { let storage = Storage::init(); let mut total_points = 0; - let options = create_get_account_cards_options(account, offset); + let options = create_account_card_getter_options(account, offset); let cards = actions::get_cards(&mut context, storage.cards, options); for i in 0..MAX_READ_REQUESTS_PER_CALL { if (cards[i].is_some()) { diff --git a/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/options.nr b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/options.nr index 2b31a632ef3..de2e9be966f 100644 --- a/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/options.nr +++ b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/options.nr @@ -4,7 +4,7 @@ use dep::aztec::note::note_getter_options::{NoteGetterOptions, Sort, SortOrder}; use crate::types::card::{Card, CARD_LEN}; // docs:start:state_vars-NoteGetterOptionsSelectSortOffset -fn create_get_account_cards_options( +fn create_account_card_getter_options( account_address: Field, offset: u32, ) -> NoteGetterOptions { @@ -16,7 +16,7 @@ fn create_get_account_cards_options( // docs:end:state_vars-NoteGetterOptionsSelectSortOffset // docs:start:state_vars-NoteGetterOptionsMultiSelects -fn create_get_exact_card_options( +fn create_exact_card_getter_options( points: u8, secret: Field, account_address: Field, @@ -46,7 +46,7 @@ fn filter_min_points( // docs:end:state_vars-OptionFilter // docs:start:state_vars-NoteGetterOptionsFilter -fn create_get_account_cards_with_min_points_options( +fn create_account_cards_with_min_points_getter_options( account_address: Field, min_points: u8, ) -> NoteGetterOptions { @@ -57,7 +57,7 @@ fn create_get_account_cards_with_min_points_options( // docs:end:state_vars-NoteGetterOptionsFilter // docs:start:state_vars-NoteGetterOptionsPickOne -fn create_get_account_largest_card_options(account_address: Field) -> NoteGetterOptions { +fn create_largest_account_card_getter_options(account_address: Field) -> NoteGetterOptions { NoteGetterOptions::new() .select(2, account_address) .sort(0, SortOrder.DESC) From 80c51e25ffa244025bcdc2a57dab672fdbd88412 Mon Sep 17 00:00:00 2001 From: Leila Wang Date: Fri, 25 Aug 2023 19:53:33 +0000 Subject: [PATCH 3/7] More examples. --- .../dev_docs/contracts/state_variables.md | 12 +++++- .../src/account_contract_interface.nr | 12 ++++++ .../docs_example_contract/src/actions.nr | 42 ++++++++++++++++++- .../docs_example_contract/src/main.nr | 33 +++++++++++++-- 4 files changed, 92 insertions(+), 7 deletions(-) create mode 100644 yarn-project/noir-contracts/src/contracts/docs_example_contract/src/account_contract_interface.nr diff --git a/docs/docs/dev_docs/contracts/state_variables.md b/docs/docs/dev_docs/contracts/state_variables.md index 3954e7e8e88..de65e916e55 100644 --- a/docs/docs/dev_docs/contracts/state_variables.md +++ b/docs/docs/dev_docs/contracts/state_variables.md @@ -42,6 +42,8 @@ It returns the type the public state was declared with. The above example return #include_code state_vars-PublicStateReadCustom /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/actions.nr rust +Every public state can be read before its value is written. The default value is 0, or { 0, 0, ... } if it's a struct. + ### `.write` The currently-stored value of a private state variable can be overwritten with `.write()`. @@ -53,12 +55,20 @@ We can pass the associated type directly to the `write()` method: #include_code state_vars-PublicStateWrite /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/actions.nr rust #include_code state_vars-PublicStateWriteCustom /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/actions.nr rust +#### Read and write + +A common pattern is reading a public state, changing the value, and then writing the new value back to the public state. Remember to always call `.write` to update the value. The value of a public state won't be changed by modifying the return value of `.read`. + +#include_code state_vars-PublicStateReadWriteCustom /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/actions.nr rust + #### Writing before calling **Important note:** Before making a call to an external function, it is important to remember to `.write` state variables that have been edited, so as to persist their new values. This is particularly important if the call to the external function might result in re-entrancy into your contract, later in the transaction. If state variables aren't written before making such an external call, then upon re-entrancy, the 'current values' of your state variables will equal the values as at the start of the original function call. -E.g. +For example, the following function calls the account contract before it updates the public state. This allows the account contract, which can have arbitrary logic defined by the account itself, to call back to this function within its `send_rewards` function. And because this function hasn't updated the public state, the conditions are still true. This means `send_rewards` will be triggered again and again. + +#include_code state_vars-PublicStateWriteBeforeCall /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/main.nr rust ## Private State Variables diff --git a/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/account_contract_interface.nr b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/account_contract_interface.nr new file mode 100644 index 00000000000..ca1b3b2d4a4 --- /dev/null +++ b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/account_contract_interface.nr @@ -0,0 +1,12 @@ +struct AccountContractInterface { + address: Field, +} + +impl AccountContractInterface { + fn at(address: Field) -> Self { + AccountContractInterface { address } + } + + fn send_rewards(_self: Self, _rewards: u8) { + } +} \ No newline at end of file diff --git a/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/actions.nr b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/actions.nr index a7103f2f78f..4dc6d8fa50c 100644 --- a/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/actions.nr +++ b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/actions.nr @@ -1,9 +1,12 @@ use dep::std::option::Option; -use dep::aztec::constants_gen::MAX_READ_REQUESTS_PER_CALL; +use dep::aztec::constants_gen::{MAX_READ_REQUESTS_PER_CALL, MAX_NOTES_PER_PAGE}; use dep::aztec::context::{ PrivateContext, }; -use dep::aztec::note::note_getter_options::NoteGetterOptions; +use dep::aztec::note::{ + note_getter_options::NoteGetterOptions, + note_viewer_options::NoteViewerOptions, +}; use dep::aztec::state_vars::{ immutable_singleton::ImmutableSingleton, map::Map, @@ -64,6 +67,17 @@ fn replace_queen( } // docs:end:state_vars-PublicStateWriteCustom +// docs:start:state_vars-PublicStateReadWriteCustom +fn add_points_to_queen( + state_var: PublicState, + new_points: u8, +) { + let mut queen = state_var.read(); + queen.points += new_points; + state_var.write(queen); +} +// docs:end:state_vars-PublicStateReadWriteCustom + // docs:start:state_vars-SingletonInit fn init_legendary_card( context: &mut PrivateContext, @@ -144,6 +158,30 @@ fn get_cards( } // docs:end:state_vars-SetGet +// docs:start:state_vars-SetView +unconstrained fn view_cards( + state_var: Set, + options: NoteViewerOptions, +) -> [Option; MAX_NOTES_PER_PAGE] { + state_var.view_notes(options) +} +// docs:end:state_vars-SetView + +unconstrained fn get_total_points(state_var: Set, account: Field, offset: u32) -> u8 { + let options = NoteViewerOptions::new().select(2, account).set_offset(offset); + let mut total_points = 0; + let notes = view_cards(state_var, options); + for i in 0..notes.len() { + if notes[i].is_some() { + total_points += notes[i].unwrap_unchecked().points; + } + } + if notes[notes.len() - 1].is_some() { + total_points += get_total_points(state_var, account, offset + notes.len() as u32); + } + total_points +} + // docs:start:state_vars-SetContains fn assert_contains_card( context: &mut PrivateContext, diff --git a/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/main.nr b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/main.nr index ae369757246..05497201445 100644 --- a/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/main.nr +++ b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/main.nr @@ -1,3 +1,4 @@ +mod account_contract_interface; mod actions; mod options; mod storage; @@ -12,8 +13,9 @@ contract DocsExample { PublicContext, }; + use crate::account_contract_interface::AccountContractInterface; use crate::actions; - use crate::options::{create_account_card_getter_options, create_get_account_largest_card_options}; + use crate::options::{create_account_card_getter_options, create_largest_account_card_getter_options}; // docs:start:storage-import use crate::storage::Storage; // docs:end:storage-import @@ -89,6 +91,29 @@ contract DocsExample { context.finish() } + // docs:start:state_vars-PublicStateWriteBeforeCall + open fn replace_queen_unsafe( + inputs: PublicContextInputs, + ) -> pub abi::PublicCircuitPublicInputs { + let context = PublicContext::new(inputs, 0); + let storage = Storage::init(); + + let account = context.msg_sender(); + let points = actions::get_total_points(storage.cards, account, 0); + + let current_queen = storage.queen.read(); + assert(account != current_queen.account); + assert(points > current_queen.points); + + AccountContractInterface::at(account).send_rewards(current_queen.points); + + let new_queen = Queen { account, points }; + storage.queen.write(new_queen); + + context.finish() + } + // docs:end:state_vars-PublicStateWriteBeforeCall + // docs:start:functions-SecretFunction fn add_common_cards( inputs: PrivateContextInputs, @@ -179,7 +204,7 @@ contract DocsExample { let storage = Storage::init(); let account = context.msg_sender(); - let options = create_get_account_largest_card_options(account); + let options = create_largest_account_card_getter_options(account); let card = actions::get_cards(&mut context, storage.cards, options)[0].unwrap_unchecked(); // highlight-next-line:state_vars-check_return_notes assert(card.owner == account); @@ -192,8 +217,8 @@ contract DocsExample { // docs:start:functions-UncontrainedFunction unconstrained fn get_total_points(account: Field) -> u8 { - // - 0 + let storage = Storage::init(); + actions::get_total_points(storage.cards, account, 0) } // docs:end:functions-UncontrainedFunction } From 6e5c2d644f06048c3f0b9b6196a6efba9da1433c Mon Sep 17 00:00:00 2001 From: Leila Wang Date: Sat, 26 Aug 2023 20:01:49 +0000 Subject: [PATCH 4/7] Update docs. --- .../dev_docs/contracts/state_variables.md | 72 +++++++++++++------ .../docs_example_contract/src/actions.nr | 5 +- .../docs_example_contract/src/storage.nr | 50 ++++++++----- .../src/storage/cards.nr | 9 --- .../src/storage/game_rules.nr | 9 --- .../src/storage/legendary_card.nr | 9 --- .../src/storage/locked.nr | 11 --- .../src/storage/profiles.nr | 12 ---- .../src/storage/queen.nr | 29 -------- .../docs_example_contract/src/types/queen.nr | 24 ++++++- .../src/types/type_serialisation.nr | 4 +- 11 files changed, 108 insertions(+), 126 deletions(-) delete mode 100644 yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage/cards.nr delete mode 100644 yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage/game_rules.nr delete mode 100644 yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage/legendary_card.nr delete mode 100644 yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage/locked.nr delete mode 100644 yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage/profiles.nr delete mode 100644 yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage/queen.nr diff --git a/docs/docs/dev_docs/contracts/state_variables.md b/docs/docs/dev_docs/contracts/state_variables.md index de65e916e55..2b1d1317bb9 100644 --- a/docs/docs/dev_docs/contracts/state_variables.md +++ b/docs/docs/dev_docs/contracts/state_variables.md @@ -8,29 +8,45 @@ Public state is persistent state that is _publicly visible_ to anyone in the wor For developers coming from other blockchain ecosystems (such as Ethereum), this will be a familiar concept, because there, _all_ state is _publicly visible_. -Aztec public state follows an account-based model. That is, each state occupies a leaf in an account-based merkle tree: the _public state tree_ (LINK). See here (LINK) for more of the technical details. +Aztec public state follows an account-based model. That is, each state occupies a leaf in an account-based merkle tree: the [_public state tree_](INSERT_LINK_HERE). See [here] (INSERT_LINK_HERE) for more of the technical details. The `PublicState` struct serves as a wrapper around conventional Noir types `T`, allowing these types to be written to and read from the public state tree. -The Aztec stdlib provides serialization methods for some common types. Check [here](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/noir-libs/noir-aztec/src/types/type_serialisation) for the complete list. - ### `::new` To declare a type `T` as a persistent, public state variable, use the `PublicState::new()` constructor. In the following example, we define a public state with a boolean type: -#include_code state_vars-PublicState /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage/locked.nr rust +#include_code state_vars-PublicState /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage.nr rust + +The BoolSerialisationMethods is part of the Aztec stdlib: + +#include_code state_vars-PublicStateBoolImport /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage.nr rust + +It contains methods that instruct its PublicState wrapper how to serialise and deserialise a boolean to and from a Field, which is the data type being saved in the public state tree. + +The Aztec stdlib provides serialization methods for various common types. Check [here](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/noir-libs/noir-aztec/src/types/type_serialisation) for the complete list. ### Custom types -It's possible to create a public state for any types. For example, to create a public state for the following struct: +It's possible to create a public state for any types. Simply define methods that guide the PublicState wrapper in serialising the custom type to field(s) to store in the public state tree, and deserialising the field(s) retrieved from the tree back to the custom type. + +The methods should be implemented in a struct that conforms to the following interface: + +#include_code TypeSerialisationInterface /yarn-project/noir-libs/noir-aztec/src/types/type_serialisation.nr rust + +For example, to create a public state for the following type: #include_code state_vars-CustomStruct /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/types/queen.nr rust -First, define how to serialise and deserialise the struct. And then initialise the PublicState with it: +First, define how to serialise and deserialise the custom type: -#include_code state_vars-PublicStateCustomStruct /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage/queen.nr rust +#include_code state_vars-PublicStateCustomStruct /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/types/queen.nr rust + +And then initialise the PublicState with it: + +#include_code state_vars-PublicStateCustomStruct /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage.nr rust ### `.read` @@ -115,21 +131,21 @@ The interplay between a private state variable and its notes can be confusing. H ## `Singleton` -Singleton is a private state variable that is unique in a way. When a singleton is initialised, 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. +Singleton is a private state variable that is unique in a way. When a Singleton is initialised, 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. ### `::new` -Here we define a singleton for storing a Card note: +Here we define a Singleton for storing a Card note: -#include_code state_vars-Singleton /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage/legendary_card.nr rust +#include_code state_vars-Singleton /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage.nr rust ### `.initialise` -The initial value of a singleton is set via calling `initialise`: +The initial value of a Singleton is set via calling `initialise`: #include_code state_vars-SingletonInit /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/actions.nr rust -When this function is called, a nullifier of the storage slot is created, preventing this singleton from being initialised again. +When this function is called, a nullifier of the storage slot is created, preventing this Singleton from being initialised again. Unlike public states, which have a default initial value of `0` (or many zeros, in the case of a struct, array or map), a private state (of type `Singleton`, `ImmutableSingleton` or `Set`) does not have a default initial value. The `initialise` method (or `insert`, in the case of a `Set`) must be called. @@ -137,15 +153,15 @@ Unlike public states, which have a default initial value of `0` (or many zeros, The 'current value' of a `Singleton` state variable may be overwritten via the `.replace` method. -To modify the 'current value' of a singleton, we may create a new note (a Card in the following example) containing some new data, and replace the current note with it: +To modify the 'current value' of a Singleton, we may create a new note (a Card in the following example) containing some new data, and replace the current note with it: #include_code state_vars-SingletonReplace /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/actions.nr rust -This function will destroy the old note under the hood. If two people are trying to modify the singleton at the same time, only one will succeed. Developers should put in place appropriate access controls to avoid race conditions (unless a race is intended!). +This function will destroy the old note under the hood. If two people are trying to modify the Singleton at the same time, only one will succeed. Developers should put in place appropriate access controls to avoid race conditions (unless a race is intended!). ### `.get_note` -This function allows us to get the note of a singleton: +This function allows us to get the note of a Singleton: #include_code state_vars-SingletonGet /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/actions.nr rust @@ -153,21 +169,31 @@ However, it's possible that at the time this function is called, the system hasn ## `ImmutableSingleton` -Immutable singleton is unique and, as the name suggests, immutable. Once it has been initialised, its value can't be changed anymore. +ImmutableSingleton represents a unique private state variable that, as the name suggests, is immutable. Once initialized, its value cannot be altered. ### `::new` -#include_code state_vars-ImmutableSingleton /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage/game_rules.nr rust +In the following example, we define an ImmutableSingleton that utilises the `RulesMethods` struct as its underlying note type: + +#include_code state_vars-ImmutableSingleton /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage.nr rust ### `.initialise` +Set the initial value of an ImmutableSingleton by calling the `initialise` method: + #include_code state_vars-ImmutableSingletonInit /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/actions.nr rust +Once initialized, an ImmutableSingleton's value remains unchangeable. This method can only be called once. + ### `.get_note` +Use this method to retrieve the value of an initialized ImmutableSingleton: + #include_code state_vars-ImmutableSingletonGet /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/actions.nr rust -Unlike a [`singleton`](#get_note-1), when you call the `get_note` function on an immutable singleton to read the value, the current note is not destroyed in the background. This means that multiple accounts can call this function simultaneously. +Unlike a [`singleton`](#get_note-1), the `get_note` function for an ImmutableSingleton doesn't destroy the current note in the background. This means that multiple accounts can concurrently call this function to read the value. + +This function will throw if the ImmutableSingleton hasn't been initialised. ## `Set` @@ -175,9 +201,11 @@ Set is used for managing a collection of notes. All notes in a set are of the sa ### `::new` -In the following example, we define a set of cards: +The `new` method creates a Set that employs a specific note type. When a new Set is created, it initially contains no notes. + +In the following example, we define a set whose underlying note type is `Card`: -#include_code state_vars-Set /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage/cards.nr rust +#include_code state_vars-Set /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage.nr rust ### `.insert` @@ -259,7 +287,7 @@ The process of applying the options to get the final notes is not constrained. I The following declares a mapping from a `Field` to a `Singleton`: -#include_code state_vars-MapSingleton /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage/profiles.nr rust +#include_code state_vars-MapSingleton /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage.nr rust The second argument `|slot| Singleton::new(slot, ProfileMethods)` is a Noir closure function. It teaches this instance of `Map` how to create a new instance of a `Singleton` whenever the `.at` method is called to access a state variable at a particular mapping key. The `slot` argument will be derived when `.at` is called, based on the lookup key provided. @@ -271,4 +299,4 @@ The only api of a map is `.at`. It returns the underlying type that occupies a s #include_code state_vars-MapAtSingletonGet /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/actions.nr rust -In both code snippets, `state_var.at(account)` returns a singleton that is linked to the requested account. +In both code snippets, `state_var.at(account)` returns a Singleton that is linked to the requested account. diff --git a/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/actions.nr b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/actions.nr index 4dc6d8fa50c..fec14b56503 100644 --- a/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/actions.nr +++ b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/actions.nr @@ -18,13 +18,10 @@ use dep::aztec::types::type_serialisation::bool_serialisation::{ BOOL_SERIALISED_LEN, }; -use crate::storage::{ - queen::QUEEN_SERIALISED_LEN, -}; use crate::types::{ card::{Card, CARD_LEN}, profile::{Profile, PROFILE_LEN}, - queen::Queen, + queen::{Queen, QUEEN_SERIALISED_LEN}, rules::{Rules, RULES_LEN}, }; diff --git a/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage.nr b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage.nr index 0a0cce65b11..4af7320a3ff 100644 --- a/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage.nr +++ b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage.nr @@ -1,10 +1,3 @@ -mod cards; -mod game_rules; -mod legendary_card; -mod locked; -mod profiles; -mod queen; - use dep::aztec::state_vars::{ immutable_singleton::ImmutableSingleton, map::Map, @@ -12,19 +5,20 @@ use dep::aztec::state_vars::{ set::Set, singleton::Singleton, }; +// docs:start:state_vars-PublicStateBoolImport use dep::aztec::types::type_serialisation::bool_serialisation::{ BOOL_SERIALISED_LEN, + BoolSerialisationMethods, }; +// docs:end:state_vars-PublicStateBoolImport use crate::types::{ - card::{Card, CARD_LEN}, - profile::{Profile, PROFILE_LEN}, - queen::Queen, - rules::{Rules, RULES_LEN}, + card::{Card, CARD_LEN, CardMethods}, + profile::{Profile, PROFILE_LEN, ProfileMethods}, + queen::{Queen, QUEEN_SERIALISED_LEN, QueenSerialisationMethods}, + rules::{Rules, RULES_LEN, RulesMethods}, }; -use queen::QUEEN_SERIALISED_LEN; - // docs:start:storage-declaration struct Storage { locked: PublicState, @@ -35,16 +29,34 @@ struct Storage { profiles: Map>, } +// docs:start:state_vars-PublicState +// docs:start:state_vars-PublicStateCustomStruct +// docs:start:state_vars-Singleton +// docs:start:state_vars-ImmutableSingleton +// docs:start:state_vars-Set +// docs:start:state_vars-MapSingleton impl Storage { fn init() -> Self { Storage { - locked: locked::init(1), - queen: queen::init(2), - game_rules: game_rules::init(3), - legendary_card: legendary_card::init(4), - cards: cards::init(5), - profiles: profiles::init(6), + // highlight-next-line:state_vars-PublicState + locked: PublicState::new(1, BoolSerialisationMethods), + // highlight-next-line:state_vars-PublicStateCustomStruct + queen: PublicState::new(2, QueenSerialisationMethods), + // highlight-next-line:state_vars-ImmutableSingleton + game_rules: ImmutableSingleton::new(3, RulesMethods), + // highlight-next-line:state_vars-Singleton + legendary_card: Singleton::new(4, CardMethods), + // highlight-next-line:state_vars-Set + cards: Set::new(5, CardMethods), + // highlight-next-line:state_vars-MapSingleton + profiles: Map::new(6, |slot| Singleton::new(slot, ProfileMethods)), } } } +// docs:end:state_vars-PublicState +// docs:end:state_vars-PublicStateCustomStruct +// docs:end:state_vars-Singleton +// docs:end:state_vars-ImmutableSingleton +// docs:end:state_vars-Set +// docs:end:state_vars-MapSingleton // docs:end:storage-declaration \ No newline at end of file diff --git a/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage/cards.nr b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage/cards.nr deleted file mode 100644 index 62f5a517f25..00000000000 --- a/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage/cards.nr +++ /dev/null @@ -1,9 +0,0 @@ -// docs:start:state_vars-Set -use dep::aztec::state_vars::set::Set; - -use crate::types::card::{Card, CardMethods, CARD_LEN}; - -fn init(storage_slot: Field) -> Set { - Set::new(storage_slot, CardMethods) -} -// docs:end:state_vars-Set \ No newline at end of file diff --git a/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage/game_rules.nr b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage/game_rules.nr deleted file mode 100644 index f037aa0ad4e..00000000000 --- a/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage/game_rules.nr +++ /dev/null @@ -1,9 +0,0 @@ -// docs:start:state_vars-ImmutableSingleton -use dep::aztec::state_vars::immutable_singleton::ImmutableSingleton; - -use crate::types::rules::{Rules, RulesMethods, RULES_LEN}; - -fn init(storage_slot: Field) -> ImmutableSingleton { - ImmutableSingleton::new(storage_slot, RulesMethods) -} -// docs:end:state_vars-ImmutableSingleton \ No newline at end of file diff --git a/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage/legendary_card.nr b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage/legendary_card.nr deleted file mode 100644 index dc85dd8220e..00000000000 --- a/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage/legendary_card.nr +++ /dev/null @@ -1,9 +0,0 @@ -// docs:start:state_vars-Singleton -use dep::aztec::state_vars::singleton::Singleton; - -use crate::types::card::{Card, CardMethods, CARD_LEN}; - -fn init(storage_slot: Field) -> Singleton { - Singleton::new(storage_slot, CardMethods) -} -// docs:end:state_vars-Singleton \ No newline at end of file diff --git a/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage/locked.nr b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage/locked.nr deleted file mode 100644 index efe4b1fbdaf..00000000000 --- a/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage/locked.nr +++ /dev/null @@ -1,11 +0,0 @@ -// docs:start:state_vars-PublicState -use dep::aztec::state_vars::public_state::PublicState; -use dep::aztec::types::type_serialisation::bool_serialisation::{ - BoolSerialisationMethods, - BOOL_SERIALISED_LEN, -}; - -fn init(storage_slot: Field) -> PublicState { - PublicState::new(storage_slot, BoolSerialisationMethods) -} -// docs:end:state_vars-PublicState \ No newline at end of file diff --git a/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage/profiles.nr b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage/profiles.nr deleted file mode 100644 index 635bf1ab558..00000000000 --- a/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage/profiles.nr +++ /dev/null @@ -1,12 +0,0 @@ -// docs:start:state_vars-MapSingleton -use dep::aztec::state_vars::{ - map::Map, - singleton::Singleton, -}; - -use crate::types::profile::{Profile, ProfileMethods, PROFILE_LEN}; - -fn init(storage_slot: Field) -> Map> { - Map::new(storage_slot, |slot| Singleton::new(slot, ProfileMethods)) -} -// docs:end:state_vars-MapSingleton \ No newline at end of file diff --git a/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage/queen.nr b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage/queen.nr deleted file mode 100644 index 8d22dc26a38..00000000000 --- a/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage/queen.nr +++ /dev/null @@ -1,29 +0,0 @@ -use dep::aztec::state_vars::public_state::PublicState; -use dep::aztec::types::type_serialisation::TypeSerialisationInterface; - -use crate::types::queen::Queen; - -// docs:start:state_vars-PublicStateCustomStruct -global QUEEN_SERIALISED_LEN: Field = 2; - -fn deserialise(fields: [Field; QUEEN_SERIALISED_LEN]) -> Queen { - Queen { - account: fields[0], - points: fields[1] as u8, - } -} - -fn serialise(queen: Queen) -> [Field; QUEEN_SERIALISED_LEN] { - [queen.account, queen.points as Field] -} - -global QueenSerialisationMethods = TypeSerialisationInterface { - deserialise, - serialise, -}; - -fn init(storage_slot: Field) -> PublicState { - // highlight-next-line:state_vars-PublicStateCustomStruct - PublicState::new(storage_slot, QueenSerialisationMethods) -} -// docs:end:state_vars-PublicStateCustomStruct \ No newline at end of file diff --git a/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/types/queen.nr b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/types/queen.nr index 383a092269d..eb85a5d11f2 100644 --- a/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/types/queen.nr +++ b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/types/queen.nr @@ -1,6 +1,28 @@ +use dep::aztec::types::type_serialisation::TypeSerialisationInterface; + // docs:start:state_vars-CustomStruct struct Queen { account: Field, points: u8, } -// docs:end:state_vars-CustomStruct \ No newline at end of file +// docs:end:state_vars-CustomStruct + +// docs:start:state_vars-PublicStateCustomStruct +global QUEEN_SERIALISED_LEN: Field = 2; + +fn deserialise(fields: [Field; QUEEN_SERIALISED_LEN]) -> Queen { + Queen { + account: fields[0], + points: fields[1] as u8, + } +} + +fn serialise(queen: Queen) -> [Field; QUEEN_SERIALISED_LEN] { + [queen.account, queen.points as Field] +} + +global QueenSerialisationMethods = TypeSerialisationInterface { + deserialise, + serialise, +}; +// docs:end:state_vars-PublicStateCustomStruct \ No newline at end of file diff --git a/yarn-project/noir-libs/noir-aztec/src/types/type_serialisation.nr b/yarn-project/noir-libs/noir-aztec/src/types/type_serialisation.nr index dfe738d2c38..e1ccdde98f9 100644 --- a/yarn-project/noir-libs/noir-aztec/src/types/type_serialisation.nr +++ b/yarn-project/noir-libs/noir-aztec/src/types/type_serialisation.nr @@ -5,7 +5,9 @@ mod u32_serialisation; /** * Before Noir supports traits, a way of specifying the serialisation and deserialisation methods for a type. */ +// docs:start:TypeSerialisationInterface struct TypeSerialisationInterface { deserialise: fn ([Field; N]) -> T, serialise: fn (T) -> [Field; N], -} \ No newline at end of file +} +// docs:end:TypeSerialisationInterface \ No newline at end of file From e1a4822c7812308ac23b667b50b50035569d7d38 Mon Sep 17 00:00:00 2001 From: Leila Wang Date: Wed, 30 Aug 2023 17:51:11 +0000 Subject: [PATCH 5/7] Rename note types. --- .../dev_docs/contracts/state_variables.md | 14 ++--- .../docs_example_contract/src/actions.nr | 60 +++++++++---------- .../docs_example_contract/src/main.nr | 12 ++-- .../docs_example_contract/src/options.nr | 14 ++--- .../docs_example_contract/src/storage.nr | 22 +++---- .../docs_example_contract/src/types.nr | 6 +- .../src/types/{card.nr => card_note.nr} | 36 +++++------ .../src/types/{profile.nr => profile_note.nr} | 30 +++++----- .../src/types/{rules.nr => rules_note.nr} | 30 +++++----- 9 files changed, 112 insertions(+), 112 deletions(-) rename yarn-project/noir-contracts/src/contracts/docs_example_contract/src/types/{card.nr => card_note.nr} (70%) rename yarn-project/noir-contracts/src/contracts/docs_example_contract/src/types/{profile.nr => profile_note.nr} (64%) rename yarn-project/noir-contracts/src/contracts/docs_example_contract/src/types/{rules.nr => rules_note.nr} (65%) diff --git a/docs/docs/dev_docs/contracts/state_variables.md b/docs/docs/dev_docs/contracts/state_variables.md index 2b1d1317bb9..14f5b794f07 100644 --- a/docs/docs/dev_docs/contracts/state_variables.md +++ b/docs/docs/dev_docs/contracts/state_variables.md @@ -135,7 +135,7 @@ Singleton is a private state variable that is unique in a way. When a Singleton ### `::new` -Here we define a Singleton for storing a Card note: +Here we define a Singleton for storing a `CardNote`: #include_code state_vars-Singleton /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage.nr rust @@ -153,7 +153,7 @@ Unlike public states, which have a default initial value of `0` (or many zeros, The 'current value' of a `Singleton` state variable may be overwritten via the `.replace` method. -To modify the 'current value' of a Singleton, we may create a new note (a Card in the following example) containing some new data, and replace the current note with it: +To modify the 'current value' of a Singleton, we may create a new note (a `CardNote` in the following example) containing some new data, and replace the current note with it: #include_code state_vars-SingletonReplace /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/actions.nr rust @@ -203,7 +203,7 @@ Set is used for managing a collection of notes. All notes in a set are of the sa The `new` method creates a Set that employs a specific note type. When a new Set is created, it initially contains no notes. -In the following example, we define a set whose underlying note type is `Card`: +In the following example, we define a set whose underlying note type is `CardNote`: #include_code state_vars-Set /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage.nr rust @@ -243,17 +243,17 @@ For example, the following function outputs an instance of `NoteGetterOptions`, #include_code state_vars-NoteGetterOptionsSelectSortOffset /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/options.nr rust -The first value of `.select` and `.sort` is the index of a field in a note type. For the note type `Card` that has the following fields: +The first value of `.select` and `.sort` is the index of a field in a note type. For the note type `CardNote` that has the following fields: -#include_code state_vars-NoteCard /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/types/card.nr rust +#include_code state_vars-CardNote /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/types/card.nr rust The indices are: 0 for `points`, 1 for `secret`, and 2 for `owner`. In the previous example, -`.select(2, account_address)` matches the 2nd field of `Card`, which is `owner`, and returns the cards whose `owner` field equals `account_address`. +`.select(2, account_address)` matches the 2nd field of `CardNote`, which is `owner`, and returns the cards whose `owner` field equals `account_address`. -`.sort(0, SortOrder.DESC)` sorts the 0th field of `Card`, which is `points`, in descending order. +`.sort(0, SortOrder.DESC)` sorts the 0th field of `CardNote`, which is `points`, in descending order. There can be as many conditions as the number of fields a note type has. The following example finds cards whose fields match the three given values: diff --git a/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/actions.nr b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/actions.nr index fec14b56503..3438b3db518 100644 --- a/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/actions.nr +++ b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/actions.nr @@ -19,10 +19,10 @@ use dep::aztec::types::type_serialisation::bool_serialisation::{ }; use crate::types::{ - card::{Card, CARD_LEN}, - profile::{Profile, PROFILE_LEN}, + card_note::{CardNote, CARD_NOTE_LEN}, + profile_note::{ProfileNote, PROFILE_NOTE_LEN}, queen::{Queen, QUEEN_SERIALISED_LEN}, - rules::{Rules, RULES_LEN}, + rules_note::{RulesNote, RULES_NOTE_LEN}, }; // docs:start:state_vars-PublicStateRead @@ -78,8 +78,8 @@ fn add_points_to_queen( // docs:start:state_vars-SingletonInit fn init_legendary_card( context: &mut PrivateContext, - state_var: Singleton, - card: &mut Card, + state_var: Singleton, + card: &mut CardNote, ) { state_var.initialise(context, card); } @@ -88,8 +88,8 @@ fn init_legendary_card( // docs:start:state_vars-SingletonReplace fn update_legendary_card( context: &mut PrivateContext, - state_var: Singleton, - card: &mut Card, + state_var: Singleton, + card: &mut CardNote, ) { state_var.replace(context, card); } @@ -98,8 +98,8 @@ fn update_legendary_card( // docs:start:state_vars-SingletonGet fn get_legendary_card( context: &mut PrivateContext, - state_var: Singleton, -) -> Card { + state_var: Singleton, +) -> CardNote { state_var.get_note(context) } // docs:end:state_vars-SingletonGet @@ -107,8 +107,8 @@ fn get_legendary_card( // docs:start:state_vars-ImmutableSingletonInit fn init_game_rules( context: &mut PrivateContext, - state_var: ImmutableSingleton, - rules: &mut Rules, + state_var: ImmutableSingleton, + rules: &mut RulesNote, ) { state_var.initialise(context, rules); } @@ -117,8 +117,8 @@ fn init_game_rules( // docs:start:state_vars-ImmutableSingletonGet fn is_valid_card( context: &mut PrivateContext, - state_var: ImmutableSingleton, - card: Card, + state_var: ImmutableSingleton, + card: CardNote, ) -> bool { let rules = state_var.get_note(context); card.points >= rules.min_points & card.points <= rules.max_points @@ -128,8 +128,8 @@ fn is_valid_card( // docs:start:state_vars-SetInsert fn add_new_card( context: &mut PrivateContext, - state_var: Set, - card: &mut Card, + state_var: Set, + card: &mut CardNote, ) { state_var.insert(context, card); } @@ -138,8 +138,8 @@ fn add_new_card( // docs:start:state_vars-SetRemove fn remove_card( context: &mut PrivateContext, - state_var: Set, - card: Card, + state_var: Set, + card: CardNote, ) { state_var.remove(context, card); } @@ -148,23 +148,23 @@ fn remove_card( // docs:start:state_vars-SetGet fn get_cards( context: &mut PrivateContext, - state_var: Set, - options: NoteGetterOptions, -) -> [Option; MAX_READ_REQUESTS_PER_CALL] { + state_var: Set, + options: NoteGetterOptions, +) -> [Option; MAX_READ_REQUESTS_PER_CALL] { state_var.get_notes(context, options) } // docs:end:state_vars-SetGet // docs:start:state_vars-SetView unconstrained fn view_cards( - state_var: Set, - options: NoteViewerOptions, -) -> [Option; MAX_NOTES_PER_PAGE] { + state_var: Set, + options: NoteViewerOptions, +) -> [Option; MAX_NOTES_PER_PAGE] { state_var.view_notes(options) } // docs:end:state_vars-SetView -unconstrained fn get_total_points(state_var: Set, account: Field, offset: u32) -> u8 { +unconstrained fn get_total_points(state_var: Set, account: Field, offset: u32) -> u8 { let options = NoteViewerOptions::new().select(2, account).set_offset(offset); let mut total_points = 0; let notes = view_cards(state_var, options); @@ -182,8 +182,8 @@ unconstrained fn get_total_points(state_var: Set, account: Field // docs:start:state_vars-SetContains fn assert_contains_card( context: &mut PrivateContext, - state_var: Set, - card: Card, + state_var: Set, + card: CardNote, ) { state_var.assert_contains_and_remove(context, card); } @@ -192,9 +192,9 @@ fn assert_contains_card( // docs:start:state_vars-MapAtSingletonInit fn add_new_profile( context: &mut PrivateContext, - state_var: Map>, + state_var: Map>, account: Field, - profile: &mut Profile, + profile: &mut ProfileNote, ) { state_var.at(account).initialise(context, profile); } @@ -203,9 +203,9 @@ fn add_new_profile( // docs:start:state_vars-MapAtSingletonGet fn get_profile( context: &mut PrivateContext, - state_var: Map>, + state_var: Map>, account: Field, -) -> Profile { +) -> ProfileNote { state_var.at(account).get_note(context) } // docs:end:state_vars-MapAtSingletonGet \ No newline at end of file diff --git a/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/main.nr b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/main.nr index 05497201445..fbad7ff4243 100644 --- a/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/main.nr +++ b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/main.nr @@ -20,9 +20,9 @@ contract DocsExample { use crate::storage::Storage; // docs:end:storage-import use crate::types::{ - card::Card, + card_note::CardNote, queen::Queen, - rules::Rules, + rules_note::RulesNote, }; global REPLACE_QUEEN_FUNCTION_SELECTOR = 11111111; @@ -42,10 +42,10 @@ contract DocsExample { let storage = Storage::init(); - let mut game_rules = Rules::new(min_points, max_points); + let mut game_rules = RulesNote::new(min_points, max_points); actions::init_game_rules(&mut context, storage.game_rules, &mut game_rules); - let mut legendary_card = Card::new(0, legendary_card_secret, 0); + let mut legendary_card = CardNote::new(0, legendary_card_secret, 0); actions::init_legendary_card(&mut context, storage.legendary_card, &mut legendary_card); context.finish() @@ -123,7 +123,7 @@ contract DocsExample { let storage = Storage::init(); for i in 0..secrets.len() as u8 { - let mut card = Card::new(0, secrets[i], 0); + let mut card = CardNote::new(0, secrets[i], 0); actions::add_new_card(&mut context, storage.cards, &mut card); } @@ -140,7 +140,7 @@ contract DocsExample { let storage = Storage::init(); let owner = inputs.call_context.msg_sender; - let mut updated_card = Card::new(new_points, new_secret, owner); + let mut updated_card = CardNote::new(new_points, new_secret, owner); assert(actions::is_valid_card(&mut context, storage.game_rules, updated_card)); diff --git a/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/options.nr b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/options.nr index de2e9be966f..7019f58016b 100644 --- a/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/options.nr +++ b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/options.nr @@ -1,13 +1,13 @@ use dep::std::option::Option; use dep::aztec::constants_gen::MAX_READ_REQUESTS_PER_CALL; use dep::aztec::note::note_getter_options::{NoteGetterOptions, Sort, SortOrder}; -use crate::types::card::{Card, CARD_LEN}; +use crate::types::card_note::{CardNote, CARD_NOTE_LEN}; // docs:start:state_vars-NoteGetterOptionsSelectSortOffset fn create_account_card_getter_options( account_address: Field, offset: u32, -) -> NoteGetterOptions { +) -> NoteGetterOptions { NoteGetterOptions::new() .select(2, account_address) .sort(0, SortOrder.DESC) @@ -20,7 +20,7 @@ fn create_exact_card_getter_options( points: u8, secret: Field, account_address: Field, -) -> NoteGetterOptions { +) -> NoteGetterOptions { NoteGetterOptions::new() .select(0, points as Field) .select(1, secret) @@ -30,9 +30,9 @@ fn create_exact_card_getter_options( // docs:start:state_vars-OptionFilter fn filter_min_points( - cards: [Option; MAX_READ_REQUESTS_PER_CALL], + cards: [Option; MAX_READ_REQUESTS_PER_CALL], min_points: u8, -) -> [Option; MAX_READ_REQUESTS_PER_CALL] { +) -> [Option; MAX_READ_REQUESTS_PER_CALL] { let mut selected_cards = [Option::none(); MAX_READ_REQUESTS_PER_CALL]; let mut num_selected = 0; for i in 0..cards.len() { @@ -49,7 +49,7 @@ fn filter_min_points( fn create_account_cards_with_min_points_getter_options( account_address: Field, min_points: u8, -) -> NoteGetterOptions { +) -> NoteGetterOptions { NoteGetterOptions::with_filter(filter_min_points, min_points) .select(2, account_address) .sort(0, SortOrder.ASC) @@ -57,7 +57,7 @@ fn create_account_cards_with_min_points_getter_options( // docs:end:state_vars-NoteGetterOptionsFilter // docs:start:state_vars-NoteGetterOptionsPickOne -fn create_largest_account_card_getter_options(account_address: Field) -> NoteGetterOptions { +fn create_largest_account_card_getter_options(account_address: Field) -> NoteGetterOptions { NoteGetterOptions::new() .select(2, account_address) .sort(0, SortOrder.DESC) diff --git a/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage.nr b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage.nr index 4af7320a3ff..fc9ae590fd5 100644 --- a/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage.nr +++ b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/storage.nr @@ -13,20 +13,20 @@ use dep::aztec::types::type_serialisation::bool_serialisation::{ // docs:end:state_vars-PublicStateBoolImport use crate::types::{ - card::{Card, CARD_LEN, CardMethods}, - profile::{Profile, PROFILE_LEN, ProfileMethods}, + card_note::{CardNote, CARD_NOTE_LEN, CardNoteMethods}, + profile_note::{ProfileNote, PROFILE_NOTE_LEN, ProfileNoteMethods}, queen::{Queen, QUEEN_SERIALISED_LEN, QueenSerialisationMethods}, - rules::{Rules, RULES_LEN, RulesMethods}, + rules_note::{RulesNote, RULES_NOTE_LEN, RulesNoteMethods}, }; // docs:start:storage-declaration struct Storage { locked: PublicState, queen: PublicState, - game_rules: ImmutableSingleton, - legendary_card: Singleton, - cards: Set, - profiles: Map>, + game_rules: ImmutableSingleton, + legendary_card: Singleton, + cards: Set, + profiles: Map>, } // docs:start:state_vars-PublicState @@ -43,13 +43,13 @@ impl Storage { // highlight-next-line:state_vars-PublicStateCustomStruct queen: PublicState::new(2, QueenSerialisationMethods), // highlight-next-line:state_vars-ImmutableSingleton - game_rules: ImmutableSingleton::new(3, RulesMethods), + game_rules: ImmutableSingleton::new(3, RulesNoteMethods), // highlight-next-line:state_vars-Singleton - legendary_card: Singleton::new(4, CardMethods), + legendary_card: Singleton::new(4, CardNoteMethods), // highlight-next-line:state_vars-Set - cards: Set::new(5, CardMethods), + cards: Set::new(5, CardNoteMethods), // highlight-next-line:state_vars-MapSingleton - profiles: Map::new(6, |slot| Singleton::new(slot, ProfileMethods)), + profiles: Map::new(6, |slot| Singleton::new(slot, ProfileNoteMethods)), } } } diff --git a/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/types.nr b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/types.nr index 655418e559c..9e0784c92ca 100644 --- a/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/types.nr +++ b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/types.nr @@ -1,4 +1,4 @@ -mod card; -mod profile; +mod card_note; +mod profile_note; mod queen; -mod rules; \ No newline at end of file +mod rules_note; \ No newline at end of file diff --git a/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/types/card.nr b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/types/card_note.nr similarity index 70% rename from yarn-project/noir-contracts/src/contracts/docs_example_contract/src/types/card.nr rename to yarn-project/noir-contracts/src/contracts/docs_example_contract/src/types/card_note.nr index cbf1ccada80..51d2ec0f425 100644 --- a/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/types/card.nr +++ b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/types/card_note.nr @@ -8,20 +8,20 @@ use dep::aztec::oracle::{ get_public_key::get_public_key, }; -global CARD_LEN: Field = 3; +global CARD_NOTE_LEN: Field = 3; -// docs:start:state_vars-NoteCard -struct Card { +// docs:start:state_vars-CardNote +struct CardNote { points: u8, secret: Field, owner: Field, header: NoteHeader, } -// docs:end:state_vars-NoteCard +// docs:end:state_vars-CardNote -impl Card { +impl CardNote { fn new(points: u8, secret: Field, owner: Field) -> Self { - Card { + CardNote { points, secret, owner, @@ -29,12 +29,12 @@ impl Card { } } - fn serialise(self) -> [Field; CARD_LEN] { + fn serialise(self) -> [Field; CARD_NOTE_LEN] { [self.points as Field, self.secret, self.owner] } - fn deserialise(preimage: [Field; CARD_LEN]) -> Self { - Card { + fn deserialise(preimage: [Field; CARD_NOTE_LEN]) -> Self { + CardNote { points: preimage[0] as u8, secret: preimage[1], owner: preimage[2], @@ -51,7 +51,7 @@ impl Card { } fn compute_nullifier(self) -> Field { - let note_hash_for_nullify = compute_note_hash_for_read_or_nullify(CardMethods, self); + let note_hash_for_nullify = compute_note_hash_for_read_or_nullify(CardNoteMethods, self); let owner_nullifying_public_key = get_public_key(self.owner); // TODO: get_secret_key should just accept an address // TODO! @@ -67,31 +67,31 @@ impl Card { } } -fn deserialise(preimage: [Field; CARD_LEN]) -> Card { - Card::deserialise(preimage) +fn deserialise(preimage: [Field; CARD_NOTE_LEN]) -> CardNote { + CardNote::deserialise(preimage) } -fn serialise(note: Card) -> [Field; CARD_LEN] { +fn serialise(note: CardNote) -> [Field; CARD_NOTE_LEN] { note.serialise() } -fn compute_note_hash(note: Card) -> Field { +fn compute_note_hash(note: CardNote) -> Field { note.compute_note_hash() } -fn compute_nullifier(note: Card) -> Field { +fn compute_nullifier(note: CardNote) -> Field { note.compute_nullifier() } -fn get_header(note: Card) -> NoteHeader { +fn get_header(note: CardNote) -> NoteHeader { note.header } -fn set_header(note: &mut Card, header: NoteHeader) { +fn set_header(note: &mut CardNote, header: NoteHeader) { note.set_header(header) } -global CardMethods = NoteInterface { +global CardNoteMethods = NoteInterface { deserialise, serialise, compute_note_hash, diff --git a/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/types/profile.nr b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/types/profile_note.nr similarity index 64% rename from yarn-project/noir-contracts/src/contracts/docs_example_contract/src/types/profile.nr rename to yarn-project/noir-contracts/src/contracts/docs_example_contract/src/types/profile_note.nr index a0e702ab799..8f42663fe9e 100644 --- a/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/types/profile.nr +++ b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/types/profile_note.nr @@ -8,29 +8,29 @@ use dep::aztec::oracle::{ get_public_key::get_public_key, }; -global PROFILE_LEN: Field = 2; +global PROFILE_NOTE_LEN: Field = 2; -struct Profile { +struct ProfileNote { avatar: Field, xp: Field, header: NoteHeader, } -impl Profile { +impl ProfileNote { fn new(avatar: Field, xp: Field) -> Self { - Profile { + ProfileNote { avatar, xp, header: NoteHeader::empty(), } } - fn serialise(self) -> [Field; PROFILE_LEN] { + fn serialise(self) -> [Field; PROFILE_NOTE_LEN] { [self.avatar, self.xp] } - fn deserialise(preimage: [Field; PROFILE_LEN]) -> Self { - Profile { + fn deserialise(preimage: [Field; PROFILE_NOTE_LEN]) -> Self { + ProfileNote { avatar: preimage[1], xp: preimage[0], header: NoteHeader::empty(), @@ -54,31 +54,31 @@ impl Profile { } } -fn deserialise(preimage: [Field; PROFILE_LEN]) -> Profile { - Profile::deserialise(preimage) +fn deserialise(preimage: [Field; PROFILE_NOTE_LEN]) -> ProfileNote { + ProfileNote::deserialise(preimage) } -fn serialise(note: Profile) -> [Field; PROFILE_LEN] { +fn serialise(note: ProfileNote) -> [Field; PROFILE_NOTE_LEN] { note.serialise() } -fn compute_note_hash(note: Profile) -> Field { +fn compute_note_hash(note: ProfileNote) -> Field { note.compute_note_hash() } -fn compute_nullifier(note: Profile) -> Field { +fn compute_nullifier(note: ProfileNote) -> Field { note.compute_nullifier() } -fn get_header(note: Profile) -> NoteHeader { +fn get_header(note: ProfileNote) -> NoteHeader { note.header } -fn set_header(note: &mut Profile, header: NoteHeader) { +fn set_header(note: &mut ProfileNote, header: NoteHeader) { note.set_header(header) } -global ProfileMethods = NoteInterface { +global ProfileNoteMethods = NoteInterface { deserialise, serialise, compute_note_hash, diff --git a/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/types/rules.nr b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/types/rules_note.nr similarity index 65% rename from yarn-project/noir-contracts/src/contracts/docs_example_contract/src/types/rules.nr rename to yarn-project/noir-contracts/src/contracts/docs_example_contract/src/types/rules_note.nr index 4c9565e5383..c204842484e 100644 --- a/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/types/rules.nr +++ b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/types/rules_note.nr @@ -4,29 +4,29 @@ use dep::aztec::note::{ utils::compute_note_hash_for_read_or_nullify, }; -global RULES_LEN: Field = 2; +global RULES_NOTE_LEN: Field = 2; -struct Rules { +struct RulesNote { min_points: u8, max_points: u8, header: NoteHeader, } -impl Rules { +impl RulesNote { fn new(min_points: u8, max_points: u8) -> Self { - Rules { + RulesNote { min_points, max_points, header: NoteHeader::empty(), } } - fn serialise(self) -> [Field; RULES_LEN] { + fn serialise(self) -> [Field; RULES_NOTE_LEN] { [self.min_points as Field, self.max_points as Field] } - fn deserialise(preimage: [Field; RULES_LEN]) -> Self { - Rules { + fn deserialise(preimage: [Field; RULES_NOTE_LEN]) -> Self { + RulesNote { min_points: preimage[0] as u8, max_points: preimage[1] as u8, header: NoteHeader::empty(), @@ -50,31 +50,31 @@ impl Rules { } } -fn deserialise(preimage: [Field; RULES_LEN]) -> Rules { - Rules::deserialise(preimage) +fn deserialise(preimage: [Field; RULES_NOTE_LEN]) -> RulesNote { + RulesNote::deserialise(preimage) } -fn serialise(note: Rules) -> [Field; RULES_LEN] { +fn serialise(note: RulesNote) -> [Field; RULES_NOTE_LEN] { note.serialise() } -fn compute_note_hash(note: Rules) -> Field { +fn compute_note_hash(note: RulesNote) -> Field { note.compute_note_hash() } -fn compute_nullifier(note: Rules) -> Field { +fn compute_nullifier(note: RulesNote) -> Field { note.compute_nullifier() } -fn get_header(note: Rules) -> NoteHeader { +fn get_header(note: RulesNote) -> NoteHeader { note.header } -fn set_header(note: &mut Rules, header: NoteHeader) { +fn set_header(note: &mut RulesNote, header: NoteHeader) { note.set_header(header) } -global RulesMethods = NoteInterface { +global RulesNoteMethods = NoteInterface { deserialise, serialise, compute_note_hash, From 77d69d8117c96579f1318c05e20b7534bdc46551 Mon Sep 17 00:00:00 2001 From: Leila Wang Date: Wed, 30 Aug 2023 18:02:20 +0000 Subject: [PATCH 6/7] Fix. --- .../contracts/docs_example_contract/src/types/card_note.nr | 6 +----- .../docs_example_contract/src/types/profile_note.nr | 4 ---- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/types/card_note.nr b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/types/card_note.nr index 51d2ec0f425..9d999c8aa7c 100644 --- a/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/types/card_note.nr +++ b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/types/card_note.nr @@ -5,7 +5,6 @@ use dep::aztec::note::{ }; use dep::aztec::oracle::{ get_secret_key::get_secret_key, - get_public_key::get_public_key, }; global CARD_NOTE_LEN: Field = 3; @@ -52,10 +51,7 @@ impl CardNote { fn compute_nullifier(self) -> Field { let note_hash_for_nullify = compute_note_hash_for_read_or_nullify(CardNoteMethods, self); - let owner_nullifying_public_key = get_public_key(self.owner); - // TODO: get_secret_key should just accept an address - // TODO! - let secret = get_secret_key(owner_nullifying_public_key); + let secret = get_secret_key(self.owner); dep::std::hash::pedersen([ note_hash_for_nullify, secret, diff --git a/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/types/profile_note.nr b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/types/profile_note.nr index 8f42663fe9e..9e8436e62c6 100644 --- a/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/types/profile_note.nr +++ b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/types/profile_note.nr @@ -3,10 +3,6 @@ use dep::aztec::note::{ note_interface::NoteInterface, utils::compute_note_hash_for_read_or_nullify, }; -use dep::aztec::oracle::{ - get_secret_key::get_secret_key, - get_public_key::get_public_key, -}; global PROFILE_NOTE_LEN: Field = 2; From f78d59184cc5f94e98526882c070b5ecfe0b17af Mon Sep 17 00:00:00 2001 From: Leila Wang Date: Thu, 31 Aug 2023 14:41:05 +0000 Subject: [PATCH 7/7] Use macros. --- .../docs_example_contract/src/main.nr | 90 +++++-------------- 1 file changed, 22 insertions(+), 68 deletions(-) diff --git a/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/main.nr b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/main.nr index fbad7ff4243..b29ed9b35a1 100644 --- a/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/main.nr +++ b/yarn-project/noir-contracts/src/contracts/docs_example_contract/src/main.nr @@ -5,14 +5,6 @@ mod storage; mod types; contract DocsExample { - use dep::aztec::abi; - use dep::aztec::abi::{PrivateCircuitPublicInputs, PrivateContextInputs, PublicContextInputs}; - use dep::aztec::constants_gen::MAX_READ_REQUESTS_PER_CALL; - use dep::aztec::context::{ - PrivateContext, - PublicContext, - }; - use crate::account_contract_interface::AccountContractInterface; use crate::actions; use crate::options::{create_account_card_getter_options, create_largest_account_card_getter_options}; @@ -28,18 +20,12 @@ contract DocsExample { global REPLACE_QUEEN_FUNCTION_SELECTOR = 11111111; global GET_POINTS_OF_COMMON_CARD_FUNCTION_SELECTOR = 11111111; + #[aztec(private)] fn constructor( - inputs: PrivateContextInputs, min_points: u8, max_points: u8, legendary_card_secret: Field, - ) -> distinct pub PrivateCircuitPublicInputs { - let mut context = PrivateContext::new(inputs, abi::hash_args([ - min_points as Field, - max_points as Field, - legendary_card_secret, - ])); - + ) { let storage = Storage::init(); let mut game_rules = RulesNote::new(min_points, max_points); @@ -47,39 +33,30 @@ contract DocsExample { let mut legendary_card = CardNote::new(0, legendary_card_secret, 0); actions::init_legendary_card(&mut context, storage.legendary_card, &mut legendary_card); - - context.finish() } // docs:start:storage-init - open fn lock(inputs: PublicContextInputs) -> pub abi::PublicCircuitPublicInputs { - let context = PublicContext::new(inputs, 0); - + #[aztec(public)] + fn lock() { // highlight-next-line:storage-init let storage = Storage::init(); storage.locked.write(true); - - context.finish() } // docs:end:storage-init // docs:start:functions-OpenFunction - open fn unlock(inputs: PublicContextInputs) -> pub abi::PublicCircuitPublicInputs { - let context = PublicContext::new(inputs, 0); - + #[aztec(public)] + fn unlock() { let storage = Storage::init(); actions::unlock(storage.locked); - - context.finish() } // docs:end:functions-OpenFunction - open fn replace_queen( - inputs: PublicContextInputs, + #[aztec(public)] + fn replace_queen( account: Field, points: u8, - ) -> pub abi::PublicCircuitPublicInputs { - let context = PublicContext::new(inputs, abi::hash_args([account, points as Field])); + ) { let storage = Storage::init(); let new_queen = Queen { account, points }; @@ -87,15 +64,11 @@ contract DocsExample { assert(actions::can_replace_queen(storage.queen, new_queen)); actions::replace_queen(storage.queen, new_queen); - - context.finish() } // docs:start:state_vars-PublicStateWriteBeforeCall - open fn replace_queen_unsafe( - inputs: PublicContextInputs, - ) -> pub abi::PublicCircuitPublicInputs { - let context = PublicContext::new(inputs, 0); + #[aztec(public)] + fn replace_queen_unsafe() { let storage = Storage::init(); let account = context.msg_sender(); @@ -109,34 +82,26 @@ contract DocsExample { let new_queen = Queen { account, points }; storage.queen.write(new_queen); - - context.finish() } // docs:end:state_vars-PublicStateWriteBeforeCall // docs:start:functions-SecretFunction - fn add_common_cards( - inputs: PrivateContextInputs, - secrets: [Field; 4], - ) -> distinct pub PrivateCircuitPublicInputs { - let mut context = PrivateContext::new(inputs, abi::hash_args(secrets)); + #[aztec(private)] + fn add_common_cards(secrets: [Field; 4]) { let storage = Storage::init(); for i in 0..secrets.len() as u8 { let mut card = CardNote::new(0, secrets[i], 0); actions::add_new_card(&mut context, storage.cards, &mut card); } - - context.finish() } // docs:end:functions-SecretFunction + #[aztec(private)] fn update_legendary_card( - inputs: PrivateContextInputs, new_points: u8, new_secret: Field, - ) -> distinct pub PrivateCircuitPublicInputs { - let mut context = PrivateContext::new(inputs, abi::hash_args([new_secret])); + ) { let storage = Storage::init(); let owner = inputs.call_context.msg_sender; @@ -145,12 +110,10 @@ contract DocsExample { assert(actions::is_valid_card(&mut context, storage.game_rules, updated_card)); actions::update_legendary_card(&mut context, storage.legendary_card, &mut updated_card); - - context.finish() } - fn become_queen(inputs: PrivateContextInputs) -> distinct pub PrivateCircuitPublicInputs { - let mut context = PrivateContext::new(inputs, 0); + #[aztec(private)] + fn become_queen() { let storage = Storage::init(); let legendary_card = actions::get_legendary_card(&mut context, storage.legendary_card); @@ -168,22 +131,19 @@ contract DocsExample { REPLACE_QUEEN_FUNCTION_SELECTOR, [owner, total_points as Field] ); - - context.finish() } + #[aztec(private)] fn get_points_of_common_cards( - inputs: PrivateContextInputs, account: Field, offset: u32, - ) -> distinct pub PrivateCircuitPublicInputs { - let mut context = PrivateContext::new(inputs, abi::hash_args([account, offset as Field])); + ) { let storage = Storage::init(); let mut total_points = 0; let options = create_account_card_getter_options(account, offset); let cards = actions::get_cards(&mut context, storage.cards, options); - for i in 0..MAX_READ_REQUESTS_PER_CALL { + for i in 0..cards.len() { if (cards[i].is_some()) { let card = cards[i].unwrap_unchecked(); assert(card.owner == account); @@ -192,15 +152,11 @@ contract DocsExample { } context.return_values.push(total_points as Field); - - context.finish() } // docs:start:state_vars-check_return_notes - fn discard_largest_card( - inputs: PrivateContextInputs, - ) -> distinct pub PrivateCircuitPublicInputs { - let mut context = PrivateContext::new(inputs, 0); + #[aztec(private)] + fn discard_largest_card() { let storage = Storage::init(); let account = context.msg_sender(); @@ -210,8 +166,6 @@ contract DocsExample { assert(card.owner == account); actions::remove_card(&mut context, storage.cards, card); - - context.finish() } // docs:end:state_vars-check_return_notes