diff --git a/docs/docs/aztec/concepts/call_types.md b/docs/docs/aztec/concepts/call_types.md index 3e13a6fab793..86eef3e9a5ab 100644 --- a/docs/docs/aztec/concepts/call_types.md +++ b/docs/docs/aztec/concepts/call_types.md @@ -164,13 +164,18 @@ 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 @@ -178,7 +183,7 @@ There are three different ways to execute an Aztec contract function using the ` #### `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 diff --git a/docs/docs/aztec/concepts/pxe/acir_simulator.md b/docs/docs/aztec/concepts/pxe/acir_simulator.md index c6bb4ac8c8d7..9a08832d43a9 100644 --- a/docs/docs/aztec/concepts/pxe/acir_simulator.md +++ b/docs/docs/aztec/concepts/pxe/acir_simulator.md @@ -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) diff --git a/docs/docs/aztec/smart_contracts/contract_creation.md b/docs/docs/aztec/smart_contracts/contract_creation.md index cbf3aaec4ef9..5b8d3db38692 100644 --- a/docs/docs/aztec/smart_contracts/contract_creation.md +++ b/docs/docs/aztec/smart_contracts/contract_creation.md @@ -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 @@ -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 diff --git a/docs/docs/aztec/smart_contracts/functions/attributes.md b/docs/docs/aztec/smart_contracts/functions/attributes.md index d67c12e21398..39efa2b8a9aa 100644 --- a/docs/docs/aztec/smart_contracts/functions/attributes.md +++ b/docs/docs/aztec/smart_contracts/functions/attributes.md @@ -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: @@ -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] @@ -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] @@ -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) - - diff --git a/docs/docs/developers/guides/js_apps/call_view_function.md b/docs/docs/developers/guides/js_apps/call_view_function.md index 6e00360639a3..1aee9c9e9a89 100644 --- a/docs/docs/developers/guides/js_apps/call_view_function.md +++ b/docs/docs/developers/guides/js_apps/call_view_function.md @@ -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. ::: diff --git a/docs/docs/developers/guides/js_apps/test.md b/docs/docs/developers/guides/js_apps/test.md index 978aa008549a..2bb920d4bf09 100644 --- a/docs/docs/developers/guides/js_apps/test.md +++ b/docs/docs/developers/guides/js_apps/test.md @@ -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 diff --git a/docs/docs/developers/guides/smart_contracts/testing.md b/docs/docs/developers/guides/smart_contracts/testing.md index a246bcff4e79..3cc882c1ce07 100644 --- a/docs/docs/developers/guides/smart_contracts/testing.md +++ b/docs/docs/developers/guides/smart_contracts/testing.md @@ -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 diff --git a/docs/docs/developers/guides/smart_contracts/writing_contracts/common_patterns/index.md b/docs/docs/developers/guides/smart_contracts/writing_contracts/common_patterns/index.md index 041a4ed26cea..a7da2f67fb47 100644 --- a/docs/docs/developers/guides/smart_contracts/writing_contracts/common_patterns/index.md +++ b/docs/docs/developers/guides/smart_contracts/writing_contracts/common_patterns/index.md @@ -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 diff --git a/docs/docs/developers/reference/smart_contract_reference/contract_artifact.md b/docs/docs/developers/reference/smart_contract_reference/contract_artifact.md index 9c9fbbce73c0..d55ab1213899 100644 --- a/docs/docs/developers/reference/smart_contract_reference/contract_artifact.md +++ b/docs/docs/developers/reference/smart_contract_reference/contract_artifact.md @@ -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. diff --git a/docs/docs/developers/reference/smart_contract_reference/macros.md b/docs/docs/developers/reference/smart_contract_reference/macros.md index cabe9000e99c..9ea4d2b3cb43 100644 --- a/docs/docs/developers/reference/smart_contract_reference/macros.md +++ b/docs/docs/developers/reference/smart_contract_reference/macros.md @@ -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 diff --git a/docs/docs/developers/reference/smart_contract_reference/storage/index.md b/docs/docs/developers/reference/smart_contract_reference/storage/index.md index ba028b0930a0..be6c3e411528 100644 --- a/docs/docs/developers/reference/smart_contract_reference/storage/index.md +++ b/docs/docs/developers/reference/smart_contract_reference/storage/index.md @@ -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: diff --git a/docs/docs/developers/reference/smart_contract_reference/storage/public_state.md b/docs/docs/developers/reference/smart_contract_reference/storage/public_state.md index 11848a79f77d..3aa98140c387 100644 --- a/docs/docs/developers/reference/smart_contract_reference/storage/public_state.md +++ b/docs/docs/developers/reference/smart_contract_reference/storage/public_state.md @@ -71,7 +71,7 @@ We have a `write` method on the `PublicMutable` struct that takes the value to w ## `PublicImmutable` -`PublicImmutable` is a type that is initialized from public once, typically during a contract deployment, but which can later be read from public, private and unconstrained execution contexts. This state variable is useful for stuff that you would usually have in `immutable` values in Solidity, e.g. this can be the name of a token or its number of decimals. +`PublicImmutable` is a type that is initialized from public once, typically during a contract deployment, but which can later be read from public, private and utility execution contexts. This state variable is useful for stuff that you would usually have in `immutable` values in Solidity, e.g. this can be the name of a token or its number of decimals. Just like the `PublicMutable` it is generic over the variable type `T`. The type `MUST` implement the `Serialize` and `Deserialize` traits. @@ -99,6 +99,6 @@ A `PublicImmutable`'s storage **must** only be set once via `initialize`. Attemp ### `read` -Returns the stored immutable value. This function is available in public, private and unconstrained contexts. +Returns the stored immutable value. This function is available in public, private and utility contexts. #include_code read_public_immutable /noir-projects/noir-contracts/contracts/docs_example_contract/src/main.nr rust diff --git a/docs/docs/developers/reference/smart_contract_reference/storage/shared_state.md b/docs/docs/developers/reference/smart_contract_reference/storage/shared_state.md index 77959e699ece..2c50606f88de 100644 --- a/docs/docs/developers/reference/smart_contract_reference/storage/shared_state.md +++ b/docs/docs/developers/reference/smart_contract_reference/storage/shared_state.md @@ -82,7 +82,7 @@ A `SharedMutable`'s storage **must** only be mutated via `schedule_value_change` ### `get_current_value` -Returns the current value in a public, private or unconstrained execution context. Once a value change is scheduled via `schedule_value_change` and a number of blocks equal to the delay passes, this automatically returns the new value. +Returns the current value in a public, private or utility execution context. Once a value change is scheduled via `schedule_value_change` and a number of blocks equal to the delay passes, this automatically returns the new value. #include_code shared_mutable_get_current_public /noir-projects/noir-contracts/contracts/auth_contract/src/main.nr rust diff --git a/docs/docs/developers/tutorials/codealong/contract_tutorials/counter_contract.md b/docs/docs/developers/tutorials/codealong/contract_tutorials/counter_contract.md index 24b733325d36..c0335fa6c324 100644 --- a/docs/docs/developers/tutorials/codealong/contract_tutorials/counter_contract.md +++ b/docs/docs/developers/tutorials/codealong/contract_tutorials/counter_contract.md @@ -127,7 +127,7 @@ The last thing we need to implement is the function in order to retrieve a count #include_code get_counter /noir-projects/noir-contracts/contracts/counter_contract/src/main.nr rust -This function is `unconstrained` which allows us to fetch data from storage without a transaction. We retrieve a reference to the `owner`'s `counter` from the `counters` Map. The `get_balance` function then operates on the owner's counter. This yields a private counter that only the private key owner can decrypt. +This is a `utility` function which and it's used to obtain the counter information out of a transaction. We retrieve a reference to the `owner`'s `counter` from the `counters` Map. The `get_balance` function then operates on the owner's counter. This yields a private counter that only the private key owner can decrypt. ## Compile diff --git a/docs/docs/developers/tutorials/codealong/contract_tutorials/nft_contract.md b/docs/docs/developers/tutorials/codealong/contract_tutorials/nft_contract.md index 6b404884d000..8716ffa5cf5f 100644 --- a/docs/docs/developers/tutorials/codealong/contract_tutorials/nft_contract.md +++ b/docs/docs/developers/tutorials/codealong/contract_tutorials/nft_contract.md @@ -123,12 +123,14 @@ Internal functions are functions that can only be called by the contract itself. - [`_store_payload_in_transient_storage_unsafe`](#_store_payload_in_transient_storage_unsafe) - a public function that is called when preparing a private balance increase. This function handles the needed public state updates. - [`finalize_transfer_to_private_unsafe`](#_finalize_transfer_to_private_unsafe) - finalizes a transfer from public to private state -### Unconstrained functions +### Utility functions -Unconstrained functions can be thought of as view functions from Solidity--they only return information from the contract storage or compute and return data without modifying contract storage. They are distinguished from functions with the `#[view]` annotation in that unconstrained functions cannot be called by other contracts. +The contract contains only 1 utility function: - [`get_private_nfts`](#get_private_nfts) - Returns an array of token IDs owned by the passed `AztecAddress` in private and a flag indicating whether a page limit was reached. +For more details on utility function see [Call Types section](../../../../aztec/concepts/call_types.md#utility) + ## Contract dependencies Before we can implement the functions, we need set up the contract storage, and before we do that we need to import the appropriate dependencies. @@ -289,7 +291,7 @@ Internal functions are functions that can only be called by this contract. The f It is labeled unsafe because the public function does not check the value of the storage slot before writing, but it is safe because of the private execution preceding this call. -This is transient storage since the storage is not permanent, but is scoped to the current transaction only, after which it will be reset. The partial note is stored the "hiding point slot" value (computed in `_prepare_private_balance_increase()`) in public storage. However subseqeuent enqueued call to `_finalize_transfer_to_private_unsafe()` will read the partial note in this slot, complete it and emit it. Since the note is completed, there is no use of storing the hiding point slot anymore so we will reset to empty. This saves a write to public storage too. +This is transient storage since the storage is not permanent, but is scoped to the current transaction only, after which it will be reset. The partial note is stored the "hiding point slot" value (computed in `_prepare_private_balance_increase()`) in public storage. However subsequent enqueued call to `_finalize_transfer_to_private_unsafe()` will read the partial note in this slot, complete it and emit it. Since the note is completed, there is no use of storing the hiding point slot anymore so we will reset to empty. This saves a write to public storage too. #include_code store_payload_in_transient_storage_unsafe /noir-projects/noir-contracts/contracts/nft_contract/src/main.nr rust @@ -307,9 +309,7 @@ Updates the public owner of the `token_id` to the `to` address. ### View function implementations -View functions in Aztec are similar to `view` functions in Solidity in that they only return information from the contract storage or compute and return data without modifying contract storage. These functions are different from unconstrained functions in that the return values are constrained by their definition in the contract. - -Public view calls that are part of a transaction will be executed by the sequencer when the transaction is being executed, so they are not private and will reveal information about the transaction. Private view calls can be safely used in private transactions for getting the same information. +NFT implements the following [view functions](../../../../aztec/concepts/call_types.md#view). #### `get_admin` @@ -343,9 +343,9 @@ Returns the name of the NFT contract in the private context. Returns the symbol of the NFT contract in the private context. -### Unconstrained function implementations +### Utility function implementations -Unconstrained functions are similar to `view` functions in Solidity in that they only return information from the contract storage or compute and return data without modifying contract storage. They are different from view functions in that the values are returned from the user's PXE and are not constrained by the contract's definition--if there is bad data in the user's PXE, they will get bad data back. +The NFT implements the following [utility](../../../../aztec/concepts/call_types.md#utility) function: #### `get_private_nfts` diff --git a/docs/docs/developers/tutorials/codealong/contract_tutorials/private_voting_contract.md b/docs/docs/developers/tutorials/codealong/contract_tutorials/private_voting_contract.md index 92ddc1c3b137..9ba62d96ba27 100644 --- a/docs/docs/developers/tutorials/codealong/contract_tutorials/private_voting_contract.md +++ b/docs/docs/developers/tutorials/codealong/contract_tutorials/private_voting_contract.md @@ -134,7 +134,7 @@ We will create a function that anyone can call that will return the number of vo #include_code get_vote noir-projects/noir-contracts/contracts/easy_private_voting_contract/src/main.nr rust -We set it as `unconstrained` and do not annotate it because it is only reading from state. +We set it as `utility` because it is only reading from state. ## Allowing an admin to end a voting period @@ -172,7 +172,7 @@ Follow the crowdfunding contracts tutorial on the [next page](./crowdfunding_con ### Optional: Learn more about concepts mentioned here -- [Unconstrained functions](../../../../aztec/smart_contracts/functions/attributes.md#unconstrained-functions). +- [Utility functions](../../../../aztec/smart_contracts/functions/attributes.md#utility-functions). - [Oracles](../../../../aztec/smart_contracts/oracles/index.md) - [Nullifier secrets](../../../../aztec/concepts/accounts/keys.md#nullifier-keys). - [How to deploy a contract to the sandbox](../../../guides/js_apps/deploy_contract.md) diff --git a/docs/docs/developers/tutorials/codealong/contract_tutorials/token_contract.md b/docs/docs/developers/tutorials/codealong/contract_tutorials/token_contract.md index 5aa6053ce908..06bf90b719f4 100644 --- a/docs/docs/developers/tutorials/codealong/contract_tutorials/token_contract.md +++ b/docs/docs/developers/tutorials/codealong/contract_tutorials/token_contract.md @@ -123,10 +123,6 @@ Step 3. Ethereum execution Aztec transactions can pass messages to Ethereum contracts through the rollup via the outbox. The data can be consumed by Ethereum contracts at a later time, but this is not part of the transaction flow for an Aztec transaction. The technical details of this are beyond the scope of this tutorial, but we will cover them in an upcoming piece. -### Unconstrained functions - -Unconstrained functions can be thought of as view functions from Solidity--they only return information from the contract storage or compute and return data without modifying contract storage. - ## Contract dependencies Before we can implement the functions, we need set up the contract storage, and before we do that we need to import the appropriate dependencies. @@ -352,9 +348,7 @@ Similar to `_finalize_transfer_to_private_unsafe`, this public internal function ### View function implementations -View functions in Aztec are similar to `view` functions in Solidity in that they only return information from the contract storage or compute and return data without modifying contract storage. These functions are different from unconstrained functions in that the return values are constrained by their definition in the contract. - -Public view calls that are part of a transaction will be executed by the sequencer when the transaction is being executed, so they are not private and will reveal information about the transaction. Private view calls can be safely used in private transactions for getting the same information. +The contract contains the following [view](../../../../aztec/concepts/call_types.md#view) functions: #### `admin` @@ -380,9 +374,9 @@ A getter function for checking the public balance of the provided Aztec account. #include_code balance_of_public /noir-projects/noir-contracts/contracts/token_contract/src/main.nr rust -### Unconstrained function implementations +### Utility function implementations -Unconstrained functions are similar to `view` functions in Solidity in that they only return information from the contract storage or compute and return data without modifying contract storage. They are different from view functions in that the values are returned from the user's PXE and are not constrained by the contract's definition--if there is bad data in the user's PXE, they will get bad data back. +The Token implements the following [utility](../../../../aztec/concepts/call_types.md#utility) functions: #### `balance_of_private` diff --git a/docs/docs/developers/tutorials/codealong/js_tutorials/aztecjs-getting-started.md b/docs/docs/developers/tutorials/codealong/js_tutorials/aztecjs-getting-started.md index 69fa56d5d843..a94f3757a365 100644 --- a/docs/docs/developers/tutorials/codealong/js_tutorials/aztecjs-getting-started.md +++ b/docs/docs/developers/tutorials/codealong/js_tutorials/aztecjs-getting-started.md @@ -196,7 +196,7 @@ No transaction is submitted as a result but a user's state can be queried. We can see that each account has the expected balance of tokens. -### Calling an unconstrained (view) function +### Calling a view function Unconstrained function call diff --git a/docs/docs/developers/tutorials/codealong/js_tutorials/simple_dapp/3_contract_interaction.md b/docs/docs/developers/tutorials/codealong/js_tutorials/simple_dapp/3_contract_interaction.md index 876c306a5bc5..cb78d197534a 100644 --- a/docs/docs/developers/tutorials/codealong/js_tutorials/simple_dapp/3_contract_interaction.md +++ b/docs/docs/developers/tutorials/codealong/js_tutorials/simple_dapp/3_contract_interaction.md @@ -6,7 +6,7 @@ In this section, we'll write the logic in our app that will interact with the co ## Showing user balance -Let's start by showing our user's private balance for the token across their accounts. To do this, we can leverage the `balance_of_private` unconstrained view function of the token contract: +Let's start by showing our user's private balance for the token across their accounts. To do this, we can leverage the `balance_of_private` utility function of the token contract: #include_code balance_of_private noir-projects/noir-contracts/contracts/token_contract/src/main.nr rust diff --git a/docs/docs/protocol-specs/bytecode/index.md b/docs/docs/protocol-specs/bytecode/index.md index c863019b947d..bff0df7628f3 100644 --- a/docs/docs/protocol-specs/bytecode/index.md +++ b/docs/docs/protocol-specs/bytecode/index.md @@ -30,11 +30,10 @@ In the context of Aztec, a contract is a set of functions which can be of one of - Private functions: The functions that run on user's machines. They are circuits that must be individually executed by the [ACVM](https://github.com/noir-lang/noir/blob/master/acvm-repo/acvm/src/pwg/mod.rs#L132) and proved by barretenberg. - Public functions: The functions that are run by sequencers. They are aggregated in a bytecode block that must be executed and proven by the AVM. -- Unconstrained functions: Helper functions that are run on users' machines but are not constrained. They are represented individually as bytecode that is executed by the ACVM. - - Unconstrained functions are often used to fetch and serialize private data, for use as witnesses to a circuit. +- Utility functions: Helper functions that are run on users' machines but are not constrained. They are represented individually as bytecode that is executed by the ACVM. - They can also be used to convey how dapps should handle a particular contract's data. -When a contract is compiled, private and unconstrained functions are compiled individually. Public functions are compiled together to a single bytecode with an initial dispatch table based on function selectors. Since public functions are run in a VM, we do not incur a huge extra proving cost for the branching that is required to execute different functions. +When a contract is compiled, private and utility functions are compiled individually. Public functions are compiled together to a single bytecode with an initial dispatch table based on function selectors. Since public functions are run in a VM, we do not incur a huge extra proving cost for the branching that is required to execute different functions. If a private function needs unconstrained hints, the bytecode that generates the unconstrained hints is embedded in the private circuit. This allows the ACVM to compute the hints during witness generation. @@ -46,11 +45,11 @@ The AVM bytecode is the compilation target of the public functions of a contract # Brillig Bytecode -Brillig bytecode is the compilation target of all the unconstrained code in a contract. Any unconstrained hint used by a private function is compiled to Brillig bytecode. Also, contracts' top level unconstrained functions are entirely compiled to Brillig bytecode. In the case of Noir, it compiles public functions entirely to a single block of brillig bytecode that is then converted to AVM bytecode. Similarly to AVM bytecode, Brillig bytecode allows control flow. +Brillig bytecode is the compilation target of all the unconstrained code in a contract. Any unconstrained hint used by a private function is compiled to Brillig bytecode. Also, contracts' utility functions are entirely compiled to Brillig bytecode. In the case of Noir, it compiles public functions entirely to a single block of brillig bytecode that is then converted to AVM bytecode. Similarly to AVM bytecode, Brillig bytecode allows control flow. Brillig bytecode will be very similar to AVM bytecode. While AVM bytecode is specifically designed to be executed by the AVM, brillig bytecode is meant to be more general and allow the use of arbitrary oracles. -Oracles allow nondeterminism during the execution of a given function, allowing the simulator entity to choose the value that an oracle will return during the simulation process. Oracles are heavily used by aztec.nr to fetch data during simulation of private and unconstrained functions, such as fetching notes. They are also used to notify the simulator about events arising during execution, such as a nullified note so that it's not offered again during the simulation. +Oracles allow nondeterminism during the execution of a given function, allowing the simulator entity to choose the value that an oracle will return during the simulation process. Oracles are heavily used by aztec.nr to fetch data during simulation of private and utility functions, such as fetching notes. They are also used to notify the simulator about events arising during execution, such as a nullified note so that it's not offered again during the simulation. However, AVM bytecode doesn't allow arbitrary oracles, any nondeterminism introduced is done in a way that the protocol can ensure that the simulator entity (the sequencer) cannot manipulate the result of an oracle. As such, when transforming brillig bytecode to AVM bytecode, all the oracles are replaced by the specific opcodes that the AVM supports for nondeterminism, like [TIMESTAMP](../public-vm/instruction-set.mdx#isa-section-timestamp), [ADDRESS](../public-vm/instruction-set.mdx#isa-section-address), etc. Any opcode that requires the simulator entity to provide data external to the AVM memory is non-deterministic. @@ -98,7 +97,7 @@ The exact form of the artifact is not specified by the protocol, but it needs at ### Function entry -If the function is public, the entry will be its ABI. If the function is private or unconstrained, the entry will be the ABI + the artifact. +If the function is public, the entry will be its ABI. If the function is private or utility, the entry will be the ABI + the artifact. ### Function artifact @@ -113,7 +112,7 @@ If the function is public, the entry will be its ABI. If the function is private | Field | Type | Description | |----------|----------|----------| | `name` | `string` | The name of the function. | -| `functionType` | `string` | `private`, `public` or `unconstrained`. | +| `functionType` | `string` | `private`, `public` or `utility`. | | `parameters` | [ABIParameter[]](#abi-parameter) | Function parameters. | | `returnTypes` | `AbiType[]` | The types of the return values. | @@ -150,11 +149,11 @@ If the function is public, the entry will be its ABI. If the function is private The protocol mandates that public bytecode needs to be published to a data availability solution, since the sequencers need to have the data available to run the public functions. Also, it needs to use an encoding that is friendly to the public VM, such as the one specified in the [AVM section](../public-vm/bytecode-validation-circuit.md). -The bytecode of private and unconstrained functions doesn't need to be published, instead, users that desire to use a given contract can add the artifact to their PXE before interacting with it. Publishing it is [supported but not required](../contract-deployment/classes.md#broadcast) by the protocol. However, the verification key of a private function is hashed into the function's leaf of the contract's function tree, so the user can prove to the protocol that he executed the function correctly. Also, contract classes contain an [artifact hash](../contract-deployment/classes.md#artifact-hash) so the PXE can verify that the artifact corresponds with the contract class. +The bytecode of private and utility functions doesn't need to be published, instead, users that desire to use a given contract can add the artifact to their PXE before interacting with it. Publishing it is [supported but not required](../contract-deployment/classes.md#broadcast) by the protocol. However, the verification key of a private function is hashed into the function's leaf of the contract's function tree, so the user can prove to the protocol that he executed the function correctly. Also, contract classes contain an [artifact hash](../contract-deployment/classes.md#artifact-hash) so the PXE can verify that the artifact corresponds with the contract class. -The encoding of private and unconstrained functions is not specified by the protocol, but it's recommended to follow [the encoding](https://github.com/noir-lang/noir/blob/master/acvm-repo/acir/src/circuit/mod.rs#L157) that Barretenberg and the ACVM share that is serialization using bincode and gzip for compression. +The encoding of private and utility functions is not specified by the protocol, but it's recommended to follow [the encoding](https://github.com/noir-lang/noir/blob/master/acvm-repo/acir/src/circuit/mod.rs#L157) that Barretenberg and the ACVM share that is serialization using bincode and gzip for compression. -This implies that the encoding of private and unconstrained functions does not need to be friendly to circuits, since when publishing it the protocol only sees a [generic array of field elements](../contract-deployment/classes.md#broadcast). +This implies that the encoding of private and utility functions does not need to be friendly to circuits, since when publishing it the protocol only sees a [generic array of field elements](../contract-deployment/classes.md#broadcast). ## Executing a private function @@ -162,9 +161,9 @@ When executing a private function, its ACIR bytecode will be executed by the PXE The fact that the correct function was executed is checked by the protocol by verifying that the [contract class ID](../contract-deployment/classes.md#class-identifier) contains one leaf in the function tree with this selector and the verification key of the function. -## Executing an unconstrained function +## Executing a utility function -When executing an unconstrained function, its Brillig bytecode will be executed by the PXE using the ACVM, similarly to private functions, but the PXE will not prove the execution. Instead, the PXE will return the result of the execution of the function to the user. +When executing a utility function, its Brillig bytecode will be executed by the PXE using the ACVM, similarly to private functions, but the PXE will not prove the execution. Instead, the PXE will return the result of the execution of the function to the user. ## Executing a public function diff --git a/docs/docs/protocol-specs/contract-deployment/classes.md b/docs/docs/protocol-specs/contract-deployment/classes.md index 55661e59f8e9..4587232a3bc8 100644 --- a/docs/docs/protocol-specs/contract-deployment/classes.md +++ b/docs/docs/protocol-specs/contract-deployment/classes.md @@ -1,6 +1,6 @@ # Contract classes -A contract class is a collection of state variable declarations, and related unconstrained, private, and public functions. Contract classes don't have any initialized state, they just define code. 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 any initialized state, they just define code. A contract class cannot be called; only a contract instance can be called. ## Rationale @@ -23,15 +23,15 @@ The structure of a contract class is defined as: | Field | Type | Description | |----------|----------|----------| | `version` | `u8` | Version identifier. Initially one, bumped for any changes to the contract class struct. | -| `artifact_hash` | `Field` | Hash of the contract artifact. The specification of this hash is not enforced by the protocol. Should include commitments to unconstrained code and compilation metadata. Intended to be used by clients to verify that an off-chain fetched artifact matches a registered class. | +| `artifact_hash` | `Field` | Hash of the contract artifact. The specification of this hash is not enforced by the protocol. Should include commitments to utility function code and compilation metadata. Intended to be used by clients to verify that an off-chain fetched artifact matches a registered class. | | `private_functions` | [`PrivateFunction[]`](#private-function) | List of individual private functions, constructors included. | | `packed_public_bytecode` | `Field[]` | [Packed bytecode representation](../public-vm/bytecode-validation-circuit.md#packed-bytecode-representation) of the AVM bytecode for all public functions in this contract. | The public function are sorted in ascending order by their function selector before being packed. This is to ensure consistent hashing later. -Note that individual public functions are not first-class citizens in the protocol, so the contract entire public function bytecode is stored in the class, unlike private or unconstrained functions which are differentiated individual circuits recognized by the protocol. +Note that individual public functions are not first-class citizens in the protocol, so the contract entire public function bytecode is stored in the class, unlike private or utility functions which are differentiated individual circuits recognized by the protocol. -As for unconstrained functions, these are not used standalone within the protocol. They are either inlined within private functions, or called from a PXE as _getters_ for a contract. Calling from a private function to an unconstrained one in a different contract is forbidden, since the caller would have no guarantee of the code run by the callee. Considering this, unconstrained functions are not part of a contract class at the protocol level. +As for utility functions, these are not used standalone within the protocol. They are called from a PXE as _getters_ for a contract. Calling from a private function to a utility function is forbidden. Considering this, utility functions are not part of a contract class at the protocol level. ### `contract_class_id` @@ -129,15 +129,15 @@ artifact_crh( ); let private_functions_artifact_tree_root: Field = merkleize(private_functions_artifact_leaves); - let unconstrained_functions_artifact_leaves: Field[] = artifact.unconstrained_functions.map(|f| + let utility_functions_artifact_leaves: Field[] = artifact.utility_functions.map(|f| sha256_modulo( VERSION, // 8-bits f.selector, // 32-bits f.metadata_hash, // 256-bits - sha256(f.unconstrained_bytecode) + sha256(f.utility_bytecode) ) ); - let utility_functions_artifact_tree_root: Field = merkleize(unconstrained_functions_artifact_leaves); + let utility_functions_artifact_tree_root: Field = merkleize(utility_functions_artifact_leaves); let artifact_hash: Field = sha256_modulo( VERSION, // 8-bits @@ -158,7 +158,7 @@ Function leaves are sorted in ascending order before being merkleized, according -Bytecode for private functions is a mix of ACIR and Brillig, whereas unconstrained function bytecode is Brillig exclusively, as described on the [bytecode section](../bytecode/index.md). +Bytecode for private functions is a mix of ACIR and Brillig, whereas utility function bytecode is Brillig exclusively, as described on the [bytecode section](../bytecode/index.md). The metadata hash for each function is suggested to be computed as the sha256 (modulo) of all JSON-serialized ABI types of the function returns. Other function data is represented in the leaf hash by its bytecode and selector. @@ -257,7 +257,7 @@ The `ContractClassRegisterer` will need to exist from the genesis of the Aztec N ### Broadcast -The `ContractClassRegisterer` has an additional private `broadcast` functions that can be used for broadcasting on-chain the bytecode, both ACIR and Brillig, for private functions and unconstrained in the contract. Any user can freely call this function. Given that ACIR and Brillig [do not have a circuit-friendly commitment](../bytecode/index.md), it is left up to nodes to perform this check. +The `ContractClassRegisterer` has an additional private `broadcast` functions that can be used for broadcasting on-chain the bytecode, both ACIR and Brillig, for private and utility functions in the contract. Any user can freely call this function. Given that ACIR and Brillig [do not have a circuit-friendly commitment](../bytecode/index.md), it is left up to nodes to perform this check. Broadcasted function artifacts that do not match with their corresponding `artifact_hash`, or that reference a `contract_class_id` that has not been broadcasted, can be safely discarded. @@ -305,7 +305,7 @@ fn broadcast_utility_function( -The broadcast functions are split between private and unconstrained to allow for private bytecode to be broadcasted, which is valuable for composability purposes, without having to also include unconstrained functions, which could be costly to do due to data broadcasting costs. Additionally, note that each broadcast function must include enough information to reconstruct the `artifact_hash` from the Contract Class, so nodes can verify it against the one previously registered. +The broadcast functions are split between private and utility to allow for private bytecode to be broadcasted, which is valuable for composability purposes, without having to also include utility functions, which could be costly to do due to data broadcasting costs. Additionally, note that each broadcast function must include enough information to reconstruct the `artifact_hash` from the Contract Class, so nodes can verify it against the one previously registered. A node that captures a `ClassPrivateFunctionBroadcasted` should perform the following validation steps before storing the private function information in its database: @@ -327,7 +327,7 @@ assert computed_artifact_hash == contract_class.artifact_hash -The check for an unconstrained function is similar: +The check for a utility function is similar: ``` // Load contract class from local db @@ -335,8 +335,8 @@ contract_class = db.get_contract_class(contract_class_id) // Compute artifact leaf and assert it belongs to the artifact artifact_function_leaf = sha256(selector, metadata_hash, sha256(bytecode)) -computed_artifact_unconstrained_function_tree_root = compute_root(artifact_function_leaf, artifact_function_tree_sibling_path, artifact_function_tree_leaf_index) -computed_artifact_hash = sha256(private_functions_artifact_tree_root, computed_artifact_unconstrained_function_tree_root, artifact_metadata_hash) +computed_artifact_utility_function_tree_root = compute_root(artifact_function_leaf, artifact_function_tree_sibling_path, artifact_function_tree_leaf_index) +computed_artifact_hash = sha256(private_functions_artifact_tree_root, computed_artifact_utility_function_tree_root, artifact_metadata_hash) assert computed_artifact_hash == contract_class.artifact_hash ``` diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr index c3fa782afa4b..e09dfcb0197b 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr @@ -40,7 +40,7 @@ impl TestEnvironment { /// With TXe tests, every test is run in a mock "contract". This facilitates the ability to write to and read from storage, /// emit and retrieve nullifiers (because they are hashed with a contract address), and formulate notes in a canonical way. /// The contract_address also represents the "msg_sender" when we call a contract with a private or public context; and when - /// we call top-level unconstrained functions, we must set this contract address to the contract being called. + /// we call utility functions, we must set this contract address to the contract being called. /// The contract address can be manipulated to do the above at any particular address, and not simply the one provided at /// the instantiation of the test. /// Returns the currently set contract address. @@ -50,7 +50,8 @@ impl TestEnvironment { /// Modifies the currently set contract address. As per above, it sets the "msg_sender" address on our subsequent calls. /// This is useful when we have multiple "accounts" that need to interact with an arbitrary contract. This also allows - /// us to change the "contract" we emit side effects from, and is required when we want to run a top-level unconstrained function on another contract. + /// us to change the "contract" we emit side effects from, and is required when we want to run a utility function + /// on another contract. pub unconstrained fn impersonate(_self: Self, address: AztecAddress) { cheatcodes::set_contract_address(address) } diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/test/utils.nr b/noir-projects/noir-contracts/contracts/token_contract/src/test/utils.nr index 3b0a83864bc7..af8580db0b10 100644 --- a/noir-projects/noir-contracts/contracts/token_contract/src/test/utils.nr +++ b/noir-projects/noir-contracts/contracts/token_contract/src/test/utils.nr @@ -140,7 +140,7 @@ pub unconstrained fn check_total_supply( cheatcodes::set_contract_address(current_contract_address); } -// docs:start:txe_test_call_unconstrained +// docs:start:txe_test_call_utility pub unconstrained fn check_private_balance( token_contract_address: AztecAddress, address: AztecAddress, @@ -148,12 +148,12 @@ pub unconstrained fn check_private_balance( ) { let current_contract_address = get_contract_address(); cheatcodes::set_contract_address(token_contract_address); - // Direct call to unconstrained + // Direct call to a utility function let balance_of_private = Token::balance_of_private(address); assert(balance_of_private == address_amount, "Private balance is not correct"); cheatcodes::set_contract_address(current_contract_address); } -// docs:end:txe_test_call_unconstrained +// docs:end:txe_test_call_utility pub unconstrained fn get_private_balance( token_contract_address: AztecAddress, @@ -161,7 +161,6 @@ pub unconstrained fn get_private_balance( ) -> u128 { let current_contract_address = get_contract_address(); cheatcodes::set_contract_address(token_contract_address); - // Direct call to unconstrained let amt = Token::balance_of_private(address); cheatcodes::set_contract_address(current_contract_address); amt diff --git a/yarn-project/simulator/src/private/simulator.ts b/yarn-project/simulator/src/private/simulator.ts index fb5052cce0ba..95039755fdfe 100644 --- a/yarn-project/simulator/src/private/simulator.ts +++ b/yarn-project/simulator/src/private/simulator.ts @@ -115,7 +115,7 @@ export class AcirSimulator { } } - // docs:start:execute_unconstrained_function + // docs:start:execute_utility_function /** * Runs a utility function. * @param call - The function call to execute. @@ -163,5 +163,5 @@ export class AcirSimulator { throw createSimulationError(err instanceof Error ? err : new Error('Unknown error during private execution')); } } - // docs:end:execute_unconstrained_function + // docs:end:execute_utility_function }