Skip to content

Commit

Permalink
feat(docs): document noir macros (#2016)
Browse files Browse the repository at this point in the history
Documents how the macros work and what they do. 

Also adds some information about the context objects, they are required
to understand the macros abstraction.

closes: #2063
  • Loading branch information
Maddiaa0 authored Sep 19, 2023
1 parent 426a3ea commit 1f1a17f
Show file tree
Hide file tree
Showing 17 changed files with 383 additions and 25 deletions.
2 changes: 1 addition & 1 deletion docs/docs/about_aztec/roadmap/cryptography_roadmap.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ The cryptography team is currently working on [Barretenberg here](https://github

## Honk

- Honk is a sumcheck-based zk-SNARK protocol with blazing-fast zk proof construction. We need to Honk allow users to prove correct execution of complicated, multi-step computations using recursion in a resource constraint environment like a cell phone. This is necessary for our mission, because we need to make sure our users' sensitive information never leaves their devices!
- Honk is a sumcheck-based zk-SNARK protocol with blazing-fast zk proof construction. We need Honk to allow users to prove correct execution of complicated, multi-step computations using recursion in a resource constraint environment like a cell phone. This is necessary for our mission, because we need to make sure our users' sensitive information never leaves their devices!
- List of Honk projects
- Completed: basic Honk prover and verifier with respectable construction and verification speeds, but no optimization.
- Upcoming:
Expand Down
122 changes: 122 additions & 0 deletions docs/docs/dev_docs/contracts/context.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
---
title: Aztec.nr Context
description: Documentation of Aztec's Private and Public execution contexts
hide_table_of_contents: false
---

import Image from "@theme/IdealImage";

# The Function Context

## What is the context
The context is an object that is made available within every function in `Aztec.nr`. As mentioned in the [kernel circuit documentation](../../concepts/advanced/circuits/kernels/private_kernel.md). At the beginning of a function's execution, the context contains all of the kernel information that application needs to execute. During the lifecycle of a transaction, the function will update the context with each of it's side effects (created notes, nullifiers etc.). At the end of a function's execution the mutated context is returned to the kernel to be checked for validity.

Behind the scenes, Aztec noir will pass data the kernel needs to and from a circuit, this is abstracted away from the developer. In an developer's eyes; the context is a useful structure that allows access and mutate the state of the `Aztec` blockchain.

## Two context's one API
The `Aztec` blockchain contains two environments [public and private](../../concepts/foundation/state_model.md).
- Private, for private transactions taking place on user's devices.
- Public, for public transactions taking place on the network's sequencers.

As there are two distinct execution environments, they both require slightly differing execution contexts. Despite their differences; the API's for interacting with each are unified. Leading to minimal context switch when working between the two environments.

The following section will cover both contexts.

## The Private Context

The code snippet below shows what is contained within the private context.
#include_code private-context /yarn-project/aztec-nr/aztec/src/context.nr rust

### Private Context Broken Down
#### Inputs
The context inputs includes all of the information that is passed from the kernel circuit into the application circuit. It contains the following values.

#include_code private-context-inputs /yarn-project/aztec-nr/aztec/src/abi.nr rust

As shown in the snippet, the application context is made up of 4 main structures. The call context, the block data, the contract deployment data and the private global variables.

First of all, the call context.

#include_code call-context /yarn-project/aztec-nr/aztec/src/abi.nr rust

The call context contains information about the current call being made:


1. Msg Sender
- The message sender is the account (Aztec Contract) that sent the message to the current context. In the first call of the kernel circuit (often the account contract call), this value will be empty. For all subsequent calls the value will be the previous call.

> The graphic below illustrates how the message sender changes throughout the kernel circuit iterations.
<Image img={require("/img/context/sender_context_change.png")} />
2. Storage contract address
- This value is the address of the current context's contract address. This value will be the value of the current contract that is being executed except for when the current call is a delegate call (Warning: This is yet to be implemented). In this case the value will be that of the sending contract.

3. Portal Contract Address
- This value stores the current contract's linked [portal contract](./portals/main.md) address. As a quick recap, this value is the value of the contracts related ethereum l1 contract address, and will be the recipient of any messages that are created by this contract.
4. Flags
- Furthermore there are a series of flags that are stored within the application context:
- is_delegate_call: Denotes whether the current call is a delegate call. If true, then the storage contract address will be the address of the sender.
- is_static_call: This will be set if and only if the current call is a static call. In a static call, state changing altering operations are not allowed.
- is_contract_deployment: This will be set if and only if the current call is the contract's constructor.

### Historic Block Data
Another structure that is contained within the context is the Historic Block Data object. This object is a special one as it contains all of the roots of Aztec's data trees.

#include_code historic-block-data /yarn-project/aztec-nr/aztec/src/abi.nr rust

### Contract Deployment Data
Just like with the `is_contract_deployment` flag mentioned earlier. This data will only be set to true when the current transaction is one in which a contract is being deployed.

#include_code contract-deployment-data /yarn-project/aztec-nr/aztec/src/abi.nr rust

### Private Global Variables
In the private execution context, we only have access to a subset of the total global variables, we are restricted to those which can be reliably proven by the kernel circuits.

#include_code private-global-variables /yarn-project/aztec-nr/aztec/src/abi.nr rust

### Args Hash
To allow for flexibility in the number of arguments supported by Aztec functions, all function inputs are reduced to a singular value which can be proven from within the application.

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](./syntax/functions.md#after-expansion) for more details.

return_values : BoundedVec<Field, RETURN_VALUES_LENGTH>,

### Read Requests
<!-- TODO(maddiaa): leaving as todo until their is further clarification around their implementation in the protocol -->

### New Commitments
New commitments contains an array of all of the commitments created in the current execution context.

### New Nullifiers
New nullifiers contains an array of the new nullifiers emitted from the current execution context.

### Nullified Commitments
Nullified commitments is an optimisation for introduced to help reduce state growth. There are often cases where commitments are created and nullified within the same transaction.
In these cases there is no reason that these commitments should take up space on the node's commitment/nullifier trees. Keeping track of nullified commitments allows us to "cancel out" and prove these cases.

### Private Call Stack
The private call stack contains all of the external private function calls that have been created within the current context. Any function call objects are hashed and then pushed to the execution stack.
The kernel circuit will orchestrate dispatching the calls and returning the values to the current context.

### Public Call Stack
The public call stack contains all of the external function calls that are created within the current context. Like the private call stack above, the calls are hashed and pushed to this stack. Unlike the private call stack, these calls are not executed client side. Whenever the function is sent to the network, it will have the public call stack attached to it. At this point the sequencer will take over and execute the transactions.

### New L2 to L1 msgs
New L2 to L1 messages contains messages that are delivered to the [l1 outbox](../../concepts/foundation/communication/cross_chain_calls.md) on the execution of each rollup.

## Public Context
The Public Context includes all of the information passed from the `Public VM` into the execution environment. It is very similar to the [Private Context](#the-private-context), however it has some minor differences (detailed below).

### Public Context Inputs
In the current version of the system, the public context is almost a clone of the private execution context. It contains the same call context data, access to the same historic tree roots, however it does NOT have access to contract deployment data, this is due to traditional contract deployments only currently being possible from private transactions.

#include_code public-context-inputs /yarn-project/aztec-nr/aztec/src/abi.nr rust


### Public Global Variables
The public global variables are provided by the rollup sequencer and consequently contain some more values than the private global variables.

#include_code public-global-variables /yarn-project/aztec-nr/aztec/src/abi.nr rust
1 change: 1 addition & 0 deletions docs/docs/dev_docs/contracts/portals/main.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ Access control on the L1 portal contract is essential to prevent consumption of

As earlier, we can use a token bridge as an example. In this case, we are burning tokens on L2 and sending a message to the portal to free them on L1.

<!-- TODO: update token standard https://github.com/AztecProtocol/aztec-packages/issues/2177 -->
#include_code non_native_token_withdraw yarn-project/noir-contracts/src/contracts/non_native_token_contract/src/main.nr rust

When the transaction is included in a rollup block the message will be inserted into the `Outbox`, where the recipient portal can consume it from. When consuming, the `msg.sender` must match the `recipient` meaning that only portal can actually consume the message.
Expand Down
82 changes: 75 additions & 7 deletions docs/docs/dev_docs/contracts/syntax/functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,29 @@ title: Functions
## `constructor`

- 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).
- A constructor doesn't have a name, because its purpose is clear: to initialise contract state.
- In Aztec terminology, a constructor is always a '`private` function' (i.e. it cannot be a `public` function, in the current version of the sandbox it cannot call public functions either).
- 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
An example of a constructor is as follows:
#include_code constructor /yarn-project/noir-contracts/src/contracts/escrow_contract/src/main.nr rust

> a.k.a. "private" functions
In this example (taken from an escrow contract), the constructor sets the deployer as an `owner`.

Although constructors are always needed, they are not required to do anything. A empty constructor can be created as follows:

#include_code empty-constructor /yarn-project/noir-contracts/src/contracts/public_token_contract/src/main.nr rust


## `Private` Functions

To create a private function you can annotate it with the `#[aztec(private)]` attribute. This will make the [private context](../context.mdx#private-context-broken-down) available within your current function's execution scope.

#include_code functions-SecretFunction /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/main.nr rust

## `open` functions
## `Public` Functions

> a.k.a. "public" functions
To create a public function you can annotate it with the `#[aztec(public)]` attribute. This will make the [public context](../context.mdx#public-context) available within your current function's execution scope.

#include_code functions-OpenFunction /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/main.nr rust

Expand All @@ -36,5 +46,63 @@ Similar to Solidity, internal functions and vars can be accessed within the cont
External is not used explicitly as it is in Solidity, but things not marked as `internal` will be external.

### `#[aztec(public)]` and `#[aztec(private)]`

These are used to annotate functions so that they are compliant with Aztec ABIs. They inject `PublicContext` and `PrivateContext` for use in contracts.

## Deep dive
### Function type attributes explained.
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 will be a private function that will be executed on a users device. Thus the compiler will create a circuit to define this function.

However; `#aztec(private)` is just syntactic sugar. At compile time, the framework inserts code that allows the function to interact with the [kernel](../../../concepts/advanced/circuits/kernels/private_kernel.md).

To help illustrate how this interacts with the internals of Aztec and its kernel circuits, we can take an example private function, and explore what it looks like after Aztec.nr's macro expansion.

#### Before expansion
#include_code simple_macro_example /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/main.nr rust


#### After expansion
#include_code simple_macro_example_expanded /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/main.nr rust

#### The expansion broken down?
Viewing the expanded noir contract uncovers a lot about how noir contracts interact with the [kernel](../../../concepts/advanced/circuits/kernels/private_kernel.md). To aid with developing intuition, we will break down each inserted line.

**Receiving context from the kernel.**
#include_code context-example-inputs /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/main.nr rust

Private function calls are able to interact with each other through orchestration from within the [kernel circuit](../../../concepts/advanced/circuits/kernels/private_kernel.md). The kernel circuit forwards information to each app circuit. This information then becomes part of the private context.
For example, within each circuit we can access some global variables. To access them we can call `context.chain_id()`. The value of this chain ID comes from the values passed into the circuit from the kernel.

The kernel can then check that all of the values passed to each circuit in a function call are the same.

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

Just as the kernel passes information into the the app circuits, the application must return information about the executed app back to the kernel. This is done through a rigid structure we call the `PrivateCircuitPublicInputs`.

> *Why is it called the `PrivateCircuitPublicInputs`*
> It is commonly asked why the return values of a function in a circuit are labelled as the `Public Inputs`. Common intuition from other programming paradigms suggests that the return values and public inputs should be distinct.
> However; In the eyes of the circuit, anything that is publicly viewable (or checkable) is a public input. Hence in this case, the return values are also public inputs.
This structure contains a host of information about the executed program. It will contain any newly created nullifiers, any messages to be sent to l2 and most importantly it will contain the actual return values of the function!

**Hashing the function inputs.**
#include_code context-example-hasher /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/main.nr rust

*What is the hasher and why is it needed?*

Inside the kernel circuits, the inputs to functions are reduced to a single value; the inputs hash. This prevents the need for multiple different kernel circuits; each supporting differing numbers of inputs. The hasher abstraction that allows us to create an array of all of the inputs that can be reduced to a single value.

**Creating the function's context.**
#include_code context-example-context /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/main.nr rust

Each Aztec function has access to a [context](../context.mdx) object. This object although ergonomically a global variable, is local. It is initialized from the inputs provided by the kernel, and a hash of the function's inputs.

#include_code context-example-context-return /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/main.nr rust

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.

**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

This function takes the application context, and converts it into the `PrivateCircuitPublicInputs` structure. This structure is then passed to the kernel circuit.
37 changes: 31 additions & 6 deletions docs/docs/dev_docs/contracts/syntax/globals.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,35 @@
---
title: Globals
title: Global Variables
description: Documentation of Aztec's Global Variables in the Public and Private Contexts
---

# Globals
# Global Variables
For developers coming from solidity, this concept will be similar to how the global `block` variable exposes a series of block values. The idea is the same in Aztec. Developers can access a namespace of values made available in each function.

- `timestamp`
- `block_number`
- `chain_id`
- `version`
`Aztec` has two execution environments, Private and Public. Each execution environment contains a different global variables object.

## Private Global Variables
#include_code private-global-variables /yarn-project/aztec-nr/aztec/src/abi.nr rust

The private global variables contain:
### Chain Id
The chain id differs depending on which Aztec instance you are on ( NOT the Ethereum hardfork that the rollup is settling to ). On original deployment of the network, this value will be 1.

### Version
The version number indicates which Aztec hardfork you are on. The Genesis block of the network will have the version number 1.

## Public Global Variables
#include_code public-global-variables /yarn-project/aztec-nr/aztec/src/abi.nr rust

The public global variables contain the values present in the `private global variables` described above, with the addition of:

### Timestamp
The timestamp is the unix timestamp in which the block has been executed. The value is provided by the block's proposer (therefore can have variance). This value will always increase.

### Block Number
The block number is an sequential identifier that labels each individual block of the network. This value will be the block number of the block the accessing transaction is included in.
The block number of the genesis block will be 1, with the number increasing by 1 for every block after.

> *Why do the available global variables differ per execution environment?*
> The global variables are constrained by the proving environment. In the case of public functions, they are executed on a sequencer that will know the timestamp and number of the next block ( as they are the block producer ).
> In the case of private functions, we cannot be sure which block our transaction will be included in, hence we can not guarantee values for the timestamp or block number.
Loading

0 comments on commit 1f1a17f

Please sign in to comment.