Skip to content

Commit

Permalink
Merge branch 'master' into dan/auto-copy-box-fix
Browse files Browse the repository at this point in the history
  • Loading branch information
dan-aztec authored Oct 9, 2023
2 parents 6c19a4f + a7710f0 commit c636548
Show file tree
Hide file tree
Showing 87 changed files with 495 additions and 452 deletions.
19 changes: 19 additions & 0 deletions barretenberg/cpp/CMakePresets.json
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,15 @@
}
},
{
"name": "gcc13",
"displayName": "Build with GCC-13",
"description": "Build with globally installed GCC-13",
"inherits": "default",
"environment": {
"CC": "gcc-13",
"CXX": "g++-13"
}
}, {
"name": "bench",
"displayName": "Build benchmarks",
"description": "Build default preset but with a special benchmark directory",
Expand Down Expand Up @@ -212,6 +221,11 @@
"inherits": "default",
"configurePreset": "gcc10"
},
{
"name": "gcc13",
"inherits": "default",
"configurePreset": "gcc13"
},
{
"name": "bench",
"inherits": "clang16",
Expand Down Expand Up @@ -297,6 +311,11 @@
"inherits": "default",
"configurePreset": "gcc10"
},
{
"name": "gcc13",
"inherits": "default",
"configurePreset": "gcc13"
},
{
"name": "bench",
"inherits": "clang16",
Expand Down
64 changes: 32 additions & 32 deletions barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -712,7 +712,7 @@ struct BrilligOutputs {
static BrilligOutputs bincodeDeserialize(std::vector<uint8_t>);
};

struct ForeignCallOutput {
struct ForeignCallParam {

struct Single {
Circuit::Value value;
Expand All @@ -732,13 +732,13 @@ struct ForeignCallOutput {

std::variant<Single, Array> value;

friend bool operator==(const ForeignCallOutput&, const ForeignCallOutput&);
friend bool operator==(const ForeignCallParam&, const ForeignCallParam&);
std::vector<uint8_t> bincodeSerialize() const;
static ForeignCallOutput bincodeDeserialize(std::vector<uint8_t>);
static ForeignCallParam bincodeDeserialize(std::vector<uint8_t>);
};

struct ForeignCallResult {
std::vector<Circuit::ForeignCallOutput> values;
std::vector<Circuit::ForeignCallParam> values;

friend bool operator==(const ForeignCallResult&, const ForeignCallResult&);
std::vector<uint8_t> bincodeSerialize() const;
Expand Down Expand Up @@ -4758,25 +4758,25 @@ Circuit::Expression serde::Deserializable<Circuit::Expression>::deserialize(Dese

namespace Circuit {

inline bool operator==(const ForeignCallOutput& lhs, const ForeignCallOutput& rhs)
inline bool operator==(const ForeignCallParam& lhs, const ForeignCallParam& rhs)
{
if (!(lhs.value == rhs.value)) {
return false;
}
return true;
}

inline std::vector<uint8_t> ForeignCallOutput::bincodeSerialize() const
inline std::vector<uint8_t> ForeignCallParam::bincodeSerialize() const
{
auto serializer = serde::BincodeSerializer();
serde::Serializable<ForeignCallOutput>::serialize(*this, serializer);
serde::Serializable<ForeignCallParam>::serialize(*this, serializer);
return std::move(serializer).bytes();
}

inline ForeignCallOutput ForeignCallOutput::bincodeDeserialize(std::vector<uint8_t> input)
inline ForeignCallParam ForeignCallParam::bincodeDeserialize(std::vector<uint8_t> input)
{
auto deserializer = serde::BincodeDeserializer(input);
auto value = serde::Deserializable<ForeignCallOutput>::deserialize(deserializer);
auto value = serde::Deserializable<ForeignCallParam>::deserialize(deserializer);
if (deserializer.get_buffer_offset() < input.size()) {
throw_or_abort("Some input bytes were not read");
}
Expand All @@ -4787,8 +4787,8 @@ inline ForeignCallOutput ForeignCallOutput::bincodeDeserialize(std::vector<uint8

template <>
template <typename Serializer>
void serde::Serializable<Circuit::ForeignCallOutput>::serialize(const Circuit::ForeignCallOutput& obj,
Serializer& serializer)
void serde::Serializable<Circuit::ForeignCallParam>::serialize(const Circuit::ForeignCallParam& obj,
Serializer& serializer)
{
serializer.increase_container_depth();
serde::Serializable<decltype(obj.value)>::serialize(obj.value, serializer);
Expand All @@ -4797,36 +4797,36 @@ void serde::Serializable<Circuit::ForeignCallOutput>::serialize(const Circuit::F

template <>
template <typename Deserializer>
Circuit::ForeignCallOutput serde::Deserializable<Circuit::ForeignCallOutput>::deserialize(Deserializer& deserializer)
Circuit::ForeignCallParam serde::Deserializable<Circuit::ForeignCallParam>::deserialize(Deserializer& deserializer)
{
deserializer.increase_container_depth();
Circuit::ForeignCallOutput obj;
Circuit::ForeignCallParam obj;
obj.value = serde::Deserializable<decltype(obj.value)>::deserialize(deserializer);
deserializer.decrease_container_depth();
return obj;
}

namespace Circuit {

inline bool operator==(const ForeignCallOutput::Single& lhs, const ForeignCallOutput::Single& rhs)
inline bool operator==(const ForeignCallParam::Single& lhs, const ForeignCallParam::Single& rhs)
{
if (!(lhs.value == rhs.value)) {
return false;
}
return true;
}

inline std::vector<uint8_t> ForeignCallOutput::Single::bincodeSerialize() const
inline std::vector<uint8_t> ForeignCallParam::Single::bincodeSerialize() const
{
auto serializer = serde::BincodeSerializer();
serde::Serializable<ForeignCallOutput::Single>::serialize(*this, serializer);
serde::Serializable<ForeignCallParam::Single>::serialize(*this, serializer);
return std::move(serializer).bytes();
}

inline ForeignCallOutput::Single ForeignCallOutput::Single::bincodeDeserialize(std::vector<uint8_t> input)
inline ForeignCallParam::Single ForeignCallParam::Single::bincodeDeserialize(std::vector<uint8_t> input)
{
auto deserializer = serde::BincodeDeserializer(input);
auto value = serde::Deserializable<ForeignCallOutput::Single>::deserialize(deserializer);
auto value = serde::Deserializable<ForeignCallParam::Single>::deserialize(deserializer);
if (deserializer.get_buffer_offset() < input.size()) {
throw_or_abort("Some input bytes were not read");
}
Expand All @@ -4837,43 +4837,43 @@ inline ForeignCallOutput::Single ForeignCallOutput::Single::bincodeDeserialize(s

template <>
template <typename Serializer>
void serde::Serializable<Circuit::ForeignCallOutput::Single>::serialize(const Circuit::ForeignCallOutput::Single& obj,
Serializer& serializer)
void serde::Serializable<Circuit::ForeignCallParam::Single>::serialize(const Circuit::ForeignCallParam::Single& obj,
Serializer& serializer)
{
serde::Serializable<decltype(obj.value)>::serialize(obj.value, serializer);
}

template <>
template <typename Deserializer>
Circuit::ForeignCallOutput::Single serde::Deserializable<Circuit::ForeignCallOutput::Single>::deserialize(
Circuit::ForeignCallParam::Single serde::Deserializable<Circuit::ForeignCallParam::Single>::deserialize(
Deserializer& deserializer)
{
Circuit::ForeignCallOutput::Single obj;
Circuit::ForeignCallParam::Single obj;
obj.value = serde::Deserializable<decltype(obj.value)>::deserialize(deserializer);
return obj;
}

namespace Circuit {

inline bool operator==(const ForeignCallOutput::Array& lhs, const ForeignCallOutput::Array& rhs)
inline bool operator==(const ForeignCallParam::Array& lhs, const ForeignCallParam::Array& rhs)
{
if (!(lhs.value == rhs.value)) {
return false;
}
return true;
}

inline std::vector<uint8_t> ForeignCallOutput::Array::bincodeSerialize() const
inline std::vector<uint8_t> ForeignCallParam::Array::bincodeSerialize() const
{
auto serializer = serde::BincodeSerializer();
serde::Serializable<ForeignCallOutput::Array>::serialize(*this, serializer);
serde::Serializable<ForeignCallParam::Array>::serialize(*this, serializer);
return std::move(serializer).bytes();
}

inline ForeignCallOutput::Array ForeignCallOutput::Array::bincodeDeserialize(std::vector<uint8_t> input)
inline ForeignCallParam::Array ForeignCallParam::Array::bincodeDeserialize(std::vector<uint8_t> input)
{
auto deserializer = serde::BincodeDeserializer(input);
auto value = serde::Deserializable<ForeignCallOutput::Array>::deserialize(deserializer);
auto value = serde::Deserializable<ForeignCallParam::Array>::deserialize(deserializer);
if (deserializer.get_buffer_offset() < input.size()) {
throw_or_abort("Some input bytes were not read");
}
Expand All @@ -4884,18 +4884,18 @@ inline ForeignCallOutput::Array ForeignCallOutput::Array::bincodeDeserialize(std

template <>
template <typename Serializer>
void serde::Serializable<Circuit::ForeignCallOutput::Array>::serialize(const Circuit::ForeignCallOutput::Array& obj,
Serializer& serializer)
void serde::Serializable<Circuit::ForeignCallParam::Array>::serialize(const Circuit::ForeignCallParam::Array& obj,
Serializer& serializer)
{
serde::Serializable<decltype(obj.value)>::serialize(obj.value, serializer);
}

template <>
template <typename Deserializer>
Circuit::ForeignCallOutput::Array serde::Deserializable<Circuit::ForeignCallOutput::Array>::deserialize(
Circuit::ForeignCallParam::Array serde::Deserializable<Circuit::ForeignCallParam::Array>::deserialize(
Deserializer& deserializer)
{
Circuit::ForeignCallOutput::Array obj;
Circuit::ForeignCallParam::Array obj;
obj.value = serde::Deserializable<decltype(obj.value)>::deserialize(deserializer);
return obj;
}
Expand Down Expand Up @@ -6115,4 +6115,4 @@ Circuit::Witness serde::Deserializable<Circuit::Witness>::deserialize(Deserializ
obj.value = serde::Deserializable<decltype(obj.value)>::deserialize(deserializer);
deserializer.decrease_container_depth();
return obj;
}
}
46 changes: 35 additions & 11 deletions docs/docs/dev_docs/contracts/syntax/storage.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@
title: Storage
---

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

As their name indicates, public state variables can be read by anyone, while private state variables can only be read by their owner, or people whom the owner has shared the data with.
Public state variables can be read by anyone, while private state variables can only be read by their owner (or people whom the owner has shared the decrypted data/note viewing key with).

As mentioned earlier in the foundational concepts ([state model](./../../../concepts/foundation/state_model.md) and [private/public execution](./../../../concepts/foundation/communication/public_private_calls.md)) private state follows a UTXO model. Where note pre-images are only known to those able to decrypt them.
Public state follows the ethereum style account model, where each contract has its own key-value datastore. Private state follows a UTXO model, where note contents (pre-images) are only known by the sender and those able to decrypt them - see ([state model](./../../../concepts/foundation/state_model.md) and [private/public execution](./../../../concepts/foundation/communication/public_private_calls.md)) for more background.

:::info
The struct **must** be called `Storage` for the Aztec.nr library to properly handle it (will be fixed in the future to support more flexibility).
The struct **must** be called `Storage` for the Aztec.nr library to properly handle it (this will be relaxed in the future).
:::

```rust
Expand All @@ -20,10 +20,10 @@ struct Storage {
```

:::info
If your storage includes private state variables it must include a `compute_note_hash_and_nullifier` function to allow the RPC to process encrypted events, see [encrypted events](./events.md#processing-encrypted-events) for more.
If your contract storage includes private state variables, it must include a `compute_note_hash_and_nullifier` function to allow the RPC to process encrypted events. See [encrypted events](./events.md#processing-encrypted-events) for more.
:::

Since Aztec.nr is written in Noir, which on its own is state-less, we need to specify how the storage struct should be initialized to read and write data correctly. This is done by specifying an `init` function that is run in functions that rely on reading or altering the state variables. This `init` function should declare the storage struct with an actual instantiation defining how variables are accessed and manipulated. The function MUST be called `init` for the Aztec.nr library to properly handle it (will be fixed in the future to support more flexibility).
Since Aztec.nr is written in Noir, which on its own is state-less, we need to specify how the storage struct should be initialized to read and write data correctly. This is done by specifying an `init` function that is run in functions that rely on reading or altering the state variables. This `init` function must declare the storage struct with an instantiation defining how variables are accessed and manipulated. The function MUST be called `init` for the Aztec.nr library to properly handle it (this will be relaxed in the future).

```rust
impl Storage {
Expand All @@ -39,12 +39,36 @@ impl Storage {
If you have defined a `Storage` struct following this naming scheme, then it will be made available to you through the reserved `storage` keyword within your contract functions.

:::warning Using slot `0` is not supported!
No storage values should be initialized at slot `0`. This is a known issue that will be fixed in the future.
No storage values should be initialized at slot `0` - storage slots begin at `1`. This is a known issue that will be fixed in the future.
:::

## Storage Slots

Public state in Aztec is implemented as a single global merkle tree of depth 254, with each contract's internal storage slot combined with its contract address to generate its position in the global tree as `global_storage_slot = pedersen_hash(contract_storage_slot, contract_address)`.

A contract's state is represented by mapping its own storage slots to each variable. For now, storage slot positions for each variable must be explicitly assigned inside the `Storage impl`. Although variables can be arrays or structs that are stored internally as contiguous blocks, each variable in the storage definition takes just 1 block, so you can increment the storage slot by 1 each time you add another variable, whether it is public or private.

When assigning contract storage slots, `Map`s are also treated as occupying only 1 storage slot (its "base_slot"), because the actual values in the global state tree are stored in derived slots calculated as `map_value_storage_slot = pedersen_hash(base_slot, key)`.

Private state is stored in a separate UTXO tree, but each private variable is still assigned a storage slot to track the meaning of the note. Each private variable's storage slot is contained in a contract's bytecode, but they do not appear at all in the global storage tree. Each contract private variable can be associated with 0, 1, or multiple notes in the UTXO tree (and some of those may have already been spent, if their nullifier is already present in the nullifier tree).
The position of each note in the UTXO tree does not matter - the relationship to contract state is contained entirely in its note header.

:::info
Private variables only require one single slot, because all notes are linked their contract variable through the `storage slot` attribute in their note header (which also contains the contract address and nonce). :::

We currently do not support any "bit packing" type optimizations as in most EVM languages.

Note: The choice of hash function for global slot position is subject to change in later versions.

## Map

A `map` is a state variable that "maps" a key to a value. In Aztec.nr, keys are `Field`s (or values that are convertible to Fields) and values can be any type - even other maps. The map is a struct defined as follows:
A `map` is a state variable that "maps" a key to a value.

:::info
In Aztec.nr, keys are always `Field`s (or types that can be serialized as Fields) and values can be any type - even other maps.
:::

The map is a struct defined as follows:

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

Expand Down Expand Up @@ -113,15 +137,15 @@ When declaring the storage for `T` as a persistent public storage variable, we u

#### Single value example

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

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

And then when initializing it in the `Storage::init` function we can do it as follows:
And then when initializing it in the `Storage::init` function we can do:

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

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

```solidity
address internal admin;
Expand Down
14 changes: 7 additions & 7 deletions yarn-project/aztec-nr/authwit/src/account.nr
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ struct AccountActions {
}

impl AccountActions {
fn init(context: Context, approved_action_storage_slot: Field, is_valid_impl: fn(&mut PrivateContext, Field) -> bool) -> Self {
pub fn init(context: Context, approved_action_storage_slot: Field, is_valid_impl: fn(&mut PrivateContext, Field) -> bool) -> Self {
AccountActions {
context: context,
is_valid_impl: is_valid_impl,
Expand All @@ -27,16 +27,16 @@ impl AccountActions {
}
}

fn private(context: &mut PrivateContext, approved_action_storage_slot: Field, is_valid_impl: fn(&mut PrivateContext, Field) -> bool) -> Self {
pub fn private(context: &mut PrivateContext, approved_action_storage_slot: Field, is_valid_impl: fn(&mut PrivateContext, Field) -> bool) -> Self {
AccountActions::init(Context::private(context), approved_action_storage_slot, is_valid_impl)
}

fn public(context: &mut PublicContext, approved_action_storage_slot: Field, is_valid_impl: fn(&mut PrivateContext, Field) -> bool) -> Self {
pub fn public(context: &mut PublicContext, approved_action_storage_slot: Field, is_valid_impl: fn(&mut PrivateContext, Field) -> bool) -> Self {
AccountActions::init(Context::public(context), approved_action_storage_slot, is_valid_impl)
}

// docs:start:entrypoint
fn entrypoint(self, payload: EntrypointPayload) {
pub fn entrypoint(self, payload: EntrypointPayload) {
let message_hash = payload.hash();
let valid_fn = self.is_valid_impl;
let private_context = self.context.private.unwrap();
Expand All @@ -45,7 +45,7 @@ impl AccountActions {
}
// docs:end:entrypoint

fn is_valid(self, message_hash: Field) -> Field {
pub fn is_valid(self, message_hash: Field) -> Field {
let valid_fn = self.is_valid_impl;
if (valid_fn(self.context.private.unwrap(), message_hash)) {
IS_VALID_SELECTOR
Expand All @@ -54,7 +54,7 @@ impl AccountActions {
}
}

fn is_valid_public(self, message_hash: Field) -> Field {
pub fn is_valid_public(self, message_hash: Field) -> Field {
let value = self.approved_action.at(message_hash).read();
if (value){
IS_VALID_SELECTOR
Expand All @@ -63,7 +63,7 @@ impl AccountActions {
}
}

fn internal_set_is_valid_storage(self, message_hash: Field, value: bool) {
pub fn internal_set_is_valid_storage(self, message_hash: Field, value: bool) {
self.approved_action.at(message_hash).write(value);
}
}
Loading

0 comments on commit c636548

Please sign in to comment.