diff --git a/noir-projects/aztec-nr/aztec/src/encrypted_logs/incoming_body.nr b/noir-projects/aztec-nr/aztec/src/encrypted_logs/incoming_body.nr index 3d9c408afd77..c79089771136 100644 --- a/noir-projects/aztec-nr/aztec/src/encrypted_logs/incoming_body.nr +++ b/noir-projects/aztec-nr/aztec/src/encrypted_logs/incoming_body.nr @@ -16,7 +16,7 @@ impl EncryptedLogIncomingBody { } pub fn from_event(event: T, randomness: Field) -> Self where T: EventInterface { - let mut plaintext = event.to_be_bytes(randomness); + let mut plaintext = event.private_to_be_bytes(randomness); EncryptedLogIncomingBody { plaintext } } @@ -38,7 +38,7 @@ mod test { use dep::protocol_types::{ address::AztecAddress, traits::Empty, constants::GENERATOR_INDEX__NOTE_NULLIFIER, grumpkin_private_key::GrumpkinPrivateKey, grumpkin_point::GrumpkinPoint, traits::Serialize, - abis::function_selector::FunctionSelector + abis::event_selector::EventSelector }; use crate::{ @@ -155,15 +155,15 @@ mod test { global TEST_EVENT_BYTES_LEN = 32 * 3 + 64; impl EventInterface for TestEvent { - fn _selector(self) -> FunctionSelector { - FunctionSelector::from_signature("TestEvent(Field,Field,Field)") + fn get_event_type_id(self) -> EventSelector { + EventSelector::from_signature("TestEvent(Field,Field,Field)") } - fn to_be_bytes(self, randomness: Field) -> [u8; TEST_EVENT_BYTES_LEN] { + fn private_to_be_bytes(self, randomness: Field) -> [u8; TEST_EVENT_BYTES_LEN] { let mut buffer: [u8; TEST_EVENT_BYTES_LEN] = [0; TEST_EVENT_BYTES_LEN]; let randomness_bytes = randomness.to_be_bytes(32); - let event_type_id_bytes = self._selector().to_field().to_be_bytes(32); + let event_type_id_bytes = self.get_event_type_id().to_field().to_be_bytes(32); for i in 0..32 { buffer[i] = randomness_bytes[i]; diff --git a/noir-projects/aztec-nr/aztec/src/event/event_interface.nr b/noir-projects/aztec-nr/aztec/src/event/event_interface.nr index fe4b63fedd7f..edfe779f400f 100644 --- a/noir-projects/aztec-nr/aztec/src/event/event_interface.nr +++ b/noir-projects/aztec-nr/aztec/src/event/event_interface.nr @@ -1,9 +1,10 @@ use crate::context::PrivateContext; use crate::note::note_header::NoteHeader; -use dep::protocol_types::{grumpkin_point::GrumpkinPoint, abis::function_selector::FunctionSelector}; +use dep::protocol_types::{grumpkin_point::GrumpkinPoint, abis::event_selector::EventSelector}; -trait EventInterface { - // Should be autogenerated by the #[aztec(event)] macro unless it is overridden by a custom implementation - fn _selector(self) -> FunctionSelector; - fn to_be_bytes(self, randomness: Field) -> [u8; N]; +trait EventInterface { + fn private_to_be_bytes(self, randomness: Field) -> [u8; M]; + // More than one generic breaks this + // fn to_be_bytes(self) -> [u8; M]; + fn get_event_type_id(self) -> EventSelector; } diff --git a/noir-projects/noir-contracts/contracts/crowdfunding_contract/src/main.nr b/noir-projects/noir-contracts/contracts/crowdfunding_contract/src/main.nr index d5f932c4a757..7ced056ff21d 100644 --- a/noir-projects/noir-contracts/contracts/crowdfunding_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/crowdfunding_contract/src/main.nr @@ -17,14 +17,8 @@ contract Crowdfunding { #[aztec(event)] struct WithdrawalProcessed { - who: AztecAddress, - amount: u64, - } - - impl Serialize<2> for WithdrawalProcessed { - fn serialize(self: Self) -> [Field; 2] { - [self.who.to_field(), self.amount as Field] - } + who: Field, + amount: Field, } // docs:start:storage @@ -103,7 +97,7 @@ contract Crowdfunding { Token::at(storage.donation_token.read_private()).transfer(operator_address, amount as Field).call(&mut context); // 3) Emit an unencrypted event so that anyone can audit how much the operator has withdrawn - let event = WithdrawalProcessed { amount, who: operator_address }; + let event = WithdrawalProcessed { amount: amount as Field, who: operator_address.to_field() }; context.emit_unencrypted_log(event.serialize()); } // docs:end:operator-withdrawals diff --git a/noir-projects/noir-contracts/contracts/test_log_contract/src/main.nr b/noir-projects/noir-contracts/contracts/test_log_contract/src/main.nr index 9412f8e19eb5..4d6f631c142c 100644 --- a/noir-projects/noir-contracts/contracts/test_log_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/test_log_contract/src/main.nr @@ -14,56 +14,12 @@ contract TestLog { value1: Field, } - // This should be autogenerated by the macros - global EXAMPLE_EVENT_0_BYTES_LEN = 32 * 2 + 32 + 32; - - impl EventInterface for ExampleEvent0 { - fn _selector(self) -> FunctionSelector { - FunctionSelector::from_signature("TestEvent(Field,Field,Field)") - } - - fn to_be_bytes(self, randomness: Field) -> [u8; EXAMPLE_EVENT_0_BYTES_LEN] { - let mut buffer: [u8; EXAMPLE_EVENT_0_BYTES_LEN] = [0; EXAMPLE_EVENT_0_BYTES_LEN]; - - let randomness_bytes = randomness.to_be_bytes(32); - let event_type_id_bytes = self._selector().to_field().to_be_bytes(32); - - for i in 0..32 { - buffer[i] = randomness_bytes[i]; - buffer[32 + i] = event_type_id_bytes[i]; - } - - let serialized_event = self.serialize(); - - for i in 0..serialized_event.len() { - let bytes = serialized_event[i].to_be_bytes(32); - for j in 0..32 { - buffer[64 + i * 32 + j] = bytes[j]; - } - } - - buffer - } - } - #[aztec(event)] struct ExampleEvent1 { value2: Field, value3: Field, } - impl Serialize<2> for ExampleEvent0 { - fn serialize(self) -> [Field; 2] { - [self.value0, self.value1] - } - } - - impl Serialize<2> for ExampleEvent1 { - fn serialize(self) -> [Field; 2] { - [self.value2, self.value3] - } - } - #[aztec(storage)] struct Storage { example_set: PrivateSet, diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis.nr index 22746ed644de..2434f8ffdeb4 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis.nr @@ -2,6 +2,7 @@ mod append_only_tree_snapshot; mod contract_class_function_leaf_preimage; +mod event_selector; mod function_selector; mod function_data; diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/event_selector.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/event_selector.nr new file mode 100644 index 000000000000..cf68fd1d587a --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/event_selector.nr @@ -0,0 +1,70 @@ +use crate::utils::field::field_from_bytes; +use dep::std::cmp::Eq; +use crate::traits::{Serialize, Deserialize, FromField, ToField, Empty}; + +global SELECTOR_SIZE = 4; + +struct EventSelector { + // 1st 4-bytes of abi-encoding of an event. + inner: u32, +} + +impl Eq for EventSelector { + fn eq(self, other: EventSelector) -> bool { + other.inner == self.inner + } +} + +impl Serialize<1> for EventSelector { + fn serialize(self: Self) -> [Field; 1] { + [self.inner as Field] + } +} + +impl Deserialize<1> for EventSelector { + fn deserialize(fields: [Field; 1]) -> Self { + Self { + inner: fields[0] as u32 + } + } +} + +impl FromField for EventSelector { + fn from_field(field: Field) -> Self { + Self { inner: field as u32 } + } +} + +impl ToField for EventSelector { + fn to_field(self) -> Field { + self.inner as Field + } +} + +impl Empty for EventSelector { + fn empty() -> Self { + Self { inner: 0 as u32 } + } +} + +impl EventSelector { + pub fn from_u32(value: u32) -> Self { + Self { inner: value } + } + + pub fn from_signature(signature: str) -> Self { + let bytes = signature.as_bytes(); + let hash = dep::std::hash::keccak256(bytes, bytes.len() as u32); + + let mut selector_be_bytes = [0; SELECTOR_SIZE]; + for i in 0..SELECTOR_SIZE { + selector_be_bytes[i] = hash[i]; + } + + EventSelector::from_field(field_from_bytes(selector_be_bytes, true)) + } + + pub fn zero() -> Self { + Self { inner: 0 } + } +} diff --git a/noir/noir-repo/aztec_macros/src/lib.rs b/noir/noir-repo/aztec_macros/src/lib.rs index d79c7b190ed0..dfc2c2edd7c8 100644 --- a/noir/noir-repo/aztec_macros/src/lib.rs +++ b/noir/noir-repo/aztec_macros/src/lib.rs @@ -7,7 +7,8 @@ use transforms::{ contract_interface::{ generate_contract_interface, stub_function, update_fn_signatures_in_contract_interface, }, - events::{generate_selector_impl, transform_events}, + event_interface::{generate_event_interface_impl, transform_event_abi}, + // events::transform_events, functions::{ check_for_public_args, export_fn_abi, transform_function, transform_unconstrained, }, @@ -72,6 +73,7 @@ fn transform( } } + generate_event_interface_impl(&mut ast).map_err(|err| (err.into(), file_id))?; generate_note_interface_impl(&mut ast).map_err(|err| (err.into(), file_id))?; Ok(ast) @@ -101,13 +103,6 @@ fn transform_module( generate_storage_layout(module, storage_struct_name.clone(), module_name)?; } - for structure in module.types.iter_mut() { - if structure.attributes.iter().any(|attr| is_custom_attribute(attr, "aztec(event)")) { - module.impls.push(generate_selector_impl(structure)); - has_transformed_module = true; - } - } - let has_initializer = module.functions.iter().any(|func| { func.def .attributes @@ -222,7 +217,9 @@ fn transform_hir( context: &mut HirContext, ) -> Result<(), (AztecMacroError, FileId)> { if has_aztec_dependency(crate_id, context) { - transform_events(crate_id, context)?; + // The old event transformer is commented out but still kept for reference + // transform_events(crate_id, context)?; + transform_event_abi(crate_id, context)?; inject_compute_note_hash_and_optionally_a_nullifier(crate_id, context)?; assign_storage_slots(crate_id, context)?; inject_note_exports(crate_id, context)?; diff --git a/noir/noir-repo/aztec_macros/src/transforms/event_interface.rs b/noir/noir-repo/aztec_macros/src/transforms/event_interface.rs new file mode 100644 index 000000000000..6b01e9546345 --- /dev/null +++ b/noir/noir-repo/aztec_macros/src/transforms/event_interface.rs @@ -0,0 +1,393 @@ +use noirc_frontend::ast::{ItemVisibility, NoirFunction, NoirTraitImpl, TraitImplItem, TypeImpl}; +use noirc_frontend::macros_api::{NodeInterner, StructId}; +use noirc_frontend::token::SecondaryAttribute; +use noirc_frontend::{ + graph::CrateId, + macros_api::{FileId, HirContext}, + parse_program, + parser::SortedModule, +}; + +use crate::utils::hir_utils::collect_crate_structs; +use crate::utils::{ast_utils::is_custom_attribute, errors::AztecMacroError}; + +// Automatic implementation of most of the methods in the EventInterface trait, guiding the user with meaningful error messages in case some +// methods must be implemented manually. +pub fn generate_event_interface_impl(module: &mut SortedModule) -> Result<(), AztecMacroError> { + // Find structs annotated with #[aztec(event)] + // Why doesn't this work ? Events are not tagged and do not appear, it seems only going through the submodule works + // let annotated_event_structs = module + // .types + // .iter_mut() + // .filter(|typ| typ.attributes.iter().any(|attr: &SecondaryAttribute| is_custom_attribute(attr, "aztec(event)"))); + // This did not work because I needed the submodule itself to add the trait impl back in to, but it would be nice if it was tagged on the module level + // let mut annotated_event_structs = module.submodules.iter_mut() + // .flat_map(|submodule| submodule.contents.types.iter_mut()) + // .filter(|typ| typ.attributes.iter().any(|attr| is_custom_attribute(attr, "aztec(event)"))); + + // To diagnose + // let test = module.types.iter_mut(); + // for event_struct in test { + // print!("\ngenerate_event_interface_impl COUNT: {}\n", event_struct.name.0.contents); + // } + + for submodule in module.submodules.iter_mut() { + let annotated_event_structs = submodule.contents.types.iter_mut().filter(|typ| { + typ.attributes.iter().any(|attr| is_custom_attribute(attr, "aztec(event)")) + }); + + for event_struct in annotated_event_structs { + // event_struct.attributes.push(SecondaryAttribute::Abi("events".to_string())); + // If one impl is pushed, this doesn't throw the "#[abi(tag)] attributes can only be used in contracts" error + // But if more than one impl is pushed, we get an increasing amount of "#[abi(tag)] attributes can only be used in contracts" errors + // We work around this by doing this addition in the HIR pass via transform_event_abi below. + + let event_type = event_struct.name.0.contents.to_string(); + let event_len = event_struct.fields.len() as u32; + // event_byte_len = event fields * 32 + randomness (32) + event_type_id (32) + let event_byte_len = event_len * 32 + 64; + + let mut event_fields = vec![]; + + for (field_ident, field_type) in event_struct.fields.iter() { + event_fields.push(( + field_ident.0.contents.to_string(), + field_type.typ.to_string().replace("plain::", ""), + )); + } + + let mut event_impl = generate_impl_stub_event(event_type.as_str())?; + event_impl + .methods + .push((generate_fn_selector(event_type.as_str(), event_len)?, event_struct.span)); + submodule.contents.impls.push(event_impl); + + let mut event_interface_trait_impl = + generate_trait_impl_stub_event_interface(event_type.as_str(), event_byte_len)?; + event_interface_trait_impl.items.push(TraitImplItem::Function( + generate_fn_get_event_type_id(event_type.as_str(), event_len)?, + )); + event_interface_trait_impl.items.push(TraitImplItem::Function( + generate_fn_private_to_be_bytes(event_type.as_str(), event_byte_len)?, + )); + // This does not work because of a weird noir bug, where two generics are not allowed + // event_interface_trait_impl.items.push(TraitImplItem::Function(generate_to_be_bytes(event_type.as_str(), event_byte_len, event_struct.span)?)); + submodule.contents.trait_impls.push(event_interface_trait_impl); + + let serialize_trait_impl = + generate_trait_impl_serialize(event_type.as_str(), event_len, &event_fields)?; + submodule.contents.trait_impls.push(serialize_trait_impl); + + let deserialize_trait_impl = + generate_trait_impl_deserialize(event_type.as_str(), event_len, &event_fields)?; + submodule.contents.trait_impls.push(deserialize_trait_impl); + } + } + + Ok(()) +} + +fn generate_impl_stub_event(event_type: &str) -> Result { + let impl_source = format!( + " +impl {event_type} {{ + }} + " + ) + .to_string(); + + let (parsed_ast, errors) = parse_program(&impl_source); + if !errors.is_empty() { + dbg!(errors); + return Err(AztecMacroError::CouldNotImplementNoteInterface { + secondary_message: Some(format!("Failed to parse Noir macro code (impl {event_type}). This is either a bug in the compiler or the Noir macro code")), + span: Option::None + }); + } + + let mut sorted_ast = parsed_ast.into_sorted(); + let event_impl = sorted_ast.impls.remove(0); + + Ok(event_impl) +} + +fn generate_trait_impl_stub_event_interface( + event_type: &str, + byte_length: u32, +) -> Result { + let trait_impl_source = format!( + " +impl dep::aztec::event::event_interface::EventInterface<{byte_length}> for {event_type} {{ + }} + " + ) + .to_string(); + + let (parsed_ast, errors) = parse_program(&trait_impl_source); + if !errors.is_empty() { + dbg!(errors); + return Err(AztecMacroError::CouldNotImplementNoteInterface { + secondary_message: Some(format!("Failed to parse Noir macro code (trait impl of {event_type} for EventInterface). This is either a bug in the compiler or the Noir macro code")), + span: Option::None + }); + } + + let mut sorted_ast = parsed_ast.into_sorted(); + let event_interface_impl = sorted_ast.trait_impls.remove(0); + + Ok(event_interface_impl) +} + +fn generate_trait_impl_serialize( + event_type: &str, + event_len: u32, + event_fields: &[(String, String)], +) -> Result { + let field_names = + event_fields.iter().map(|field| format!("self.{}", field.0)).collect::>(); + let field_input = field_names.join(","); + + let trait_impl_source = format!( + " + impl dep::aztec::protocol_types::traits::Serialize<{event_len}> for {event_type} {{ + fn serialize(self: {event_type}) -> [Field; {event_len}] {{ + [{field_input}] + }} + }} + " + ) + .to_string(); + + let (parsed_ast, errors) = parse_program(&trait_impl_source); + if !errors.is_empty() { + dbg!(errors); + return Err(AztecMacroError::CouldNotImplementNoteInterface { + secondary_message: Some(format!("Failed to parse Noir macro code (trait impl of Serialize for {event_type}). This is either a bug in the compiler or the Noir macro code")), + span: Option::None + }); + } + + let mut sorted_ast = parsed_ast.into_sorted(); + let serialize_impl = sorted_ast.trait_impls.remove(0); + + Ok(serialize_impl) +} + +fn generate_trait_impl_deserialize( + event_type: &str, + event_len: u32, + event_fields: &[(String, String)], +) -> Result { + let field_names: Vec = event_fields + .iter() + .enumerate() + .map(|(index, field)| format!("{}: fields[{}]", field.0, index)) + .collect::>(); + let field_input = field_names.join(","); + + let trait_impl_source = format!( + " + impl dep::aztec::protocol_types::traits::Deserialize<{event_len}> for {event_type} {{ + fn deserialize(fields: [Field; {event_len}]) -> {event_type} {{ + {event_type} {{ {field_input} }} + }} + }} + " + ) + .to_string(); + + let (parsed_ast, errors) = parse_program(&trait_impl_source); + if !errors.is_empty() { + dbg!(errors); + return Err(AztecMacroError::CouldNotImplementNoteInterface { + secondary_message: Some(format!("Failed to parse Noir macro code (trait impl of Deserialize for {event_type}). This is either a bug in the compiler or the Noir macro code")), + span: Option::None + }); + } + + let mut sorted_ast = parsed_ast.into_sorted(); + let deserialize_impl = sorted_ast.trait_impls.remove(0); + + Ok(deserialize_impl) +} + +fn generate_fn_get_event_type_id( + event_type: &str, + field_length: u32, +) -> Result { + let from_signature_input = + std::iter::repeat("Field").take(field_length as usize).collect::>().join(","); + let function_source = format!( + " + fn get_event_type_id(self: {event_type}) -> dep::aztec::protocol_types::abis::event_selector::EventSelector {{ + dep::aztec::protocol_types::abis::event_selector::EventSelector::from_signature(\"{event_type}({from_signature_input})\") + }} + ", + ) + .to_string(); + + let (function_ast, errors) = parse_program(&function_source); + if !errors.is_empty() { + dbg!(errors); + return Err(AztecMacroError::CouldNotImplementNoteInterface { + secondary_message: Some(format!("Failed to parse Noir macro code (fn get_event_type_id, implemented for EventInterface of {event_type}). This is either a bug in the compiler or the Noir macro code")), + span: Option::None + }); + } + + let mut function_ast = function_ast.into_sorted(); + let mut noir_fn = function_ast.functions.remove(0); + noir_fn.def.visibility = ItemVisibility::Public; + Ok(noir_fn) +} + +fn generate_fn_selector( + event_type: &str, + field_length: u32, +) -> Result { + let from_signature_input = + std::iter::repeat("Field").take(field_length as usize).collect::>().join(","); + let function_source = format!( + " + fn selector() -> dep::aztec::protocol_types::abis::event_selector::EventSelector {{ + dep::aztec::protocol_types::abis::event_selector::EventSelector::from_signature(\"{event_type}({from_signature_input})\") + }} + ", + ) + .to_string(); + + let (function_ast, errors) = parse_program(&function_source); + if !errors.is_empty() { + dbg!(errors); + return Err(AztecMacroError::CouldNotImplementNoteInterface { + secondary_message: Some(format!("Failed to parse Noir macro code (fn selector, implemented for {event_type}). This is either a bug in the compiler or the Noir macro code")), + span: Option::None + }); + } + + let mut function_ast = function_ast.into_sorted(); + let mut noir_fn = function_ast.functions.remove(0); + noir_fn.def.visibility = ItemVisibility::Public; + Ok(noir_fn) +} + +fn generate_fn_private_to_be_bytes( + event_type: &str, + byte_length: u32, +) -> Result { + let function_source = format!( + " + fn private_to_be_bytes(self: {event_type}, randomness: Field) -> [u8; {byte_length}] {{ + let mut buffer: [u8; {byte_length}] = [0; {byte_length}]; + + let randomness_bytes = randomness.to_be_bytes(32); + let event_type_id_bytes = self.get_event_type_id().to_field().to_be_bytes(32); + + for i in 0..32 {{ + buffer[i] = randomness_bytes[i]; + buffer[32 + i] = event_type_id_bytes[i]; + }} + + let serialized_event = self.serialize(); + + for i in 0..serialized_event.len() {{ + let bytes = serialized_event[i].to_be_bytes(32); + for j in 0..32 {{ + buffer[64 + i * 32 + j] = bytes[j]; + }} + }} + + buffer + }} + " + ) + .to_string(); + + let (function_ast, errors) = parse_program(&function_source); + if !errors.is_empty() { + dbg!(errors); + return Err(AztecMacroError::CouldNotImplementNoteInterface { + secondary_message: Some(format!("Failed to parse Noir macro code (fn private_to_be_bytes, implemented for EventInterface of {event_type}). This is either a bug in the compiler or the Noir macro code")), + span: Option::None + }); + } + + let mut function_ast = function_ast.into_sorted(); + let mut noir_fn = function_ast.functions.remove(0); + noir_fn.def.visibility = ItemVisibility::Public; + Ok(noir_fn) +} + +// This does not work because of a weird noir bug, where two generics are not allowed +// event_interface_trait_impl.items.push(TraitImplItem::Function(generate_to_be_bytes(event_type.as_str(), event_byte_len, event_struct.span)?)); +// fn generate_fn_to_be_bytes( +// event_type: &str, +// byte_length: u32, +// impl_span: Span, +// ) -> Result { +// let byte_length_without_randomness = byte_length - 32; +// let function_source = format!( +// " +// fn to_be_bytes(self: {event_type}) -> [u8; {byte_length_without_randomness}] {{ +// let mut buffer: [u8; {byte_length_without_randomness}] = [0; {byte_length_without_randomness}]; + +// let event_type_id_bytes = self.get_event_type_id().to_field().to_be_bytes(32); + +// for i in 0..32 {{ +// buffer[i] = event_type_id_bytes[i]; +// }} + +// let serialized_event = self.serialize(); + +// for i in 0..serialized_event.len() {{ +// let bytes = serialized_event[i].to_be_bytes(32); +// for j in 0..32 {{ +// buffer[32 + i * 32 + j] = bytes[j]; +// }} +// }} + +// buffer +// }} +// ") +// .to_string(); + +// let (function_ast, errors) = parse_program(&function_source); +// if !errors.is_empty() { +// dbg!(errors); +// return Err(AztecMacroError::CouldNotImplementNoteInterface { +// secondary_message: Some(format!("Failed to parse Noir macro code (fn to_be_bytes, implemented for EventInterface of {event_type}). This is either a bug in the compiler or the Noir macro code")), +// span: Option::None +// }); +// } + +// let mut function_ast = function_ast.into_sorted(); +// let mut noir_fn = function_ast.functions.remove(0); +// noir_fn.def.visibility = ItemVisibility::Public; +// noir_fn.def.span = impl_span; +// Ok(noir_fn) +// } + +// We do this pass in the HIR to work around the "#[abi(tag)] attributes can only be used in contracts" error +pub fn transform_event_abi( + crate_id: &CrateId, + context: &mut HirContext, +) -> Result<(), (AztecMacroError, FileId)> { + for struct_id in collect_crate_structs(crate_id, context) { + let attributes = context.def_interner.struct_attributes(&struct_id); + if attributes.iter().any(|attr| is_custom_attribute(attr, "aztec(event)")) { + transform_event(struct_id, &mut context.def_interner)?; + } + } + Ok(()) +} + +fn transform_event( + struct_id: StructId, + interner: &mut NodeInterner, +) -> Result<(), (AztecMacroError, FileId)> { + interner.update_struct_attributes(struct_id, |struct_attributes| { + struct_attributes.push(SecondaryAttribute::Abi("events".to_string())); + }); + + Ok(()) +} diff --git a/noir/noir-repo/aztec_macros/src/transforms/events.rs b/noir/noir-repo/aztec_macros/src/transforms/events.rs index 69cb6ddafc3e..74855177595e 100644 --- a/noir/noir-repo/aztec_macros/src/transforms/events.rs +++ b/noir/noir-repo/aztec_macros/src/transforms/events.rs @@ -1,186 +1,186 @@ -use iter_extended::vecmap; -use noirc_errors::Span; -use noirc_frontend::ast::{ - ExpressionKind, FunctionDefinition, FunctionReturnType, ItemVisibility, Literal, NoirFunction, - Visibility, -}; -use noirc_frontend::{ - graph::CrateId, - macros_api::{ - BlockExpression, FileId, HirContext, HirExpression, HirLiteral, HirStatement, NodeInterner, - NoirStruct, PathKind, StatementKind, StructId, StructType, Type, TypeImpl, - UnresolvedTypeData, - }, - token::SecondaryAttribute, -}; - -use crate::{ - chained_dep, - utils::{ - ast_utils::{ - call, expression, ident, ident_path, is_custom_attribute, make_statement, make_type, - path, variable_path, - }, - constants::SIGNATURE_PLACEHOLDER, - errors::AztecMacroError, - hir_utils::{collect_crate_structs, signature_of_type}, - }, -}; - -/// Generates the impl for an event selector -/// -/// Inserts the following code: -/// ```noir -/// impl SomeStruct { -/// fn selector() -> FunctionSelector { -/// aztec::protocol_types::abis::function_selector::FunctionSelector::from_signature("SIGNATURE_PLACEHOLDER") -/// } -/// } -/// ``` -/// -/// This allows developers to emit events without having to write the signature of the event every time they emit it. -/// The signature cannot be known at this point since types are not resolved yet, so we use a signature placeholder. -/// It'll get resolved after by transforming the HIR. -pub fn generate_selector_impl(structure: &mut NoirStruct) -> TypeImpl { - structure.attributes.push(SecondaryAttribute::Abi("events".to_string())); - let struct_type = - make_type(UnresolvedTypeData::Named(path(structure.name.clone()), vec![], true)); - - let selector_path = - chained_dep!("aztec", "protocol_types", "abis", "function_selector", "FunctionSelector"); - let mut from_signature_path = selector_path.clone(); - from_signature_path.segments.push(ident("from_signature")); - - let selector_fun_body = BlockExpression { - statements: vec![make_statement(StatementKind::Expression(call( - variable_path(from_signature_path), - vec![expression(ExpressionKind::Literal(Literal::Str( - SIGNATURE_PLACEHOLDER.to_string(), - )))], - )))], - }; - - // Define `FunctionSelector` return type - let return_type = - FunctionReturnType::Ty(make_type(UnresolvedTypeData::Named(selector_path, vec![], true))); - - let mut selector_fn_def = FunctionDefinition::normal( - &ident("selector"), - &vec![], - &[], - &selector_fun_body, - &[], - &return_type, - ); - - selector_fn_def.visibility = ItemVisibility::Public; - - // Seems to be necessary on contract modules - selector_fn_def.return_visibility = Visibility::Public; - - TypeImpl { - object_type: struct_type, - type_span: structure.span, - generics: vec![], - methods: vec![(NoirFunction::normal(selector_fn_def), Span::default())], - } -} - -/// Computes the signature for a resolved event type. -/// It has the form 'EventName(Field,(Field),[u8;2])' -fn event_signature(event: &StructType) -> String { - let fields = vecmap(event.get_fields(&[]), |(_, typ)| signature_of_type(&typ)); - format!("{}({})", event.name.0.contents, fields.join(",")) -} - -/// Substitutes the signature literal that was introduced in the selector method previously with the actual signature. -fn transform_event( - struct_id: StructId, - interner: &mut NodeInterner, -) -> Result<(), (AztecMacroError, FileId)> { - let struct_type = interner.get_struct(struct_id); - let selector_id = interner - .lookup_method(&Type::Struct(struct_type.clone(), vec![]), struct_id, "selector", false) - .ok_or_else(|| { - let error = AztecMacroError::EventError { - span: struct_type.borrow().location.span, - message: "Selector method not found".to_owned(), - }; - (error, struct_type.borrow().location.file) - })?; - let selector_function = interner.function(&selector_id); - - let compute_selector_statement = interner.statement( - selector_function.block(interner).statements().first().ok_or_else(|| { - let error = AztecMacroError::EventError { - span: struct_type.borrow().location.span, - message: "Compute selector statement not found".to_owned(), - }; - (error, struct_type.borrow().location.file) - })?, - ); - - let compute_selector_expression = match compute_selector_statement { - HirStatement::Expression(expression_id) => match interner.expression(&expression_id) { - HirExpression::Call(hir_call_expression) => Some(hir_call_expression), - _ => None, - }, - _ => None, - } - .ok_or_else(|| { - let error = AztecMacroError::EventError { - span: struct_type.borrow().location.span, - message: "Compute selector statement is not a call expression".to_owned(), - }; - (error, struct_type.borrow().location.file) - })?; - - let first_arg_id = compute_selector_expression.arguments.first().ok_or_else(|| { - let error = AztecMacroError::EventError { - span: struct_type.borrow().location.span, - message: "Compute selector statement is not a call expression".to_owned(), - }; - (error, struct_type.borrow().location.file) - })?; - - match interner.expression(first_arg_id) { - HirExpression::Literal(HirLiteral::Str(signature)) - if signature == SIGNATURE_PLACEHOLDER => - { - let selector_literal_id = *first_arg_id; - - let structure = interner.get_struct(struct_id); - let signature = event_signature(&structure.borrow()); - interner.update_expression(selector_literal_id, |expr| { - *expr = HirExpression::Literal(HirLiteral::Str(signature.clone())); - }); - - // Also update the type! It might have a different length now than the placeholder. - interner.push_expr_type( - selector_literal_id, - Type::String(Box::new(Type::Constant(signature.len() as u32))), - ); - Ok(()) - } - _ => Err(( - AztecMacroError::EventError { - span: struct_type.borrow().location.span, - message: "Signature placeholder literal does not match".to_owned(), - }, - struct_type.borrow().location.file, - )), - } -} - -pub fn transform_events( - crate_id: &CrateId, - context: &mut HirContext, -) -> Result<(), (AztecMacroError, FileId)> { - for struct_id in collect_crate_structs(crate_id, context) { - let attributes = context.def_interner.struct_attributes(&struct_id); - if attributes.iter().any(|attr| is_custom_attribute(attr, "aztec(event)")) { - transform_event(struct_id, &mut context.def_interner)?; - } - } - Ok(()) -} +// use iter_extended::vecmap; +// use noirc_errors::Span; +// use noirc_frontend::ast::{ +// ExpressionKind, FunctionDefinition, FunctionReturnType, ItemVisibility, Literal, NoirFunction, +// Visibility, +// }; +// use noirc_frontend::{ +// graph::CrateId, +// macros_api::{ +// BlockExpression, FileId, HirContext, HirExpression, HirLiteral, HirStatement, NodeInterner, +// NoirStruct, PathKind, StatementKind, StructId, StructType, Type, TypeImpl, +// UnresolvedTypeData, +// }, +// token::SecondaryAttribute, +// }; + +// use crate::{ +// chained_dep, +// utils::{ +// ast_utils::{ +// call, expression, ident, ident_path, is_custom_attribute, make_statement, make_type, +// path, variable_path, +// }, +// constants::SIGNATURE_PLACEHOLDER, +// errors::AztecMacroError, +// hir_utils::{collect_crate_structs, signature_of_type}, +// }, +// }; + +// Generates the impl for an event selector +// +// Inserts the following code: +// ```noir +// impl SomeStruct { +// fn selector() -> FunctionSelector { +// aztec::protocol_types::abis::function_selector::FunctionSelector::from_signature("SIGNATURE_PLACEHOLDER") +// } +// } +// ``` +// +// This allows developers to emit events without having to write the signature of the event every time they emit it. +// The signature cannot be known at this point since types are not resolved yet, so we use a signature placeholder. +// It'll get resolved after by transforming the HIR. +// pub fn generate_selector_impl(structure: &mut NoirStruct) -> TypeImpl { +// structure.attributes.push(SecondaryAttribute::Abi("events".to_string())); +// let struct_type = +// make_type(UnresolvedTypeData::Named(path(structure.name.clone()), vec![], true)); + +// let selector_path = +// chained_dep!("aztec", "protocol_types", "abis", "function_selector", "FunctionSelector"); +// let mut from_signature_path = selector_path.clone(); +// from_signature_path.segments.push(ident("from_signature")); + +// let selector_fun_body = BlockExpression { +// statements: vec![make_statement(StatementKind::Expression(call( +// variable_path(from_signature_path), +// vec![expression(ExpressionKind::Literal(Literal::Str( +// SIGNATURE_PLACEHOLDER.to_string(), +// )))], +// )))], +// }; + +// // Define `FunctionSelector` return type +// let return_type = +// FunctionReturnType::Ty(make_type(UnresolvedTypeData::Named(selector_path, vec![], true))); + +// let mut selector_fn_def = FunctionDefinition::normal( +// &ident("selector"), +// &vec![], +// &[], +// &selector_fun_body, +// &[], +// &return_type, +// ); + +// selector_fn_def.visibility = ItemVisibility::Public; + +// // Seems to be necessary on contract modules +// selector_fn_def.return_visibility = Visibility::Public; + +// TypeImpl { +// object_type: struct_type, +// type_span: structure.span, +// generics: vec![], +// methods: vec![(NoirFunction::normal(selector_fn_def), Span::default())], +// } +// } + +// Computes the signature for a resolved event type. +// It has the form 'EventName(Field,(Field),[u8;2])' +// fn event_signature(event: &StructType) -> String { +// let fields = vecmap(event.get_fields(&[]), |(_, typ)| signature_of_type(&typ)); +// format!("{}({})", event.name.0.contents, fields.join(",")) +// } + +// Substitutes the signature literal that was introduced in the selector method previously with the actual signature. +// fn transform_event( +// struct_id: StructId, +// interner: &mut NodeInterner, +// ) -> Result<(), (AztecMacroError, FileId)> { +// let struct_type = interner.get_struct(struct_id); +// let selector_id = interner +// .lookup_method(&Type::Struct(struct_type.clone(), vec![]), struct_id, "selector", false) +// .ok_or_else(|| { +// let error = AztecMacroError::EventError { +// span: struct_type.borrow().location.span, +// message: "Selector method not found".to_owned(), +// }; +// (error, struct_type.borrow().location.file) +// })?; +// let selector_function = interner.function(&selector_id); + +// let compute_selector_statement = interner.statement( +// selector_function.block(interner).statements().first().ok_or_else(|| { +// let error = AztecMacroError::EventError { +// span: struct_type.borrow().location.span, +// message: "Compute selector statement not found".to_owned(), +// }; +// (error, struct_type.borrow().location.file) +// })?, +// ); + +// let compute_selector_expression = match compute_selector_statement { +// HirStatement::Expression(expression_id) => match interner.expression(&expression_id) { +// HirExpression::Call(hir_call_expression) => Some(hir_call_expression), +// _ => None, +// }, +// _ => None, +// } +// .ok_or_else(|| { +// let error = AztecMacroError::EventError { +// span: struct_type.borrow().location.span, +// message: "Compute selector statement is not a call expression".to_owned(), +// }; +// (error, struct_type.borrow().location.file) +// })?; + +// let first_arg_id = compute_selector_expression.arguments.first().ok_or_else(|| { +// let error = AztecMacroError::EventError { +// span: struct_type.borrow().location.span, +// message: "Compute selector statement is not a call expression".to_owned(), +// }; +// (error, struct_type.borrow().location.file) +// })?; + +// match interner.expression(first_arg_id) { +// HirExpression::Literal(HirLiteral::Str(signature)) +// if signature == SIGNATURE_PLACEHOLDER => +// { +// let selector_literal_id = *first_arg_id; + +// let structure = interner.get_struct(struct_id); +// let signature = event_signature(&structure.borrow()); +// interner.update_expression(selector_literal_id, |expr| { +// *expr = HirExpression::Literal(HirLiteral::Str(signature.clone())); +// }); + +// // Also update the type! It might have a different length now than the placeholder. +// interner.push_expr_type( +// selector_literal_id, +// Type::String(Box::new(Type::Constant(signature.len() as u32))), +// ); +// Ok(()) +// } +// _ => Err(( +// AztecMacroError::EventError { +// span: struct_type.borrow().location.span, +// message: "Signature placeholder literal does not match".to_owned(), +// }, +// struct_type.borrow().location.file, +// )), +// } +// } + +// pub fn transform_events( +// crate_id: &CrateId, +// context: &mut HirContext, +// ) -> Result<(), (AztecMacroError, FileId)> { +// for struct_id in collect_crate_structs(crate_id, context) { +// let attributes = context.def_interner.struct_attributes(&struct_id); +// if attributes.iter().any(|attr| is_custom_attribute(attr, "aztec(event)")) { +// transform_event(struct_id, &mut context.def_interner)?; +// } +// } +// Ok(()) +// } diff --git a/noir/noir-repo/aztec_macros/src/transforms/mod.rs b/noir/noir-repo/aztec_macros/src/transforms/mod.rs index bd419bced6fc..bd4d4a1110de 100644 --- a/noir/noir-repo/aztec_macros/src/transforms/mod.rs +++ b/noir/noir-repo/aztec_macros/src/transforms/mod.rs @@ -1,5 +1,6 @@ pub mod compute_note_hash_and_optionally_a_nullifier; pub mod contract_interface; +pub mod event_interface; pub mod events; pub mod functions; pub mod note_interface; diff --git a/noir/noir-repo/aztec_macros/src/transforms/note_interface.rs b/noir/noir-repo/aztec_macros/src/transforms/note_interface.rs index fdce8b81db29..c91ba4e069ae 100644 --- a/noir/noir-repo/aztec_macros/src/transforms/note_interface.rs +++ b/noir/noir-repo/aztec_macros/src/transforms/note_interface.rs @@ -190,19 +190,19 @@ pub fn generate_note_interface_impl(module: &mut SortedModule) -> Result<(), Azt } if !check_trait_method_implemented(trait_impl, "compute_note_content_hash") { - let get_header_fn = + let compute_note_content_hash_fn = generate_compute_note_content_hash(¬e_type, note_interface_impl_span)?; - trait_impl.items.push(TraitImplItem::Function(get_header_fn)); + trait_impl.items.push(TraitImplItem::Function(compute_note_content_hash_fn)); } if !check_trait_method_implemented(trait_impl, "to_be_bytes") { - let get_header_fn = generate_note_to_be_bytes( + let to_be_bytes_fn = generate_note_to_be_bytes( ¬e_type, note_bytes_len.as_str(), note_serialized_len.as_str(), note_interface_impl_span, )?; - trait_impl.items.push(TraitImplItem::Function(get_header_fn)); + trait_impl.items.push(TraitImplItem::Function(to_be_bytes_fn)); } } diff --git a/noir/noir-repo/aztec_macros/src/utils/constants.rs b/noir/noir-repo/aztec_macros/src/utils/constants.rs index 848cca0477d8..03842cbdb534 100644 --- a/noir/noir-repo/aztec_macros/src/utils/constants.rs +++ b/noir/noir-repo/aztec_macros/src/utils/constants.rs @@ -1,4 +1,4 @@ pub const FUNCTION_TREE_HEIGHT: u32 = 5; pub const MAX_CONTRACT_PRIVATE_FUNCTIONS: usize = 2_usize.pow(FUNCTION_TREE_HEIGHT); -pub const SIGNATURE_PLACEHOLDER: &str = "SIGNATURE_PLACEHOLDER"; +// pub const SIGNATURE_PLACEHOLDER: &str = "SIGNATURE_PLACEHOLDER"; pub const SELECTOR_PLACEHOLDER: &str = "SELECTOR_PLACEHOLDER"; diff --git a/noir/noir-repo/compiler/noirc_frontend/src/node_interner.rs b/noir/noir-repo/compiler/noirc_frontend/src/node_interner.rs index cef49332b001..cd82685c31e5 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/node_interner.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/node_interner.rs @@ -623,6 +623,15 @@ impl NodeInterner { f(&mut value); } + pub fn update_struct_attributes( + &mut self, + type_id: StructId, + f: impl FnOnce(&mut StructAttributes), + ) { + let value = self.struct_attributes.get_mut(&type_id).unwrap(); + f(value); + } + pub fn update_trait(&mut self, trait_id: TraitId, f: impl FnOnce(&mut Trait)) { let value = self.traits.get_mut(&trait_id).unwrap(); f(value);