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
2 changes: 1 addition & 1 deletion boxes/boxes/vanilla/contracts/src/main.nr
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ pub contract PrivateVoting {

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

let secret = self.context.request_nsk_app(msg_sender_npk_m_hash); // get secret key of caller of function
let nullifier = std::hash::pedersen_hash([self.msg_sender().unwrap().to_field(), secret]); // derive nullifier from sender and secret
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ It is also worth mentioning Noir's `unconstrained` function type [here (Noir doc
- `#[noinitcheck]` - The function is able to be called before an initializer (if one exists)
- `#[view]` - Makes calls to the function static
- `#[only_self]` - Available only for `external` functions - any external caller except the current contract is rejected.
- `#[internal]` - NOT YET IMPLEMENTED - Function can only be called from within the contract and the call itself is inlined (e.g. akin to EVM's JUMP and not EVM's CALL)
- `#[internal]` - Function can only be called from within the contract and the call itself is inlined (e.g. akin to EVM's JUMP and not EVM's CALL)
- `#[note]` - Creates a custom note
- `#[storage]` - Defines contract storage

Expand Down
23 changes: 19 additions & 4 deletions noir-projects/aztec-nr/aztec/src/contract_self.nr
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ use protocol_types::{
/// - `EnqueueSelf`: Macro-generated type for enqueuing calls to the contract's own non-view functions
/// - `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
pub struct ContractSelf<Context, Storage, CallSelf, EnqueueSelf, CallSelfStatic, EnqueueSelfStatic> {
pub struct ContractSelf<Context, Storage, CallSelf, EnqueueSelf, CallSelfStatic, EnqueueSelfStatic, CallInternal> {
/// The address of this contract
pub address: AztecAddress,
/// The contract's storage instance, representing the struct to which the `#[storage]` macro was applied in your
Expand Down Expand Up @@ -107,13 +107,23 @@ pub struct ContractSelf<Context, Storage, CallSelf, EnqueueSelf, CallSelfStatic,
/// self.enqueue_self_static.some_public_view_function(args)
/// ```
pub enqueue_self_static: EnqueueSelfStatic,
/// Provides type-safe methods for calling internal functions.
///
/// In private and public contexts this will be a struct with appropriate methods;
/// in utility context it will be the unit type `()`.
///
/// Example API:
/// ```noir
/// self.internal.some_internal_function(args)
/// ```
pub internal: CallInternal,
}

/// Implementation for `ContractSelf` in private execution contexts.
///
/// This implementation is used when a contract function is marked with `#[external("private")]`.
/// Private functions execute client-side and generate zero-knowledge proofs of their execution.
impl<Storage, CallSelf, EnqueueSelf, CallSelfStatic, EnqueueSelfStatic> ContractSelf<&mut PrivateContext, Storage, CallSelf, EnqueueSelf, CallSelfStatic, EnqueueSelfStatic> {
impl<Storage, CallSelf, EnqueueSelf, CallSelfStatic, EnqueueSelfStatic, CallInternal> ContractSelf<&mut PrivateContext, Storage, CallSelf, EnqueueSelf, CallSelfStatic, EnqueueSelfStatic, CallInternal> {
/// Creates a new `ContractSelf` instance for a private function.
///
/// This constructor is called automatically by the macro system and should not be called directly.
Expand All @@ -124,6 +134,7 @@ impl<Storage, CallSelf, EnqueueSelf, CallSelfStatic, EnqueueSelfStatic> Contract
enqueue_self: EnqueueSelf,
call_self_static: CallSelfStatic,
enqueue_self_static: EnqueueSelfStatic,
internal: CallInternal,
) -> Self {
Self {
context,
Expand All @@ -133,6 +144,7 @@ impl<Storage, CallSelf, EnqueueSelf, CallSelfStatic, EnqueueSelfStatic> Contract
enqueue_self,
call_self_static,
enqueue_self_static,
internal,
}
}

Expand Down Expand Up @@ -193,7 +205,7 @@ impl<Storage, CallSelf, EnqueueSelf, CallSelfStatic, EnqueueSelfStatic> Contract
/// This implementation is used when a contract function is marked with `#[external("public")]`.
/// Public functions are executed by the sequencer in the Aztec Virtual Machine (AVM) and can work only with public
/// state.
impl<Storage, CallSelf, CallSelfStatic> ContractSelf<PublicContext, Storage, CallSelf, (), CallSelfStatic, ()> {
impl<Storage, CallSelf, CallSelfStatic, CallInternal> ContractSelf<PublicContext, Storage, CallSelf, (), CallSelfStatic, (), CallInternal> {
/// Creates a new `ContractSelf` instance for a public function.
///
/// This constructor is called automatically by the macro system and should not be called directly.
Expand All @@ -202,6 +214,7 @@ impl<Storage, CallSelf, CallSelfStatic> ContractSelf<PublicContext, Storage, Cal
storage: Storage,
call_self: CallSelf,
call_self_static: CallSelfStatic,
internal: CallInternal,
) -> Self {
Self {
context,
Expand All @@ -211,6 +224,7 @@ impl<Storage, CallSelf, CallSelfStatic> ContractSelf<PublicContext, Storage, Cal
enqueue_self: (),
call_self_static,
enqueue_self_static: (),
internal,
}
}

Expand Down Expand Up @@ -270,7 +284,7 @@ impl<Storage, CallSelf, CallSelfStatic> ContractSelf<PublicContext, Storage, Cal
/// This implementation is used when a contract function is marked with `#[external("utility")]`.
/// Utility functions are unconstrained functions that can read private state for offchain queries.
/// They are typically used for view functions that need to access private notes (e.g. a Token's balance_of function).
impl<Storage> ContractSelf<UtilityContext, Storage, (), (), (), ()> {
impl<Storage> ContractSelf<UtilityContext, Storage, (), (), (), (), ()> {
/// Creates a new `ContractSelf` instance for a utility function.
///
/// This constructor is called automatically by the macro system and should not be called directly.
Expand All @@ -283,6 +297,7 @@ impl<Storage> ContractSelf<UtilityContext, Storage, (), (), (), ()> {
enqueue_self: (),
call_self_static: (),
enqueue_self_static: (),
internal: (),
}
}
}
3 changes: 3 additions & 0 deletions noir-projects/aztec-nr/aztec/src/macros/aztec.nr
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::macros::{
calls_generation::internal_functions::generate_call_internal_struct,
dispatch::generate_public_dispatch,
functions::{self_call_registry, stub_registry, utils::check_each_fn_macroified},
internals_functions_generation::{create_fn_abi_exports, process_functions},
Expand All @@ -20,6 +21,7 @@ pub comptime fn aztec(m: Module) -> Quoted {

let interface = generate_contract_interface(m);
let self_call_structs = generate_self_call_structs(m);
let call_internal_struct = generate_call_internal_struct(m);

// We generate ABI exports for all the external functions in the contract.
let fn_abi_exports = create_fn_abi_exports(m);
Expand Down Expand Up @@ -54,6 +56,7 @@ pub comptime fn aztec(m: Module) -> Quoted {
quote {
$interface
$self_call_structs
$call_internal_struct
$functions
$fn_abi_exports
$contract_library_method_compute_note_hash_and_nullifier
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
//! Generates functionality such that the following API for performing calls to `#[internal("...")]` functions is
//! available:
//! ```noir
//! self.internal.my_internal_function(...)
//! ```
//! Injected into the contract by the `#[aztec]` macro.

use crate::macros::internals_functions_generation::internal_functions_registry;

/// Generates a method for the `CallInternal` struct that makes a call to the `#[internal("private")]` function `f`.
comptime fn generate_private_internal_function_call(f: FunctionDefinition) -> Quoted {
let original_function_name = f.name();
let original_return_type = f.return_type();
let original_params = f
.parameters()
.map(|(param_name, param_type)| quote { $param_name: $param_type })
.join(quote {, });

let params_at_callsite =
f.parameters().map(|(param_name, _)| quote { $param_name }).join(quote {, });

let fn_name = f"__aztec_nr_internals__{original_function_name}".quoted_contents();

quote {
pub fn $original_function_name(self: CallInternal<&mut aztec::context::private_context::PrivateContext>, $original_params) -> $original_return_type {
$fn_name(self.context, $params_at_callsite)
}
}
}

/// Generates a method for the `CallInternal` struct that makes a call to the `#[internal("public")]` function `f`.
comptime fn generate_public_internal_function_call(f: FunctionDefinition) -> Quoted {
let original_function_name = f.name();
let original_return_type = f.return_type();
let original_params = f
.parameters()
.map(|(param_name, param_type)| quote { $param_name: $param_type })
.join(quote {, });

let params_at_callsite =
f.parameters().map(|(param_name, _)| quote { $param_name }).join(quote {, });

let fn_name = f"__aztec_nr_internals__{original_function_name}".quoted_contents();

quote {
pub unconstrained fn $original_function_name(self: CallInternal<aztec::context::public_context::PublicContext>, $original_params) -> $original_return_type {
$fn_name(self.context, $params_at_callsite)
}
}
}

/// Generates a struct which is injected into contracts via the `#[aztec]` macro and which is then instantiated in the
/// external and internal functions' bodies and provided into the `ContractSelf` struct. This then allows for the
/// following API:
///
/// ```noir
/// self.internal.my_internal_function(arg1, arg2);
/// ```
pub(crate) comptime fn generate_call_internal_struct(m: Module) -> Quoted {
let private_internal_functions = internal_functions_registry::get_private_functions(m);
let public_internal_functions = internal_functions_registry::get_public_functions(m);

let private_internal_functions_calls = private_internal_functions
.map(|function| generate_private_internal_function_call(function))
.join(quote {});
let public_internal_functions_calls = public_internal_functions
.map(|function| generate_public_internal_function_call(function))
.join(quote {});

quote {
pub struct CallInternal<Context> {
pub context: Context,
}

impl CallInternal<&mut aztec::context::private_context::PrivateContext> {
$private_internal_functions_calls
}

impl CallInternal<aztec::context::public_context::PublicContext> {
$public_internal_functions_calls
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub(crate) mod internal_functions;
42 changes: 37 additions & 5 deletions noir-projects/aztec-nr/aztec/src/macros/functions/mod.nr
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ pub(crate) mod auth_registry;
pub(crate) mod utils;

use crate::macros::{
internals_functions_generation::external_functions_registry,
internals_functions_generation::{external_functions_registry, internal_functions_registry},
utils::{is_fn_external, module_has_initializer},
};
use super::utils::{fn_has_noinitcheck, is_fn_initializer, is_fn_only_self, is_fn_view};
Expand Down Expand Up @@ -159,19 +159,51 @@ pub comptime fn external(f: FunctionDefinition, f_type: CtString) {
}
}

/// Same as in Solidity internal functions are functions that are callable from inside the contract. Unlike #[only_self]
/// functions, internal functions are inlined (e.g. akin to EVM's JUMP and not EVM's CALL).
///
/// Internal function can be called using the following API:
/// ```noir
/// self.internal.my_internal_function(...)
/// ```
///
/// Private internal functions can only be called from other private external or internal functions.
/// Public internal functions can only be called from other public external or internal functions.
pub comptime fn internal(f: FunctionDefinition, f_type: CtString) {
let function_name = f.name();
if is_fn_external(f) {
panic(
f"The #[internal] attribute cannot be applied to external functions - {function_name} is marked as both #[external] and #[internal(\"{f_type}\")]",
);
}

if f_type.eq("private") {
assert_valid_private(f);
internal_functions_registry::add_private(f);
} else if f_type.eq("public") {
assert_valid_public(f);
internal_functions_registry::add_public(f);
} else {
let function_name = f.name();
panic(
f"Function '{function_name}' is marked as #[internal(\"{f_type}\")], but '{f_type}' is not a valid internal function type. Internal functions must be one of 'private', 'public'",
);
}
}

comptime fn assert_valid_private(f: FunctionDefinition) {
let visibility = f.visibility();
if visibility != quote {} {
let name = f.name();
panic(
f"A function marked as #[external(\"private\")] must not have public Noir visibility - {name}'s visibility is '{visibility}'",
f"A function marked as #[external(\"private\")] or #[internal(\"private\")] must not have public Noir visibility - {name}'s visibility is '{visibility}'",
);
}

if f.is_unconstrained() {
let name = f.name();
panic(
f"#[external(\"private\")] functions must not be unconstrained - {name} is",
f"#[external(\"private\")] or #[internal(\"private\")] functions must not be unconstrained - {name} is",
);
}
}
Expand All @@ -181,14 +213,14 @@ comptime fn assert_valid_public(f: FunctionDefinition) {
if visibility != quote {} {
let name = f.name();
panic(
f"A function marked as #[external(\"public\")] must not have public Noir visibility - {name}'s visibility is '{visibility}'",
f"A function marked as #[external(\"public\")] or #[internal(\"public\")] must not have public Noir visibility - {name}'s visibility is '{visibility}'",
);
}

if f.is_unconstrained() {
let name = f.name();
panic(
f"#[external(\"public\")] functions must not be unconstrained - {name} is",
f"#[external(\"public\")] or #[internal(\"public\")] functions must not be unconstrained - {name} is",
);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// TODO(F-131): Drop all this and instead just iterate over the functions directly in the `#[aztec]` macro. This
// approach was already implemented for `#[internal(...)]` functions.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrelated but realized that the registry approach used here is unnecessary and convoluted - we can just directly iterate over the functions and generate the calls accordingly. I already used this approach for generating the calls to internal. Will tackle this in a followup PR.


//! Registry of self-call function stubs (CallSelf, CallSelfStatic, EnqueueSelf and EnqueueSelfStatic) that allow for
//! the following API:
//! self.call_self.some_private_function(args)
Expand Down
19 changes: 13 additions & 6 deletions noir-projects/aztec-nr/aztec/src/macros/functions/utils.nr
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ use crate::macros::{
notes::NOTES,
utils::{
fn_has_authorize_once, fn_has_noinitcheck, is_fn_contract_library_method, is_fn_external,
is_fn_initializer, is_fn_only_self, is_fn_test, is_fn_view, module_has_initializer,
module_has_storage,
is_fn_initializer, is_fn_internal, is_fn_only_self, is_fn_test, is_fn_view,
module_has_initializer, module_has_storage,
},
};
use dep::protocol_types::meta::utils::derive_serialization_quotes;
Expand Down Expand Up @@ -91,7 +91,8 @@ pub(crate) comptime fn transform_private(f: FunctionDefinition) -> Quoted {
let enqueue_self: EnqueueSelf<&mut aztec::context::private_context::PrivateContext> = EnqueueSelf { address: self_address, context: &mut context };
let call_self_static: CallSelfStatic<&mut aztec::context::private_context::PrivateContext> = CallSelfStatic { address: self_address, context: &mut context };
let enqueue_self_static: EnqueueSelfStatic<&mut aztec::context::private_context::PrivateContext> = EnqueueSelfStatic { address: self_address, context: &mut context };
aztec::contract_self::ContractSelf::new_private(&mut context, storage, call_self, enqueue_self, call_self_static, enqueue_self_static)
let internal: CallInternal<&mut aztec::context::private_context::PrivateContext> = CallInternal { context: &mut context };
aztec::contract_self::ContractSelf::new_private(&mut context, storage, call_self, enqueue_self, call_self_static, enqueue_self_static, internal)
};
};

Expand Down Expand Up @@ -276,7 +277,8 @@ pub(crate) comptime fn transform_public(f: FunctionDefinition) -> Quoted {
let self_address = context.this_address();
let call_self: CallSelf<aztec::context::public_context::PublicContext> = CallSelf { address: self_address, context };
let call_self_static: CallSelfStatic<aztec::context::public_context::PublicContext> = CallSelfStatic { address: self_address, context };
aztec::contract_self::ContractSelf::new_public(context, storage, call_self, call_self_static)
let internal: CallInternal<aztec::context::public_context::PublicContext> = CallInternal { context };
aztec::contract_self::ContractSelf::new_public(context, storage, call_self, call_self_static, internal)
};
};

Expand Down Expand Up @@ -528,9 +530,14 @@ pub(crate) comptime fn create_authorize_once_check(
pub(crate) comptime fn check_each_fn_macroified(m: Module) {
for f in m.functions() {
let name = f.name();
if !is_fn_external(f) & !is_fn_contract_library_method(f) & !is_fn_test(f) {
if !is_fn_external(f)
& !is_fn_contract_library_method(f)
& !is_fn_internal(f)
& !is_fn_test(f) {
// We don't suggest that #[contract_library_method] is allowed because we don't want to introduce another
// concept
panic(
f"Function {name} must be marked as either #[external(...)], #[contract_library_method], or #[test]",
f"Function {name} must be marked as either #[external(...)], #[internal(...)], or #[test]",
);
}
}
Expand Down
Loading
Loading