Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
15 changes: 10 additions & 5 deletions docs/docs/aztec/concepts/call_types.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,21 +164,26 @@ Public functions in other contracts can be called both regularly and statically,
This is the same function that was called by privately enqueuing a call to it! Public functions can be called either directly in a public context, or asynchronously by enqueuing in a private context.
:::

### Top-level Unconstrained
### Utility

Contract functions with the `unconstrained` Noir keyword are a special type of function still under development, and their semantics will likely change in the near future. They are used to perform state queries from an off-chain client (from both private and public state!), and are never included in any transaction. No guarantees are made on the correctness of the result since the entire execution is unconstrained and heavily reliant on oracle calls.
Contract functions marked with `#[utility]` are used to perform state queries from an off-chain client (from both private and public state!) or to modify local contract-related PXE state (e.g. when processing logs in Aztec.nr), and are never included in any transaction.
No guarantees are made on the correctness of the result since the entire execution is unconstrained and heavily reliant on oracle calls.

Any programming language could be used to construct these queries, since all they do is perform arbitrary computation on data that is either publicly available from any node, or locally available from the PXE. Top-level unconstrained functions exist because they let developers utilize the rest of the contract code directly by being part of the same Noir contract, and e.g. use the same libraries, structs, etc. instead of having to rely on manual computation of storage slots, struct layout and padding, and so on.
Any programming language could be used to construct these queries, since all they do is perform arbitrary computation on data that is either publicly available from any node, or locally available from the PXE.
Utility functions exist because they let developers utilize the rest of the contract code directly by being part of the same Noir contract, and e.g. use the same libraries, structs, etc. instead of having to rely on manual computation of storage slots, struct layout and padding, and so on.

A reasonable mental model for them is that of a Solidity `view` function that can never be called in any transaction, and is only ever invoked via `eth_call`. Note that in these the caller assumes that the node is acting honestly by executing the true contract bytecode with correct blockchain state, the same way the Aztec version assumes the oracles are returning legitimate data.
A reasonable mental model for them is that of a Solidity `view` function that can never be called in any transaction, and is only ever invoked via `eth_call`.
However, unlike view functions, `utility` functions can modify local off-chain PXE state via oracle calls.
This is commonly done when processing contract-emitted logs.
Note that in these the caller assumes that the node is acting honestly by executing the true contract bytecode with correct blockchain state, the same way the Aztec version assumes the oracles are returning legitimate data.

### aztec.js

There are three different ways to execute an Aztec contract function using the `aztec.js` library, with close similarities to their [JSON-RPC counterparts](#json-rpc).

#### `simulate`

This is used to get a result out of an execution, either private or public. It creates no transaction and spends no gas. The mental model is fairly close to that of [`eth_call`](#eth_call), in that it can be used to call any type of function, simulate its execution and get a result out of it. `simulate` is also the only way to run [top-level unconstrained functions](#top-level-unconstrained).
This is used to get a result out of an execution, either private or public. It creates no transaction and spends no gas. The mental model is fairly close to that of [`eth_call`](#eth_call), in that it can be used to call any type of function, simulate its execution and get a result out of it. `simulate` is also the only way to run [utility functions](#utility).

#include_code public_getter /noir-projects/noir-contracts/contracts/auth_contract/src/main.nr rust

Expand Down
27 changes: 2 additions & 25 deletions docs/docs/aztec/concepts/pxe/acir_simulator.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,5 @@ Simulating a function implies generating the partial witness and the public inpu

## Simulating functions

It simulates three types of functions:

### Private Functions

Private functions are simulated and proved client-side, and verified client-side in the private kernel circuit.

They are run with the assistance of a DB oracle that provides any private data requested by the function. You can read more about oracle functions in the smart contract section [here](../../smart_contracts/oracles/index.md).

Private functions can call other private functions and can request to call a public function. The public function execution will be performed by the sequencer asynchronously, so private functions don't have direct access to the return values of public functions.

### Public Functions

Public functions are simulated and proved on the sequencer side, and verified by the [public kernel circuit](../../concepts/advanced/circuits/kernels/public_kernel.md).

They are run with the assistance of an oracle that provides any value read from the public state tree.

Public functions can call other public functions as well as private functions. Public function composability can happen atomically, but public to private function calls happen asynchronously (the private function call must happen in a future block).

### Unconstrained Functions

Unconstrained functions are used to extract useful data for users, such as the user balance. They are not proven, and are simulated client-side.

They are run with the assistance of a DB oracle that provides any private data requested by the function.

At the moment, unconstrained functions cannot call any other function. It is not possible for them to call constrained functions, but it is on the roadmap to allow them to call other unconstrained functions.
It simulates private, public and utility functions.
For more details on these function types see [Aztec Call Types](../pxe/acir_simulator.md#aztec-call-types)
4 changes: 2 additions & 2 deletions docs/docs/aztec/smart_contracts/contract_creation.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ In the Aztec protocol, contracts are deployed as _instances_ of contract _classe

## Contract Classes

A contract class is a collection of state variable declarations, and related unconstrained, private, and public functions. Contract classes don't have state, they just define code (storage structure and function logic). A contract class cannot be called; only a contract instance can be called.
A contract class is a collection of state variable declarations, and related utility, private, and public functions. Contract classes don't have state, they just define code (storage structure and function logic). A contract class cannot be called; only a contract instance can be called.

### Key Benefits of Contract Classes

Expand All @@ -21,7 +21,7 @@ A contract class includes:
- `private_functions`: List of individual private functions, including constructors
- `packed_public_bytecode`: Packed bytecode representation of the AVM bytecode for all public functions

The specification of the artifact hash is not enforced by the protocol. It should include commitments to unconstrained code and compilation metadata. It is intended to be used by clients to verify that an off-chain fetched artifact matches a registered class.
The specification of the artifact hash is not enforced by the protocol. It should include commitments to utility functions code and compilation metadata. It is intended to be used by clients to verify that an off-chain fetched artifact matches a registered class.

### Contract Class Registration

Expand Down
33 changes: 16 additions & 17 deletions docs/docs/aztec/smart_contracts/functions/attributes.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,22 +75,29 @@ 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
## Utility functions #[utility]

Unconstrained functions are an underlying part of Noir. In short, they are functions which are not directly constrained and therefore should be seen as untrusted. 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.
Contract functions marked with `#[utility]` are used to perform state queries from an off-chain client (from both private and public state!) or to modify local contract-related PXE state (e.g. when processing logs in Aztec.nr), and are never included in any transaction.
No guarantees are made on the correctness of the result since the entire execution is unconstrained and heavily reliant on oracle calls.

Defining a function as `unconstrained` tells Aztec to simulate it completely client-side in the [ACIR simulator](../../concepts/pxe/index.md) without generating proofs. They are useful for extracting information from a user through an [oracle](../oracles/index.md).
Any programming language could be used to construct these queries, since all they do is perform arbitrary computation on data that is either publicly available from any node, or locally available from the PXE.
Utility functions exist because they let developers utilize the rest of the contract code directly by being part of the same Noir contract, and e.g. use the same libraries, structs, etc. instead of having to rely on manual computation of storage slots, struct layout and padding, and so on.

When an unconstrained function is called, it prompts the ACIR simulator to
A reasonable mental model for them is that of a Solidity `view` function that can never be called in any transaction, and is only ever invoked via `eth_call`.
However, unlike view functions, `utility` functions can modify local off-chain PXE state via oracle calls.
This is commonly done when processing contract-emitted logs.
Note that in these the caller assumes that the node is acting honestly by executing the true contract bytecode with correct blockchain state, the same way the Aztec version assumes the oracles are returning legitimate data.

When a utility function is called, it prompts the ACIR simulator to

1. generate the execution environment
2. execute the function within this environment

To generate the environment, the simulator gets the blockheader from the [PXE database](../../concepts/pxe/index.md#database) and passes it along with the contract address to `UnconstrainedExecutionOracle`. This creates a context that simulates the state of the blockchain at a specific block, allowing the unconstrained function to access and interact with blockchain data as it would appear in that block, but without affecting the actual blockchain state.
To generate the environment, the simulator gets the block header from the [PXE database](../../concepts/pxe/index.md#database) and passes it along with the contract address to `UtilityExecutionOracle`. This creates a context that simulates the state of the blockchain at a specific block, allowing the utility function to access and interact with blockchain data as it would appear in that block, but without affecting the actual blockchain state.

Once the execution environment is created, `execute_unconstrained_function` is invoked:
Once the execution environment is created, `runUtility` function is invoked on the simulator:

#include_code execute_unconstrained_function yarn-project/simulator/src/private/simulator.ts typescript
#include_code execute_utility_function yarn-project/simulator/src/private/simulator.ts typescript

This:

Expand All @@ -104,7 +111,7 @@ Beyond using them inside your other functions, they are convenient for providing
#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.
Note, that utility 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 #[public]
Expand Down Expand Up @@ -141,13 +148,7 @@ let storage = Storage::init(&mut context);

## Constrained `view` Functions #[view]

The `#[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) keyword but are executed in a constrained environment. It is not possible to update state within an `#[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 `#[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 `#[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.

`#[view]` functions can be combined with other Aztec attributes like `#[private]` or `#[public]`.
The `#[view]` attribute can be applied to a `#[private]` or a `#[public]` function and it guarantees that the function cannot modify any contract state (just like `view` functions in Solidity).

## `Initializer` Functions #[initializer]

Expand Down Expand Up @@ -347,5 +348,3 @@ Key things to keep in mind:

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


2 changes: 1 addition & 1 deletion docs/docs/developers/guides/js_apps/call_view_function.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,6 @@ Call the `simulate` function on the typescript contract wrapper like this:
#include_code simulate_function yarn-project/end-to-end/src/composed/docs_examples.test.ts typescript

:::info Note
- If the simulated function is `unconstrained` you will get a properly typed value.
- If the simulated function is `utility` you will get a properly typed value.
- If the simulated function is `public` or `private` it will return a Field array of size 4.
:::
2 changes: 1 addition & 1 deletion docs/docs/developers/guides/js_apps/test.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ Alternatively, you can [create a new account.](./create_account.md).

You can send transactions within your tests with Aztec.js. Read how to do that in these guides:

- [Call a view (unconstrained) function](./call_view_function.md)
- [Simulate a function](./call_view_function.md)
- [Send a transaction](./send_transaction.md)

### Using debug options
Expand Down
6 changes: 3 additions & 3 deletions docs/docs/developers/guides/smart_contracts/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,11 +127,11 @@ To call the public `transfer_in_public` function:

#include_code call_public /noir-projects/noir-contracts/contracts/token_contract/src/test/transfer_in_public.nr rust

#### Unconstrained
#### Utility

Unconstrained functions can be directly called from the contract interface. Notice that we need to set the contract address to the specific token contract that we are calling before making the call. This is to ensure that `view_notes` works properly.
Utility functions can be directly called from the contract interface. Notice that we need to set the contract address to the specific token contract that we are calling before making the call. This is to ensure that `view_notes` works properly.

#include_code txe_test_call_unconstrained /noir-projects/noir-contracts/contracts/token_contract/src/test/utils.nr rust
#include_code txe_test_call_utility /noir-projects/noir-contracts/contracts/token_contract/src/test/utils.nr rust

### Creating accounts

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ When you send someone a note, the note hash gets added to the note hash tree. To

#include_code offchain_delivery yarn-project/end-to-end/src/composed/e2e_persistence.test.ts typescript

Note that this requires your contract to have an unconstrained function that processes these notes and adds them to PXE.
Note that this requires your contract to have a utility function that processes these notes and adds them to PXE.

#include_code deliver_note_contract_method noir-projects/noir-contracts/contracts/token_blacklist_contract/src/main.nr rust

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ The function type can have one of the following values:

- Private: The function is ran and proved locally by the clients, and its bytecode not published to the network.
- Public: The function is ran and proved by the sequencer, and its bytecode is published to the network.
- Unconstrained: The function is ran locally by the clients to generate digested information useful for the user. It's not meant to be transacted against.
- Utility: The function is ran locally by the clients to generate digested information useful for the user. The function cannot be called in a transaction

#### `function.isInternal`
The is internal property is a boolean that indicates whether the function is internal to the contract and cannot be called from outside.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ In addition to the function macros in Noir, Aztec also has its own macros for sp
It is also worth mentioning Noir's `unconstrained` function type [here (Noir docs page)](https://noir-lang.org/docs/noir/concepts/unconstrained/).

- `#[aztec]` - Defines a contract, placed above `contract ContractName{}`
- `#[public]` or `#[private]` - Whether the function is to be executed from a public or private context (see Further Reading)
- `#[public]`, `#[private]` or `#[utility]` - Whether the function is to be executed from a public, private or utility context (see Further Reading)
- `#[initializer]` - If one or more functions are marked as an initializer, then one of them must be called before any non-initializer functions
- `#[noinitcheck]` - The function is able to be called before an initializer (if one exists)
- `#[view]` - Makes calls to the function static
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@ On this and the following pages in this section, you’ll learn:

## The `Context` parameter

Aztec contracts have three different modes of execution: private, public, and top-level unconstrained. How storage is accessed depends on the execution mode: for example, `PublicImmutable` can be read in all execution modes but only initialized in public, while `PrivateMutable` is entirely unavailable in public.
Aztec contracts have three different modes of execution: private, public, and utility. How storage is accessed depends on the execution mode: for example, `PublicImmutable` can be read in all execution modes but only initialized in public, while `PrivateMutable` is entirely unavailable in public.

Aztec.nr prevents developers from calling functions unavailable in the current execution mode via the `context` variable that is injected into all contract functions. Its type indicates the current execution mode:

- `&mut PrivateContext` for private execution
- `&mut PublicContext` for public execution
- `UncontrainedContext` for top-level unconstrained execution
- `UtilityContext` for utility execution

All state variables are generic over this `Context` type, and expose different methods in each execution mode. In the example above, `PublicImmutable`'s `initialize` function is only available with a public execution context, and so the following code results in a compilation error:

Expand Down
Loading