Skip to content

Commit 9b298a1

Browse files
authored
chore: Accumulated backports to v4-next (#22884)
BEGIN_COMMIT_OVERRIDE fix(aztec-up): run acceptance test after CI3 completes release (#22850) feat(aztec-nr): assert contract function return types and params are serializable (#22877) fix(aztec-up): pin FOUNDRY_DIR in foundry install to avoid XDG_CONFIG_HOME mismatch (#22886) END_COMMIT_OVERRIDE
2 parents f2630a6 + c5dc225 commit 9b298a1

23 files changed

Lines changed: 227 additions & 38 deletions

File tree

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
# Tests that the installed Aztec toolchain works end-to-end.
2-
# Exercises the full developer onboarding path: aztec init -> compile -> test -> start -> codegen -> TS end-to-end test.
3-
name: Full Dev Path Test
1+
# Validates that a published Aztec CLI release can be installed and used end-to-end.
2+
# Runs after CI3 completes a tag release (via workflow_run), or manually via workflow_dispatch.
3+
name: Aztec CLI Acceptance Test
44

55
on:
66
workflow_dispatch:
@@ -9,22 +9,30 @@ on:
99
description: "Version to install (e.g. latest, nightly, 4.3.0, 4.3.0-nightly.20260420)"
1010
required: true
1111
type: string
12-
push:
13-
tags:
12+
workflow_run:
13+
workflows: ["CI3"]
14+
types:
15+
- completed
16+
branches:
1417
- "v*"
1518

1619
jobs:
17-
full-dev-path:
20+
release-acceptance:
1821
runs-on: ubuntu-latest
22+
if: >-
23+
github.event_name == 'workflow_dispatch' ||
24+
(github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success')
1925
timeout-minutes: 30
2026
env:
21-
VERSION: ${{ github.event.inputs.version || github.ref_name }}
27+
VERSION: ${{ github.event.inputs.version || github.event.workflow_run.head_branch }}
2228
steps:
2329
- name: Checkout
2430
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
31+
with:
32+
ref: ${{ github.event.workflow_run.head_sha || github.sha }}
2533

26-
- name: Run full dev path test
27-
run: ./aztec-up/test/full-dev-path/run-test.sh
34+
- name: Run Aztec CLI acceptance test
35+
run: ./aztec-up/test/aztec-cli-acceptance-test/run-test.sh
2836

2937
- name: Notify Slack on success
3038
if: success() && github.event_name != 'workflow_dispatch'
@@ -33,7 +41,7 @@ jobs:
3341
run: |
3442
export CI=1
3543
./ci3/slack_notify "#team-fairies" \
36-
"Full Dev Path Test passed for version ${VERSION} :white_check_mark:"
44+
"Aztec CLI Acceptance Test passed for version ${VERSION} :white_check_mark:"
3745
3846
- name: Notify Slack and dispatch ClaudeBox on failure
3947
if: failure() && github.event_name != 'workflow_dispatch'
@@ -44,6 +52,6 @@ jobs:
4452
RUN_URL="https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
4553
export CI=1
4654
./ci3/slack_notify_with_claudebox_kickoff "#team-fairies" \
47-
"Full Dev Path Test FAILED (version ${VERSION}): <${RUN_URL}|View Run>" \
48-
"Full dev path test failed for version ${VERSION}. CI run: ${RUN_URL}. Investigate the failure and explain the root cause." \
55+
"Aztec CLI Acceptance Test FAILED (version ${VERSION}): <${RUN_URL}|View Run>" \
56+
"Aztec CLI acceptance test failed for version ${VERSION}. CI run: ${RUN_URL}. Investigate the failure and explain the root cause." \
4957
--link "$RUN_URL"

aztec-up/bin/0.0.1/install

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -173,11 +173,14 @@ function install_foundry {
173173
temp_foundry_dir=$(mktemp -d) || { echo "Error: Failed to create temp directory" >&2; exit 1; }
174174
mkdir -p "$temp_foundry_dir/bin"
175175

176-
# Always install/update foundryup
177-
curl --max-time 30 -L https://foundry.paradigm.xyz | bash
176+
# The foundry installer defaults to $XDG_CONFIG_HOME/.foundry when that var is set, which
177+
# puts foundryup somewhere we don't expect. We'll pin FOUNDRY_DIR so we control the install
178+
# location regardless of the caller's environment.
179+
local foundry_home="$HOME/.foundry"
180+
FOUNDRY_DIR="$foundry_home" curl --max-time 30 -L https://foundry.paradigm.xyz | FOUNDRY_DIR="$foundry_home" bash
178181

179182
# Install foundry to temp location and move to version directory
180-
FOUNDRY_DIR="$temp_foundry_dir" timeout 30 "$HOME/.foundry/bin/foundryup" -i "$foundry_version"
183+
FOUNDRY_DIR="$temp_foundry_dir" timeout 30 "$foundry_home/bin/foundryup" -i "$foundry_version"
181184

182185
# Move the foundry binaries to our version bin directory
183186
for binary in forge cast anvil chisel; do

aztec-up/test/full-dev-path/README.md renamed to aztec-up/test/aztec-cli-acceptance-test/README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
# Full Dev Path Test
1+
# Aztec CLI Acceptance Test
22

3-
Tests that the installed Aztec toolchain works end-to-end. Exercises the complete developer onboarding path:
3+
Tests that the installed Aztec CLI toolchain works end-to-end. Exercises the complete developer onboarding path:
44

55
1. `aztec init` - scaffold a new workspace with a Counter contract and test crate
66
2. `aztec compile` - compile the scaffolded contract
@@ -30,6 +30,6 @@ VERSION=4.3.0 ./run-test.sh
3030

3131
## Architecture
3232

33-
- **`run-test.sh`** - Bash launcher. Runs the aztec installer (unless skipped), sets up PATH, then `exec node full-dev-path.ts`.
34-
- **`full-dev-path.ts`** - Orchestrator. Runs each CLI step against the installed toolchain and, after codegen, copies `counter.test.ts` into the scaffolded workspace and spawns `node --test` on it. Each phase is wrapped in `step(name, fn)` so failures clearly identify which step broke. Always emits a machine-readable result line for CI/Slack integration: `TEST_RESULT=pass version=...` on success, or `TEST_RESULT=fail step=... version=... error="..."` on failure (with a full banner printed above it).
33+
- **`run-test.sh`** - Bash launcher. Runs the aztec installer (unless skipped), sets up PATH, then `exec node aztec-cli-acceptance-test.ts`.
34+
- **`aztec-cli-acceptance-test.ts`** - Orchestrator. Runs each CLI step against the installed toolchain and, after codegen, copies `counter.test.ts` into the scaffolded workspace and spawns `node --test` on it. Each phase is wrapped in `step(name, fn)` so failures clearly identify which step broke. Always emits a machine-readable result line for CI/Slack integration: `TEST_RESULT=pass version=...` on success, or `TEST_RESULT=fail step=... version=... error="..."` on failure (with a full banner printed above it).
3535
- **`counter.test.ts`** - The `node:test` suite that drives the deployed Counter end-to-end through the codegen'd bindings. Lives here as a template; copied into the workspace at test time so it can statically `import { CounterContract } from './artifacts/Counter.js'` with real codegen types and resolve `@aztec/*` via the workspace's `node_modules` symlink to the install.

aztec-up/test/full-dev-path/full-dev-path.ts renamed to aztec-up/test/aztec-cli-acceptance-test/aztec-cli-acceptance-test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ if (!existsSync(join(AZTEC_INSTALL_DIR, 'package.json'))) {
4545
process.exit(2);
4646
}
4747

48-
const TMP_DIR = mkdtempSync(join(tmpdir(), 'aztec-full-dev-path-'));
48+
const TMP_DIR = mkdtempSync(join(tmpdir(), 'aztec-cli-acceptance-test-'));
4949
const WORKSPACE_DIR = join(TMP_DIR, 'my_workspace');
5050

5151
// Exit codes follow the Unix 128+signal convention for signal terminations.
@@ -231,7 +231,7 @@ function reportFailure(stepName: string, aztecVersion: string, err: unknown) {
231231
const banner = '='.repeat(72);
232232

233233
console.error(`\n${banner}`);
234-
console.error('FULL DEV PATH TEST FAILED');
234+
console.error('AZTEC CLI ACCEPTANCE TEST FAILED');
235235
console.error(banner);
236236
console.error(`Step: ${stepName}`);
237237
console.error(`Version: ${aztecVersion}`);
File renamed without changes.

aztec-up/test/full-dev-path/run-test.sh renamed to aztec-up/test/aztec-cli-acceptance-test/run-test.sh

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
#!/usr/bin/env bash
2-
# Launcher for the full-dev-path test.
2+
# Launcher for the Aztec CLI acceptance test.
33
#
44
# Steps:
55
# 1. Install Node via NVM if not present (skipped with SKIP_INSTALL=1)
66
# 2. Install the Aztec toolchain via the public installer (skipped with SKIP_INSTALL=1)
7-
# 3. Run full-dev-path.ts which exercises the installed toolchain end-to-end
7+
# 3. Run aztec-cli-acceptance-test.ts which exercises the installed toolchain end-to-end
88
#
99
# Env vars:
1010
# SKIP_INSTALL=1 Skip steps 1-2 and use the already-installed toolchain (dev-box inner loop).
@@ -37,4 +37,4 @@ export PATH="$HOME/.aztec/current/bin:$HOME/.aztec/bin:$PATH"
3737
export AZTEC_INSTALL_DIR="${AZTEC_INSTALL_DIR:-$HOME/.aztec/current}"
3838

3939
echo ">>> Running test"
40-
exec node --no-warnings "${script_dir}/full-dev-path.ts"
40+
exec node --no-warnings "${script_dir}/aztec-cli-acceptance-test.ts"

noir-projects/aztec-nr/aztec/src/authwit/entrypoint/app.nr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use crate::{authwit::entrypoint::FunctionCall, context::PrivateContext};
22
use crate::protocol::{
33
constants::DOM_SEP__SIGNATURE_PAYLOAD,
44
hash::poseidon2_hash_with_separator,
5-
traits::{Hash, Serialize},
5+
traits::{Deserialize, Hash, Serialize},
66
};
77
use std::meta::derive;
88

@@ -12,7 +12,7 @@ global ACCOUNT_MAX_CALLS: u32 = 5;
1212
// - default_entrypoint.ts
1313
// - account_entrypoint.ts (specifically `getEntrypointAbi()`)
1414
// - default_multi_call_entrypoint.ts (specifically `getEntrypointAbi()`)
15-
#[derive(Serialize)]
15+
#[derive(Deserialize, Serialize)]
1616
pub struct AppPayload {
1717
function_calls: [FunctionCall; ACCOUNT_MAX_CALLS],
1818
// A nonce that enables transaction cancellation. When the cancellable flag is enabled, this nonce is used to

noir-projects/aztec-nr/aztec/src/authwit/entrypoint/function_call.nr

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
1-
use crate::protocol::{abis::function_selector::FunctionSelector, address::AztecAddress, traits::Serialize};
1+
use crate::protocol::{
2+
abis::function_selector::FunctionSelector,
3+
address::AztecAddress,
4+
traits::{Deserialize, Serialize},
5+
};
26
use std::meta::derive;
37

48
// @dev: If you change the following struct you have to update:
59
// - account_entrypoint.ts (specifically `getEntrypointAbi()`)
610
// - default_multi_call_entrypoint.ts (specifically `getEntrypointAbi()`)
711
// - function_call.ts
812
// - encoding.ts
9-
#[derive(Serialize)]
13+
#[derive(Deserialize, Serialize)]
1014
pub struct FunctionCall {
1115
// args_hash is calldata_hash if is_public is true
1216
pub args_hash: Field,

noir-projects/aztec-nr/aztec/src/macros/functions/mod.nr

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ pub(crate) mod auth_registry;
55

66
use crate::macros::{
77
internals_functions_generation::{external_functions_registry, internal_functions_registry},
8-
utils::{is_fn_external, module_has_initializer},
8+
utils::{assert_params_serializable, assert_return_type_serializable, is_fn_external, module_has_initializer},
99
};
1010
use super::utils::{fn_has_allow_phase_change, fn_has_noinitcheck, is_fn_initializer, is_fn_only_self, is_fn_view};
1111
use auth_registry::AUTHORIZE_ONCE_REGISTRY;
@@ -342,6 +342,9 @@ pub comptime fn external(f: FunctionDefinition, f_type: CtString) {
342342
// In this function we perform basic checks on the function to ensure it is valid and then we add the function to
343343
// the external functions registry for later processing by the `#[aztec]` macro.
344344

345+
assert_return_type_serializable(f);
346+
assert_params_serializable(f);
347+
345348
if f_type.eq("private") {
346349
assert_valid_private(f);
347350
external_functions_registry::add_private(f);

noir-projects/aztec-nr/aztec/src/macros/utils.nr

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::protocol::meta::derive_serialize;
2-
use std::meta::{ctstring::AsCtString, unquote};
2+
use std::meta::{ctstring::AsCtString, type_of, unquote};
33

44
pub(crate) comptime fn is_fn_external(f: FunctionDefinition) -> bool {
55
f.has_named_attribute("external")
@@ -153,6 +153,44 @@ pub(crate) comptime fn get_trait_impl_method(typ: Type, target_trait: Quoted, ta
153153
.as_typed_expr()
154154
}
155155

156+
/// Asserts that the return type of a contract function implements both `Serialize` and `Deserialize`.
157+
/// Functions with a `()` return type are skipped since unit values are not serialized.
158+
pub(crate) comptime fn assert_return_type_serializable(f: FunctionDefinition) {
159+
let return_type = f.return_type();
160+
if return_type != type_of(()) {
161+
let fn_name = f.name();
162+
let serialize_constraint = quote { crate::protocol::traits::Serialize }.as_trait_constraint();
163+
assert(
164+
return_type.implements(serialize_constraint),
165+
f"Contract function '{fn_name}' returns a value of type {return_type}, which does not implement the Serialize trait. Add #[derive(Serialize)] to {return_type}'s declaration.",
166+
);
167+
let deserialize_constraint = quote { crate::protocol::traits::Deserialize }.as_trait_constraint();
168+
assert(
169+
return_type.implements(deserialize_constraint),
170+
f"Contract function '{fn_name}' returns a value of type {return_type}, which does not implement the Deserialize trait. Add #[derive(Deserialize)] to {return_type}'s declaration.",
171+
);
172+
}
173+
}
174+
175+
/// Asserts that all parameters of a contract function implement both `Serialize` and `Deserialize`.
176+
pub(crate) comptime fn assert_params_serializable(f: FunctionDefinition) {
177+
let fn_name = f.name();
178+
179+
for param in f.parameters() {
180+
let (name, param_type) = param;
181+
let serialize_constraint = quote { crate::protocol::traits::Serialize }.as_trait_constraint();
182+
assert(
183+
param_type.implements(serialize_constraint),
184+
f"Parameter '{name}' of contract function '{fn_name}' has type {param_type}, which does not implement the Serialize trait. Add #[derive(Serialize)] to {param_type}'s declaration.",
185+
);
186+
let deserialize_constraint = quote { crate::protocol::traits::Deserialize }.as_trait_constraint();
187+
assert(
188+
param_type.implements(deserialize_constraint),
189+
f"Parameter '{name}' of contract function '{fn_name}' has type {param_type}, which does not implement the Deserialize trait. Add #[derive(Deserialize)] to {param_type}'s declaration.",
190+
);
191+
}
192+
}
193+
156194
/// Generates a quote that implements `Serialize` for a given struct `s`. If the struct already implements `Serialize`,
157195
/// we return an empty quote.
158196
pub comptime fn derive_serialize_if_not_implemented(s: TypeDefinition) -> Quoted {

0 commit comments

Comments
 (0)