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
7 changes: 7 additions & 0 deletions .github/workflows/aztec-cli-acceptance-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@ jobs:
with:
ref: ${{ github.event.workflow_run.head_sha || github.sha }}

# Node is only used to run the .ts harness in run-test.sh, which needs >=22.18 for TS
# type-stripping. The aztec CLI installer manages its own node version independently.
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22

- name: Run Aztec CLI acceptance test
run: ./aztec-up/test/aztec-cli-acceptance-test/run-test.sh

Expand Down
7 changes: 6 additions & 1 deletion .test_patterns.yml
Original file line number Diff line number Diff line change
Expand Up @@ -146,14 +146,19 @@ tests:
- *palla

# yarn-project tests
# Attempt to catch all kv-store browser test failures (consider them quarantined for now)
- regex: "yarn-project/kv-store"
error_regex: "vitest"
owners:
- *martin
- regex: "yarn-project/kv-store"
error_regex: "Could not import your test module"
owners:
- *grego
- regex: "yarn-project/kv-store"
error_regex: "timeout: sending signal TERM to command"
owners:
- *alex
- *martin
- regex: "yarn-project/kv-store"
error_regex: "Failed to fetch dynamically imported module"
owners:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ use crate::protocol::{address::AztecAddress, traits::{Deserialize, Serialize}};
/// - `CallSelfStatic`: Macro-generated type for calling contract's own view functions
/// - `EnqueueSelfStatic`: Macro-generated type for enqueuing calls to the contract's own view functions
/// - `CallInternal`: Macro-generated type for calling internal functions
pub struct ContractSelfPrivate<Storage, CallSelf, EnqueueSelf, CallSelfStatic, EnqueueSelfStatic, CallInternal> {
/// - `CallSelfUtility`: Macro-generated type for calling the contract's own utility functions
pub struct ContractSelfPrivate<Storage, CallSelf, EnqueueSelf, CallSelfStatic, EnqueueSelfStatic, CallInternal, CallSelfUtility> {
/// The address of this contract
pub address: AztecAddress,

Expand Down Expand Up @@ -110,17 +111,17 @@ pub struct ContractSelfPrivate<Storage, CallSelf, EnqueueSelf, CallSelfStatic, E
/// ```
pub internal: CallInternal,

/// Gateway for calling utility functions from this private context.
/// A struct that allows for ergonomic calling of utility functions from this private context.
///
/// Example API:
/// ```noir
/// // Safety: result is unconstrained
/// unsafe { self.utility.call(MyContract::at(address).my_utility_function(args)) }
/// ```
pub utility: PrivateUtility,
pub utility: PrivateUtilityCalls<CallSelfUtility>,
}

impl<Storage, CallSelf, EnqueueSelf, CallSelfStatic, EnqueueSelfStatic, CallInternal> ContractSelfPrivate<Storage, CallSelf, EnqueueSelf, CallSelfStatic, EnqueueSelfStatic, CallInternal> {
impl<Storage, CallSelf, EnqueueSelf, CallSelfStatic, EnqueueSelfStatic, CallInternal, CallSelfUtility> ContractSelfPrivate<Storage, CallSelf, EnqueueSelf, CallSelfStatic, EnqueueSelfStatic, CallInternal, CallSelfUtility> {
/// Creates a new `ContractSelfPrivate` instance for a private function.
///
/// This constructor is called automatically by the macro system and should not be called directly.
Expand All @@ -132,6 +133,7 @@ impl<Storage, CallSelf, EnqueueSelf, CallSelfStatic, EnqueueSelfStatic, CallInte
call_self_static: CallSelfStatic,
enqueue_self_static: EnqueueSelfStatic,
internal: CallInternal,
utility: PrivateUtilityCalls<CallSelfUtility>,
) -> Self {
Self {
context,
Expand All @@ -142,7 +144,7 @@ impl<Storage, CallSelf, EnqueueSelf, CallSelfStatic, EnqueueSelfStatic, CallInte
call_self_static,
enqueue_self_static,
internal,
utility: PrivateUtility {},
utility,
}
}

Expand Down Expand Up @@ -407,16 +409,31 @@ impl<Storage, CallSelf, EnqueueSelf, CallSelfStatic, EnqueueSelfStatic, CallInte
}
}

/// Gateway for calling utility functions from a private context.
/// A struct that allows for ergonomic calling of utility functions from a private context.
///
/// Accessible via `self.utility` in private functions. Results are not part of any circuit
/// proof; use them to inform logic, not as inputs to constrained assertions.
// Implemented as a separate empty struct because Noir does not allow passing `&mut PrivateContext`
// (held by `ContractSelfPrivate`) into unconstrained code. This struct holds no mutable references,
// so it can be passed freely into `unconstrained` functions.
pub struct PrivateUtility {}
///
/// ## Type Parameters
///
/// - `CallSelf`: Macro-generated type for calling the contract's own utility functions (same type
/// as `CallSelf` in \[`ContractSelfUtility`\])
// Implemented as a separate struct (rather than inlined in ContractSelfPrivate) because Noir does
// not allow passing `&mut PrivateContext` (held by `ContractSelfPrivate`) into unconstrained code.
// This struct holds no mutable references, so it can be passed freely into `unconstrained`
// functions.
pub struct PrivateUtilityCalls<CallSelf> {
/// Provides type-safe methods for calling this contract's own utility functions.
///
/// Example API:
/// ```noir
/// // Safety: result is unconstrained
/// unsafe { self.utility.call_self.some_utility_function(args) }
/// ```
pub call_self: CallSelf,
}

impl PrivateUtility {
impl<CallSelf> PrivateUtilityCalls<CallSelf> {
/// Makes a utility contract call from a private function.
///
/// Note: only same-contract utility calls are currently supported. See TODO(F-29).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ use crate::protocol::{address::AztecAddress, traits::Deserialize};
///
/// - `Storage`: The contract's storage struct (defined with [`storage`](crate::macros::storage::storage), or `()` if
/// the contract has no storage
pub struct ContractSelfUtility<Storage> {
/// - `CallSelf`: Macro-generated type for calling the contract's own utility functions (same type
/// as `CallSelf` in \[`PrivateUtilityCalls`\])
pub struct ContractSelfUtility<Storage, CallSelf> {
/// The address of this contract
pub address: AztecAddress,

Expand All @@ -34,14 +36,22 @@ pub struct ContractSelfUtility<Storage> {

/// The utility execution context.
pub context: UtilityContext,

/// Provides type-safe methods for calling this contract's own utility functions.
///
/// Example API:
/// ```noir
/// self.call_self.some_utility_function(args)
/// ```
pub call_self: CallSelf,
}

impl<Storage> ContractSelfUtility<Storage> {
impl<Storage, CallSelf> ContractSelfUtility<Storage, CallSelf> {
/// Creates a new `ContractSelfUtility` instance for a utility function.
///
/// This constructor is called automatically by the macro system and should not be called directly.
pub fn new(context: UtilityContext, storage: Storage) -> Self {
Self { context, storage, address: context.this_address() }
pub fn new(context: UtilityContext, storage: Storage, call_self: CallSelf) -> Self {
Self { context, storage, address: context.this_address(), call_self }
}

/// Makes a utility contract call from another utility function.
Expand Down
1 change: 1 addition & 0 deletions noir-projects/aztec-nr/aztec/src/contract_self/mod.nr
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ pub mod contract_self_private;
pub mod contract_self_public;
pub mod contract_self_utility;

pub use contract_self_private::PrivateUtilityCalls;
pub use contract_self_private::ContractSelfPrivate;
pub use contract_self_public::ContractSelfPublic;
pub use contract_self_utility::ContractSelfUtility;
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ use crate::macros::{
calls_generation::external_functions_stubs::{
create_private_self_call_stub, create_private_static_stub, create_private_stub,
create_public_self_call_static_stub, create_public_self_call_stub, create_public_self_enqueue_static_stub,
create_public_self_enqueue_stub, create_public_static_stub, create_public_stub, create_utility_stub,
create_utility_stub_from_parts,
create_public_self_enqueue_stub, create_public_static_stub, create_public_stub, create_utility_self_call_stub,
create_utility_stub, create_utility_stub_from_parts,
},
internals_functions_generation::external_functions_registry,
offchain_receive::{
Expand Down Expand Up @@ -77,6 +77,7 @@ comptime fn create_offchain_receive_stub() -> Quoted {
pub(crate) comptime fn generate_external_function_self_calls_structs(m: Module) -> Quoted {
let private_functions = external_functions_registry::get_private_functions(m);
let public_functions = external_functions_registry::get_public_functions(m);
let utility_functions = external_functions_registry::get_utility_functions(m);

let call_self_private_methods = private_functions
.map(|function| {
Expand Down Expand Up @@ -138,6 +139,9 @@ pub(crate) comptime fn generate_external_function_self_calls_structs(m: Module)
})
.join(quote {});

let call_self_utility_methods =
utility_functions.map(|function| create_utility_self_call_stub(function)).join(quote {});

quote {
pub struct CallSelf<Context> {
pub address: aztec::protocol::address::AztecAddress,
Expand Down Expand Up @@ -182,5 +186,13 @@ pub(crate) comptime fn generate_external_function_self_calls_structs(m: Module)
impl EnqueueSelfStatic<&mut aztec::context::PrivateContext> {
$enqueue_self_static_methods
}

pub struct CallSelfUtility {
pub address: aztec::protocol::address::AztecAddress,
}

impl CallSelfUtility {
$call_self_utility_methods
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,24 @@ pub comptime fn create_public_self_enqueue_static_stub(f: FunctionDefinition) ->
}
}

/// Creates a stub for calling a utility function on the same contract (for CallSelfUtility).
pub comptime fn create_utility_self_call_stub(f: FunctionDefinition) -> Quoted {
let (fn_name, fn_parameters_list, serialized_args_array_construction, serialized_args_array_name, _, _, _, fn_selector) =
create_stub_base(f);
let fn_return_type = f.return_type();

quote {
pub unconstrained fn $fn_name(self, $fn_parameters_list) -> $fn_return_type {
$serialized_args_array_construction
let selector = $FROM_FIELD($fn_selector);
let returns = aztec::oracle::call_utility_function::call_utility_function(
self.address, selector, $serialized_args_array_name,
);
aztec::protocol::traits::Deserialize::deserialize(returns)
}
}
}

/// Creates a stub for enqueuing a public function from private context (for EnqueueSelf<&mut PrivateContext>)
pub comptime fn create_public_self_enqueue_stub(f: FunctionDefinition) -> Quoted {
let (fn_name, fn_parameters_list, serialized_args_array_construction, serialized_args_array_name, _serialized_args_array_len, _fn_name_str, _fn_name_len, fn_selector) =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@ pub(crate) comptime fn generate_private_external(f: FunctionDefinition) -> Quote
let call_self_static: CallSelfStatic<&mut aztec::context::PrivateContext> = CallSelfStatic { address: self_address, context: &mut context };
let enqueue_self_static: EnqueueSelfStatic<&mut aztec::context::PrivateContext> = EnqueueSelfStatic { address: self_address, context: &mut context };
let internal: CallInternal<&mut aztec::context::PrivateContext> = CallInternal { context: &mut context };
aztec::contract_self::ContractSelfPrivate::new(&mut context, storage, call_self, enqueue_self, call_self_static, enqueue_self_static, internal)
let call_self_utility = CallSelfUtility { address: self_address };
let utility: aztec::contract_self::PrivateUtilityCalls<CallSelfUtility> = aztec::contract_self::PrivateUtilityCalls { call_self: call_self_utility };
aztec::contract_self::ContractSelfPrivate::new(&mut context, storage, call_self, enqueue_self, call_self_static, enqueue_self_static, internal, utility)
};
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ pub(crate) comptime fn generate_utility_external(f: FunctionDefinition) -> Quote
let mut self = {
let context = aztec::context::UtilityContext::new();
$storage_init
aztec::contract_self::ContractSelfUtility::new(context, storage)
let self_address = context.this_address();
let call_self = CallSelfUtility { address: self_address };
aztec::contract_self::ContractSelfUtility::new(context, storage, call_self)
};
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@ pub(crate) comptime fn generate_private_internal(f: FunctionDefinition) -> Quote
let call_self_static: CallSelfStatic<&mut aztec::context::PrivateContext> = CallSelfStatic { address: self_address, context };
let enqueue_self_static: EnqueueSelfStatic<&mut aztec::context::PrivateContext> = EnqueueSelfStatic { address: self_address, context };
let internal: CallInternal<&mut aztec::context::PrivateContext> = CallInternal { context };
aztec::contract_self::ContractSelfPrivate::new(context, storage, call_self, enqueue_self, call_self_static, enqueue_self_static, internal)
let call_self_utility = CallSelfUtility { address: self_address };
let utility: aztec::contract_self::PrivateUtilityCalls<CallSelfUtility> = aztec::contract_self::PrivateUtilityCalls { call_self: call_self_utility };
aztec::contract_self::ContractSelfPrivate::new(context, storage, call_self, enqueue_self, call_self_static, enqueue_self_static, internal, utility)
};

$body
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -999,14 +999,17 @@ impl TestEnvironment {
// `MessageContext`, and might contain information about data (e.g. notes, events) contained in the message. In
// a real contract this data would have either been supplied by the `process_message` caller, of retrieved
// along with the message from a tagged log.
let (tx_hash, unique_note_hashes_in_tx, nullifiers_in_tx) = txe_oracles::get_last_tx_effects();
let effects = txe_oracles::get_last_tx_effects();

// Real messages would also have a recipient, a concept which is currently half-supported in `TestEnvironment`
// as events are properly scoped by recipient, but notes use a global scope instead. We therefore simply set
// the zero address as the recipient if one is not supplied, which PXE accepts as a scope despite this not
// being a registered account.
let message_context =
MessageContext { tx_hash, unique_note_hashes_in_tx, first_nullifier_in_tx: nullifiers_in_tx.get(0) };
let message_context = MessageContext {
tx_hash: effects.tx_hash,
unique_note_hashes_in_tx: effects.note_hashes,
first_nullifier_in_tx: effects.nullifiers.get(0),
};

// The scope controls which PXE account can see discovered notes/events. We use the zero address as the
// default scope if no recipient is supplied, which PXE accepts despite this not being a registered account.
Expand Down
47 changes: 45 additions & 2 deletions noir-projects/aztec-nr/aztec/src/test/helpers/txe_oracles.nr
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ use crate::protocol::{
abis::function_selector::FunctionSelector,
address::AztecAddress,
constants::{
CONTRACT_INSTANCE_LENGTH, MAX_NOTE_HASHES_PER_TX, MAX_NULLIFIERS_PER_TX, NULL_MSG_SENDER_CONTRACT_ADDRESS,
CONTRACT_INSTANCE_LENGTH, MAX_NOTE_HASHES_PER_TX, MAX_NULLIFIERS_PER_TX,
MAX_PRIVATE_LOGS_PER_TX, NULL_MSG_SENDER_CONTRACT_ADDRESS, PRIVATE_LOG_SIZE_IN_FIELDS,
},
contract_instance::ContractInstance,
traits::{Deserialize, ToField},
Expand Down Expand Up @@ -84,8 +85,50 @@ pub unconstrained fn get_next_block_timestamp() -> u64 {}
#[oracle(aztec_txe_getLastBlockTimestamp)]
pub unconstrained fn get_last_block_timestamp() -> u64 {}

/// Note hashes emitted by a single transaction, as surfaced by [`get_last_tx_effects`].
pub type TxNoteHashes = BoundedVec<Field, MAX_NOTE_HASHES_PER_TX>;

/// Nullifiers emitted by a single transaction, as surfaced by [`get_last_tx_effects`].
pub type TxNullifiers = BoundedVec<Field, MAX_NULLIFIERS_PER_TX>;

/// A single private log's `log_data` as surfaced by [`get_last_tx_effects`]: the first field is
/// the kernel-siloed tag and the remainder is the ciphertext payload.
pub type TxPrivateLog = BoundedVec<Field, PRIVATE_LOG_SIZE_IN_FIELDS>;

/// Private logs emitted by a single transaction, in emission order, as surfaced by
/// [`get_last_tx_effects`].
pub type TxPrivateLogs = BoundedVec<TxPrivateLog, MAX_PRIVATE_LOGS_PER_TX>;

/// The side effects of a single transaction, as surfaced by [`get_last_tx_effects`].
pub struct TxEffects {
pub tx_hash: Field,
pub note_hashes: TxNoteHashes,
pub nullifiers: TxNullifiers,
pub private_logs: TxPrivateLogs,
}

/// Returns the effects of the last transaction included by the TXE.
pub unconstrained fn get_last_tx_effects() -> TxEffects {
let (tx_hash, note_hashes, nullifiers, raw_log_storage, log_lengths, log_count) =
get_last_tx_effects_oracle();

let mut private_logs = BoundedVec::new();
for i in 0..log_count {
private_logs.push(BoundedVec::from_parts(raw_log_storage[i], log_lengths[i]));
}

TxEffects { tx_hash, note_hashes, nullifiers, private_logs }
}

#[oracle(aztec_txe_getLastTxEffects)]
pub unconstrained fn get_last_tx_effects() -> (Field, BoundedVec<Field, MAX_NOTE_HASHES_PER_TX>, BoundedVec<Field, MAX_NULLIFIERS_PER_TX>) {}
unconstrained fn get_last_tx_effects_oracle() -> (
Field,
TxNoteHashes,
TxNullifiers,
[[Field; PRIVATE_LOG_SIZE_IN_FIELDS]; MAX_PRIVATE_LOGS_PER_TX],
[u32; MAX_PRIVATE_LOGS_PER_TX],
u32,
) {}

/// Returns the raw offchain effect payloads emitted by the last top-level call into TXE.
/// Each effect is a variable-length field array (e.g. offchain messages have a different size
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ pub contract NestedUtility {
if n == 0 {
1
} else {
x * self.call(NestedUtility::at(self.address).pow_utility(x, n - 1))
x * self.call_self.pow_utility(x, n - 1)
}
}

Expand All @@ -21,7 +21,7 @@ pub contract NestedUtility {
// Safety: this is a test contract; the unconstrained result is returned directly
// and never used as input to a constrained assertion
unsafe {
self.utility.call(NestedUtility::at(self.address).pow_utility(x, n))
self.utility.call_self.pow_utility(x, n)
}
}
}
Loading
Loading