Skip to content
Merged
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
8 changes: 4 additions & 4 deletions boxes/boxes/react/src/contracts/src/main.nr
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use aztec::macros::aztec;
contract BoxReact {
use aztec::{
messages::message_delivery::MessageDelivery,
macros::{functions::{initializer, private, utility}, storage::storage},
macros::{functions::{external, initializer}, storage::storage},
protocol_types::address::AztecAddress,
state_vars::{Map, PrivateMutable},
};
Expand All @@ -15,7 +15,7 @@ contract BoxReact {
numbers: Map<AztecAddress, PrivateMutable<ValueNote, Context>, Context>,
}

#[private]
#[external("private")]
#[initializer]
fn constructor(number: Field, owner: AztecAddress) {
let numbers = storage.numbers;
Expand All @@ -28,7 +28,7 @@ contract BoxReact {
);
}

#[private]
#[external("private")]
fn setNumber(number: Field, owner: AztecAddress) {
let numbers = storage.numbers;

Expand All @@ -44,7 +44,7 @@ contract BoxReact {
}


#[utility]
#[external("utility")]
unconstrained fn getNumber(owner: AztecAddress) -> ValueNote {
let numbers = storage.numbers;
numbers.at(owner).view_note()
Expand Down
12 changes: 6 additions & 6 deletions boxes/boxes/vanilla/contracts/src/main.nr
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use dep::aztec::macros::aztec;
pub contract PrivateVoting {
use dep::aztec::keys::getters::get_public_keys;
use dep::aztec::macros::{
functions::{initializer, internal, private, public, utility},
functions::{external, initializer, internal},
storage::storage,
};
use dep::aztec::state_vars::{Map, PublicImmutable, PublicMutable};
Expand All @@ -26,15 +26,15 @@ pub contract PrivateVoting {
active_at_block: PublicImmutable<u32, Context>, // when people can start voting
}

#[public]
#[external("public")]
#[initializer]
fn constructor(admin: AztecAddress) {
storage.admin.write(admin);
storage.vote_ended.write(false);
storage.active_at_block.initialize(context.block_number());
}

#[private]
#[external("private")]
fn cast_vote(candidate: Field) {
let msg_sender_npk_m_hash = get_public_keys(context.msg_sender().unwrap()).npk_m.hash();

Expand All @@ -46,21 +46,21 @@ pub contract PrivateVoting {
);
}

#[public]
#[external("public")]
#[internal]
fn add_to_tally_public(candidate: Field) {
assert(storage.vote_ended.read() == false, "Vote has ended"); // assert that vote has not ended
let new_tally = storage.tally.at(candidate).read() + 1;
storage.tally.at(candidate).write(new_tally);
}

#[public]
#[external("public")]
fn end_vote() {
assert(storage.admin.read().eq(context.msg_sender().unwrap()), "Only admin can end votes"); // assert that caller is admin
storage.vote_ended.write(true);
}

#[utility]
#[external("utility")]
unconstrained fn get_vote(candidate: Field) -> Field {
storage.tally.at(candidate).read()
}
Expand Down
8 changes: 4 additions & 4 deletions boxes/boxes/vite/src/contracts/src/main.nr
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use aztec::macros::aztec;
contract BoxReact {
use aztec::{
messages::message_delivery::MessageDelivery,
macros::{functions::{initializer, private, utility}, storage::storage},
macros::{functions::{external, initializer}, storage::storage},
protocol_types::address::AztecAddress,
state_vars::{Map, PrivateMutable},
};
Expand All @@ -15,7 +15,7 @@ contract BoxReact {
numbers: Map<AztecAddress, PrivateMutable<ValueNote, Context>, Context>,
}

#[private]
#[external("private")]
#[initializer]
fn constructor(number: Field, owner: AztecAddress) {
let numbers = storage.numbers;
Expand All @@ -28,7 +28,7 @@ contract BoxReact {
);
}

#[private]
#[external("private")]
fn setNumber(number: Field, owner: AztecAddress) {
let numbers = storage.numbers;

Expand All @@ -43,7 +43,7 @@ contract BoxReact {
);
}

#[utility]
#[external("utility")]
unconstrained fn getNumber(owner: AztecAddress) -> ValueNote {
let numbers = storage.numbers;
numbers.at(owner).view_note()
Expand Down
2 changes: 1 addition & 1 deletion boxes/init/src/main.nr
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use dep::aztec::macros::aztec;

#[aztec]
contract Main {
#[private]
#[external("private")]
#[initializer]
fn constructor() { }
}
6 changes: 3 additions & 3 deletions docs/docs/developers/docs/concepts/call_types.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ While Ethereum contracts are defined by bytecode that runs on the EVM, Aztec con

### Private Execution

Contract functions marked with `#[private]` can only be called privately, and as such 'run' in the user's device. Since they're circuits, their 'execution' is actually the generation of a zk-SNARK proof that'll later be sent to the sequencer for verification.
Contract functions marked with `#[external("private")]` can only be called privately, and as such 'run' in the user's device. Since they're circuits, their 'execution' is actually the generation of a zk-SNARK proof that'll later be sent to the sequencer for verification.

#### Private Calls

Expand Down Expand Up @@ -153,7 +153,7 @@ For this reason it is encouraged to try to avoid public function calls and inste

### Public Execution

Contract functions marked with `#[public]` can only be called publicly, and are executed by the sequencer. The computation model is very similar to the EVM: all state, parameters, etc. are known to the entire network, and no data is private. Static execution like the EVM's `STATICCALL` is possible too, with similar semantics (state can be accessed but not modified, etc.).
Contract functions marked with `#[external("public")]` can only be called publicly, and are executed by the sequencer. The computation model is very similar to the EVM: all state, parameters, etc. are known to the entire network, and no data is private. Static execution like the EVM's `STATICCALL` is possible too, with similar semantics (state can be accessed but not modified, etc.).

Since private calls are always run in a user's device, it is not possible to perform any private execution from a public context. A reasonably good mental model for public execution is that of an EVM in which some work has already been done privately, and all that is know about it is its correctness and side-effects (new notes and nullifiers, enqueued public calls, etc.). A reverted public execution will also revert the private side-effects.

Expand All @@ -167,7 +167,7 @@ This is the same function that was called by privately enqueuing a call to it! P

### Utility

Contract functions marked with `#[utility]` cannot be called as part of a transaction, and are only invoked by applications that interact with contracts to perform state queries from an offchain client (from both private and public state!) or to modify local contract-related PXE state (e.g. when processing logs in Aztec.nr). No guarantees are made on the correctness of the result since the entire execution is unconstrained and heavily reliant on oracle calls. It is possible however to verify that the bytecode being executed is the correct one, since a contract's address includes a commitment to all of its utility functions.
Contract functions marked with `#[external("utility")]` cannot be called as part of a transaction, and are only invoked by applications that interact with contracts to perform state queries from an offchain client (from both private and public state!) or to modify local contract-related PXE state (e.g. when processing logs in Aztec.nr). No guarantees are made on the correctness of the result since the entire execution is unconstrained and heavily reliant on oracle calls. It is possible however to verify that the bytecode being executed is the correct one, since a contract's address includes a commitment to all of its utility functions.

### aztec.js

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ Contract upgrades in Aztec have to be initiated by the contract that wishes to b
use dep::aztec::protocol_types::contract_class_id::ContractClassId;
use contract_instance_registry::ContractInstanceRegistry;

#[private]
#[external("private")]
fn update_to(new_class_id: ContractClassId) {
ContractInstanceRegistry::at(CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS)
.update(new_class_id)
Expand All @@ -43,7 +43,7 @@ fn update_to(new_class_id: ContractClassId) {
The `update` function in the registry is a public function, so you can enqueue it from a private function like the example or call it from a public function directly.

:::note
Recall that `#[private]` means calling this function preserves privacy, and it still CAN be called externally by anyone.
Recall that `#[external("private")]` means calling this function preserves privacy, and it still CAN be called externally by anyone.
So the `update_to` function above allows anyone to update the contract that implements it. A more complete implementation should have a proper authorization systems to secure contracts from malicious upgrades.
:::

Expand All @@ -54,7 +54,7 @@ This means that they have a delay before entering into effect. The default delay
use dep::aztec::protocol_types::contract_class_id::ContractClassId;
use contract_instance_registry::ContractInstanceRegistry;

#[private]
#[external("private")]
fn set_update_delay(new_delay: u64) {
ContractInstanceRegistry::at(CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS)
.set_update_delay(new_delay)
Expand Down Expand Up @@ -107,7 +107,7 @@ Consider this contract as an example:
contract Updatable {
...

#[private]
#[external("private")]
fn update_to(new_class_id: ContractClassId) {
ContractInstanceRegistry::at(CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS).update(new_class_id).enqueue(
&mut context,
Expand Down Expand Up @@ -164,7 +164,7 @@ await RandomContract.at(address, wallet);

```rust
contract Updatable {
#[private]
#[external("private")]
fn update_to(new_class_id: ContractClassId) {
// TODO: Add access control
assert(context.msg_sender() == owner, "Unauthorized");
Expand All @@ -175,7 +175,7 @@ contract Updatable {
.enqueue(&mut context);
}

#[private]
#[external("private")]
fn set_update_delay(new_delay: u64) {
// TODO: Add access control
ContractInstanceRegistry::at(CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,17 @@ On this page you will learn about function attributes and macros.

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

## Private functions #[private]
# External functions #[external("...")]

A private function operates on private information, and is executed by the user on their device. Annotate the function with the `#[private]` attribute to tell the compiler it's a private function. This will make the [private context](./context.md#the-private-context) available within the function's execution scope. The compiler will create a circuit to define this function.
Like in Solidity, external functions can be called from outside the contract.
There are 3 types of external functions differing in the execution environment they are executed in: private, public, and utility.
We will describe each type in the following sections.

`#[private]` is just syntactic sugar. At compile time, the Aztec.nr framework inserts code that allows the function to interact with the [kernel](../../advanced/circuits/kernels/private_kernel.md).
## Private functions #[external("private")]

A private function operates on private information, and is executed by the user on their device. Annotate the function with the `#[external("private")]` attribute to tell the compiler it's a private function. This will make the [private context](./context.md#the-private-context) available within the function's execution scope. The compiler will create a circuit to define this function.

`#[external("private")]` is just syntactic sugar. At compile time, the Aztec.nr framework inserts code that allows the function to interact with the [kernel](../../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.

Expand Down Expand Up @@ -76,9 +82,9 @@ Any state variables declared in the `Storage` struct can now be accessed as norm

This function takes the application context, and converts it into the `PrivateCircuitPublicInputs` structure. This structure is then passed to the kernel circuit.

## Utility functions #[utility]
## Utility functions #[external("utility")]

Contract functions marked with `#[utility]` are used to perform state queries from an offchain 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](https://noir-lang.org/docs/explainers/explainer-oracle).
Contract functions marked with `#[external("utility")]` are used to perform state queries from an offchain 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](https://noir-lang.org/docs/explainers/explainer-oracle).

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 as Noir contract code because they let developers utilize the rest of the contract code directly by being part of the same Noir crate, 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.

Expand Down Expand Up @@ -110,15 +116,15 @@ Beyond using them inside your other functions, they are convenient for providing
Note, that utility functions can have access to both private and (historical) public data when executed on the user's device. This is possible since these functions are not invoked as part of transactions, so we don't need to worry about preventing a contract from e.g. accidentally using stale or unverified public state.
:::

## Public functions #[public]
## Public functions #[external("public")]

A public function is executed by the sequencer and has access to a state model that is very similar to that of the EVM and Ethereum. Even though they work in an EVM-like model for public transactions, they are able to write data into private storage that can be consumed later by a private function.

:::note
All data inserted into private storage from a public function will be publicly viewable (not private).
:::

To create a public function you can annotate it with the `#[public]` attribute. This will make the public context available within the function's execution scope.
To create a public function you can annotate it with the `#[external("public")]` attribute. This will make the public context available within the function's execution scope.

#include_code set_minter /noir-projects/noir-contracts/contracts/app/token_contract/src/main.nr rust

Expand All @@ -144,7 +150,7 @@ let storage = Storage::init(&mut context);

## Constrained `view` Functions #[view]

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).
The `#[view]` attribute can be applied to a `#[external("private")]` or a `#[external("public")]` function and it guarantees that the function cannot modify any contract state (just like `view` functions in Solidity).

## `Initializer` Functions #[initializer]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ let notes = storage.my_notes.at(owner).get_notes(options);
// Assuming MyNote has an 'owner' field
let mut options = NoteGetterOptions::new();
options = options.select(
MyNote::properties().owner,
Comparator.EQ,
MyNote::properties().owner,
Comparator.EQ,
owner
);
```
Expand Down Expand Up @@ -84,7 +84,7 @@ fn filter_above_threshold(
) -> [Option<RetrievedNote<Note>>; MAX_NOTES] {
let mut result = [Option::none(); MAX_NOTES];
let mut count = 0;

for note in notes {
if note.is_some() & (note.unwrap().note.value >= min) {
result[count] = note;
Expand Down Expand Up @@ -140,7 +140,7 @@ contract.methods.read_notes(Comparator.GTE, 5).simulate({ from: defaultAddress }
```rust
use dep::aztec::note::note_viewer_options::NoteViewerOptions;

#[utility]
#[external("utility")]
unconstrained fn view_notes(comparator: u8, value: Field) -> auto {
let mut options = NoteViewerOptions::new();
options = options.select(MyNote::properties().value, comparator, value);
Expand Down
Loading
Loading