Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ Use generated interfaces instead of manual function calls:

```rust
contract MyContract {
use dep::token::Token;
use token::Token;

#[external("private")]
fn transfer_tokens(token_address: AztecAddress, recipient: AztecAddress, amount: u128) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ tags: [debugging, errors, logging, local_network, aztec.nr]
description: This guide shows you how to debug issues in your Aztec contracts.
---

<!-- need to move some into aztec.js -->

This guide shows you how to debug issues in your Aztec development environment.

## Prerequisites
Expand All @@ -33,7 +31,7 @@ Log values from your contract using `debug_log`:

```rust
// Import debug logging
use dep::aztec::oracle::debug_log::{ debug_log, debug_log_format };
use aztec::oracle::logging::{ debug_log, debug_log_format };

// Log simple messages
debug_log("checkpoint reached");
Expand All @@ -49,7 +47,7 @@ debug_log_format("values: {0}, {1}, {2}", [val1, val2, val3]);
```

:::note
Debug logs appear only during local execution. Private functions always execute locally, but public functions must be simulated to show logs. Use `.simulate()` or `.prove()` in TypeScript, or `env.simulate_public_function()` in TXE tests.
Debug logs appear only during local execution. Private functions always execute locally, but public functions must be simulated to show logs. Use `.simulate()` or `.prove()` in TypeScript, or `env.call_public()` in TXE tests.
:::

To see debug logs from your tests, set `LOG_LEVEL` when running:
Expand Down Expand Up @@ -160,7 +158,7 @@ link.click();

## Interpret error messages

### Kernel circuit errors (2xxx)
### Circuit and protocol errors

- **Private kernel errors (2xxx)**: Issues with private function execution
- **Public kernel errors (3xxx)**: Issues with public function execution
Expand Down Expand Up @@ -207,7 +205,7 @@ LOG_LEVEL=verbose aztec start --local-network
### Common debug imports

```rust
use dep::aztec::oracle::debug_log::{ debug_log, debug_log_format };
use aztec::oracle::logging::{ debug_log, debug_log_format };
```

### Check contract registration
Expand All @@ -218,7 +216,7 @@ await wallet.getContractMetadata(myContractInstance.address);

### Decode L1 errors

Check hex errors against [Errors.sol](https://github.com/AztecProtocol/aztec-packages/blob/master/l1-contracts/src/core/libraries/Errors.sol)
Check hex errors against [Errors.sol](https://github.com/AztecProtocol/aztec-packages/blob/v4.2.0-aztecnr-rc.2/l1-contracts/src/core/libraries/Errors.sol)

## Tips

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,26 @@ Capsules provide per-contract non-volatile storage in the PXE. Data is stored lo
```rust
use aztec::oracle::capsules;

// Inside a contract function, use self.address for contract_address
let contract_address: AztecAddress = self.address;
// Capsule operations are unconstrained, so these values are typically
// passed in as parameters from the calling context.
let contract_address: AztecAddress = /* self.address */;
let slot: Field = 1;
// scope is an AztecAddress used for capsule isolation, allowing multiple
// independent namespaces within the same contract.
let scope: AztecAddress = /* e.g. the account address */;

// Store data at a slot (overwrites existing data)
capsules::store(contract_address, slot, value);
capsules::store(contract_address, slot, value, scope);

// Load data (returns Option<T>)
let result: Option<MyStruct> = capsules::load(contract_address, slot);
let result: Option<MyStruct> = capsules::load(contract_address, slot, scope);

// Delete data at a slot
capsules::delete(contract_address, slot);
capsules::delete(contract_address, slot, scope);

// Copy contiguous slots (supports overlapping regions)
// copy(contract_address, src_slot, dst_slot, num_entries: u32)
capsules::copy(contract_address, src_slot, dst_slot, 3);
// copy(contract_address, src_slot, dst_slot, num_entries: u32, scope)
capsules::copy(contract_address, src_slot, dst_slot, 3, scope);
```

Types must implement `Serialize` and `Deserialize` traits.
Expand All @@ -42,12 +46,12 @@ All capsule operations are `unconstrained`. Data loaded from capsules should be

```rust
use aztec::capsules::CapsuleArray;
use protocol::hash::sha256_to_field;
use aztec::protocol::hash::sha256_to_field;

// Use a hash for base_slot to avoid collisions with other storage
global BASE_SLOT: Field = sha256_to_field("MY_CONTRACT::MY_ARRAY".as_bytes());

let array: CapsuleArray<Field> = CapsuleArray::at(contract_address, BASE_SLOT);
let array: CapsuleArray<Field> = CapsuleArray::at(contract_address, BASE_SLOT, scope);

array.push(value); // Append to end
let value = array.get(index); // Read at index (throws if out of bounds)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ pub struct UintNote {

The `UintNote` struct itself only contains the `value` field. Additional fields including `owner`, `randomness`, and `storage_slot` are passed as parameters during note hash computation.

When creating the note locally during private execution, the `owner` and `storage_slot` are known, but the `value` potentially is not (e.g., it depends on some onchain dynamic variable). First, a **partial note** can be created during private execution that commits to the `owner`, `randomness`, and `storage_slot`, and then the note is *"completed"* to create a full note by later adding the `value` field, usually during public execution.
When creating the note locally during private execution, the `owner` and `storage_slot` are known, but the `value` potentially is not (e.g., it depends on some onchain dynamic variable). First, a **partial note** can be created during private execution that commits to the `owner` and `randomness`, and then the note is *"completed"* to create a full note by later adding the `storage_slot` and `value` fields, usually during public execution.

<Image img={require("@site/static/img/partial-notes.png")} />

Expand Down Expand Up @@ -62,7 +62,7 @@ pub struct UintNote {

**Phase 1: Partial Commitment (Private Execution)**

The private fields (`owner`, `randomness`, and `storage_slot`) are committed during local, private execution:
The private fields (`owner` and `randomness`) are committed during local, private execution:

```rust title="compute_partial_commitment" showLineNumbers
fn compute_partial_commitment(owner: AztecAddress, randomness: Field) -> Field {
Expand All @@ -75,7 +75,7 @@ fn compute_partial_commitment(owner: AztecAddress, randomness: Field) -> Field {
This creates a partial note commitment:

```
partial_commitment = H(owner, storage_slot, randomness)
partial_commitment = H(owner, randomness)
```

**Phase 2: Note Completion (Public Execution)**
Expand All @@ -99,8 +99,8 @@ fn compute_complete_note_hash(self, storage_slot: Field, value: u128) -> Field {
The resulting structure is a nested commitment:

```
note_hash = H(H(owner, storage_slot, randomness), value)
= H(partial_commitment, value)
note_hash = H(H(owner, randomness), storage_slot, value)
= H(partial_commitment, storage_slot, value)
```

## Universal Note Format
Expand All @@ -109,8 +109,8 @@ All notes in Aztec use the partial note format internally, even when all data is

When a note is created with all fields known (including `owner`, `storage_slot`, `randomness`, and `value`):

1. A partial commitment is computed from the private fields (`owner`, `storage_slot`, `randomness`)
2. The partial commitment is immediately completed with the `value` field
1. A partial commitment is computed from the private fields (`owner`, `randomness`)
2. The partial commitment is immediately completed with the `storage_slot` and `value` fields

```rust title="compute_note_hash" showLineNumbers
fn compute_note_hash(self, owner: AztecAddress, storage_slot: Field, randomness: Field) -> Field {
Expand All @@ -135,7 +135,6 @@ fn compute_note_hash(self, owner: AztecAddress, storage_slot: Field, randomness:

This two-step process ensures that notes with identical field values produce identical note hashes, regardless of whether they were created as partial notes or complete notes.

<Image img={require("@site/static/img/shrek.jpeg")} />

## Partial Notes in Practice

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,9 @@ To get a sense of things, here is a table of gate counts for common operations:
| ~75 | Hashing 3 fields with Poseidon2 |
| 3500 | Reading a value from a tree (public data tree, note hash tree, nullifier tree) |
| 4000 | Reading a delayed public mutable read |
| X000 | Calculating sha256 |
| X000 | Constrained encryption of a log of Y fields |
| X000 | Constrained encryption and tag a log of Y fields |
| ~5,000 | Calculating sha256 (varies by input size) |
| Varies | Constrained encryption of a private log (depends on field count) |
| Varies | Constrained encryption and tagging of a private log (depends on field count) |

### Optimization: use arithmetic instead of non-arithmetic operations

Expand All @@ -106,7 +106,7 @@ Because the underlying equation in the proving backend makes use of multiplicati
For example:

```rust
comptime global TWO_POW_32: Field = 2.pow_32(16);
comptime global TWO_POW_16: Field = 2.pow_32(16);
// ...
{
#[external("private")]
Expand All @@ -116,7 +116,7 @@ comptime global TWO_POW_32: Field = 2.pow_32(16);

#[external("private")]
fn mul_efficient(number: Field) -> u128 {
(number * TWO_POW_32) as u128
(number * TWO_POW_16) as u128
} // 5184 gates (60 gates less)
}
```
Expand Down Expand Up @@ -153,7 +153,7 @@ For example, use boolean equality effectively instead of `>=`:
}
}
sum
} // 45068 gates (751 gates less)
} // 45068 gates (751 gates more due to the boolean operations, but the pattern demonstrates how to avoid range checks)
}
```

Expand Down Expand Up @@ -279,9 +279,12 @@ Like with sqrt, we have the inefficient function that does the sort with constra
// Safety: calculate in unconstrained function, then constrain the result
let sorted_array = unsafe { super::sort_array(array) };
// constrain that sorted_array elements are sorted
for i in 0..super::ARRAY_SIZE as u32 {
for i in 0..super::ARRAY_SIZE as u32 - 1 {
assert(sorted_array[i] <= sorted_array[i + 1], "array should be sorted");
}
// Note: A production implementation should also verify that sorted_array is a
// permutation of the input array to prevent a malicious prover from returning
// arbitrary sorted values.
sorted_array
} // 5870 gates (953 gates less) for 10 elements, 12582 gates for 100 elements (115198 gates less)
}
Expand Down Expand Up @@ -309,7 +312,7 @@ Note: The stdlib provides a highly optimized version of sort on arrays, `array.s
```rust
#[external("private")]
fn sort_stdlib(array: [u32; super::ARRAY_SIZE]) -> [u32; super::ARRAY_SIZE] {
array.sort();
array.sort()
} // 5943 gates (880 gates less) for 10 elements, 13308 gates for 100 elements (114472 gates less)
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,15 +98,16 @@ use aztec::macros::aztec;
pub contract MyContract {
use aztec::{
macros::storage,
state_vars::{PrivateMutable, PublicMutable}
state_vars::{Owned, PrivateMutable, PublicMutable}
};
use uint_note::UintNote;

// The storage struct can have any name, but is typically called `Storage`. It must have the `#[storage]` macro applied to it.
// This struct must also have a generic type called C or Context.
#[storage]
struct Storage<Context> {
// A private numeric value which can change over time. This value will be hidden, and only those with the secret can know its current value.
my_private_state_variable: Owned<PrivateMutable<NoteType, Context>>,
my_private_state_variable: Owned<PrivateMutable<UintNote, Context>, Context>,
// A public numeric value which can change over time. This value will be known to everyone and is equivalent to the Solidity example above.
my_public_state_variable: PublicMutable<u128, Context>,
}
Expand Down Expand Up @@ -142,6 +143,7 @@ use aztec::macros::aztec;
#[aztec]
contract MyContract {
use aztec::macros::functions::external;
use aztec::protocol::address::AztecAddress;

#[external("private")]
fn my_private_function(parameter_a: u128, parameter_b: AztecAddress) {
Expand All @@ -154,7 +156,7 @@ contract MyContract {
}

#[external("utility")]
fn my_utility_function(parameter_a: u128, parameter_b: AztecAddress) {
unconstrained fn my_utility_function(parameter_a: u128, parameter_b: AztecAddress) {
// ...
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ fn update_to(new_class_id: ContractClassId) {
}
```

:::info
To use the `ContractInstanceRegistry`, add this dependency to your `Nargo.toml`:
```toml
contract_instance_registry = { git="https://github.com/AztecProtocol/aztec-packages/", tag="v4.2.0-aztecnr-rc.2", directory="noir-projects/noir-contracts/contracts/protocol_interface/contract_instance_registry_interface" }
```
:::

The `update` function in the registry is a public function, so you can enqueue it from a private function (as shown above) or call it directly from a public function.

:::warning[Access Control]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,21 +29,21 @@ Aztec.nr provides pre-built note types for common use cases:

```toml
# In Nargo.toml
uint_note = { git="https://github.com/AztecProtocol/aztec-packages/", tag="v4.2.0-aztecnr-rc.2", directory="noir-projects/aztec-nr/uint-note" }
uint_note = { git="https://github.com/AztecProtocol/aztec-nr", tag="v4.2.0-aztecnr-rc.2", directory="uint-note" }
```

**FieldNote** - For storing single Field values:

```toml
# In Nargo.toml
field_note = { git="https://github.com/AztecProtocol/aztec-packages/", tag="v4.2.0-aztecnr-rc.2", directory="noir-projects/aztec-nr/field-note" }
field_note = { git="https://github.com/AztecProtocol/aztec-nr", tag="v4.2.0-aztecnr-rc.2", directory="field-note" }
```

**AddressNote** - For storing Aztec addresses:

```toml
# In Nargo.toml
address_note = { git="https://github.com/AztecProtocol/aztec-packages/", tag="v4.2.0-aztecnr-rc.2", directory="noir-projects/aztec-nr/address-note" }
address_note = { git="https://github.com/AztecProtocol/aztec-nr", tag="v4.2.0-aztecnr-rc.2", directory="address-note" }
```

:::
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,25 +18,17 @@ aztec = { git="https://github.com/AztecProtocol/aztec-nr/", tag="v4.2.0-aztecnr-
### Aztec (required)

```toml
aztec = { git="https://github.com/AztecProtocol/aztec-packages/", tag="v4.2.0-aztecnr-rc.2", directory="noir-projects/aztec-nr/aztec" }
aztec = { git="https://github.com/AztecProtocol/aztec-nr/", tag="v4.2.0-aztecnr-rc.2", directory="aztec" }
```

The core Aztec library required for every Aztec.nr smart contract.

### Protocol Types

```toml
protocol = { git="https://github.com/AztecProtocol/aztec-packages/", tag="v4.2.0-aztecnr-rc.2", directory="noir-projects/noir-protocol-circuits/crates/types"}
```

Contains types used in the Aztec protocol (addresses, constants, hashes, etc.).

## Note Types

### Address Note

```toml
address_note = { git="https://github.com/AztecProtocol/aztec-packages/", tag="v4.2.0-aztecnr-rc.2", directory="noir-projects/aztec-nr/address-note" }
address_note = { git="https://github.com/AztecProtocol/aztec-nr/", tag="v4.2.0-aztecnr-rc.2", directory="address-note" }
```

Provides `AddressNote`, a note type for storing `AztecAddress` values.
Expand Down Expand Up @@ -76,3 +68,22 @@ compressed_string = { git="https://github.com/AztecProtocol/aztec-nr/", tag="v4.
```

Provides `CompressedString` and `FieldCompressedString` utilities for working with compressed string data.

## Updating your aztec dependencies

When `aztec compile` warns that your aztec dependency tag does not match the CLI version, update the `tag` field in every Aztec.nr entry in your `Nargo.toml` to match the CLI version you are running.

For example, if your CLI is `v4.2.0-aztecnr-rc.2`, change:

```toml
aztec = { git="https://github.com/AztecProtocol/aztec-nr/", tag="v<old-version>", directory="aztec" }
```

to:

```toml
aztec = { git="https://github.com/AztecProtocol/aztec-nr/", tag="v4.2.0-aztecnr-rc.2", directory="aztec" }
```

Repeat for every other Aztec.nr dependency in your `Nargo.toml` (e.g. `address_note`,
`balance_set`, etc.). You can check your current CLI version with `aztec --version`.
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,8 @@ L2 to L1 messages are only available after the epoch proof is submitted to L1. S
* @param _amount - The amount to withdraw
* @param _withCaller - Flag to use `msg.sender` as caller, otherwise address(0)
* @param _epoch - The epoch the message is in
* @param _leafIndex - The amount to withdraw
* @param _path - Flag to use `msg.sender` as caller, otherwise address(0)
* @param _leafIndex - The index of the message leaf in the tree
* @param _path - The Merkle proof path for the message leaf
* Must match the caller of the message (specified from L2) to consume it.
*/
function withdraw(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ use aztec::messages::message_delivery::MessageDelivery;

#[external("private")]
fn transfer(to: AztecAddress, amount: u128) {
let from = self.msg_sender().unwrap();
let from = self.msg_sender();

// ... transfer logic ...

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ It is also useful in private functions when dealing with tasks of an unknown siz
This macro inserts a check at the beginning of the function to ensure that the caller is the contract itself. This is done by adding the following assertion:

```rust
assert(self.msg_sender().unwrap() == self.address, "Function can only be called internally");
assert(self.msg_sender() == self.address, "Function can only be called internally");
```

## #[allow_phase_change]
Expand Down
Loading
Loading