Skip to content

Commit f3c8b1d

Browse files
chore: golden tests for nargo compile (#5)
Co-authored-by: Tom French <[email protected]>
1 parent b2356b9 commit f3c8b1d

File tree

17 files changed

+635
-5
lines changed

17 files changed

+635
-5
lines changed

.github/workflows/benchmark.yml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717
- name: Install Nargo
1818
uses: noir-lang/[email protected]
1919
with:
20-
toolchain: 1.0.0-beta.12
20+
toolchain: 1.0.0-beta.13
2121

2222
- name: Install bb
2323
run: |
@@ -63,5 +63,3 @@ jobs:
6363
fail-on-alert: false
6464
max-items-in-chart: 50
6565
skip-fetch-gh-pages: true
66-
67-

.github/workflows/golden_test.yml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
name: Golden Tests
2+
3+
on:
4+
push:
5+
branches:
6+
- master
7+
pull_request:
8+
9+
jobs:
10+
test:
11+
name: Golden Tests
12+
runs-on: ubuntu-latest
13+
steps:
14+
- name: Checkout sources
15+
uses: actions/checkout@v4
16+
- name: Install Nargo
17+
uses: noir-lang/[email protected]
18+
with:
19+
toolchain: 1.0.0-beta.13
20+
21+
- name: Compile correct private_token_contract
22+
working-directory: ./contract-benchmarks/private_token_contract
23+
run: nargo compile
24+
- name: Run golden tests
25+
run: ./scripts/test.sh

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Ignore Noir build outputs in benchmarks
22
contract-benchmarks/**/target/
3+
test_contracts/**/result.txt
34

45
# Local reports
56
gates-report.json

README.md

Lines changed: 125 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,125 @@
1-
# aztec-contracts
2-
aztec contracts
1+
## Test Contracts - Error Cases
2+
3+
This directory contains test contracts that test common compilation errors that were hard to understand in Aztec contracts. These tests are based on the `private_token_contract` example, with modifications to trigger specific error scenarios.
4+
5+
### Test Cases
6+
7+
#### 1. `private_token_contract_storage_macro` - Missing `#[storage]` Attribute
8+
9+
**Modification**: Removed the `#[storage]` attribute from the `Storage` struct.
10+
11+
**Original Code**:
12+
```rust
13+
#[storage]
14+
struct Storage<Context> {
15+
balances: Map<AztecAddress, EasyPrivateUint<Context>, Context>,
16+
}
17+
```
18+
19+
**Modified Code**:
20+
```rust
21+
struct Storage<Context> {
22+
balances: Map<AztecAddress, EasyPrivateUint<Context>, Context>,
23+
}
24+
```
25+
26+
27+
#### 2. `private_token_contract_struct_as_arg` - Invalid Function Parameter Type
28+
29+
**Modification**: Added and empty `Arbitrary` struct. Changed the `mint` function parameter from `u64` to a custom `Arbitrary` struct.
30+
31+
**Original Code**:
32+
```rust
33+
fn mint(amount: u64, owner: AztecAddress) {
34+
let balances = storage.balances;
35+
balances.at(owner).add(amount, owner);
36+
}
37+
```
38+
39+
**Modified Code**:
40+
```rust
41+
pub struct Arbitrary {}
42+
43+
fn mint(amount: Arbitrary, owner: AztecAddress) {
44+
//let balances = storage.balances;
45+
//balances.at(owner).add(amount, owner);
46+
}
47+
```
48+
49+
**Expected Errors**:
50+
- `No matching impl found for Arbitrary: Serialize<N = _>` - The custom struct doesn't implement the `Serialize` trait required for function parameters
51+
- `Type annotation needed` - Generic type inference fails due to the serialization error
52+
53+
**Purpose**: The reason we see this error is function arguments need to be serialized. We need to define Serialize/Deserialize trait implementation for the new struct,
54+
55+
#### 3. `private_token_contract_private_struct` - Private Struct in Function
56+
57+
**Modification**: Added a private struct `Arbitrary` and used it as a function parameter in a `#[private]` function.
58+
59+
**Original Code**:
60+
```rust
61+
#[private]
62+
fn mint(amount: u64, owner: AztecAddress) {
63+
let balances = storage.balances;
64+
balances.at(owner).add(amount, owner);
65+
}
66+
```
67+
68+
**Modified Code**:
69+
```rust
70+
#[derive(Serialize, Deserialize)]
71+
struct Arbitrary {
72+
value: u64,
73+
}
74+
75+
#[private]
76+
fn mint(amount: Arbitrary, owner: AztecAddress) {
77+
//let balances = storage.balances;
78+
//balances.at(owner).add(amount, owner);
79+
}
80+
```
81+
82+
**Expected Errors**:
83+
- `Type Arbitrary is more private than item mint_parameters::amount`
84+
- `Type Arbitrary is more private than item mint`
85+
86+
**Purpose**: It might be confusing to see mint function labelled as #[private], but got error `Type Arbitrary is more private than item mint`
87+
88+
#### 4. `private_token_contract_utility_macro` - Missing `#[utility]` Attribute
89+
90+
**Modification**: Removed the `#[utility]` attribute from the `get_balance` function.
91+
92+
**Original Code**:
93+
```rust
94+
#[utility]
95+
unconstrained fn get_balance(owner: AztecAddress) -> Field {
96+
storage.balances.at(owner).get_value()
97+
}
98+
```
99+
100+
101+
**Modified Code**:
102+
```rust
103+
unconstrained fn get_balance(owner: AztecAddress) -> Field {
104+
storage.balances.at(owner).get_value()
105+
}
106+
```
107+
108+
**Expected Errors**:
109+
- `Function get_balance must be marked as either #[private], #[public], #[utility], #[contract_library_method], or #[test]`
110+
111+
### Running the Tests
112+
113+
These test contracts are designed to fail compilation and capture the error output.
114+
To run the tests:
115+
116+
```bash
117+
./scripts/test.sh
118+
```
119+
120+
The script will:
121+
1. Compile each test contract (expecting failures)
122+
2. Capture the error output
123+
3. Compare against expected error messages
124+
4. Report whether the error messages match expectations
125+

scripts/test.sh

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
ROOT_DIR=$(cd "$(dirname "$0")/.." && pwd) # repo root
2+
CONTRACTS_DIR="$ROOT_DIR/test_contracts" # error contracts root
3+
4+
5+
for dir in "$CONTRACTS_DIR"/*/; do # iterate over each contract
6+
[[ -d "$dir" ]] || continue
7+
CONTRACT_NAME=$(basename "$dir")
8+
expected_output_file="$CONTRACTS_DIR/${CONTRACT_NAME}/expected_stderr.txt"
9+
result_file="$CONTRACTS_DIR/${CONTRACT_NAME}/result.txt"
10+
11+
cd "$dir" && nargo compile 2> "$result_file" || true
12+
13+
# Remove paths before /nargo/github.com to avoid environment dependency.
14+
if [[ "$OSTYPE" == "darwin"* ]]; then
15+
sed -i "" "s@[^[:space:]]*/nargo/github\.com@/nargo/github.com@g" "$result_file"
16+
else
17+
sed -i "s@[^[:space:]]*/nargo/github\.com@/nargo/github.com@g" "$result_file"
18+
fi
19+
20+
if diff "$expected_output_file" "$result_file"; then
21+
echo "$CONTRACT_NAME: Files are identical"
22+
else
23+
echo "$CONTRACT_NAME: Files differ"
24+
exit 1
25+
fi
26+
27+
done
28+
29+
echo "All tests passed!"
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
[package]
2+
name = "private_token_contract"
3+
authors = [""]
4+
compiler_version = ">=0.25.0"
5+
type = "contract"
6+
7+
[dependencies]
8+
aztec = {tag = "v3.0.0-nightly.20250922", git = "https://github.com/aztecprotocol/aztec-packages/", directory = "noir-projects/aztec-nr/aztec" }
9+
value_note = {tag = "v3.0.0-nightly.20250922", git = "https://github.com/aztecprotocol/aztec-packages/", directory = "noir-projects/aztec-nr/value-note" }
10+
easy_private_state = {tag = "v3.0.0-nightly.20250922", git = "https://github.com/aztecprotocol/aztec-packages/", directory = "noir-projects/aztec-nr/easy-private-state" }
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
error: Type `Arbitrary` is more private than item `mint_parameters::amount`
2+
┌─ /nargo/github.com/aztecprotocol/aztec-packages/v3.0.0-nightly.20250922/noir-projects/aztec-nr/aztec/src/macros/functions/abi_export.nr:42:9
3+
4+
42 │ f.parameters().map(|(name, typ): (Quoted, Type)| quote { pub $name: $typ }).join(quote {,});
5+
│ --------------
6+
7+
┌─ src/main.nr:34:5
8+
9+
34 │ #[private]
10+
│ ---------- While running this function attribute
11+
12+
13+
error: Type `Arbitrary` is more private than item `mint`
14+
┌─ /nargo/github.com/aztecprotocol/aztec-packages/v3.0.0-nightly.20250922/noir-projects/aztec-nr/aztec/src/macros/functions/call_interface_stubs.nr:42:19
15+
16+
42 │ let fn_name = f.name();
17+
│ --------
18+
19+
┌─ src/main.nr:4:1
20+
21+
4 │ #[aztec]
22+
│ -------- While running this function attribute
23+
24+
25+
Aborting due to 2 previous errors
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// docs:start:private_token_contract
2+
use dep::aztec::macros::aztec;
3+
4+
#[aztec]
5+
pub contract PrivateToken {
6+
use dep::aztec::macros::{functions::{initializer, private, utility}, storage::storage};
7+
use dep::aztec::{protocol_types::address::AztecAddress, state_vars::Map};
8+
use dep::easy_private_state::EasyPrivateUint;
9+
use dep::aztec::protocol_types::traits::Serialize;
10+
use dep::aztec::protocol_types::traits::Deserialize;
11+
12+
#[storage]
13+
struct Storage<Context> {
14+
balances: Map<AztecAddress, EasyPrivateUint<Context>, Context>,
15+
}
16+
17+
#[derive(Serialize, Deserialize)]
18+
struct Arbitrary {
19+
value: u64,
20+
}
21+
22+
/**
23+
* initialize the contract's initial state variables.
24+
*/
25+
#[private]
26+
#[initializer]
27+
fn constructor(initial_supply: u64, owner: AztecAddress) {
28+
let balances = storage.balances;
29+
30+
balances.at(owner).add(initial_supply, owner);
31+
}
32+
33+
// Mints `amount` of tokens to `owner`.
34+
#[private]
35+
fn mint(amount: Arbitrary, owner: AztecAddress) {
36+
//let balances = storage.balances;
37+
38+
//balances.at(owner).add(amount, owner);
39+
}
40+
41+
// Transfers `amount` of tokens from `sender` to a `recipient`.
42+
#[private]
43+
fn transfer(amount: u64, sender: AztecAddress, recipient: AztecAddress) {
44+
let balances = storage.balances;
45+
46+
balances.at(sender).sub(amount, sender);
47+
balances.at(recipient).add(amount, recipient);
48+
}
49+
50+
// Helper function to get the balance of a user.
51+
#[utility]
52+
unconstrained fn get_balance(owner: AztecAddress) -> Field {
53+
storage.balances.at(owner).get_value()
54+
}
55+
}
56+
// docs:end:private_token_contract
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
[package]
2+
name = "private_token_contract"
3+
authors = [""]
4+
compiler_version = ">=0.25.0"
5+
type = "contract"
6+
7+
[dependencies]
8+
aztec = {tag = "v3.0.0-nightly.20250922", git = "https://github.com/aztecprotocol/aztec-packages/", directory = "noir-projects/aztec-nr/aztec" }
9+
value_note = {tag = "v3.0.0-nightly.20250922", git = "https://github.com/aztecprotocol/aztec-packages/", directory = "noir-projects/aztec-nr/value-note" }
10+
easy_private_state = {tag = "v3.0.0-nightly.20250922", git = "https://github.com/aztecprotocol/aztec-packages/", directory = "noir-projects/aztec-nr/easy-private-state" }
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
warning: unused variable owner
2+
┌─ src/main.nr:44:34
3+
4+
44 │ unconstrained fn get_balance(owner: AztecAddress) -> Field {
5+
│ ----- unused variable
6+
7+
8+
warning: struct `Storage` is never constructed
9+
┌─ src/main.nr:10:12
10+
11+
10 │ struct Storage<Context> {
12+
│ ------- struct is never constructed
13+
14+
15+
error: Type fn(TypeDefinition) -> Quoted has no member named balances
16+
┌─ src/main.nr:20:32
17+
18+
20 │ let balances = storage.balances;
19+
│ --------
20+
21+
22+
error: Object type is unknown in method call
23+
┌─ src/main.nr:22:9
24+
25+
22 │ balances.at(owner).add(initial_supply, owner);
26+
│ -------- Type must be known by this point to know which method to call
27+
28+
= Try adding a type annotation for the object type before this method call
29+
30+
error: Type fn(TypeDefinition) -> Quoted has no member named balances
31+
┌─ src/main.nr:28:32
32+
33+
28 │ let balances = storage.balances;
34+
│ --------
35+
36+
37+
error: Object type is unknown in method call
38+
┌─ src/main.nr:30:9
39+
40+
30 │ balances.at(owner).add(amount, owner);
41+
│ -------- Type must be known by this point to know which method to call
42+
43+
= Try adding a type annotation for the object type before this method call
44+
45+
error: Type fn(TypeDefinition) -> Quoted has no member named balances
46+
┌─ src/main.nr:36:32
47+
48+
36 │ let balances = storage.balances;
49+
│ --------
50+
51+
52+
error: Object type is unknown in method call
53+
┌─ src/main.nr:38:9
54+
55+
38 │ balances.at(sender).sub(amount, sender);
56+
│ -------- Type must be known by this point to know which method to call
57+
58+
= Try adding a type annotation for the object type before this method call
59+
60+
error: Object type is unknown in method call
61+
┌─ src/main.nr:39:9
62+
63+
39 │ balances.at(recipient).add(amount, recipient);
64+
│ -------- Type must be known by this point to know which method to call
65+
66+
= Try adding a type annotation for the object type before this method call
67+
68+
error: Type fn(TypeDefinition) -> Quoted has no member named balances
69+
┌─ src/main.nr:45:17
70+
71+
45 │ storage.balances.at(owner).get_value()
72+
│ --------
73+
74+
75+
Aborting due to 8 previous errors

0 commit comments

Comments
 (0)