Skip to content

Commit

Permalink
feat(aztec_noir): abstract storage initialisation (#2406)
Browse files Browse the repository at this point in the history
After a discussion with lasse, decided to get this over the line,
removes the storage init and places it inside the macro expansion

todo:
-  update docs
- release noir

related work:
- noir-lang/noir#2750


fixes: #2163
fixes: #1890
  • Loading branch information
Maddiaa0 authored Sep 26, 2023
1 parent 51352ae commit 974b037
Show file tree
Hide file tree
Showing 26 changed files with 184 additions and 233 deletions.
8 changes: 8 additions & 0 deletions docs/docs/dev_docs/contracts/syntax/functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,14 @@ Each Aztec function has access to a [context](./context.mdx) object. This object
As previously mentioned we use the kernel to pass information between circuits. This means that the return values of functions must also be passed to the kernel (where they can be later passed on to another function).
We achieve this by pushing return values to the execution context, which we then pass to the kernel.

**Making the contract's storage available**
#include_code storage-example-context /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/main.nr rust

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

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


**Returning the function context to the kernel.**
#include_code context-example-finish /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/main.nr rust

Expand Down
9 changes: 1 addition & 8 deletions docs/docs/dev_docs/contracts/syntax/storage.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,7 @@ impl Storage {
}
```

To use storage in functions, e.g., functions where you would read or write storage, you need to initialize the struct first, and then you can read and write afterwards. Below are snippets for initializing in private and public functions.

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

:::info
https://github.com/AztecProtocol/aztec-packages/pull/2406 is removing the need to explicitly initialize the storage in each function before reading or writing. This will be updated in the docs once the PR is merged.
:::
If you have defined a `Storage` struct following this naming scheme, then it will be made available to you through the reserved `storage` keyword within your contract functions.

:::warning Using slot `0` is not supported!
No storage values should be initialized at slot `0`. This is a known issue that will be fixed in the future.
Expand Down
15 changes: 2 additions & 13 deletions docs/docs/dev_docs/tutorials/writing_token_contract.md
Original file line number Diff line number Diff line change
Expand Up @@ -293,13 +293,8 @@ Public functions are declared with the `#[aztec(public)]` macro above the functi

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

Every public function initializes storage using the public context like so:

```rust
let storage = Storage::init(Context::public(&mut context));
```

After this, storage is referenced as `storage.variable`. We won't go over this step in any of the following function descriptions.
Storage is referenced as `storage.variable`.

#### `set_admin`

Expand Down Expand Up @@ -374,13 +369,7 @@ Private functions are declared with the `#[aztec(private)]` macro above the func

As described in the [execution contexts section above](#execution-contexts), private function logic and transaction information is hidden from the world and is executed on user devices. Private functions update private state, but can pass data to the public execution context (e.g. see the [`unshield`](#unshield) function).

Every private function initializes storage using the private context like so:

```rust
let storage = Storage::init(Context::private(&mut context));
```

After this, storage is referenced as `storage.variable`. We won't go over this step in any of the following function descriptions.
Storage is referenced as `storage.variable`.

#### `redeem_shield`

Expand Down
10 changes: 5 additions & 5 deletions yarn-project/aztec.js/src/abis/ecdsa_account_contract.json

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions yarn-project/aztec.js/src/abis/schnorr_account_contract.json

Large diffs are not rendered by default.

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions yarn-project/boxes/private-token/src/contracts/src/main.nr
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ contract PrivateToken {
initial_supply: Field,
owner: Field
) {
let storage = Storage::init(Context::private(&mut context));

// Insert new note to a set of user notes and emit the newly created encrypted note preimage via oracle call.
let owner_balance = storage.balances.at(owner);
if (initial_supply != 0) {
Expand All @@ -53,7 +53,7 @@ contract PrivateToken {
amount: Field,
owner: Field
) {
let storage = Storage::init(Context::private(&mut context));


// Insert new note to a set of user notes and emit the newly created encrypted note preimage via oracle call.
let owner_balance = storage.balances.at(owner);
Expand All @@ -66,7 +66,7 @@ contract PrivateToken {
amount: Field,
recipient: Field,
) {
let storage = Storage::init(Context::private(&mut context));

let sender = context.msg_sender();

// Pick from the set of sender's notes to spend amount.
Expand All @@ -82,7 +82,7 @@ contract PrivateToken {
unconstrained fn getBalance(
owner: Field,
) -> Field {
let storage = Storage::init(Context::none());


// Get the set of notes owned by the user.
let owner_balance = storage.balances.at(owner);
Expand Down
4 changes: 2 additions & 2 deletions yarn-project/noir-compiler/src/noir-version.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"tag": "0.11.1-aztec.0",
"commit": "2103b2ffb640fe457b24be09b6d63fe6ee1c6ac1"
"tag": "0.12.0-aztec.0",
"commit": "3b43709ddd5feffdbbc1723ba7dbcf63531aae05"
}
Original file line number Diff line number Diff line change
@@ -1,72 +1,16 @@
mod cards;
mod game;

use dep::aztec::{
context::{PrivateContext, PublicContext, Context},
state_vars::{
map::Map,
public_state::PublicState,
},
};

use dep::std::option::Option;

use cards::{Deck};
use game::{Game, GameSerializationMethods, GAME_SERIALIZED_LEN};

struct Storage {
collections: Map<Deck>,
game_decks: Map<Map<Deck>>,
games: Map<PublicState<Game, GAME_SERIALIZED_LEN>>,
}
contract CardGame {
use dep::aztec::{
context::Context,
state_vars::{
map::Map,
public_state::PublicState,
},
};

impl Storage {
fn init(
context: Context,
) -> Self {
Storage {
collections: Map::new(
context,
1,
|context, slot| {
Deck::new(
context,
slot,
)
},
),
game_decks: Map::new(
context,
2,
|context, slot| {
Map::new(
context,
slot,
|context, slot|{
Deck::new(
context,
slot,
)
}
)
},
),
games: Map::new(
context,
3,
|context, slot| {
PublicState::new(
context,
slot,
GameSerializationMethods,
)
},
)
}
}
}

contract CardGame {
use dep::std::option::Option;
use dep::value_note::{
balance_utils,
Expand All @@ -82,15 +26,13 @@ contract CardGame {
abi::{
Hasher, PrivateContextInputs,
},
context::{PrivateContext, Context},
note::{
note_header::NoteHeader,
utils as note_utils,
},
oracle::compute_selector::compute_selector
};

use crate::Storage;
use crate::cards::{
PACK_CARDS,
Deck,
Expand All @@ -103,8 +45,62 @@ contract CardGame {
NUMBER_OF_CARDS_DECK,
PLAYABLE_CARDS,
PlayerEntry,
Game
Game,
GameSerializationMethods,
GAME_SERIALIZED_LEN
};

struct Storage {
collections: Map<Deck>,
game_decks: Map<Map<Deck>>,
games: Map<PublicState<Game, GAME_SERIALIZED_LEN>>,
}

impl Storage {
fn init(
context: Context,
) -> pub Self {
Storage {
collections: Map::new(
context,
1,
|context, slot| {
Deck::new(
context,
slot,
)
},
),
game_decks: Map::new(
context,
2,
|context, slot| {
Map::new(
context,
slot,
|context, slot|{
Deck::new(
context,
slot,
)
}
)
},
),
games: Map::new(
context,
3,
|context, slot| {
PublicState::new(
context,
slot,
GameSerializationMethods,
)
},
)
}
}
}

#[aztec(private)]
fn constructor() {}
Expand All @@ -113,7 +109,7 @@ contract CardGame {
fn buy_pack(
seed: Field, // The randomness used to generate the cards. Passed in for now.
) {
let storage = Storage::init(Context::private(&mut context));

let buyer = context.msg_sender();
let mut cards = get_pack_cards(seed, buyer);

Expand All @@ -127,7 +123,7 @@ contract CardGame {
cards_fields: [Field; 2],
) {
let cards = cards_fields.map(|card_field| Card::from_field(card_field));
let storage = Storage::init(Context::private(&mut context));

let player = context.msg_sender();

let mut collection = storage.collections.at(player);
Expand All @@ -145,7 +141,7 @@ contract CardGame {
player: Field,
deck_strength: u32,
) {
let storage = Storage::init(Context::public(&mut context));

let game_storage = storage.games.at(game as Field);

let mut game_data = game_storage.read();
Expand All @@ -156,7 +152,7 @@ contract CardGame {

#[aztec(public)]
fn start_game(game: u32) {
let storage = Storage::init(Context::public(&mut context));

let game_storage = storage.games.at(game as Field);

let mut game_data = game_storage.read();
Expand All @@ -169,7 +165,7 @@ contract CardGame {
game: u32,
card: Card,
) {
let storage = Storage::init(Context::private(&mut context));

let player = context.msg_sender();

let mut game_deck = storage.game_decks.at(game as Field).at(player);
Expand All @@ -181,7 +177,7 @@ contract CardGame {

#[aztec(public)]
internal fn on_card_played(game: u32, player: Field, card_as_field: Field) {
let storage = Storage::init(Context::public(&mut context));

let game_storage = storage.games.at(game as Field);

let mut game_data = game_storage.read();
Expand All @@ -199,7 +195,7 @@ contract CardGame {
game: u32,
cards_fields: [Field; PLAYABLE_CARDS],
) {
let storage = Storage::init(Context::private(&mut context));

let player = context.msg_sender();
let cards = cards_fields.map(|card_field| Card::from_field(card_field));

Expand All @@ -216,7 +212,7 @@ contract CardGame {

#[aztec(public)]
internal fn on_cards_claimed(game: u32, player: Field, cards_hash: Field) {
let storage = Storage::init(Context::public(&mut context));

let game_storage = storage.games.at(game as Field);
let mut game_data = game_storage.read();

Expand All @@ -235,21 +231,21 @@ contract CardGame {
}

unconstrained fn view_collection_cards(owner: Field, offset: u32) -> [Option<Card>; MAX_NOTES_PER_PAGE] {
let storage = Storage::init(Context::none());

let collection = storage.collections.at(owner);

collection.view_cards(offset)
}

unconstrained fn view_game_cards(game: u32, player: Field, offset: u32) -> [Option<Card>; MAX_NOTES_PER_PAGE] {
let storage = Storage::init(Context::none());

let game_deck = storage.game_decks.at(game as Field).at(player);

game_deck.view_cards(offset)
}

unconstrained fn view_game(game: u32) -> Game {
Storage::init(Context::none()).games.at(game as Field).read()
storage.games.at(game as Field).read()
}

// Computes note hash and nullifier.
Expand Down
Loading

0 comments on commit 974b037

Please sign in to comment.