From 0b2c82dec2ed3129fa2482d00774db0397f904a1 Mon Sep 17 00:00:00 2001 From: Cat McGee Date: Mon, 24 Jun 2024 23:01:20 +0100 Subject: [PATCH 1/7] smol restructure --- .../functions/function_types_macros.md | 51 ++----------------- .../functions/inner_workings.md | 34 +++++++++++-- .../smart_contract_reference/macros.md | 19 +++++++ 3 files changed, 54 insertions(+), 50 deletions(-) create mode 100644 docs/docs/reference/smart_contract_reference/macros.md diff --git a/docs/docs/aztec/concepts/smart_contracts/functions/function_types_macros.md b/docs/docs/aztec/concepts/smart_contracts/functions/function_types_macros.md index 144abb2be2d..d089d78cabd 100644 --- a/docs/docs/aztec/concepts/smart_contracts/functions/function_types_macros.md +++ b/docs/docs/aztec/concepts/smart_contracts/functions/function_types_macros.md @@ -4,53 +4,12 @@ sidebar_position: 2 tags: [functions, macros] --- -This page explains three types of functions that exist on Aztec - public, private, and unconstrained; as well as all macros. +This page explains how all the macros work on Aztec. -## All Aztec macros +If you are looking for a reference for all macros available, go to the [macros reference](../../../../reference/smart_contract_reference/macros.md). -In addition to the function macros in Noir, Aztec also has its own macros for specific functions. An Aztec contract function can be annotated with more than 1 macro. -It is also worth mentioning Noir's `unconstrained` function type [here](https://noir-lang.org/docs/noir/concepts/unconstrained/). +## What are macros? -- `#[aztec(public)]` or `#[aztec(private)]` - Whether the function is public or private (more in next section) -- `#[aztec(initializer)]` - If one or more functions are marked as an initializer, then one of them must be called before any non-initilizer functions -- `#[aztec(noinitcheck)]` - The function is able to be called before an initializer (if one exists) -- `#[aztec(view)]` - Makes calls to the function static (see also [Static calls](../../../../protocol-specs/calls/static-calls)) -- `#[aztec(internal)]` - Function can only be called from within the contract +Macros allow developers to write code faster. They autogenerate code depending on their function. -## Example - -See [Private token contract](./../../../../tutorials/contract_tutorials/token_contract.md). - -# Public, Private, and unconstrained types - -For a deeper dive into how some of these functions work under the hood, check out the [Inner Workings](./inner_workings.md) page. - -## `Public` Functions - -A public function is executed by the sequencer and has access to a state model that is very similar to that of the EVM and Ethereum. Even though they work in an EVM-like model for public transactions, they are able to write data into private storage that can be consumed later by a private function. - -:::note -All data inserted into private storage from a public function will be publicly viewable (not private). -::: - -To create a public function you can annotate it with the `#[aztec(public)]` attribute. This will make the [public context](./context.md) available within the function's execution scope. - -#include_code set_minter /noir-projects/noir-contracts/contracts/token_contract/src/main.nr rust - -## `Private` Functions - -A private function operates on private information, and is executed by the user. Annotate the function with the `#[aztec(private)]` attribute to tell the compiler it's a private function. This will make the [private context](./context.md#the-private-context) available within the function's execution scope. - -#include_code redeem_shield /noir-projects/noir-contracts/contracts/token_contract/src/main.nr rust - -## `unconstrained` functions - -Unconstrained functions are an underlying part of Noir. In short, they are functions which are not directly constrained and therefore should be seen as un-trusted. That they are un-trusted means that the developer must make sure to constrain their return values when used. Note: Calling an unconstrained function from a private function means that you are injecting unconstrained values. - -Beyond using them inside your other functions, they are convenient for providing an interface that reads storage, applies logic and returns values to a UI or test. Below is a snippet from exposing the `balance_of_private` function from a token implementation, which allows a user to easily read their balance, similar to the `balanceOf` function in the ERC20 standard. - -#include_code balance_of_private /noir-projects/noir-contracts/contracts/token_contract/src/main.nr rust - -:::info -Note, that unconstrained functions can have access to both public and private data when executed on the user's device. This is possible since it is not actually part of the circuits that are executed in contract execution. -::: +## #[aztec(private)] \ No newline at end of file diff --git a/docs/docs/aztec/concepts/smart_contracts/functions/inner_workings.md b/docs/docs/aztec/concepts/smart_contracts/functions/inner_workings.md index 4a46590ad2a..3058f75e717 100644 --- a/docs/docs/aztec/concepts/smart_contracts/functions/inner_workings.md +++ b/docs/docs/aztec/concepts/smart_contracts/functions/inner_workings.md @@ -1,14 +1,14 @@ --- -title: Inner Workings of Functions +title: Inner Workings of Functions and Macros sidebar_position: 3 tags: [functions] --- Below, we go more into depth of what is happening under the hood when you create a function in an Aztec contract and what the attributes are really doing. -## Private functions +## Private functions (#[aztec(private)]) -Aztec.nr uses an attribute system to annotate a function's type. Annotating a function with the `#[aztec(private)]` attribute tells the framework that this is a private function that will be executed on a users device. The compiler will create a circuit to define this function. +A private function operates on private information, and is executed by the user on their device. Annotate the function with the `#[aztec(private)]` attribute to tell the compiler it's a private function. This will make the [private context](./context.md#the-private-context) available within the function's execution scope. The compiler will create a circuit to define this function. `#aztec(private)` is just syntactic sugar. At compile time, the Aztec.nr framework inserts code that allows the function to interact with the [kernel](../../circuits/kernels/private_kernel.md). @@ -73,7 +73,9 @@ Any state variables declared in the `Storage` struct can now be accessed as norm This function takes the application context, and converts it into the `PrivateCircuitPublicInputs` structure. This structure is then passed to the kernel circuit. -## Unconstrained functions +## Unconstrained functions (#[aztec(unconstrained)]) + +Unconstrained functions are an underlying part of Noir. In short, they are functions which are not directly constrained and therefore should be seen as un-trusted. That they are un-trusted means that the developer must make sure to constrain their return values when used. Note: Calling an unconstrained function from a private function means that you are injecting unconstrained values. Defining a function as `unconstrained` tells Aztec to simulate it completely client-side in the [ACIR simulator](../../pxe/acir_simulator.md) without generating proofs. They are useful for extracting information from a user through an [oracle](../oracles). @@ -94,3 +96,27 @@ This: 2. Converts `args` into a format suitable for the ACVM (Abstract Circuit Virtual Machine), creating an initial witness (witness = set of inputs required to compute the function). `args` might be an oracle to request a user's balance 3. Executes the function in the ACVM, which involves running the ACIR with the initial witness and the context. If requesting a user's balance, this would query the balance from the PXE database 4. Extracts the return values from the `partialWitness` and decodes them based on the artifact to get the final function output. The [artifact](../../../../reference/smart_contract_reference/contract_artifact.md) is the compiled output of the contract, and has information like the function signature, parameter types, and return types + +Beyond using them inside your other functions, they are convenient for providing an interface that reads storage, applies logic and returns values to a UI or test. Below is a snippet from exposing the `balance_of_private` function from a token implementation, which allows a user to easily read their balance, similar to the `balanceOf` function in the ERC20 standard. + +#include_code balance_of_private /noir-projects/noir-contracts/contracts/token_contract/src/main.nr rust + +:::info +Note, that unconstrained functions can have access to both public and private data when executed on the user's device. This is possible since it is not actually part of the circuits that are executed in contract execution. +::: + +## `Public` Functions (#[aztec(public)]) + +A public function is executed by the sequencer and has access to a state model that is very similar to that of the EVM and Ethereum. Even though they work in an EVM-like model for public transactions, they are able to write data into private storage that can be consumed later by a private function. + +:::note +All data inserted into private storage from a public function will be publicly viewable (not private). +::: + +To create a public function you can annotate it with the `#[aztec(public)]` attribute. This will make the [public context](./context.md) available within the function's execution scope. + +#include_code set_minter /noir-projects/noir-contracts/contracts/token_contract/src/main.nr rust + +## Further reading +- [How do macros work](./function_types_macros.md) +- [Macros reference](../../../../reference/smart_contract_reference/macros.md) \ No newline at end of file diff --git a/docs/docs/reference/smart_contract_reference/macros.md b/docs/docs/reference/smart_contract_reference/macros.md new file mode 100644 index 00000000000..779b69cf6f3 --- /dev/null +++ b/docs/docs/reference/smart_contract_reference/macros.md @@ -0,0 +1,19 @@ +--- +title: Aztec macros +sidebar_position: 6 +--- + +## All Aztec macros + +In addition to the function macros in Noir, Aztec also has its own macros for specific functions. An Aztec contract function can be annotated with more than 1 macro. +It is also worth mentioning Noir's `unconstrained` function type [here](https://noir-lang.org/docs/noir/concepts/unconstrained/). + +- `#[aztec(public)]` or `#[aztec(private)]` - Whether the function is public or private (more in next section) +- `#[aztec(initializer)]` - If one or more functions are marked as an initializer, then one of them must be called before any non-initilizer functions +- `#[aztec(noinitcheck)]` - The function is able to be called before an initializer (if one exists) +- `#[aztec(view)]` - Makes calls to the function static (see also [Static calls](../../../../protocol-specs/calls/static-calls)) +- `#[aztec(internal)]` - Function can only be called from within the contract +- `#[aztec(note)]` - Creates a custom note + +## Further reading +[What are Aztec macros?](../../aztec/concepts/smart_contracts/functions/function_types_macros.md) From cc7cf8ac98fc098f49eac539e75838b161f8c818 Mon Sep 17 00:00:00 2001 From: Cat McGee Date: Mon, 24 Jun 2024 23:14:00 +0100 Subject: [PATCH 2/7] structure --- .../functions/function_types_macros.md | 15 --------------- .../functions/inner_workings.md | 18 ++++++++++++++---- 2 files changed, 14 insertions(+), 19 deletions(-) delete mode 100644 docs/docs/aztec/concepts/smart_contracts/functions/function_types_macros.md diff --git a/docs/docs/aztec/concepts/smart_contracts/functions/function_types_macros.md b/docs/docs/aztec/concepts/smart_contracts/functions/function_types_macros.md deleted file mode 100644 index d089d78cabd..00000000000 --- a/docs/docs/aztec/concepts/smart_contracts/functions/function_types_macros.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -title: Function Macros -sidebar_position: 2 -tags: [functions, macros] ---- - -This page explains how all the macros work on Aztec. - -If you are looking for a reference for all macros available, go to the [macros reference](../../../../reference/smart_contract_reference/macros.md). - -## What are macros? - -Macros allow developers to write code faster. They autogenerate code depending on their function. - -## #[aztec(private)] \ No newline at end of file diff --git a/docs/docs/aztec/concepts/smart_contracts/functions/inner_workings.md b/docs/docs/aztec/concepts/smart_contracts/functions/inner_workings.md index 3058f75e717..07d245379b1 100644 --- a/docs/docs/aztec/concepts/smart_contracts/functions/inner_workings.md +++ b/docs/docs/aztec/concepts/smart_contracts/functions/inner_workings.md @@ -6,7 +6,9 @@ tags: [functions] Below, we go more into depth of what is happening under the hood when you create a function in an Aztec contract and what the attributes are really doing. -## Private functions (#[aztec(private)]) +If you are looking for a reference of function macros, go [here](../../../../reference/smart_contract_reference/macros.md). + +## Private functions #[aztec(private)] A private function operates on private information, and is executed by the user on their device. Annotate the function with the `#[aztec(private)]` attribute to tell the compiler it's a private function. This will make the [private context](./context.md#the-private-context) available within the function's execution scope. The compiler will create a circuit to define this function. @@ -73,7 +75,7 @@ Any state variables declared in the `Storage` struct can now be accessed as norm This function takes the application context, and converts it into the `PrivateCircuitPublicInputs` structure. This structure is then passed to the kernel circuit. -## Unconstrained functions (#[aztec(unconstrained)]) +## Unconstrained functions #[aztec(unconstrained)] Unconstrained functions are an underlying part of Noir. In short, they are functions which are not directly constrained and therefore should be seen as un-trusted. That they are un-trusted means that the developer must make sure to constrain their return values when used. Note: Calling an unconstrained function from a private function means that you are injecting unconstrained values. @@ -105,7 +107,7 @@ Beyond using them inside your other functions, they are convenient for providing Note, that unconstrained functions can have access to both public and private data when executed on the user's device. This is possible since it is not actually part of the circuits that are executed in contract execution. ::: -## `Public` Functions (#[aztec(public)]) +## `Public` Functions #[aztec(public)] A public function is executed by the sequencer and has access to a state model that is very similar to that of the EVM and Ethereum. Even though they work in an EVM-like model for public transactions, they are able to write data into private storage that can be consumed later by a private function. @@ -117,6 +119,14 @@ To create a public function you can annotate it with the `#[aztec(public)]` attr #include_code set_minter /noir-projects/noir-contracts/contracts/token_contract/src/main.nr rust +TODO more to write here + ## Further reading - [How do macros work](./function_types_macros.md) -- [Macros reference](../../../../reference/smart_contract_reference/macros.md) \ No newline at end of file +- [Macros reference](../../../../reference/smart_contract_reference/macros.md) + +## Constrained `view` Functions #[aztec(view)] +## `Initializer` Functions #[aztec(initializer)] +## #[aztec(noinitcheck)] +## `Internal` functions #[aztec(internal)] +## Custom notes #[aztec(note)] From c8d3b9a56fb03be8429a5c8bb8c8dc21788408b5 Mon Sep 17 00:00:00 2001 From: Cat McGee Date: Sun, 30 Jun 2024 19:03:36 +0100 Subject: [PATCH 3/7] added macros --- .../functions/inner_workings.md | 232 +++++++++++++++++- 1 file changed, 226 insertions(+), 6 deletions(-) diff --git a/docs/docs/aztec/concepts/smart_contracts/functions/inner_workings.md b/docs/docs/aztec/concepts/smart_contracts/functions/inner_workings.md index 07d245379b1..002fc6b7e54 100644 --- a/docs/docs/aztec/concepts/smart_contracts/functions/inner_workings.md +++ b/docs/docs/aztec/concepts/smart_contracts/functions/inner_workings.md @@ -24,7 +24,7 @@ To help illustrate how this interacts with the internals of Aztec and its kernel #include_code simple_macro_example_expanded /noir-projects/noir-contracts/contracts/docs_example_contract/src/main.nr rust -#### The expansion broken down? +#### The expansion broken down Viewing the expanded Aztec contract uncovers a lot about how Aztec contracts interact with the [kernel](../../circuits/kernels/private_kernel.md). To aid with developing intuition, we will break down each inserted line. @@ -119,14 +119,234 @@ To create a public function you can annotate it with the `#[aztec(public)]` attr #include_code set_minter /noir-projects/noir-contracts/contracts/token_contract/src/main.nr rust -TODO more to write here - -## Further reading -- [How do macros work](./function_types_macros.md) -- [Macros reference](../../../../reference/smart_contract_reference/macros.md) +Under the hood: + +- Context Creation: The macro inserts code at the beginning of the function to create a`PublicContext` object: +```rust +let mut context = PublicContext::new(inputs); +``` +This context provides access to public state and transaction information +- Function Signature Modification: The macro modifies the function signature to include a `PublicContextInputs` parameter: +```rust +fn function_name(inputs: PublicContextInputs, ...other_params) -> ReturnType +``` +- Return Type Transformation: For functions that return a value, the macro wraps the return type in a `PublicCircuitPublicInputs` struct: +```rust +-> protocol_types::abis::public_circuit_public_inputs::PublicCircuitPublicInputs +``` +- Storage Access: If the contract has a storage struct defined, the macro inserts code to initialize the storage: +```rust +let storage = Storage::init(&mut context); +``` +- Function Body Wrapping: The original function body is wrapped in a new scope that handles the context and return value +- Visibility Control: The function is marked as pub, making it accessible from outside the contract. +- Unconstrained Execution: Public functions are marked as unconstrained, meaning they don't generate proofs and are executed directly by the sequencer. ## Constrained `view` Functions #[aztec(view)] + +The `#[aztec(view)]` attribute is used to define constrained view functions in Aztec contracts. These functions are similar to view functions in Solidity, in that they are read-only and do not modify the contract's state. They are similar to the [`unconstrained`](#unconstrained-functions-aztecunconstrained) keyword but are executed in a constrained environment. It is not possible to update state within an `#[aztec(view)]` function. + +This means the results of these functions are verifiable and can be trusted, as they are part of the proof generation and verification process. This is unlike unconstrained functions, where results are provided by the PXE and are not verified. + +This makes `#[aztec(view)]` functions suitable for critical read-only operations where the integrity of the result is crucial. Unconstrained functions, on the other hand, are executed entirely client-side without generating any proofs. It is better to use `#[aztec(view)]` if the result of the function will affect some sort of state, and they can be used for cross-contract calls. + +`#[aztec(view)]` functions can be combined with other Aztec attributes like `#[aztec(private)]` or `#[aztec(public)]`. + ## `Initializer` Functions #[aztec(initializer)] + +This is used to designate functions as initializers (or constructors) for an Aztec contract. These functions are responsible for setting up the initial state of the contract when it is first deployed. The macro does two important things: + +- `assert_initialization_matches_address_preimage(context)`: This checks that the arguments and sender to the initializer match the commitments from the address preimage +- `mark_as_initialized(&mut context)`: This is called at the end of the function to emit the initialization nullifier, marking the contract as fully initialized and ensuring this function cannot be called again + +Key things to keep in mind: + +- A contract can have multiple initializer functions defined, but only one initializer function should be called for the lifetime of a contract instance +- Other functions in the contract will have an initialization check inserted, ie they cannot be called until the contract is initialized, unless they are marked with [`#[aztec(noinitcheck)])`](#aztecnoinitcheck) + ## #[aztec(noinitcheck)] + +In normal circumstances, all functions in an Aztec contract (except initializers) have an initialization check inserted at the beginning of the function body. This check ensures that the contract has been initialized before any other function can be called. However, there may be scenarios where you want a function to be callable regardless of the contract's initialization state. This is when you would use `#[aztec(noinitcheck)]`. + +When a function is annotated with `#[aztec(noinitcheck)]`: + +- The Aztec macro processor skips the [insertion of the initialization check](#initializer-functions-aztecinitializer) for this specific function +- The function can be called at any time, even if the contract hasn't been initialized yet + ## `Internal` functions #[aztec(internal)] + +This macro inserts a check at the beginning of the function to ensure that the caller is the contract itself. This is done by adding the following assertion: + +```rust +assert(context.msg_sender() == context.this_address(), "Function can only be called internally"); +``` + +## Custom notes #[aztec(note)] + +Certainly! Let's draft the documentation for the "Custom notes #[aztec(note)]" section, focusing on the functionality provided by this attribute. I'll use the code you've shared to provide accurate and detailed information about how this attribute works under the hood. + ## Custom notes #[aztec(note)] + +The `#[aztec(note)]` attribute is used to define custom note types in Aztec contracts. Learn more about notes [here](../../../concepts/storage/index.md). + +When a struct is annotated with `#[aztec(note)]`, the Aztec macro applies a series of transformations and generates implementations to turn it into a note that can be used in contracts to store private data. + +1. **NoteInterface Implementation**: The macro automatically implements most methods of the `NoteInterface` trait for the annotated struct. This includes: + + - `serialize_content` and `deserialize_content` + - `get_header` and `set_header` + - `get_note_type_id` + - `compute_note_content_hash` + - `to_be_bytes` + - A `properties` method in the note's implementation + +2. **Automatic Header Field**: If the struct doesn't already have a `header` field of type `NoteHeader`, one is automatically created + +3. **Note Type ID Generation**: A unique `note_type_id` is automatically computed for the note type using a Keccak hash of the struct name + +4. **Serialization and Deserialization**: Methods for converting the note to and from a series of `Field` elements are generated, assuming each field can be converted to/from a `Field` + +5. **Property Metadata**: A separate struct is generated to describe the note's fields, which is used for efficient retrieval of note data + +6. **Export Information**: The note type and its ID are automatically exported + + +### Before expansion + +Here is how you could define a custom note: + +```rust +#[aztec(note)] +struct CustomNote { + data: Field, + owner: Address, +} +``` + +### After expansaion + +```rust +impl CustomNote { + fn serialize_content(self: CustomNote) -> [Field; NOTE_SERIALIZED_LEN] { + [self.data, self.owner.to_field()] + } + + fn deserialize_content(serialized_note: [Field; NOTE_SERIALIZED_LEN]) -> Self { + CustomNote { + data: serialized_note[0] as Field, + owner: Address::from_field(serialized_note[1]), + header: NoteHeader::empty() + } + } + + fn get_note_type_id() -> Field { + // Automatically generated unique ID based on Keccak hash of the struct name + 0xd2de93eaab1d59abddf06134e737665f076f556feb7b6d3d72ca557b430b14d2 + } + + fn get_header(note: CustomNote) -> aztec::note::note_header::NoteHeader { + note.header + } + + fn set_header(self: &mut CustomNote, header: aztec::note::note_header::NoteHeader) { + self.header = header; + } + + fn compute_note_content_hash(self: CustomNote) -> Field { + aztec::hash::pedersen_hash( + self.serialize_content(), + aztec::protocol_types::constants::GENERATOR_INDEX__NOTE_CONTENT_HASH + ) + } + + fn to_be_bytes(self: CustomNote, storage_slot: Field) -> [u8; NOTE_BYTES_LEN] { + let mut buffer: [u8; NOTE_BYTES_LEN] = [0; NOTE_BYTES_LEN]; + buffer + } + + pub fn properties() -> CustomNoteProperties { + CustomNoteProperties { + data: aztec::note::note_getter_options::PropertySelector { index: 0, offset: 0, length: 32 }, + owner: aztec::note::note_getter_options::PropertySelector { index: 1, offset: 0, length: 32 } + } + } +} + +struct CustomNoteProperties { + data: aztec::note::note_getter_options::PropertySelector, + owner: aztec::note::note_getter_options::PropertySelector, +} +``` +Key things to keep in mind: + +- Developers can override any of the auto-generated methods by specifying a note interface +- The note's fields are automatically serialized and deserialized in the order they are defined in the struct + +## Storage struct #[aztec(storage)] + +The `#[aztec(storage)]` attribute is used to define the storage structure for an Aztec contract. + +When a struct is annotated with `#[aztec(storage)]`, the macro does this under the hood: + +1. **Context Injection**: injects a `Context` generic parameter into the storage struct and all its fields. This allows the storage to interact with the Aztec context, eg when using `context.msg_sender()` + +2. **Storage Implementation Generation**: generates an `impl` block for the storage struct with an `init` function. The developer can override this by implementing a `impl` block themselves + +3. **Storage Slot Assignment**: automatically assigns storage slots to each field in the struct based on their serialized length + +4. **Storage Layout Generation**: a `StorageLayout` struct and a global variable are generated to export the storage layout information for use in the contract artifact + +### Before expansion + +```rust +#[aztec(storage)] +struct Storage { + balance: PublicMutable, + owner: PublicMutable
, + token_map: Map, +} +``` + +### After expansion + +```rust +struct Storage { + balance: PublicMutable, + owner: PublicMutable, + token_map: Map, +} + +impl Storage { + fn init(context: Context) -> Self { + Storage { + balance: PublicMutable::new(context, 1), + owner: PublicMutable::new(context, 2), + token_map: Map::new(context, 3, |context, slot| Field::new(context, slot)), + } + } +} + +struct StorageLayout { + balance: dep::aztec::prelude::Storable, + owner: dep::aztec::prelude::Storable, + token_map: dep::aztec::prelude::Storable, +} + +#[abi(storage)] +global CONTRACT_NAME_STORAGE_LAYOUT = StorageLayout { + balance: dep::aztec::prelude::Storable { slot: 1 }, + owner: dep::aztec::prelude::Storable { slot: 2 }, + token_map: dep::aztec::prelude::Storable { slot: 3 }, +}; +``` + +Key things to keep in mind: + +- Only one storage struct can be defined per contract +- `Map` types and private `Note` types always occupy a single storage slot + +## Further reading +- [How do macros work](./function_types_macros.md) +- [Macros reference](../../../../reference/smart_contract_reference/macros.md) + + From ffbdf5a8cf9705bc8a1c329e0b4f09e45ea29d34 Mon Sep 17 00:00:00 2001 From: Cat McGee Date: Sun, 30 Jun 2024 19:09:05 +0100 Subject: [PATCH 4/7] build errors --- docs/docs/aztec/concepts/smart_contracts/functions/index.md | 1 - .../aztec/concepts/smart_contracts/functions/inner_workings.md | 2 +- docs/docs/reference/smart_contract_reference/macros.md | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/docs/aztec/concepts/smart_contracts/functions/index.md b/docs/docs/aztec/concepts/smart_contracts/functions/index.md index 4c181e199b4..2d43827275b 100644 --- a/docs/docs/aztec/concepts/smart_contracts/functions/index.md +++ b/docs/docs/aztec/concepts/smart_contracts/functions/index.md @@ -21,7 +21,6 @@ There are also special oracle functions, which can get data from outside of the Explore this section to learn: - [How function visibility works in Aztec](./visibility.md) -- [Function types and Macros](./function_types_macros.md), and how to write them - How to write an [initializer function](../../../../guides/smart_contracts/writing_contracts/initializers.md) - [Calling functions from within the same smart contract and from different contracts](../../../../guides/smart_contracts/writing_contracts/call_functions.md), including calling private functions from private functions, public from public, and even private from public - [Oracles](../oracles/index.md) and how Aztec smart contracts might use them diff --git a/docs/docs/aztec/concepts/smart_contracts/functions/inner_workings.md b/docs/docs/aztec/concepts/smart_contracts/functions/inner_workings.md index 002fc6b7e54..0628e29f352 100644 --- a/docs/docs/aztec/concepts/smart_contracts/functions/inner_workings.md +++ b/docs/docs/aztec/concepts/smart_contracts/functions/inner_workings.md @@ -346,7 +346,7 @@ Key things to keep in mind: - `Map` types and private `Note` types always occupy a single storage slot ## Further reading -- [How do macros work](./function_types_macros.md) +- [How do macros work](./inner_workings.md) - [Macros reference](../../../../reference/smart_contract_reference/macros.md) diff --git a/docs/docs/reference/smart_contract_reference/macros.md b/docs/docs/reference/smart_contract_reference/macros.md index 779b69cf6f3..99bdefc3492 100644 --- a/docs/docs/reference/smart_contract_reference/macros.md +++ b/docs/docs/reference/smart_contract_reference/macros.md @@ -16,4 +16,4 @@ It is also worth mentioning Noir's `unconstrained` function type [here](https:// - `#[aztec(note)]` - Creates a custom note ## Further reading -[What are Aztec macros?](../../aztec/concepts/smart_contracts/functions/function_types_macros.md) +[How do Aztec macros work?](../../aztec/concepts/smart_contracts/functions/inner_workings.md) From 59ad238cf0181360324c8f7c88774b4b3ef56cc5 Mon Sep 17 00:00:00 2001 From: Cat McGee Date: Mon, 1 Jul 2024 20:24:40 +0900 Subject: [PATCH 5/7] Update docs/docs/reference/smart_contract_reference/macros.md Co-authored-by: James Zaki --- docs/docs/reference/smart_contract_reference/macros.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/reference/smart_contract_reference/macros.md b/docs/docs/reference/smart_contract_reference/macros.md index 99bdefc3492..91798ffbcd2 100644 --- a/docs/docs/reference/smart_contract_reference/macros.md +++ b/docs/docs/reference/smart_contract_reference/macros.md @@ -8,7 +8,7 @@ sidebar_position: 6 In addition to the function macros in Noir, Aztec also has its own macros for specific functions. An Aztec contract function can be annotated with more than 1 macro. It is also worth mentioning Noir's `unconstrained` function type [here](https://noir-lang.org/docs/noir/concepts/unconstrained/). -- `#[aztec(public)]` or `#[aztec(private)]` - Whether the function is public or private (more in next section) +- `#[aztec(public)]` or `#[aztec(private)]` - Whether the function is to be executed from a public or private context (see Further Reading) - `#[aztec(initializer)]` - If one or more functions are marked as an initializer, then one of them must be called before any non-initilizer functions - `#[aztec(noinitcheck)]` - The function is able to be called before an initializer (if one exists) - `#[aztec(view)]` - Makes calls to the function static (see also [Static calls](../../../../protocol-specs/calls/static-calls)) From 0b72d4b59104f60d058a86bad06c2ce748f3443b Mon Sep 17 00:00:00 2001 From: Cat McGee Date: Mon, 1 Jul 2024 13:32:49 +0100 Subject: [PATCH 6/7] addressed comments --- .../smart_contracts/functions/index.md | 2 ++ .../functions/inner_workings.md | 34 +++++++++++++------ 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/docs/docs/aztec/concepts/smart_contracts/functions/index.md b/docs/docs/aztec/concepts/smart_contracts/functions/index.md index 2d43827275b..f8f3f4be570 100644 --- a/docs/docs/aztec/concepts/smart_contracts/functions/index.md +++ b/docs/docs/aztec/concepts/smart_contracts/functions/index.md @@ -25,3 +25,5 @@ Explore this section to learn: - [Calling functions from within the same smart contract and from different contracts](../../../../guides/smart_contracts/writing_contracts/call_functions.md), including calling private functions from private functions, public from public, and even private from public - [Oracles](../oracles/index.md) and how Aztec smart contracts might use them - [How functions work under the hood](./inner_workings.md) + +Find a function macros reference [here](../../../../reference/smart_contract_reference/macros.md). \ No newline at end of file diff --git a/docs/docs/aztec/concepts/smart_contracts/functions/inner_workings.md b/docs/docs/aztec/concepts/smart_contracts/functions/inner_workings.md index 0628e29f352..8b01fa45062 100644 --- a/docs/docs/aztec/concepts/smart_contracts/functions/inner_workings.md +++ b/docs/docs/aztec/concepts/smart_contracts/functions/inner_workings.md @@ -75,7 +75,7 @@ Any state variables declared in the `Storage` struct can now be accessed as norm This function takes the application context, and converts it into the `PrivateCircuitPublicInputs` structure. This structure is then passed to the kernel circuit. -## Unconstrained functions #[aztec(unconstrained)] +## Unconstrained functions Unconstrained functions are an underlying part of Noir. In short, they are functions which are not directly constrained and therefore should be seen as un-trusted. That they are un-trusted means that the developer must make sure to constrain their return values when used. Note: Calling an unconstrained function from a private function means that you are injecting unconstrained values. @@ -148,7 +148,7 @@ The `#[aztec(view)]` attribute is used to define constrained view functions in A This means the results of these functions are verifiable and can be trusted, as they are part of the proof generation and verification process. This is unlike unconstrained functions, where results are provided by the PXE and are not verified. -This makes `#[aztec(view)]` functions suitable for critical read-only operations where the integrity of the result is crucial. Unconstrained functions, on the other hand, are executed entirely client-side without generating any proofs. It is better to use `#[aztec(view)]` if the result of the function will affect some sort of state, and they can be used for cross-contract calls. +This makes `#[aztec(view)]` functions suitable for critical read-only operations where the integrity of the result is crucial. Unconstrained functions, on the other hand, are executed entirely client-side without generating any proofs. It is better to use `#[aztec(view)]` if the result of the function will be used in another function that will affect state, and they can be used for cross-contract calls. `#[aztec(view)]` functions can be combined with other Aztec attributes like `#[aztec(private)]` or `#[aztec(public)]`. @@ -183,10 +183,6 @@ assert(context.msg_sender() == context.this_address(), "Function can only be cal ## Custom notes #[aztec(note)] -Certainly! Let's draft the documentation for the "Custom notes #[aztec(note)]" section, focusing on the functionality provided by this attribute. I'll use the code you've shared to provide accurate and detailed information about how this attribute works under the hood. - -## Custom notes #[aztec(note)] - The `#[aztec(note)]` attribute is used to define custom note types in Aztec contracts. Learn more about notes [here](../../../concepts/storage/index.md). When a struct is annotated with `#[aztec(note)]`, the Aztec macro applies a series of transformations and generates implementations to turn it into a note that can be used in contracts to store private data. @@ -259,10 +255,28 @@ impl CustomNote { ) } - fn to_be_bytes(self: CustomNote, storage_slot: Field) -> [u8; NOTE_BYTES_LEN] { - let mut buffer: [u8; NOTE_BYTES_LEN] = [0; NOTE_BYTES_LEN]; - buffer - } + fn to_be_bytes(self, storage_slot: Field) -> [u8; 128] { + assert(128 == 2 * 32 + 64, "Note byte length must be equal to (serialized_length * 32) + 64 bytes"); + let serialized_note = self.serialize_content(); + + let mut buffer: [u8; 128] = [0; 128]; + + let storage_slot_bytes = storage_slot.to_be_bytes(32); + let note_type_id_bytes = CustomNote::get_note_type_id().to_be_bytes(32); + + for i in 0..32 { + buffer[i] = storage_slot_bytes[i]; + buffer[32 + i] = note_type_id_bytes[i]; + } + + for i in 0..serialized_note.len() { + let bytes = serialized_note[i].to_be_bytes(32); + for j in 0..32 { + buffer[64 + i * 32 + j] = bytes[j]; + } + } + buffer + } pub fn properties() -> CustomNoteProperties { CustomNoteProperties { From c83c3be6b6944b7cd07c1e2dc9243d64bcf6ac2c Mon Sep 17 00:00:00 2001 From: Cat McGee Date: Mon, 1 Jul 2024 13:36:19 +0100 Subject: [PATCH 7/7] explore this section -> learn more --- docs/docs/aztec/concepts/smart_contracts/functions/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/aztec/concepts/smart_contracts/functions/index.md b/docs/docs/aztec/concepts/smart_contracts/functions/index.md index f8f3f4be570..edc475c13d0 100644 --- a/docs/docs/aztec/concepts/smart_contracts/functions/index.md +++ b/docs/docs/aztec/concepts/smart_contracts/functions/index.md @@ -18,7 +18,7 @@ Initializers are regular functions that set an "initialized" flag (a nullifier) There are also special oracle functions, which can get data from outside of the smart contract. In the context of Aztec, oracles are often used to get user-provided inputs. -Explore this section to learn: +## Learn more about functions - [How function visibility works in Aztec](./visibility.md) - How to write an [initializer function](../../../../guides/smart_contracts/writing_contracts/initializers.md)