Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(docs): Function transforms (hidden macros) #7784

Merged
merged 37 commits into from
Oct 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
285edeb
some fixes
catmcgee Jul 28, 2024
5d9478e
getting started & simple tutorial fixes
catmcgee Jul 28, 2024
f6b57b6
token bridge tutorial
catmcgee Jul 30, 2024
4813413
tutorials
catmcgee Jul 30, 2024
d0326ea
aztec-nr -> Aztec.nr
catmcgee Jul 30, 2024
f9c7a8c
updating
catmcgee Jul 30, 2024
45cd111
small fixes
catmcgee Jul 31, 2024
60eabe0
Merge branch 'master' into docs/small-fixes
catmcgee Jul 31, 2024
9e37aa7
format
catmcgee Jul 31, 2024
fcedc9e
Merge branch 'master' into docs/small-fixes
catmcgee Aug 1, 2024
a5a9ae3
Apply suggestions from code review
catmcgee Aug 6, 2024
f50f3d2
finish a sentence
catmcgee Aug 6, 2024
d0af4fc
nargo fmt
catmcgee Aug 6, 2024
cd86d7c
docs build error
catmcgee Aug 6, 2024
e3fff64
function hidden mcros
catmcgee Aug 6, 2024
a8777eb
build
catmcgee Aug 6, 2024
464e9cf
buld errors
catmcgee Aug 6, 2024
fb4b5d9
Merge branch 'master' into docs/function-transforms
critesjosh Aug 6, 2024
307001f
change name of file
catmcgee Aug 7, 2024
0b31872
Merge branch 'master' into docs/function-transforms
catmcgee Aug 7, 2024
bbafe57
Merge branch 'master' into docs/function-transforms
catmcgee Aug 7, 2024
72bab18
Merge branch 'master' into docs/function-transforms
catmcgee Aug 8, 2024
6aa47b9
review suggestions
catmcgee Aug 17, 2024
7ceb4f8
Merge remote-tracking branch 'origin/master' into docs/function-trans…
catmcgee Sep 18, 2024
25d43f9
links
catmcgee Sep 18, 2024
d89a36e
noir > rust
catmcgee Sep 18, 2024
8c27f07
Merge branch 'master' into docs/function-transforms
catmcgee Oct 21, 2024
c704235
Merge branch 'master' into docs/function-transforms
catmcgee Oct 21, 2024
cea5e73
build errors
catmcgee Oct 21, 2024
14f7370
build errors
catmcgee Oct 21, 2024
0a8a99e
broken link
catmcgee Oct 24, 2024
c5f214d
Merge remote-tracking branch 'origin/master' into docs/function-trans…
catmcgee Oct 24, 2024
5267779
Merge branch 'master' into docs/function-transforms
catmcgee Oct 24, 2024
69f9c6c
Merge branch 'master' into docs/function-transforms
catmcgee Oct 24, 2024
ebd45a2
pls
catmcgee Oct 25, 2024
65b02f1
Merge branch 'master' into docs/function-transforms
catmcgee Oct 25, 2024
a023a3f
Merge branch 'master' into docs/function-transforms
catmcgee Oct 25, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
---
title: Inner Workings of Functions and Macros
title: Function Attributes and Macros
sidebar_position: 4
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.
On this page you will learn about function attributes and macros.

If you are looking for a reference of function macros, go [here](../../../reference/developer_references/smart_contract_reference/macros.md).

Expand Down Expand Up @@ -358,5 +358,7 @@ Key things to keep in mind:

## Further reading

- [How do macros work](./inner_workings.md)
- [Macros reference](../../../reference/developer_references/smart_contract_reference/macros.md)
- [How do macros work](./attributes.md)


7 changes: 4 additions & 3 deletions docs/docs/aztec/smart_contracts/functions/context.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,11 @@ The `args_hash` is the result of pedersen hashing all of a function's inputs.

### Return Values

The return values are a set of values that are returned from an applications execution to be passed to other functions through the kernel. Developers do not need to worry about passing their function return values to the `context` directly as `Aztec.nr` takes care of it for you. See the documentation surrounding `Aztec.nr` [macro expansion](./inner_workings.md) for more details.

return_values : BoundedVec\<Field, RETURN_VALUES_LENGTH\>,
The return values are a set of values that are returned from an applications execution to be passed to other functions through the kernel. Developers do not need to worry about passing their function return values to the `context` directly as `Aztec.nr` takes care of it for you. See the documentation surrounding `Aztec.nr` [macro expansion](./attributes.md#after-expansion) for more details.

```rust
return_values : BoundedVec\<Field, RETURN_VALUES_LENGTH\>,
```
## Max Block Number

Some data structures impose time constraints, e.g. they may make it so that a value can only be changed after a certain delay. Interacting with these in private involves creating proofs that are only valid as long as they are included before a certain future point in time. To achieve this, the `set_tx_max_block_number` function can be used to set this property:
Expand Down
277 changes: 277 additions & 0 deletions docs/docs/aztec/smart_contracts/functions/function_transforms.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,277 @@
---
title: Inner Workings of Functions
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. The [next page](./attributes.md) will give you more information about what the attributes are really doing.


## Function transformation

When you define a function in an Aztec contract, it undergoes several transformations when it is compiled. These transformations prepare the function for execution. These transformations include:

- [Creating a context for the function](#context-creation)
- [Handling function inputs](#input-handling)
- [Processing return values](#return-value-handling)
- [Computing note hashes and nullifiers](#computing-note-hash-and-nullifier)
- [Generating function signatures](#function-signature-generation)
- [Generating contract artifacts](#contract-artifacts)

Let's explore each of these transformations in detail.

## Context creation

Every function in an Aztec contract operates within a specific context which provides some extra information and functionality. This is either a `PrivateContext` or `PublicContext` object, depending on whether it is a private or public function. For private functions, it creates a hash of all input parameters to ensure privacy.

### Private functions

For private functions, the context creation involves hashing all input parameters:

```rust
let mut args_hasher = ArgsHasher::new();
// Hash each parameter
args_hasher.add(param1);
args_hasher.add(param2);
// add all parameters

let mut context = PrivateContext::new(inputs, args_hasher.hash());
```

This hashing process is important because it is used to verify the function's execution without exposing the input data.

### Public functions

For public functions, context creation is simpler:

```rust
let mut context = PublicContext::new(inputs);
```

These `inputs` are explained in the [private and public input injection](#private-and-public-input-injection) further down on this page.

### Using the context in functions

Once created, the context object provides various useful methods. Here are some common use cases:

#### Accessing storage

The context allows you to interact with contract storage. eg if you have a function that calls storage like this:

```rust
let sender_balance = storage.balances.at(owner);
```

This calls the context to read from the appropriate storage slot.

#### Interacting with other contracts

The context provides methods to call other contracts:

```rust
let token_contract = TokenContract::at(token);
```

Under the hood, this creates a new instance of the contract interface with the specified address.

## Private and public input injection

An additional parameter is automatically added to every function.

The injected input is always the first parameter of the transformed function and is of type `PrivateContextInputs` for private functions or `PublicContextInputs` for public functions.

Original function definition
```rust
fn my_function(param1: Type1, param2: Type2) { ... }
```

Transformed function with injected input
```rust
fn my_function(inputs: PrivateContextInputs, param1: Type1, param2: Type2) { ... }
```

The `inputs` parameter includes:

- msg sender, ie the address of the account calling the function
- contract address
- chain ID
- block context, eg the block number & timestamp
- function selector of the function being called

This makes these inputs available to be consumed within private annotated functions.

## Return value handling

Return values in Aztec contracts are processed differently from traditional smart contracts when using private functions.

### Private functions

- The original return value is assigned to a special variable:
```rust
let macro__returned__values = original_return_expression;
```

- A new `ArgsHasher` is created for the return values:
```rust
let mut returns_hasher = ArgsHasher::new();
```

- The hash of the return value is set in the context:
```rust
context.set_return_hash(returns_hasher);
```

- The function's return type is changed to `PrivateCircuitPublicInputs`, which is returned by calling `context.finish()` at the end of the function.

This process allows the return values to be included in the function's computation result while maintaining privacy.

### Public functions

In public functions, the return value is directly used, and the function's return type remains as specified by the developer.

## Computing note hash and nullifier

A function called `compute_note_hash_and_optionally_a_nullifier` is automatically generated and injected into all contracts that use notes. This function tells Aztec how to compute hashes and nullifiers for notes used in the contract. You can optionally write this function yourself if you want notes to be handled a specific way.

The function is automatically generated based on the note types defined in the contract. Here's how it works:

- The function takes several parameters:
```rust
fn compute_note_hash_and_optionally_a_nullifier(
contract_address: AztecAddress,
nonce: Field,
storage_slot: Field,
note_type_id: Field,
compute_nullifier: bool,
serialized_note: [Field; MAX_NOTE_FIELDS_LENGTH],
) -> [Field; 4]
```

- It creates a `NoteHeader` using the provided args:
```rust
let note_header = NoteHeader::new(contract_address, nonce, storage_slot);
```

- The function then checks the `note_type_id` against all note types defined in the contract. For each note type, it includes a condition like this:
```rust
if (note_type_id == NoteType::get_note_type_id()) {
aztec::note::utils::compute_note_hash_and_optionally_a_nullifier(
NoteType::deserialize_content,
note_header,
compute_nullifier,
serialized_note
)
}
```

- The function returns an array of 4 Field elements, which represent the note hash and, if computed, the nullifier.

## Function signature generation

Unique function signatures are generated for each contract function.

The function signature is computed like this:

```rust
fn compute_fn_signature_hash(fn_name: &str, parameters: &[Type]) -> u32 {
let signature = format!(
"{}({})",
fn_name,
parameters.iter().map(signature_of_type).collect::<Vec<_>>().join(",")
);
let mut keccak = Keccak::v256();
let mut result = [0u8; 32];
keccak.update(signature.as_bytes());
keccak.finalize(&mut result);
// Take the first 4 bytes of the hash and convert them to an integer
// If you change the following value you have to change NUM_BYTES_PER_NOTE_TYPE_ID in l1_note_payload.ts as well
let num_bytes_per_note_type_id = 4;
u32::from_be_bytes(result[0..num_bytes_per_note_type_id].try_into().unwrap())
}
```

- A string representation of the function is created, including the function name and parameter types
- This signature string is then hashed using Keccak-256
- The first 4 bytes of the resulting hash are coverted to a u32 integer

### Integration into contract interface

The computed function signatures are integrated into the contract interface like this:

- During contract compilation, placeholder values (0) are initially used for function selectors

- After type checking, the `update_fn_signatures_in_contract_interface()` function is called to replace these placeholders with the actual computed signatures

- For each function in the contract interface:
- The function's parameters are extracted
- The signature hash is computed using `compute_fn_signature_hash`
- The placeholder in the contract interface is replaced with the computed hash

This process ensures that each function in the contract has a unique, deterministic signature based on its name and parameter types. They are inspired by Solidity's function selector mechanism.

## Contract artifacts

Contract artifacts in Aztec are automatically generated structures that describe the contract's interface. They provide information about the contract's functions, their parameters, and return types.

### Contract artifact generation process

For each function in the contract, an artifact is generated like this:

- A struct is created to represent the function's parameters:

```rust
struct {function_name}_parameters {
// Function parameters are listed here
}
```

This struct is only created if the function has parameters.

- An ABI struct is generated for the function:

```rust
let export_struct_source = format!(
"
#[abi(functions)]
struct {}_abi {{
{}{}
}}",
func.name(),
parameters,
return_type
);
```

- These structs are added to the contract's types.

### Content of artifacts

The artifacts contain:

- Function name
- Parameters (if any), including their names and types
- Return type (if the function has returns)

For example, for a function `transfer(recipient: Address, amount: Field) -> bool`, the artifact would look like:

```rust
struct transfer_parameters {
recipient: Address,
amount: Field,
}

#[abi(functions)]
struct transfer_abi {
parameters: transfer_parameters,
return_type: bool,
}
```

Contract artifacts are important because:

- They provide a machine-readable description of the contract
- They can be used to generate bindings for interacting with the contract (read [here](../../../guides/developer_guides/smart_contracts/how_to_compile_contract.md) to learn how to create TypeScript bindings)
- They help decode function return values in the simulator

## Further reading
- [Function attributes and macros](./attributes.md)
4 changes: 2 additions & 2 deletions docs/docs/aztec/smart_contracts/functions/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ title: Defining Functions
tags: [functions]
---

Functions serve as the building blocks of smart contracts. Functions can be either **public**, ie they are publicly available for anyone to see and can directly interact with public state, or **private**, meaning they are executed completely client-side in the [PXE](../../concepts/pxe/index.md). Read more about how private functions work [here](./inner_workings.md#private-functions).
Functions serve as the building blocks of smart contracts. Functions can be either **public**, ie they are publicly available for anyone to see and can directly interact with public state, or **private**, meaning they are executed completely client-side in the [PXE](../../concepts/pxe/index.md). Read more about how private functions work [here](./attributes.md#private-functions).

For a more practical guide of using multiple types of functions, follow the [token tutorial](../../../tutorials/codealong/contract_tutorials/token_contract.md).

Expand All @@ -24,6 +24,6 @@ There are also special oracle functions, which can get data from outside of the
- [How function visibility works in Aztec](./visibility.md)
- How to write an [initializer function](../../../guides/developer_guides/smart_contracts/writing_contracts/initializers.md)
- [Oracles](../oracles/index.md) and how Aztec smart contracts might use them
- [How functions work under the hood](./inner_workings.md)
- [How functions work under the hood](./attributes.md)

Find a function macros reference [here](../../../reference/developer_references/smart_contract_reference/macros.md)
2 changes: 1 addition & 1 deletion docs/docs/aztec/smart_contracts/functions/visibility.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,4 @@ A good place to use `internal` is when you want a private function to be able to
Note that non-internal functions could be used directly as an entry-point, which currently means that the `msg_sender` would be `0`, so for now, using address `0` as a burn address is not recommended. You can learn more about this in the [Accounts concept page](../../concepts/accounts/keys.md).
:::

To understand how visibility works under the hood, check out the [Inner Workings page](./inner_workings.md).
To understand how visibility works under the hood, check out the [Inner Workings page](./attributes.md).
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,6 @@ If you are also planning to be able to access the data with a note in public sta

## Further reading

- [What is `#[note]` actually doing? + functions such as serialize() and deserialize()](../../../../../aztec/smart_contracts/functions/inner_workings.md#custom-notes-aztecnote)
- [What is `#[note]` actually doing? + functions such as serialize() and deserialize()](../../../../../aztec/smart_contracts/functions//attributes.md#custom-notes-note)
- [Macros reference](../../../../../reference/developer_references/smart_contract_reference/macros.md)
- [Keys, including npk_m_hash (nullifier public key master)](../../../../../aztec/concepts/accounts/keys.md)
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ It is also worth mentioning Noir's `unconstrained` function type [here (Noir doc
- `#[storage]` - Defines contract storage

## Further reading
[How do Aztec macros work? (Concepts)](../../../aztec/smart_contracts/functions/inner_workings.md)
[How do Aztec macros work?](../../../aztec/smart_contracts/functions/function_transforms.md)
2 changes: 1 addition & 1 deletion docs/docs/tutorials/codealong/aztecjs-getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -347,4 +347,4 @@ Follow the [dapp tutorial](./simple_dapp/index.md).
### Optional: Learn more about concepts mentioned here

- [Authentication witness](../../aztec/concepts/accounts/authwit.md)
- [Functions under the hood](../../aztec/smart_contracts/functions/inner_workings.md)
- [Functions under the hood](../../aztec/smart_contracts/functions/function_transforms.md)
Original file line number Diff line number Diff line change
Expand Up @@ -172,4 +172,4 @@ Follow the tutorial [here](../../../aztecjs-getting-started).
### Optional: Learn more about concepts mentioned here

- [Portals (protocol specs)](../../../../../protocol-specs/l1-smart-contracts/index.md)
- [Functions under the hood (concepts)](../../../../../aztec/smart_contracts/functions/inner_workings.md)
- [Functions under the hood (concepts)](../../../../../aztec/smart_contracts/functions/function_transforms.md)
Original file line number Diff line number Diff line change
Expand Up @@ -159,4 +159,4 @@ Follow the private voting contract tutorial on the [next page](./private_voting_

### Optional: Learn more about concepts mentioned here

- [Functions and annotations like `#[private]`](../../../aztec/smart_contracts/functions/inner_workings.md)
- [Functions and annotations like `#[private]`](../../../aztec/smart_contracts/functions/function_transforms.md#private-functions)
Loading