diff --git a/boxes/boxes/vanilla/contracts/src/main.nr b/boxes/boxes/vanilla/contracts/src/main.nr index aeda892b3cab..c13859c1c940 100644 --- a/boxes/boxes/vanilla/contracts/src/main.nr +++ b/boxes/boxes/vanilla/contracts/src/main.nr @@ -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 diff --git a/docs/docs/developers/docs/aztec-nr/framework-description/macros.md b/docs/docs/developers/docs/aztec-nr/framework-description/macros.md index 371982fc586f..ca87fdc6aca2 100644 --- a/docs/docs/developers/docs/aztec-nr/framework-description/macros.md +++ b/docs/docs/developers/docs/aztec-nr/framework-description/macros.md @@ -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 diff --git a/noir-projects/aztec-nr/aztec/src/contract_self.nr b/noir-projects/aztec-nr/aztec/src/contract_self.nr index bd502ffc746a..0f9e85811ed0 100644 --- a/noir-projects/aztec-nr/aztec/src/contract_self.nr +++ b/noir-projects/aztec-nr/aztec/src/contract_self.nr @@ -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 { +pub struct ContractSelf { /// 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 @@ -107,13 +107,23 @@ pub struct ContractSelf ContractSelf<&mut PrivateContext, Storage, CallSelf, EnqueueSelf, CallSelfStatic, EnqueueSelfStatic> { +impl 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. @@ -124,6 +134,7 @@ impl Contract enqueue_self: EnqueueSelf, call_self_static: CallSelfStatic, enqueue_self_static: EnqueueSelfStatic, + internal: CallInternal, ) -> Self { Self { context, @@ -133,6 +144,7 @@ impl Contract enqueue_self, call_self_static, enqueue_self_static, + internal, } } @@ -193,7 +205,7 @@ impl 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 ContractSelf { +impl ContractSelf { /// Creates a new `ContractSelf` instance for a public function. /// /// This constructor is called automatically by the macro system and should not be called directly. @@ -202,6 +214,7 @@ impl ContractSelf Self { Self { context, @@ -211,6 +224,7 @@ impl ContractSelf ContractSelf ContractSelf { +impl ContractSelf { /// Creates a new `ContractSelf` instance for a utility function. /// /// This constructor is called automatically by the macro system and should not be called directly. @@ -283,6 +297,7 @@ impl ContractSelf { enqueue_self: (), call_self_static: (), enqueue_self_static: (), + internal: (), } } } diff --git a/noir-projects/aztec-nr/aztec/src/macros/aztec.nr b/noir-projects/aztec-nr/aztec/src/macros/aztec.nr index 69f0309e397b..f86292dff7ac 100644 --- a/noir-projects/aztec-nr/aztec/src/macros/aztec.nr +++ b/noir-projects/aztec-nr/aztec/src/macros/aztec.nr @@ -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}, @@ -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); @@ -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 diff --git a/noir-projects/aztec-nr/aztec/src/macros/calls_generation/internal_functions.nr b/noir-projects/aztec-nr/aztec/src/macros/calls_generation/internal_functions.nr new file mode 100644 index 000000000000..4ba1ddafcce7 --- /dev/null +++ b/noir-projects/aztec-nr/aztec/src/macros/calls_generation/internal_functions.nr @@ -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, $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 { + pub context: Context, + } + + impl CallInternal<&mut aztec::context::private_context::PrivateContext> { + $private_internal_functions_calls + } + + impl CallInternal { + $public_internal_functions_calls + } + } +} diff --git a/noir-projects/aztec-nr/aztec/src/macros/calls_generation/mod.nr b/noir-projects/aztec-nr/aztec/src/macros/calls_generation/mod.nr new file mode 100644 index 000000000000..5474e067624a --- /dev/null +++ b/noir-projects/aztec-nr/aztec/src/macros/calls_generation/mod.nr @@ -0,0 +1 @@ +pub(crate) mod internal_functions; diff --git a/noir-projects/aztec-nr/aztec/src/macros/functions/mod.nr b/noir-projects/aztec-nr/aztec/src/macros/functions/mod.nr index c5dbe62e5476..97aa21cf8420 100644 --- a/noir-projects/aztec-nr/aztec/src/macros/functions/mod.nr +++ b/noir-projects/aztec-nr/aztec/src/macros/functions/mod.nr @@ -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}; @@ -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", ); } } @@ -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", ); } } diff --git a/noir-projects/aztec-nr/aztec/src/macros/functions/self_call_registry.nr b/noir-projects/aztec-nr/aztec/src/macros/functions/self_call_registry.nr index 5a9d34635dc7..d006ff49bec4 100644 --- a/noir-projects/aztec-nr/aztec/src/macros/functions/self_call_registry.nr +++ b/noir-projects/aztec-nr/aztec/src/macros/functions/self_call_registry.nr @@ -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. + //! Registry of self-call function stubs (CallSelf, CallSelfStatic, EnqueueSelf and EnqueueSelfStatic) that allow for //! the following API: //! self.call_self.some_private_function(args) diff --git a/noir-projects/aztec-nr/aztec/src/macros/functions/utils.nr b/noir-projects/aztec-nr/aztec/src/macros/functions/utils.nr index 318cf3b0f8bc..5f35e253736f 100644 --- a/noir-projects/aztec-nr/aztec/src/macros/functions/utils.nr +++ b/noir-projects/aztec-nr/aztec/src/macros/functions/utils.nr @@ -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; @@ -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) }; }; @@ -276,7 +277,8 @@ pub(crate) comptime fn transform_public(f: FunctionDefinition) -> Quoted { let self_address = context.this_address(); let call_self: CallSelf = CallSelf { address: self_address, context }; let call_self_static: CallSelfStatic = CallSelfStatic { address: self_address, context }; - aztec::contract_self::ContractSelf::new_public(context, storage, call_self, call_self_static) + let internal: CallInternal = CallInternal { context }; + aztec::contract_self::ContractSelf::new_public(context, storage, call_self, call_self_static, internal) }; }; @@ -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]", ); } } diff --git a/noir-projects/aztec-nr/aztec/src/macros/internals_functions_generation/internal.nr b/noir-projects/aztec-nr/aztec/src/macros/internals_functions_generation/internal.nr new file mode 100644 index 000000000000..f71ebd30936a --- /dev/null +++ b/noir-projects/aztec-nr/aztec/src/macros/internals_functions_generation/internal.nr @@ -0,0 +1,105 @@ +use crate::macros::utils::module_has_storage; + +/// Generates a private internal function based on the function that was originally marked with #[internal("private")]. +/// For more details on why we do this instead of just transforming the original function, see the documentation of this +/// module. +pub(crate) comptime fn generate_private_internal(f: FunctionDefinition) -> Quoted { + let original_function_name = f.name(); + let fn_name = f"__aztec_nr_internals__{original_function_name}".quoted_contents(); + let return_type = f.return_type(); + + let original_params = f + .parameters() + .map(|(param_name, param_type)| quote { $param_name: $param_type }) + .join(quote {, }); + + let params = quote { + context: &mut aztec::context::private_context::PrivateContext, + $original_params + }; + + let storage_init = if module_has_storage(f.module()) { + quote { + let storage = Storage::init(context); + } + } else { + quote { + let storage = (); + } + }; + + let body = f.body(); + + // Internal functions are inlined within external functions. For this reason we mark them with + // #[contract_library_method] to prevent them from being compiled as entry points. + quote { + #[contract_library_method] + fn $fn_name($params) -> $return_type { + #[allow(unused_variables)] + let mut self = { + $storage_init + let self_address = context.this_address(); + let call_self: CallSelf<&mut aztec::context::private_context::PrivateContext> = CallSelf { address: self_address, context }; + let enqueue_self: EnqueueSelf<&mut aztec::context::private_context::PrivateContext> = EnqueueSelf { address: self_address, context }; + let call_self_static: CallSelfStatic<&mut aztec::context::private_context::PrivateContext> = CallSelfStatic { address: self_address, context }; + let enqueue_self_static: EnqueueSelfStatic<&mut aztec::context::private_context::PrivateContext> = EnqueueSelfStatic { address: self_address, context }; + let internal: CallInternal<&mut aztec::context::private_context::PrivateContext> = CallInternal { context }; + aztec::contract_self::ContractSelf::new_private(context, storage, call_self, enqueue_self, call_self_static, enqueue_self_static, internal) + }; + + $body + } + } +} + +/// Generates a public internal function based on the function that was originally marked with #[internal("public")]. +/// For more details on why we do this instead of just transforming the original function, see the documentation of this +/// module. +pub(crate) comptime fn generate_public_internal(f: FunctionDefinition) -> Quoted { + let original_function_name = f.name(); + let fn_name = f"__aztec_nr_internals__{original_function_name}".quoted_contents(); + let return_type = f.return_type(); + + let original_params = f + .parameters() + .map(|(param_name, param_type)| quote { $param_name: $param_type }) + .join(quote {, }); + + let params = quote { + context: aztec::context::public_context::PublicContext, + $original_params + }; + + let storage_init = if module_has_storage(f.module()) { + quote { + let storage = Storage::init(context); + } + } else { + quote { + let storage = (); + } + }; + + let body = f.body(); + + // Internal public functions are marked as unconstrained because they are inlined within external public functions, + // which are also compiled as unconstrained and later transpiled to AVM bytecode. Since these internal functions are + // intended to be inlined, we mark them with #[contract_library_method] to prevent them from being compiled as entry + // points. + quote { + #[contract_library_method] + unconstrained fn $fn_name($params) -> $return_type { + #[allow(unused_variables)] + let mut self = { + $storage_init + let self_address = context.this_address(); + let call_self: CallSelf = CallSelf { address: self_address, context }; + let call_self_static: CallSelfStatic = CallSelfStatic { address: self_address, context }; + let internal: CallInternal = CallInternal { context }; + aztec::contract_self::ContractSelf::new_public(context, storage, call_self, call_self_static, internal) + }; + + $body + } + } +} diff --git a/noir-projects/aztec-nr/aztec/src/macros/internals_functions_generation/internal_functions_registry.nr b/noir-projects/aztec-nr/aztec/src/macros/internals_functions_generation/internal_functions_registry.nr new file mode 100644 index 000000000000..1dd1ede413bc --- /dev/null +++ b/noir-projects/aztec-nr/aztec/src/macros/internals_functions_generation/internal_functions_registry.nr @@ -0,0 +1,45 @@ +//! Registry for internal functions that are added to the corresponding global variable when the `#[internal(...)]` +//! macro is applied. This registry is then used by the `#[aztec]` macro to transform the internal functions. +//! +//! Note that we need to use this registry approach instead of directly iterating over the functions in the `#[aztec]` +//! macro, because at that point we do not have the information about whether a given internal function is private or +//! public. We only see the internal attribute itself, not the "private" or "public" argument. + +use poseidon::poseidon2::Poseidon2Hasher; +use std::{collections::umap::UHashMap, hash::BuildHasherDefault}; + +// Key is a contract module, value is an array of function definitions. +comptime mut global PRIVATE_INTERNAL_REGISTRY: UHashMap> = + UHashMap::default(); +comptime mut global PUBLIC_INTERNAL_REGISTRY: UHashMap> = + UHashMap::default(); + +comptime fn add_to_registry( + registry: &mut UHashMap>, + f: FunctionDefinition, +) { + let module = f.module(); + let current_functions = registry.get(module); + let functions_to_insert = if current_functions.is_some() { + current_functions.unwrap().push_back(f) + } else { + &[f] + }; + registry.insert(module, functions_to_insert); +} + +pub(crate) comptime fn add_private(f: FunctionDefinition) { + add_to_registry(&mut PRIVATE_INTERNAL_REGISTRY, f); +} + +pub(crate) comptime fn add_public(f: FunctionDefinition) { + add_to_registry(&mut PUBLIC_INTERNAL_REGISTRY, f); +} + +pub(crate) comptime fn get_private_functions(m: Module) -> [FunctionDefinition] { + PRIVATE_INTERNAL_REGISTRY.get(m).unwrap_or(&[]) +} + +pub(crate) comptime fn get_public_functions(m: Module) -> [FunctionDefinition] { + PUBLIC_INTERNAL_REGISTRY.get(m).unwrap_or(&[]) +} diff --git a/noir-projects/aztec-nr/aztec/src/macros/internals_functions_generation/mod.nr b/noir-projects/aztec-nr/aztec/src/macros/internals_functions_generation/mod.nr index 7b81452b262a..98855d237add 100644 --- a/noir-projects/aztec-nr/aztec/src/macros/internals_functions_generation/mod.nr +++ b/noir-projects/aztec-nr/aztec/src/macros/internals_functions_generation/mod.nr @@ -1,17 +1,17 @@ //! The functionality in this module is triggered by the `#[aztec]` macro. It generates new functions, prefixed with -//! `__aztec_nr_internals___`, from the ones marked with `#[external(...)]` attributes. The original functions are then -//! modified to be uncallable. This prevents developers from inadvertently calling a function directly, instead of -//! performing a proper contract call. +//! `__aztec_nr_internals___`, from the ones marked with `#[external(...)]` and `#[internal(...)]` attributes. The +//! original functions are then modified to be uncallable. This prevents developers from inadvertently calling a +//! function directly, instead of performing a proper contract call. pub mod abi_attributes; pub(crate) mod external_functions_registry; +pub(crate) mod internal_functions_registry; mod abi_export; +mod internal; use crate::macros::functions::utils::{transform_private, transform_public, transform_utility}; use abi_export::create_fn_abi_export; -use external_functions_registry::{ - get_private_functions, get_public_functions, get_utility_functions, -}; +use internal::{generate_private_internal, generate_public_internal}; /// Modifies a function such that when it's called directly, it will result in a compilation error with a reasonable /// error message. @@ -49,12 +49,13 @@ comptime fn make_functions_uncallable( }); } -/// Produces new functions for all external contract functions, prefixing them with `__aztec_nr_internals___`, and then -/// renders the original functions uncallable. +/// Produces new functions for all external and internal contract functions, prefixing them with +/// `__aztec_nr_internals___`, and then renders the original functions uncallable. pub(crate) comptime fn process_functions(m: Module) -> Quoted { - let private_functions = get_private_functions(m); - let public_functions = get_public_functions(m); - let utility_functions = get_utility_functions(m); + // EXTERNAL FUNCTIONS + 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 transformed_private_functions = private_functions.map(|function| transform_private(function)).join(quote {}); @@ -79,22 +80,47 @@ pub(crate) comptime fn process_functions(m: Module) -> Quoted { "Calling utility functions directly from within the contract is not supported. You attempted to call ", ); + // INTERNAL FUNCTIONS + let private_internal_functions = internal_functions_registry::get_private_functions(m); + let public_internal_functions = internal_functions_registry::get_public_functions(m); + + let generated_private_internal_functions = private_internal_functions + .map(|function| generate_private_internal(function)) + .join(quote {}); + let generated_public_internal_functions = + public_internal_functions.map(|function| generate_public_internal(function)).join(quote {}); + + // Just like for external functions, we make the internal functions not directly callable. + make_functions_uncallable( + private_internal_functions, + "Direct invocation of private internal functions is not supported. You attempted to call ", + ); + make_functions_uncallable( + public_internal_functions, + "Direct invocation of public internal functions is not supported. You attempted to call ", + ); + // We return the new functions' quotes to be injected into the contract. quote { $transformed_private_functions $transformed_public_functions $transformed_utility_functions + $generated_private_internal_functions + $generated_public_internal_functions } } // See docs of create_fn_abi_export for information on what this does. pub(crate) comptime fn create_fn_abi_exports(m: Module) -> Quoted { - let private_functions_exports = - get_private_functions(m).map(|function| create_fn_abi_export(function)).join(quote {}); - let public_functions_exports = - get_public_functions(m).map(|function| create_fn_abi_export(function)).join(quote {}); - let utility_functions_exports = - get_utility_functions(m).map(|function| create_fn_abi_export(function)).join(quote {}); + let private_functions_exports = external_functions_registry::get_private_functions(m) + .map(|function| create_fn_abi_export(function)) + .join(quote {}); + let public_functions_exports = external_functions_registry::get_public_functions(m) + .map(|function| create_fn_abi_export(function)) + .join(quote {}); + let utility_functions_exports = external_functions_registry::get_utility_functions(m) + .map(|function| create_fn_abi_export(function)) + .join(quote {}); quote { $private_functions_exports diff --git a/noir-projects/aztec-nr/aztec/src/macros/mod.nr b/noir-projects/aztec-nr/aztec/src/macros/mod.nr index f0821c9f3b9c..52d0a8bec09d 100644 --- a/noir-projects/aztec-nr/aztec/src/macros/mod.nr +++ b/noir-projects/aztec-nr/aztec/src/macros/mod.nr @@ -1,5 +1,6 @@ pub mod aztec; pub mod dispatch; +pub(crate) mod calls_generation; pub mod internals_functions_generation; pub mod functions; pub mod utils; diff --git a/noir-projects/aztec-nr/aztec/src/macros/utils.nr b/noir-projects/aztec-nr/aztec/src/macros/utils.nr index e48e2db13aa8..a082e9c25d76 100644 --- a/noir-projects/aztec-nr/aztec/src/macros/utils.nr +++ b/noir-projects/aztec-nr/aztec/src/macros/utils.nr @@ -5,6 +5,10 @@ pub(crate) comptime fn is_fn_external(f: FunctionDefinition) -> bool { f.has_named_attribute("external") } +pub(crate) comptime fn is_fn_internal(f: FunctionDefinition) -> bool { + f.has_named_attribute("internal") +} + pub(crate) comptime fn is_fn_contract_library_method(f: FunctionDefinition) -> bool { f.has_named_attribute("contract_library_method") } diff --git a/noir-projects/noir-contracts-comp-failures/contracts/panic_on_direct_private_internal_fn_call/Nargo.toml b/noir-projects/noir-contracts-comp-failures/contracts/panic_on_direct_private_internal_fn_call/Nargo.toml new file mode 100644 index 000000000000..f1ad725a4ce8 --- /dev/null +++ b/noir-projects/noir-contracts-comp-failures/contracts/panic_on_direct_private_internal_fn_call/Nargo.toml @@ -0,0 +1,8 @@ +[package] +name = "panic_on_direct_private_internal_fn_call" +authors = [""] +compiler_version = ">=0.25.0" +type = "contract" + +[dependencies] +aztec = { path = "../../../aztec-nr/aztec" } diff --git a/noir-projects/noir-contracts-comp-failures/contracts/panic_on_direct_private_internal_fn_call/expected_error b/noir-projects/noir-contracts-comp-failures/contracts/panic_on_direct_private_internal_fn_call/expected_error new file mode 100644 index 000000000000..9a25b891f016 --- /dev/null +++ b/noir-projects/noir-contracts-comp-failures/contracts/panic_on_direct_private_internal_fn_call/expected_error @@ -0,0 +1 @@ +Direct invocation of private internal functions is not supported. You attempted to call arbitrary_private_function. See documentation for instructions on the proper invocation of functions. diff --git a/noir-projects/noir-contracts-comp-failures/contracts/panic_on_direct_private_internal_fn_call/src/main.nr b/noir-projects/noir-contracts-comp-failures/contracts/panic_on_direct_private_internal_fn_call/src/main.nr new file mode 100644 index 000000000000..d373215993c0 --- /dev/null +++ b/noir-projects/noir-contracts-comp-failures/contracts/panic_on_direct_private_internal_fn_call/src/main.nr @@ -0,0 +1,14 @@ +use aztec::macros::aztec; + +#[aztec] +pub contract PanicOnDirectInternalPrivateFnCall { + use aztec::macros::functions::{internal, external}; + + #[internal("private")] + fn arbitrary_private_function() {} + + #[external("private")] + fn function_calling_private_function_directly() { + arbitrary_private_function(); + } +} diff --git a/noir-projects/noir-contracts-comp-failures/contracts/panic_on_direct_public_internal_fn_call/Nargo.toml b/noir-projects/noir-contracts-comp-failures/contracts/panic_on_direct_public_internal_fn_call/Nargo.toml new file mode 100644 index 000000000000..f740d59f6626 --- /dev/null +++ b/noir-projects/noir-contracts-comp-failures/contracts/panic_on_direct_public_internal_fn_call/Nargo.toml @@ -0,0 +1,8 @@ +[package] +name = "panic_on_direct_public_internal_fn_call" +authors = [""] +compiler_version = ">=0.25.0" +type = "contract" + +[dependencies] +aztec = { path = "../../../aztec-nr/aztec" } diff --git a/noir-projects/noir-contracts-comp-failures/contracts/panic_on_direct_public_internal_fn_call/expected_error b/noir-projects/noir-contracts-comp-failures/contracts/panic_on_direct_public_internal_fn_call/expected_error new file mode 100644 index 000000000000..742fcd7af6e0 --- /dev/null +++ b/noir-projects/noir-contracts-comp-failures/contracts/panic_on_direct_public_internal_fn_call/expected_error @@ -0,0 +1 @@ +Direct invocation of public internal functions is not supported. You attempted to call arbitrary_public_function. See documentation for instructions on the proper invocation of functions. diff --git a/noir-projects/noir-contracts-comp-failures/contracts/panic_on_direct_public_internal_fn_call/src/main.nr b/noir-projects/noir-contracts-comp-failures/contracts/panic_on_direct_public_internal_fn_call/src/main.nr new file mode 100644 index 000000000000..4f77878eed26 --- /dev/null +++ b/noir-projects/noir-contracts-comp-failures/contracts/panic_on_direct_public_internal_fn_call/src/main.nr @@ -0,0 +1,14 @@ +use aztec::macros::aztec; + +#[aztec] +pub contract PanicOnDirectInternalPublicFnCall { + use aztec::macros::functions::{internal, external}; + + #[internal("public")] + fn arbitrary_public_function() {} + + #[external("public")] + fn function_calling_public_function_directly() { + arbitrary_public_function(); + } +} diff --git a/noir-projects/noir-contracts/contracts/app/nft_contract/src/main.nr b/noir-projects/noir-contracts/contracts/app/nft_contract/src/main.nr index 209024d6d88c..9fc30b466c81 100644 --- a/noir-projects/noir-contracts/contracts/app/nft_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/app/nft_contract/src/main.nr @@ -11,9 +11,8 @@ pub contract NFT { use crate::types::nft_note::{NFTNote, PartialNFTNote}; use aztec::{ authwit::auth::compute_authwit_nullifier, - context::{PrivateContext, PublicContext}, macros::{ - functions::{authorize_once, external, initializer, only_self, view}, + functions::{authorize_once, external, initializer, internal, only_self, view}, storage::storage, }, messages::message_delivery::MessageDelivery, @@ -154,7 +153,7 @@ pub contract NFT { let from = self.msg_sender().unwrap(); // We prepare the private balance increase. - let partial_note = _prepare_private_balance_increase(to, self.context, self.storage); + let partial_note = self.internal._prepare_private_balance_increase(to); // At last we finalize the transfer. Usage of the `unsafe` method here is safe because we set the `from` // function argument to a message sender, guaranteeing that he can transfer only his own NFTs. @@ -165,27 +164,18 @@ pub contract NFT { /// `finalize_transfer_to_private` with the returned partial note. #[external("private")] fn prepare_private_balance_increase(to: AztecAddress) -> PartialNFTNote { - _prepare_private_balance_increase(to, self.context, self.storage) + self.internal._prepare_private_balance_increase(to) } - /// This function exists separately from `prepare_private_balance_increase` solely as an optimization as it allows - /// us to have it inlined in the `transfer_to_private` function which results in one less kernel iteration. - /// - /// TODO(#9180): Consider adding macro support for functions callable both as an entrypoint and as an internal - /// function. - #[contract_library_method] - fn _prepare_private_balance_increase( - to: AztecAddress, - context: &mut PrivateContext, - storage: Storage<&mut PrivateContext>, - ) -> PartialNFTNote { + #[internal("private")] + fn _prepare_private_balance_increase(to: AztecAddress) -> PartialNFTNote { // We setup a partial note with unpopulated/zero token id for 'to'. let partial_note = NFTNote::partial( to, - storage.private_nfts.at(to).storage_slot, - context, + self.storage.private_nfts.at(to).storage_slot, + self.context, to, - context.msg_sender().unwrap(), + self.msg_sender().unwrap(), ); partial_note @@ -204,13 +194,7 @@ pub contract NFT { // Completer is the entity that can complete the partial note. In this case, it's the same as the account // `from` from whose account the token is being transferred. let from_and_completer = self.msg_sender().unwrap(); - _finalize_transfer_to_private( - from_and_completer, - token_id, - partial_note, - self.context, - self.storage, - ); + self.internal._finalize_transfer_to_private(from_and_completer, token_id, partial_note); } /// This is a wrapper around `_finalize_transfer_to_private` placed here so that a call @@ -223,33 +207,25 @@ pub contract NFT { token_id: Field, partial_note: PartialNFTNote, ) { - _finalize_transfer_to_private( - from_and_completer, - token_id, - partial_note, - self.context, - self.storage, - ); + self.internal._finalize_transfer_to_private(from_and_completer, token_id, partial_note); } // In all the flows in this contract, `from` (the account from which we're transferring the NFT) and `completer` // (the entity that can complete the partial note) are the same so we represent them with a single argument. - #[contract_library_method] + #[internal("public")] fn _finalize_transfer_to_private( from_and_completer: AztecAddress, token_id: Field, partial_note: PartialNFTNote, - context: PublicContext, - storage: Storage, ) { - let public_owners_storage = storage.public_owners.at(token_id); + let public_owners_storage = self.storage.public_owners.at(token_id); assert(public_owners_storage.read().eq(from_and_completer), "invalid NFT owner"); // Set the public NFT owner to zero public_owners_storage.write(AztecAddress::zero()); // We finalize the transfer by completing the partial note. - partial_note.complete(context, from_and_completer, token_id); + partial_note.complete(self.context, from_and_completer, token_id); } /** diff --git a/noir-projects/noir-contracts/contracts/app/simple_token_contract/src/main.nr b/noir-projects/noir-contracts/contracts/app/simple_token_contract/src/main.nr index 755ca3207607..14b4fdd68bc0 100644 --- a/noir-projects/noir-contracts/contracts/app/simple_token_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/app/simple_token_contract/src/main.nr @@ -14,10 +14,10 @@ pub contract SimpleToken { use aztec::{ authwit::auth::compute_authwit_nullifier, - context::{PrivateCallInterface, PrivateContext, PublicContext}, + context::{PrivateCallInterface, PrivateContext}, macros::{ events::event, - functions::{authorize_once, external, initializer, only_self, view}, + functions::{authorize_once, external, initializer, internal, only_self, view}, storage::storage, }, messages::message_delivery::MessageDelivery, @@ -137,13 +137,7 @@ pub contract SimpleToken { fn private_transfer(to: AztecAddress, amount: u128) { let from = self.msg_sender().unwrap(); - let change = subtract_balance( - self.context, - self.storage, - from, - amount, - INITIAL_TRANSFER_CALL_MAX_NOTES, - ); + let change = self.internal.subtract_balance(from, amount, INITIAL_TRANSFER_CALL_MAX_NOTES); self.storage.balances.at(from).add(from, change).emit( from, MessageDelivery.UNCONSTRAINED_ONCHAIN, @@ -167,27 +161,23 @@ pub contract SimpleToken { fn transfer_from_public_to_private(to: AztecAddress, amount: u128) { let from = self.msg_sender().unwrap(); - let partial_note = _prepare_private_balance_increase(to, self.context, self.storage); + let partial_note = self.internal._prepare_private_balance_increase(to); self.enqueue_self._finalize_transfer_to_private_unsafe(from, amount, partial_note); } #[external("private")] fn prepare_private_balance_increase(to: AztecAddress, from: AztecAddress) -> PartialUintNote { - _prepare_private_balance_increase(to, self.context, self.storage) + self.internal._prepare_private_balance_increase(to) } - #[contract_library_method] - fn _prepare_private_balance_increase( - to: AztecAddress, - context: &mut PrivateContext, - storage: Storage<&mut PrivateContext>, - ) -> PartialUintNote { + #[internal("private")] + fn _prepare_private_balance_increase(to: AztecAddress) -> PartialUintNote { let partial_note = UintNote::partial( to, - storage.balances.at(to).set.storage_slot, - context, + self.storage.balances.at(to).set.storage_slot, + self.context, to, - context.msg_sender().unwrap(), + self.context.msg_sender().unwrap(), ); partial_note @@ -196,13 +186,7 @@ pub contract SimpleToken { #[external("public")] fn finalize_transfer_to_private(amount: u128, partial_note: PartialUintNote) { let from_and_completer = self.msg_sender().unwrap(); - _finalize_transfer_to_private( - from_and_completer, - amount, - partial_note, - self.context, - self.storage, - ); + self.internal._finalize_transfer_to_private(from_and_completer, amount, partial_note); } #[external("public")] @@ -212,32 +196,26 @@ pub contract SimpleToken { amount: u128, partial_note: PartialUintNote, ) { - _finalize_transfer_to_private( - from_and_completer, - amount, - partial_note, - self.context, - self.storage, - ); + self.internal._finalize_transfer_to_private(from_and_completer, amount, partial_note); } - #[contract_library_method] + #[internal("public")] fn _finalize_transfer_to_private( from_and_completer: AztecAddress, amount: u128, partial_note: PartialUintNote, - context: PublicContext, - storage: Storage, ) { - let from_balance = storage.public_balances.at(from_and_completer).read().sub(amount); - storage.public_balances.at(from_and_completer).write(from_balance); + let balance_storage = self.storage.public_balances.at(from_and_completer); + + let from_balance = balance_storage.read().sub(amount); + balance_storage.write(from_balance); - partial_note.complete(context, from_and_completer, amount); + partial_note.complete(self.context, from_and_completer, amount); } #[external("private")] fn mint_privately(from: AztecAddress, to: AztecAddress, amount: u128) { - let partial_note = _prepare_private_balance_increase(to, self.context, self.storage); + let partial_note = self.internal._prepare_private_balance_increase(to); self.enqueue_self._finalize_mint_to_private_unsafe( self.msg_sender().unwrap(), amount, @@ -247,13 +225,7 @@ pub contract SimpleToken { #[external("public")] fn finalize_mint_to_private(amount: u128, partial_note: PartialUintNote) { - _finalize_mint_to_private( - self.msg_sender().unwrap(), - amount, - partial_note, - self.context, - self.storage, - ); + self.internal._finalize_mint_to_private(self.msg_sender().unwrap(), amount, partial_note); } #[external("public")] @@ -263,43 +235,33 @@ pub contract SimpleToken { amount: u128, partial_note: PartialUintNote, ) { - _finalize_mint_to_private( - minter_and_completer, - amount, - partial_note, - self.context, - self.storage, - ); + self.internal._finalize_mint_to_private(minter_and_completer, amount, partial_note); } - #[contract_library_method] + #[internal("public")] fn _finalize_mint_to_private( completer: AztecAddress, amount: u128, partial_note: PartialUintNote, - context: PublicContext, - storage: Storage, ) { - let supply = storage.total_supply.read().add(amount); - storage.total_supply.write(supply); + let supply = self.storage.total_supply.read().add(amount); + self.storage.total_supply.write(supply); - partial_note.complete(context, completer, amount); + partial_note.complete(self.context, completer, amount); } #[external("public")] #[only_self] fn _increase_public_balance(to: AztecAddress, amount: u128) { - _increase_public_balance_inner(to, amount, self.storage); + self.internal._increase_public_balance_inner(to, amount); } - #[contract_library_method] - fn _increase_public_balance_inner( - to: AztecAddress, - amount: u128, - storage: Storage, - ) { - let new_balance = storage.public_balances.at(to).read().add(amount); - storage.public_balances.at(to).write(new_balance); + #[internal("public")] + fn _increase_public_balance_inner(to: AztecAddress, amount: u128) { + let balance_storage = self.storage.public_balances.at(to); + + let new_balance = balance_storage.read().add(amount); + balance_storage.write(new_balance); } #[external("public")] @@ -316,21 +278,17 @@ pub contract SimpleToken { self.context.push_nullifier(nullifier); } - #[contract_library_method] - fn subtract_balance( - context: &mut PrivateContext, - storage: Storage<&mut PrivateContext>, - account: AztecAddress, - amount: u128, - max_notes: u32, - ) -> u128 { - let subtracted = storage.balances.at(account).try_sub(amount, max_notes); + #[internal("private")] + fn subtract_balance(account: AztecAddress, amount: u128, max_notes: u32) -> u128 { + let subtracted = self.storage.balances.at(account).try_sub(amount, max_notes); assert(subtracted > 0 as u128, "Balance too low"); if subtracted >= amount { subtracted - amount } else { let remaining = amount - subtracted; - compute_recurse_subtract_balance_call(*context, account, remaining).call(context) + compute_recurse_subtract_balance_call(*self.context, account, remaining).call( + self.context, + ) } } @@ -347,12 +305,6 @@ pub contract SimpleToken { #[only_self] #[external("private")] fn _recurse_subtract_balance(account: AztecAddress, amount: u128) -> u128 { - subtract_balance( - self.context, - self.storage, - account, - amount, - RECURSIVE_TRANSFER_CALL_MAX_NOTES, - ) + self.internal.subtract_balance(account, amount, RECURSIVE_TRANSFER_CALL_MAX_NOTES) } } diff --git a/noir-projects/noir-contracts/contracts/app/token_contract/src/main.nr b/noir-projects/noir-contracts/contracts/app/token_contract/src/main.nr index efc4245e57c3..a3e066625b8f 100644 --- a/noir-projects/noir-contracts/contracts/app/token_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/app/token_contract/src/main.nr @@ -2,7 +2,7 @@ mod types; mod test; -use dep::aztec::macros::aztec; +use aztec::macros::aztec; // Minimal token implementation that supports `AuthWit` accounts. // The auth message follows a similar pattern to the cross-chain message and includes a designated caller. @@ -15,23 +15,22 @@ pub contract Token { // Libs use std::ops::{Add, Sub}; - use dep::compressed_string::FieldCompressedString; + use compressed_string::FieldCompressedString; - use dep::aztec::{ - context::{PrivateCallInterface, PrivateContext, PublicContext}, + use aztec::{ + authwit::auth::compute_authwit_nullifier, + context::{PrivateCallInterface, PrivateContext}, macros::{ events::event, - functions::{authorize_once, external, initializer, only_self, view}, + functions::{authorize_once, external, initializer, internal, only_self, view}, storage::storage, }, messages::message_delivery::MessageDelivery, + protocol_types::{address::AztecAddress, traits::ToField}, state_vars::{Map, PublicImmutable, PublicMutable}, }; - use dep::uint_note::uint_note::{PartialUintNote, UintNote}; - use aztec::protocol_types::{address::AztecAddress, traits::ToField}; - - use aztec::authwit::auth::compute_authwit_nullifier; + use uint_note::uint_note::{PartialUintNote, UintNote}; use crate::types::balance_set::BalanceSet; @@ -230,7 +229,7 @@ pub contract Token { self.enqueue_self._increase_public_balance(to, amount); // We prepare the private balance increase (the partial note for the change). - _prepare_private_balance_increase(from, self.context, self.storage) + self.internal._prepare_private_balance_increase(from) } #[external("private")] @@ -243,13 +242,7 @@ pub contract Token { // Since the sum of the amounts in the notes we nullified was potentially larger than amount, we create a new // note for `from` with the change amount, e.g. if `amount` is 10 and two notes are nullified with amounts 8 and // 5, then the change will be 3 (since 8 + 5 - 10 = 3). - let change = subtract_balance( - self.context, - self.storage, - from, - amount, - INITIAL_TRANSFER_CALL_MAX_NOTES, - ); + let change = self.internal.subtract_balance(from, amount, INITIAL_TRANSFER_CALL_MAX_NOTES); self.storage.balances.at(from).add(from, change).emit( from, MessageDelivery.UNCONSTRAINED_ONCHAIN, @@ -263,15 +256,9 @@ pub contract Token { self.emit(Transfer { from, to, amount }, to, MessageDelivery.UNCONSTRAINED_ONCHAIN); } - #[contract_library_method] - fn subtract_balance( - context: &mut PrivateContext, - storage: Storage<&mut PrivateContext>, - account: AztecAddress, - amount: u128, - max_notes: u32, - ) -> u128 { - let subtracted = storage.balances.at(account).try_sub(amount, max_notes); + #[internal("private")] + fn subtract_balance(account: AztecAddress, amount: u128, max_notes: u32) -> u128 { + let subtracted = self.storage.balances.at(account).try_sub(amount, max_notes); // Failing to subtract any amount means that the owner was unable to produce more notes that could be nullified. // We could in some cases fail early inside try_sub if we detected that fewer notes than the maximum were // returned and we were still unable to reach the target amount, but that'd make the code more complicated, and @@ -303,13 +290,7 @@ pub contract Token { #[only_self] #[external("private")] fn _recurse_subtract_balance(account: AztecAddress, amount: u128) -> u128 { - subtract_balance( - self.context, - self.storage, - account, - amount, - RECURSIVE_TRANSFER_CALL_MAX_NOTES, - ) + self.internal.subtract_balance(account, amount, RECURSIVE_TRANSFER_CALL_MAX_NOTES) } /** @@ -357,7 +338,7 @@ pub contract Token { let from = self.msg_sender().unwrap(); // We prepare the private balance increase (the partial note). - let partial_note = _prepare_private_balance_increase(to, self.context, self.storage); + let partial_note = self.internal._prepare_private_balance_increase(to); // At last we finalize the transfer. Usage of the `unsafe` method here is safe because we set the `from` // function argument to a message sender, guaranteeing that he can transfer only his own tokens. @@ -369,28 +350,21 @@ pub contract Token { /// returned partial note. #[external("private")] fn prepare_private_balance_increase(to: AztecAddress) -> PartialUintNote { - _prepare_private_balance_increase(to, self.context, self.storage) + self.internal._prepare_private_balance_increase(to) } /// This function exists separately from `prepare_private_balance_increase` solely as an optimization as it allows /// us to have it inlined in the `transfer_to_private` function which results in one fewer kernel iteration. Note /// that in this case we don't pass `completer` as an argument to this function because in all the callsites we /// want to use the message sender as the completer anyway. - /// - /// TODO(#9180): Consider adding macro support for functions callable both as an entrypoint and as an internal - /// function. - #[contract_library_method] - fn _prepare_private_balance_increase( - to: AztecAddress, - context: &mut PrivateContext, - storage: Storage<&mut PrivateContext>, - ) -> PartialUintNote { + #[internal("private")] + fn _prepare_private_balance_increase(to: AztecAddress) -> PartialUintNote { let partial_note = UintNote::partial( to, - storage.balances.at(to).set.storage_slot, - context, + self.storage.balances.at(to).set.storage_slot, + self.context, to, - context.msg_sender().unwrap(), + self.msg_sender().unwrap(), ); partial_note @@ -409,13 +383,7 @@ pub contract Token { // Completer is the entity that can complete the partial note. In this case, it's the same as the account // `from` from whose balance we're subtracting the `amount`. let from_and_completer = self.msg_sender().unwrap(); - _finalize_transfer_to_private( - from_and_completer, - amount, - partial_note, - self.context, - self.storage, - ); + self.internal._finalize_transfer_to_private(from_and_completer, amount, partial_note); } /// Finalizes a transfer of token `amount` from private balance of `from` to a private balance of `to`. @@ -453,32 +421,26 @@ pub contract Token { amount: u128, partial_note: PartialUintNote, ) { - _finalize_transfer_to_private( - from_and_completer, - amount, - partial_note, - self.context, - self.storage, - ); + self.internal._finalize_transfer_to_private(from_and_completer, amount, partial_note); } // In all the flows in this contract, `from` (the account from which we're subtracting the `amount`) and // `completer` (the entity that can complete the partial note) are the same so we represent them with a single // argument. - #[contract_library_method] + #[internal("public")] fn _finalize_transfer_to_private( from_and_completer: AztecAddress, amount: u128, partial_note: PartialUintNote, - context: PublicContext, - storage: Storage, ) { // First we subtract the `amount` from the public balance of `from_and_completer` - let from_balance = storage.public_balances.at(from_and_completer).read().sub(amount); - storage.public_balances.at(from_and_completer).write(from_balance); + let balance_storage = self.storage.public_balances.at(from_and_completer); + + let from_balance = balance_storage.read().sub(amount); + balance_storage.write(from_balance); // We finalize the transfer by completing the partial note. - partial_note.complete(context, from_and_completer, amount); + partial_note.complete(self.context, from_and_completer, amount); } /// Mints token `amount` to a private balance of `to`. Message sender has to have minter permissions (checked @@ -486,7 +448,7 @@ pub contract Token { #[external("private")] fn mint_to_private(to: AztecAddress, amount: u128) { // We prepare the partial note to which we'll "send" the minted amount. - let partial_note = _prepare_private_balance_increase(to, self.context, self.storage); + let partial_note = self.internal._prepare_private_balance_increase(to); // At last we finalize the mint. Usage of the `unsafe` method here is safe because we set // the `minter_and_completer` function argument to a message sender, guaranteeing that only a message sender @@ -512,13 +474,7 @@ pub contract Token { let minter_and_completer = self.msg_sender().unwrap(); assert(self.storage.minters.at(minter_and_completer).read(), "caller is not minter"); - _finalize_mint_to_private( - minter_and_completer, - amount, - partial_note, - self.context, - self.storage, - ); + self.internal._finalize_mint_to_private(minter_and_completer, amount, partial_note); } /// This is a wrapper around `_finalize_mint_to_private` placed here so that a call @@ -533,48 +489,30 @@ pub contract Token { ) { // We check the minter permissions as it was not done in `mint_to_private` function. assert(self.storage.minters.at(minter_and_completer).read(), "caller is not minter"); - _finalize_mint_to_private( - minter_and_completer, - amount, - partial_note, - self.context, - self.storage, - ); + self.internal._finalize_mint_to_private(minter_and_completer, amount, partial_note); } - #[contract_library_method] + #[internal("public")] fn _finalize_mint_to_private( completer: AztecAddress, // entity that can complete the partial note amount: u128, partial_note: PartialUintNote, - context: PublicContext, - storage: Storage, ) { // First we increase the total supply by the `amount` - let supply = storage.total_supply.read().add(amount); - storage.total_supply.write(supply); + let supply = self.storage.total_supply.read().add(amount); + self.storage.total_supply.write(supply); // We finalize the transfer by completing the partial note. - partial_note.complete(context, completer, amount); + partial_note.complete(self.context, completer, amount); } - /// Internal /// - /// TODO(#9180): Consider adding macro support for functions callable both as an entrypoint and as an internal - /// function. #[external("public")] #[only_self] fn _increase_public_balance(to: AztecAddress, amount: u128) { - _increase_public_balance_inner(to, amount, self.storage); - } + let to_balance = self.storage.public_balances.at(to); - #[contract_library_method] - fn _increase_public_balance_inner( - to: AztecAddress, - amount: u128, - storage: Storage, - ) { - let new_balance = storage.public_balances.at(to).read().add(amount); - storage.public_balances.at(to).write(new_balance); + let new_balance = to_balance.read().add(amount); + to_balance.write(new_balance); } #[external("public")] diff --git a/noir-projects/noir-contracts/contracts/docs/docs_example_contract/src/main.nr b/noir-projects/noir-contracts/contracts/docs/docs_example_contract/src/main.nr index 681c31acb4b7..b39443f99659 100644 --- a/noir-projects/noir-contracts/contracts/docs/docs_example_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/docs/docs_example_contract/src/main.nr @@ -163,6 +163,7 @@ pub contract DocsExample { let enqueue_self = (); let call_self_static = (); let enqueue_self_static = (); + let internal = (); aztec::contract_self::ContractSelf::new_private( &mut context, storage, @@ -170,6 +171,7 @@ pub contract DocsExample { enqueue_self, call_self_static, enqueue_self_static, + internal, ) }; // docs:end:contract_self_creation diff --git a/noir-projects/noir-contracts/contracts/protocol/fee_juice_contract/src/main.nr b/noir-projects/noir-contracts/contracts/protocol/fee_juice_contract/src/main.nr index c68e7fb874f2..8aa103ce2fe1 100644 --- a/noir-projects/noir-contracts/contracts/protocol/fee_juice_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/protocol/fee_juice_contract/src/main.nr @@ -5,8 +5,7 @@ use dep::aztec::macros::aztec; #[aztec] pub contract FeeJuice { use dep::aztec::{ - context::private_context::PrivateContext, - macros::{functions::{external, only_self, view}, storage::storage}, + macros::{functions::{external, internal, only_self, view}, storage::storage}, protocol_types::{ address::{AztecAddress, EthAddress}, constants::FEE_JUICE_ADDRESS, @@ -25,32 +24,31 @@ pub contract FeeJuice { balances: Map, Context>, } - /// Verifies that a message from L1 exists with the correct claim imformation, consumes it (emitting a nullifier) + /// Verifies that a message from L1 exists with the correct claim information, consumes it (emitting a nullifier) /// and enqueues the increase in public balance of the recipient - #[contract_library_method] - fn claim_helper( - context: &mut PrivateContext, - to: AztecAddress, - amount: u128, - secret: Field, - message_leaf_index: Field, - ) { + #[internal("private")] + fn claim_helper(to: AztecAddress, amount: u128, secret: Field, message_leaf_index: Field) { let content_hash = get_bridge_gas_msg_hash(to, amount); let portal_address = EthAddress::from_field(FEE_JUICE_ADDRESS.to_field()); assert(!portal_address.is_zero()); // Consume message and emit nullifier - context.consume_l1_to_l2_message(content_hash, secret, portal_address, message_leaf_index); + self.context.consume_l1_to_l2_message( + content_hash, + secret, + portal_address, + message_leaf_index, + ); // TODO(palla/gas) Emit an unencrypted log to announce which L1 to L2 message has been claimed // Otherwise, we cannot trace L1 deposits to their corresponding claims on L2 - FeeJuice::at(context.this_address())._increase_public_balance(to, amount).enqueue(context); + self.enqueue_self._increase_public_balance(to, amount); } /// Claims FeeJuice by consuming an L1 to L2 message with the provided information. #[external("private")] fn claim(to: AztecAddress, amount: u128, secret: Field, message_leaf_index: Field) { - claim_helper(self.context, to, amount, secret, message_leaf_index); + self.internal.claim_helper(to, amount, secret, message_leaf_index); } /// Claims FeeJuice by consuming an L1 to L2 message with the provided information. After enqueuing the @@ -64,7 +62,7 @@ pub contract FeeJuice { secret: Field, message_leaf_index: Field, ) { - claim_helper(self.context, to, amount, secret, message_leaf_index); + self.internal.claim_helper(to, amount, secret, message_leaf_index); self.context.end_setup(); } diff --git a/noir-projects/noir-contracts/contracts/test/pending_note_hashes_contract/src/main.nr b/noir-projects/noir-contracts/contracts/test/pending_note_hashes_contract/src/main.nr index c6685c9b0e84..04b793a4b85b 100644 --- a/noir-projects/noir-contracts/contracts/test/pending_note_hashes_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/test/pending_note_hashes_contract/src/main.nr @@ -8,8 +8,7 @@ use aztec::macros::aztec; pub contract PendingNoteHashes { // Libs use aztec::{ - context::PrivateContext, - macros::{functions::external, storage::storage}, + macros::{functions::{external, internal}, storage::storage}, messages::message_delivery::MessageDelivery, note::{note_emission::NoteEmission, note_getter_options::NoteGetterOptions}, protocol_types::{ @@ -298,7 +297,7 @@ pub contract PendingNoteHashes { sender: AztecAddress, how_many_recursions: u64, ) { - create_max_notes(owner, self.storage); + self.internal.create_max_notes(owner); self.call_self.recursively_destroy_and_create_notes(owner, sender, how_many_recursions); } @@ -311,8 +310,8 @@ pub contract PendingNoteHashes { ) { assert(executions_left > 0); - destroy_max_notes(owner, self.storage); - create_max_notes(owner, self.storage); + self.internal.destroy_max_notes(owner); + self.internal.create_max_notes(owner); let executions_left = executions_left - 1; @@ -346,9 +345,9 @@ pub contract PendingNoteHashes { bad_note_emission.emit(owner, MessageDelivery.CONSTRAINED_ONCHAIN); } - #[contract_library_method] - fn create_max_notes(owner: AztecAddress, storage: Storage<&mut PrivateContext>) { - let owner_balance = storage.balances.at(owner); + #[internal("private")] + fn create_max_notes(owner: AztecAddress) { + let owner_balance = self.storage.balances.at(owner); for i in 0..max_notes_per_call() { let note = ValueNote::new(i as Field, owner); @@ -356,9 +355,9 @@ pub contract PendingNoteHashes { } } - #[contract_library_method] - fn destroy_max_notes(owner: AztecAddress, storage: Storage<&mut PrivateContext>) { - let owner_balance = storage.balances.at(owner); + #[internal("private")] + fn destroy_max_notes(owner: AztecAddress) { + let owner_balance = self.storage.balances.at(owner); // Note that we're relying on PXE actually returning the notes, we're not constraining that any specific // number of notes are deleted. let _ = owner_balance.pop_notes(NoteGetterOptions::new()); diff --git a/noir-projects/noir-contracts/contracts/test/test_log_contract/src/main.nr b/noir-projects/noir-contracts/contracts/test/test_log_contract/src/main.nr index 06df9f3ba1e6..657242730a85 100644 --- a/noir-projects/noir-contracts/contracts/test/test_log_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/test/test_log_contract/src/main.nr @@ -32,7 +32,7 @@ pub contract TestLog { fn emit_encrypted_events(other: AztecAddress, preimages: [Field; 4]) { let event0 = ExampleEvent0 { value0: preimages[0], value1: preimages[1] }; - self.emit(event0, self.context.msg_sender().unwrap(), MessageDelivery.CONSTRAINED_ONCHAIN); + self.emit(event0, self.msg_sender().unwrap(), MessageDelivery.CONSTRAINED_ONCHAIN); // We duplicate the emission, but swapping the sender and recipient: self.emit(event0, other, MessageDelivery.CONSTRAINED_ONCHAIN); @@ -42,7 +42,7 @@ pub contract TestLog { value3: preimages[3] as u8, }; - self.emit(event1, self.context.msg_sender().unwrap(), MessageDelivery.CONSTRAINED_ONCHAIN); + self.emit(event1, self.msg_sender().unwrap(), MessageDelivery.CONSTRAINED_ONCHAIN); } #[external("public")]