From 48414603c90760c2da6c624e38a14738540dde54 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Wed, 26 Apr 2023 17:34:24 +0100 Subject: [PATCH 001/140] WIP derive Event --- crates/env/src/topics.rs | 2 + crates/ink/macro/src/event.rs | 116 ++++++++++++++++++++++++++++++++++ crates/ink/macro/src/lib.rs | 7 ++ 3 files changed, 125 insertions(+) create mode 100644 crates/ink/macro/src/event.rs diff --git a/crates/env/src/topics.rs b/crates/env/src/topics.rs index a55eac11b6..a2536cdb32 100644 --- a/crates/env/src/topics.rs +++ b/crates/env/src/topics.rs @@ -195,6 +195,8 @@ pub trait Topics { /// builder. type RemainingTopics: EventTopicsAmount; + // const EVENT_SIGNATURE_TOPIC: [u8; 32]; + /// Guides event topic serialization using the given topics builder. fn topics( &self, diff --git a/crates/ink/macro/src/event.rs b/crates/ink/macro/src/event.rs new file mode 100644 index 0000000000..344b6be86e --- /dev/null +++ b/crates/ink/macro/src/event.rs @@ -0,0 +1,116 @@ +// Copyright 2018-2022 Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use proc_macro2::TokenStream as TokenStream2; +use quote::{ + quote, + quote_spanned, +}; +use syn::spanned::Spanned; + +/// Derives the `ink::Event` trait for the given `struct`. +pub fn event_derive(mut s: synstructure::Structure) -> TokenStream2 { + s.bind_with(|_| synstructure::BindStyle::Move) + .add_bounds(synstructure::AddBounds::Fields) + .underscore_const(true); + match &s.ast().data { + syn::Data::Struct(_) => event_derive_struct(s), + _ => { + panic!("can only derive `Event` for Rust `struct` items") + } + } +} + +/// `Event` derive implementation for `struct` types. +fn event_derive_struct(mut s: synstructure::Structure) -> TokenStream2 { + assert_eq!(s.variants().len(), 1, "can only operate on structs"); + let mut variant= &s.variants_mut()[0]; + + let anonymous = false; // todo read from struct attribute e.g. #[event(anonymous)] + + // let decode_body = variant.construct(|field, _index| { + // let ty = &field.ty; + // let span = ty.span(); + // quote_spanned!(span => + // <#ty as ::ink::storage::traits::Storable>::decode(__input)? + // ) + // }); + // let encode_body = variant.each(|binding| { + // let span = binding.ast().ty.span(); + // quote_spanned!(span => + // ::ink::storage::traits::Storable::encode(#binding, __dest); + // ) + // }); + let topics = variant.filter(|bi| { + bi.ast().attrs.iter().any(|attr| { + attr.path.is_ident("topic") + }) + }); + + let len_topics = topics.bindings().len(); + // Anonymous events require 1 fewer topics since they do not include their signature. + let anonymous_topics_offset = usize::from(!anonymous); + let remaining_topics_ty = match len_topics + anonymous_topics_offset { + 0 => quote_spanned!(span=> ::ink::env::topics::state::NoRemainingTopics), + n => quote_spanned!(span=> [::ink::env::topics::state::HasRemainingTopics; #n]), + }; + + let event_signature_topic = signature_topic(variant.ast().fields, &variant.ast().ident); + + s.gen_impl(quote! { + gen impl ::ink::env::Topics for @Self { + type RemainingTopics = #remaining_topics_ty; + + fn topics( + &self, + builder: ::ink::env::topics::TopicsBuilder<::ink::env::topics::state::Uninit, E, B>, + ) -> >::Output + where + E: ::ink::env::Environment, + B: ::ink::env::topics::TopicsBuilderBackend, + { + match self { #topics_body } + builder + .build::() + #event_signature_topic + #( + #topic_impls + )* + .finish() + } + } + }) +} + +/// The signature topic of an event variant. +/// +/// Calculated with `blake2b("Event(field1_type,field2_type)")`. +pub fn signature_topic(fields: &syn::Fields, event_ident: &syn::Ident) -> [syn::LitInt; 32] { + let fields = fields + .iter() + .map(|field| { + field + .ty + .to_token_stream() + .to_string() + .replace(" ", "") + }) + .collect::>() + .join(","); + let topic_str = format!("{}({fields})", event_ident); + let input = topic_str.as_bytes(); + let mut output = [0; 32]; + ink_ir::blake2b_256(&input, &mut output); + output.map(::hex_padded_suffixed) +} diff --git a/crates/ink/macro/src/lib.rs b/crates/ink/macro/src/lib.rs index 4cd03837d6..70d42b637c 100644 --- a/crates/ink/macro/src/lib.rs +++ b/crates/ink/macro/src/lib.rs @@ -22,6 +22,7 @@ extern crate proc_macro; mod blake2b; mod chain_extension; mod contract; +mod event; mod ink_test; mod selector; mod storage; @@ -1279,6 +1280,12 @@ pub fn chain_extension(attr: TokenStream, item: TokenStream) -> TokenStream { chain_extension::generate(attr.into(), item.into()).into() } +synstructure::decl_derive!( + [Event] => + /// todo + event::event_derive +); + synstructure::decl_derive!( [Storable] => /// Derives `ink::storage`'s `Storable` trait for the given `struct`, `enum` or `union`. From bce872d5324a94a72b48805147ff82d5bab551ad Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Wed, 26 Apr 2023 17:36:05 +0100 Subject: [PATCH 002/140] Move tests to top level --- crates/ink/macro/src/lib.rs | 3 +++ crates/ink/macro/src/storage/mod.rs | 3 --- crates/ink/macro/src/{storage => }/tests/mod.rs | 0 crates/ink/macro/src/{storage => }/tests/storable.rs | 0 crates/ink/macro/src/{storage => }/tests/storable_hint.rs | 0 crates/ink/macro/src/{storage => }/tests/storage_key.rs | 0 crates/ink/macro/src/{storage => }/tests/storage_layout.rs | 0 7 files changed, 3 insertions(+), 3 deletions(-) rename crates/ink/macro/src/{storage => }/tests/mod.rs (100%) rename crates/ink/macro/src/{storage => }/tests/storable.rs (100%) rename crates/ink/macro/src/{storage => }/tests/storable_hint.rs (100%) rename crates/ink/macro/src/{storage => }/tests/storage_key.rs (100%) rename crates/ink/macro/src/{storage => }/tests/storage_layout.rs (100%) diff --git a/crates/ink/macro/src/lib.rs b/crates/ink/macro/src/lib.rs index 70d42b637c..a5c214b93d 100644 --- a/crates/ink/macro/src/lib.rs +++ b/crates/ink/macro/src/lib.rs @@ -29,6 +29,9 @@ mod storage; mod storage_item; mod trait_def; +#[cfg(test)] +mod tests; + use proc_macro::TokenStream; /// Computes and expands into the BLAKE2b 256-bit hash of the string input. diff --git a/crates/ink/macro/src/storage/mod.rs b/crates/ink/macro/src/storage/mod.rs index c46e882640..99cbbf66e2 100644 --- a/crates/ink/macro/src/storage/mod.rs +++ b/crates/ink/macro/src/storage/mod.rs @@ -28,6 +28,3 @@ pub use self::{ storage_key::storage_key_derive, storage_layout::storage_layout_derive, }; - -#[cfg(test)] -mod tests; diff --git a/crates/ink/macro/src/storage/tests/mod.rs b/crates/ink/macro/src/tests/mod.rs similarity index 100% rename from crates/ink/macro/src/storage/tests/mod.rs rename to crates/ink/macro/src/tests/mod.rs diff --git a/crates/ink/macro/src/storage/tests/storable.rs b/crates/ink/macro/src/tests/storable.rs similarity index 100% rename from crates/ink/macro/src/storage/tests/storable.rs rename to crates/ink/macro/src/tests/storable.rs diff --git a/crates/ink/macro/src/storage/tests/storable_hint.rs b/crates/ink/macro/src/tests/storable_hint.rs similarity index 100% rename from crates/ink/macro/src/storage/tests/storable_hint.rs rename to crates/ink/macro/src/tests/storable_hint.rs diff --git a/crates/ink/macro/src/storage/tests/storage_key.rs b/crates/ink/macro/src/tests/storage_key.rs similarity index 100% rename from crates/ink/macro/src/storage/tests/storage_key.rs rename to crates/ink/macro/src/tests/storage_key.rs diff --git a/crates/ink/macro/src/storage/tests/storage_layout.rs b/crates/ink/macro/src/tests/storage_layout.rs similarity index 100% rename from crates/ink/macro/src/storage/tests/storage_layout.rs rename to crates/ink/macro/src/tests/storage_layout.rs From cbdf8fff60b0394fc40f3539f6e6a103b2a58d9a Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Wed, 26 Apr 2023 17:40:53 +0100 Subject: [PATCH 003/140] Add failing Event derive test --- crates/ink/macro/src/tests/event.rs | 52 +++++++++++++++++++++++++++++ crates/ink/macro/src/tests/mod.rs | 9 ++--- 2 files changed, 57 insertions(+), 4 deletions(-) create mode 100644 crates/ink/macro/src/tests/event.rs diff --git a/crates/ink/macro/src/tests/event.rs b/crates/ink/macro/src/tests/event.rs new file mode 100644 index 0000000000..f07a76b9b1 --- /dev/null +++ b/crates/ink/macro/src/tests/event.rs @@ -0,0 +1,52 @@ +// Copyright 2018-2022 Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// These tests are partly testing if code is expanded correctly. +// Hence the syntax contains a number of verbose statements which +// are not properly cleaned up. +#![allow(clippy::absurd_extreme_comparisons)] +#![allow(clippy::identity_op)] +#![allow(clippy::eq_op)] +#![allow(clippy::match_single_binding)] + +use crate::event::event_derive; + +#[test] +fn unit_struct_works() { + todo!() + // crate::test_derive! { + // event_derive { + // struct UnitStruct; + // } + // expands to { + // const _: () = { + // impl ::ink::storage::traits::Storable for UnitStruct { + // #[inline(always)] + // #[allow(non_camel_case_types)] + // fn decode<__ink_I: ::scale::Input>(__input: &mut __ink_I) -> ::core::result::Result { + // ::core::result::Result::Ok(UnitStruct) + // } + // + // #[inline(always)] + // #[allow(non_camel_case_types)] + // fn encode<__ink_O: ::scale::Output + ?::core::marker::Sized>(&self, __dest: &mut __ink_O) { + // match self { + // UnitStruct => { } + // } + // } + // } + // }; + // } + // } +} \ No newline at end of file diff --git a/crates/ink/macro/src/tests/mod.rs b/crates/ink/macro/src/tests/mod.rs index 8a0bbfbe70..25a42b1d1c 100644 --- a/crates/ink/macro/src/tests/mod.rs +++ b/crates/ink/macro/src/tests/mod.rs @@ -12,16 +12,17 @@ // See the License for the specific language governing permissions and // limitations under the License. +mod event; mod storable; mod storable_hint; mod storage_key; mod storage_layout; use crate::storage::{ - storable::storable_derive, - storable_hint::storable_hint_derive, - storage_key::storage_key_derive, - storage_layout::storage_layout_derive, + storable_derive, + storable_hint_derive, + storage_key_derive, + storage_layout_derive, }; #[macro_export] From 0e42a02ee27b2c525e1d05367acf711e434433bd Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Wed, 26 Apr 2023 17:55:28 +0100 Subject: [PATCH 004/140] Test compiles, now fails --- crates/ink/macro/src/event.rs | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/crates/ink/macro/src/event.rs b/crates/ink/macro/src/event.rs index 344b6be86e..81587b0fa6 100644 --- a/crates/ink/macro/src/event.rs +++ b/crates/ink/macro/src/event.rs @@ -35,7 +35,6 @@ pub fn event_derive(mut s: synstructure::Structure) -> TokenStream2 { /// `Event` derive implementation for `struct` types. fn event_derive_struct(mut s: synstructure::Structure) -> TokenStream2 { assert_eq!(s.variants().len(), 1, "can only operate on structs"); - let mut variant= &s.variants_mut()[0]; let anonymous = false; // todo read from struct attribute e.g. #[event(anonymous)] @@ -52,18 +51,20 @@ fn event_derive_struct(mut s: synstructure::Structure) -> TokenStream2 { // ::ink::storage::traits::Storable::encode(#binding, __dest); // ) // }); - let topics = variant.filter(|bi| { + s.variants_mut()[0].filter(|bi| { bi.ast().attrs.iter().any(|attr| { - attr.path.is_ident("topic") + attr.path().is_ident("topic") }) }); - let len_topics = topics.bindings().len(); + let variant= &s.variants()[0]; + + let len_topics = variant.bindings().len(); // Anonymous events require 1 fewer topics since they do not include their signature. let anonymous_topics_offset = usize::from(!anonymous); let remaining_topics_ty = match len_topics + anonymous_topics_offset { - 0 => quote_spanned!(span=> ::ink::env::topics::state::NoRemainingTopics), - n => quote_spanned!(span=> [::ink::env::topics::state::HasRemainingTopics; #n]), + 0 => quote_spanned!(s.ast().span()=> ::ink::env::topics::state::NoRemainingTopics), + n => quote_spanned!(s.ast().span()=> [::ink::env::topics::state::HasRemainingTopics; #n]), }; let event_signature_topic = signature_topic(variant.ast().fields, &variant.ast().ident); @@ -80,14 +81,15 @@ fn event_derive_struct(mut s: synstructure::Structure) -> TokenStream2 { E: ::ink::env::Environment, B: ::ink::env::topics::TopicsBuilderBackend, { - match self { #topics_body } - builder - .build::() - #event_signature_topic - #( - #topic_impls - )* - .finish() + todo!() + // match self { #topics_body } + // builder + // .build::() + // #event_signature_topic + // #( + // #topic_impls + // )* + // .finish() } } }) @@ -100,9 +102,7 @@ pub fn signature_topic(fields: &syn::Fields, event_ident: &syn::Ident) -> [syn:: let fields = fields .iter() .map(|field| { - field - .ty - .to_token_stream() + quote::ToTokens::to_token_stream(&field.ty) .to_string() .replace(" ", "") }) From bb979c99122488b30d11b0382761222c086a6897 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Thu, 27 Apr 2023 08:51:55 +0100 Subject: [PATCH 005/140] Passing test, now impl --- crates/ink/macro/src/tests/event.rs | 48 ++++++++++++++--------------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/crates/ink/macro/src/tests/event.rs b/crates/ink/macro/src/tests/event.rs index f07a76b9b1..66fa25963e 100644 --- a/crates/ink/macro/src/tests/event.rs +++ b/crates/ink/macro/src/tests/event.rs @@ -24,29 +24,27 @@ use crate::event::event_derive; #[test] fn unit_struct_works() { - todo!() - // crate::test_derive! { - // event_derive { - // struct UnitStruct; - // } - // expands to { - // const _: () = { - // impl ::ink::storage::traits::Storable for UnitStruct { - // #[inline(always)] - // #[allow(non_camel_case_types)] - // fn decode<__ink_I: ::scale::Input>(__input: &mut __ink_I) -> ::core::result::Result { - // ::core::result::Result::Ok(UnitStruct) - // } - // - // #[inline(always)] - // #[allow(non_camel_case_types)] - // fn encode<__ink_O: ::scale::Output + ?::core::marker::Sized>(&self, __dest: &mut __ink_O) { - // match self { - // UnitStruct => { } - // } - // } - // } - // }; - // } - // } + crate::test_derive! { + event_derive { + struct UnitStruct; + } + expands to { + const _: () = { + impl ::ink::env::Topics for UnitStruct { + type RemainingTopics = [::ink::env::topics::state::HasRemainingTopics; 1usize]; + + fn topics( + &self, + builder: ::ink::env::topics::TopicsBuilder<::ink::env::topics::state::Uninit, E, B>, + ) -> >::Output + where + E: ::ink::env::Environment, + B: ::ink::env::topics::TopicsBuilderBackend, + { + todo!() + } + } + }; + } + } } \ No newline at end of file From 5cf66c29f3c18e402c58f3103d2b96a550803cd0 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Thu, 27 Apr 2023 11:44:31 +0100 Subject: [PATCH 006/140] Passing test with no fields --- crates/env/src/topics.rs | 4 +- crates/ink/macro/src/event.rs | 61 +++++++++++++++++++++-------- crates/ink/macro/src/tests/event.rs | 11 +++++- 3 files changed, 56 insertions(+), 20 deletions(-) diff --git a/crates/env/src/topics.rs b/crates/env/src/topics.rs index a2536cdb32..98cecda4a3 100644 --- a/crates/env/src/topics.rs +++ b/crates/env/src/topics.rs @@ -195,7 +195,9 @@ pub trait Topics { /// builder. type RemainingTopics: EventTopicsAmount; - // const EVENT_SIGNATURE_TOPIC: [u8; 32]; + /// The unique signature topic of the event. `None` for anonymous events. + /// todo: document how this is calculated + const SIGNATURE_TOPIC: Option<[u8; 32]> = None; /// Guides event topic serialization using the given topics builder. fn topics( diff --git a/crates/ink/macro/src/event.rs b/crates/ink/macro/src/event.rs index 81587b0fa6..1d9da428d9 100644 --- a/crates/ink/macro/src/event.rs +++ b/crates/ink/macro/src/event.rs @@ -35,6 +35,7 @@ pub fn event_derive(mut s: synstructure::Structure) -> TokenStream2 { /// `Event` derive implementation for `struct` types. fn event_derive_struct(mut s: synstructure::Structure) -> TokenStream2 { assert_eq!(s.variants().len(), 1, "can only operate on structs"); + let span = s.ast().span(); let anonymous = false; // todo read from struct attribute e.g. #[event(anonymous)] @@ -52,22 +53,51 @@ fn event_derive_struct(mut s: synstructure::Structure) -> TokenStream2 { // ) // }); s.variants_mut()[0].filter(|bi| { - bi.ast().attrs.iter().any(|attr| { - attr.path().is_ident("topic") - }) + bi.ast() + .attrs + .iter() + .any(|attr| attr.path().is_ident("topic")) }); - let variant= &s.variants()[0]; + let variant = &s.variants()[0]; let len_topics = variant.bindings().len(); // Anonymous events require 1 fewer topics since they do not include their signature. let anonymous_topics_offset = usize::from(!anonymous); let remaining_topics_ty = match len_topics + anonymous_topics_offset { - 0 => quote_spanned!(s.ast().span()=> ::ink::env::topics::state::NoRemainingTopics), - n => quote_spanned!(s.ast().span()=> [::ink::env::topics::state::HasRemainingTopics; #n]), + 0 => quote_spanned!(span=> ::ink::env::topics::state::NoRemainingTopics), + n => quote_spanned!(span=> [::ink::env::topics::state::HasRemainingTopics; #n]), + }; + + let signature_topic = signature_topic(variant.ast().fields, &variant.ast().ident); + let event_signature_topic = if anonymous { + None + } else { + Some(quote_spanned!(span=> + .push_topic(&Self::SIGNATURE_TOPIC.expect("non-anonymous events must have a signature topic")) + )) }; - let event_signature_topic = signature_topic(variant.ast().fields, &variant.ast().ident); + let pat = variant.pat(); + let topics = variant.bindings().iter().fold(quote!(), |acc, field| { + let field_ty = &field.ast().ty; + let field_span = field_ty.span(); + quote_spanned!(field_span=> + #acc + .push_topic::<#field_ty>(#field) + ) + }); + let topics_builder = quote!( + #pat => { + builder + .build::() + #event_signature_topic + #topics + .finish() + } + ); + + println!("topics_builder: {}", topics_builder.to_string()); s.gen_impl(quote! { gen impl ::ink::env::Topics for @Self { @@ -81,15 +111,9 @@ fn event_derive_struct(mut s: synstructure::Structure) -> TokenStream2 { E: ::ink::env::Environment, B: ::ink::env::topics::TopicsBuilderBackend, { - todo!() - // match self { #topics_body } - // builder - // .build::() - // #event_signature_topic - // #( - // #topic_impls - // )* - // .finish() + match self { + #topics_builder + } } } }) @@ -98,7 +122,10 @@ fn event_derive_struct(mut s: synstructure::Structure) -> TokenStream2 { /// The signature topic of an event variant. /// /// Calculated with `blake2b("Event(field1_type,field2_type)")`. -pub fn signature_topic(fields: &syn::Fields, event_ident: &syn::Ident) -> [syn::LitInt; 32] { +pub fn signature_topic( + fields: &syn::Fields, + event_ident: &syn::Ident, +) -> [syn::LitInt; 32] { let fields = fields .iter() .map(|field| { diff --git a/crates/ink/macro/src/tests/event.rs b/crates/ink/macro/src/tests/event.rs index 66fa25963e..073deb61b9 100644 --- a/crates/ink/macro/src/tests/event.rs +++ b/crates/ink/macro/src/tests/event.rs @@ -41,10 +41,17 @@ fn unit_struct_works() { E: ::ink::env::Environment, B: ::ink::env::topics::TopicsBuilderBackend, { - todo!() + match self { + UnitStruct => { + builder + .build::() + .push_topic(&Self::SIGNATURE_TOPIC.expect("non-anonymous events must have a signature topic")) + .finish() + } + } } } }; } } -} \ No newline at end of file +} From a78360ad1a3bea244fb686592836eb04fea3470f Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Thu, 27 Apr 2023 12:13:38 +0100 Subject: [PATCH 007/140] Add events integration-tests example --- integration-tests/events/.gitignore | 9 +++ integration-tests/events/Cargo.toml | 30 +++++++ integration-tests/events/event-def/Cargo.toml | 17 ++++ integration-tests/events/event-def/src/lib.rs | 6 ++ integration-tests/events/lib.rs | 78 +++++++++++++++++++ 5 files changed, 140 insertions(+) create mode 100644 integration-tests/events/.gitignore create mode 100644 integration-tests/events/Cargo.toml create mode 100644 integration-tests/events/event-def/Cargo.toml create mode 100644 integration-tests/events/event-def/src/lib.rs create mode 100644 integration-tests/events/lib.rs diff --git a/integration-tests/events/.gitignore b/integration-tests/events/.gitignore new file mode 100644 index 0000000000..bf910de10a --- /dev/null +++ b/integration-tests/events/.gitignore @@ -0,0 +1,9 @@ +# Ignore build artifacts from the local tests sub-crate. +/target/ + +# Ignore backup files creates by cargo fmt. +**/*.rs.bk + +# Remove Cargo.lock when creating an executable, leave it for libraries +# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock +Cargo.lock \ No newline at end of file diff --git a/integration-tests/events/Cargo.toml b/integration-tests/events/Cargo.toml new file mode 100644 index 0000000000..5d37dd87e0 --- /dev/null +++ b/integration-tests/events/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "events" +version = "4.2.0" +authors = ["Parity Technologies "] +edition = "2021" +publish = false + +[dependencies] +ink = { path = "../../crates/ink", default-features = false } +scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] } +scale-info = { version = "2.5", default-features = false, features = ["derive"], optional = true } + +event-def = { path = "event-def", default-features = false } + +[dev-dependencies] +ink_e2e = { path = "../../crates/e2e" } + +[lib] +path = "lib.rs" + +[features] +default = ["std"] +std = [ + "ink/std", + "scale/std", + "scale-info/std", + "event-def/std", +] +ink-as-dependency = [] +e2e-tests = [] diff --git a/integration-tests/events/event-def/Cargo.toml b/integration-tests/events/event-def/Cargo.toml new file mode 100644 index 0000000000..b5ed9cd33c --- /dev/null +++ b/integration-tests/events/event-def/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "event-def" +version = "0.1.0" +edition = "2021" + +[dependencies] +ink = { path = "../../../crates/ink", default-features = false } +scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] } +scale-info = { version = "2.5", default-features = false, features = ["derive"], optional = true } + +[features] +default = ["std"] +std = [ + "ink/std", + "scale/std", + "scale-info/std", +] diff --git a/integration-tests/events/event-def/src/lib.rs b/integration-tests/events/event-def/src/lib.rs new file mode 100644 index 0000000000..a64301d1da --- /dev/null +++ b/integration-tests/events/event-def/src/lib.rs @@ -0,0 +1,6 @@ +#![cfg_attr(not(feature = "std"), no_std, no_main)] + +#[derive(ink::Event)] +pub struct Flipped { + pub flipped: bool, +} diff --git a/integration-tests/events/lib.rs b/integration-tests/events/lib.rs new file mode 100644 index 0000000000..a09ff55f04 --- /dev/null +++ b/integration-tests/events/lib.rs @@ -0,0 +1,78 @@ +#![cfg_attr(not(feature = "std"), no_std, no_main)] + +#[ink::contract] +pub mod events { + use event_def::Flipped; + + #[ink(storage)] + pub struct Events { + value: bool, + } + + impl Events { + /// Creates a new events smart contract initialized with the given value. + #[ink(constructor)] + pub fn new(init_value: bool) -> Self { + Self { value: init_value } + } + + /// Flips the current value of the boolean. + #[ink(message)] + pub fn flip(&mut self) { + self.value = !self.value; + self.env().emit_event(Flipped { flipped: self.value }) + } + } + + #[cfg(test)] + mod tests { + use super::*; + + #[ink::test] + fn it_works() { + let mut events = Events::new(false); + events.flip(); + // todo: check events. + } + } + + #[cfg(all(test, feature = "e2e-tests"))] + mod e2e_tests { + use super::*; + use ink_e2e::build_message; + + type E2EResult = std::result::Result>; + + #[ink_e2e::test] + async fn it_works(mut client: ink_e2e::Client) -> E2EResult<()> { + // // given + // let constructor = FlipperRef::new(false); + // let contract_acc_id = client + // .instantiate("events", &ink_e2e::alice(), constructor, 0, None) + // .await + // .expect("instantiate failed") + // .account_id; + // + // let get = build_message::(contract_acc_id.clone()) + // .call(|events| events.get()); + // let get_res = client.call_dry_run(&ink_e2e::bob(), &get, 0, None).await; + // assert!(matches!(get_res.return_value(), false)); + // + // // when + // let flip = build_message::(contract_acc_id.clone()) + // .call(|events| events.flip()); + // let _flip_res = client + // .call(&ink_e2e::bob(), flip, 0, None) + // .await + // .expect("flip failed"); + // + // // then + // let get = build_message::(contract_acc_id.clone()) + // .call(|events| events.get()); + // let get_res = client.call_dry_run(&ink_e2e::bob(), &get, 0, None).await; + // assert!(matches!(get_res.return_value(), true)); + // + // Ok(()) + } + } +} From eb1b771daba8392e64ff5ef7ddac79c16e1ccbb2 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Thu, 27 Apr 2023 12:15:40 +0100 Subject: [PATCH 008/140] Expose Event derive macro at the top level ::ink::Event --- crates/ink/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/ink/src/lib.rs b/crates/ink/src/lib.rs index 700be36351..212ab77aba 100644 --- a/crates/ink/src/lib.rs +++ b/crates/ink/src/lib.rs @@ -67,6 +67,7 @@ pub use self::{ prelude::IIP2_WILDCARD_COMPLEMENT_SELECTOR, }; pub use ink_macro::{ + Event, blake2x256, chain_extension, contract, From c0dbde22ad1ae80c8c3cfe12e425086f25500ea6 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Thu, 27 Apr 2023 12:16:10 +0100 Subject: [PATCH 009/140] Remove println! --- crates/ink/macro/src/event.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/crates/ink/macro/src/event.rs b/crates/ink/macro/src/event.rs index 1d9da428d9..37d5842d3b 100644 --- a/crates/ink/macro/src/event.rs +++ b/crates/ink/macro/src/event.rs @@ -97,8 +97,6 @@ fn event_derive_struct(mut s: synstructure::Structure) -> TokenStream2 { } ); - println!("topics_builder: {}", topics_builder.to_string()); - s.gen_impl(quote! { gen impl ::ink::env::Topics for @Self { type RemainingTopics = #remaining_topics_ty; From e979a9a18cde872d0174fd2bdec26cedcd6b0949 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Thu, 27 Apr 2023 12:31:15 +0100 Subject: [PATCH 010/140] Remove ContractEventBase --- crates/ink/src/reflect/event.rs | 52 --------------------------------- crates/ink/src/reflect/mod.rs | 2 -- 2 files changed, 54 deletions(-) delete mode 100644 crates/ink/src/reflect/event.rs diff --git a/crates/ink/src/reflect/event.rs b/crates/ink/src/reflect/event.rs deleted file mode 100644 index 69181659e3..0000000000 --- a/crates/ink/src/reflect/event.rs +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2018-2022 Parity Technologies (UK) Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -/// Defines a base event type for the contract. -/// -/// This is usually the event enum that comprises all defined event types. -/// -/// # Usage -/// -/// ``` -/// #[ink::contract] -/// pub mod contract { -/// #[ink(storage)] -/// pub struct Contract {} -/// -/// #[ink(event)] -/// pub struct Event1 {} -/// -/// #[ink(event)] -/// pub struct Event2 {} -/// -/// impl Contract { -/// #[ink(constructor)] -/// pub fn constructor() -> Self { -/// Self {} -/// } -/// -/// #[ink(message)] -/// pub fn message(&self) {} -/// } -/// } -/// -/// use contract::Contract; -/// # use ink::reflect::ContractEventBase; -/// -/// type BaseEvent = ::Type; -/// ``` -pub trait ContractEventBase { - /// The generated base event enum. - type Type; -} diff --git a/crates/ink/src/reflect/mod.rs b/crates/ink/src/reflect/mod.rs index 349ca327e1..4df2d8e1c5 100644 --- a/crates/ink/src/reflect/mod.rs +++ b/crates/ink/src/reflect/mod.rs @@ -25,7 +25,6 @@ mod contract; mod dispatch; -mod event; mod trait_def; pub use self::{ @@ -41,7 +40,6 @@ pub use self::{ DispatchableMessageInfo, ExecuteDispatchable, }, - event::ContractEventBase, trait_def::{ TraitDefinitionRegistry, TraitInfo, From 8f442443b5e7429dc57695e969a3b31ba17d2ac8 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Thu, 27 Apr 2023 18:01:13 +0100 Subject: [PATCH 011/140] WIP rewiring event codegen for use with derive. --- crates/ink/codegen/src/generator/events.rs | 25 +++++++++++----------- crates/ink/src/codegen/event/emit.rs | 11 +++------- 2 files changed, 16 insertions(+), 20 deletions(-) diff --git a/crates/ink/codegen/src/generator/events.rs b/crates/ink/codegen/src/generator/events.rs index 48f16b14eb..671d86e44b 100644 --- a/crates/ink/codegen/src/generator/events.rs +++ b/crates/ink/codegen/src/generator/events.rs @@ -38,16 +38,16 @@ impl GenerateCode for Events<'_> { return TokenStream2::new() } let emit_event_trait_impl = self.generate_emit_event_trait_impl(); - let event_base = self.generate_event_base(); - let topic_guards = self.generate_topic_guards(); - let topics_impls = self.generate_topics_impls(); + // let event_base = self.generate_event_base(); + // let topic_guards = self.generate_topic_guards(); + // let topics_impls = self.generate_topics_impls(); let event_structs = self.generate_event_structs(); quote! { #emit_event_trait_impl - #event_base - #( #topic_guards )* + // #event_base + // #( #topic_guards )* #( #event_structs )* - #( #topics_impls )* + // #( #topics_impls )* } } } @@ -56,18 +56,19 @@ impl<'a> Events<'a> { /// Used to allow emitting user defined events directly instead of converting /// them first into the automatically generated base trait of the contract. fn generate_emit_event_trait_impl(&self) -> TokenStream2 { - let storage_ident = &self.contract.module().storage().ident(); quote! { const _: () = { - impl<'a> ::ink::codegen::EmitEvent<#storage_ident> for ::ink::EnvAccess<'a, Environment> { + impl<'a> ::ink::codegen::EmitEvent for ::ink::EnvAccess<'a, Environment> { fn emit_event(self, event: E) where - E: Into<<#storage_ident as ::ink::reflect::ContractEventBase>::Type>, + E: ::ink::env::Topics, { + // todo: use pattern from playground to enforce max topics length here? + // get concrete ::MAX_TOPICS. Do `Assert` inside emit_event??? ::ink::env::emit_event::< Environment, - <#storage_ident as ::ink::reflect::ContractEventBase>::Type - >(event.into()); + E + >(event); } } }; @@ -291,7 +292,7 @@ impl<'a> Events<'a> { }); quote_spanned!(span => #( #attrs )* - #[derive(scale::Encode, scale::Decode)] + #[derive(::ink::Event, scale::Encode, scale::Decode)] pub struct #ident { #( #fields ),* } diff --git a/crates/ink/src/codegen/event/emit.rs b/crates/ink/src/codegen/event/emit.rs index 702bfd687f..ebf8d1f2f6 100644 --- a/crates/ink/src/codegen/event/emit.rs +++ b/crates/ink/src/codegen/event/emit.rs @@ -12,15 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::reflect::ContractEventBase; - /// Allows for `self.env().emit_event(...)` syntax in ink! implementation blocks. -pub trait EmitEvent -where - C: ContractEventBase, -{ - /// Emits an event that can be trivially converted into the base event. +pub trait EmitEvent { + /// Emits an event. fn emit_event(self, event: E) where - E: Into<::Type>; + E: ink_env::Topics; } From a3200019c8386086eea3125a0d2ae551dd4846e3 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Thu, 4 May 2023 11:21:40 +0100 Subject: [PATCH 012/140] Direct impl of emit_event --- crates/ink/codegen/src/generator/events.rs | 30 ++----------------- crates/ink/src/codegen/event/emit.rs | 21 ------------- crates/ink/src/codegen/event/mod.rs | 14 ++++----- crates/ink/src/codegen/mod.rs | 1 - crates/ink/src/env_access.rs | 8 +++++ crates/ink/src/lib.rs | 2 +- integration-tests/events/event-def/src/lib.rs | 2 +- 7 files changed, 17 insertions(+), 61 deletions(-) delete mode 100644 crates/ink/src/codegen/event/emit.rs diff --git a/crates/ink/codegen/src/generator/events.rs b/crates/ink/codegen/src/generator/events.rs index 671d86e44b..6eaf9c1087 100644 --- a/crates/ink/codegen/src/generator/events.rs +++ b/crates/ink/codegen/src/generator/events.rs @@ -33,17 +33,13 @@ impl_as_ref_for_generator!(Events); impl GenerateCode for Events<'_> { fn generate_code(&self) -> TokenStream2 { - if self.contract.module().events().next().is_none() { - // Generate no code in case there are no event definitions. - return TokenStream2::new() - } - let emit_event_trait_impl = self.generate_emit_event_trait_impl(); + // let emit_event_trait_impl = self.generate_emit_event_trait_impl(); // let event_base = self.generate_event_base(); // let topic_guards = self.generate_topic_guards(); // let topics_impls = self.generate_topics_impls(); let event_structs = self.generate_event_structs(); quote! { - #emit_event_trait_impl + // #emit_event_trait_impl // #event_base // #( #topic_guards )* #( #event_structs )* @@ -53,28 +49,6 @@ impl GenerateCode for Events<'_> { } impl<'a> Events<'a> { - /// Used to allow emitting user defined events directly instead of converting - /// them first into the automatically generated base trait of the contract. - fn generate_emit_event_trait_impl(&self) -> TokenStream2 { - quote! { - const _: () = { - impl<'a> ::ink::codegen::EmitEvent for ::ink::EnvAccess<'a, Environment> { - fn emit_event(self, event: E) - where - E: ::ink::env::Topics, - { - // todo: use pattern from playground to enforce max topics length here? - // get concrete ::MAX_TOPICS. Do `Assert` inside emit_event??? - ::ink::env::emit_event::< - Environment, - E - >(event); - } - } - }; - } - } - /// Generates the base event enum that comprises all user defined events. /// All emitted events are converted into a variant of this enum before being /// serialized and emitted to apply their unique event discriminant (ID). diff --git a/crates/ink/src/codegen/event/emit.rs b/crates/ink/src/codegen/event/emit.rs deleted file mode 100644 index ebf8d1f2f6..0000000000 --- a/crates/ink/src/codegen/event/emit.rs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2018-2022 Parity Technologies (UK) Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -/// Allows for `self.env().emit_event(...)` syntax in ink! implementation blocks. -pub trait EmitEvent { - /// Emits an event. - fn emit_event(self, event: E) - where - E: ink_env::Topics; -} diff --git a/crates/ink/src/codegen/event/mod.rs b/crates/ink/src/codegen/event/mod.rs index 73002bdc4d..d2c2bac908 100644 --- a/crates/ink/src/codegen/event/mod.rs +++ b/crates/ink/src/codegen/event/mod.rs @@ -12,15 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -mod emit; mod topics; -pub use self::{ - emit::EmitEvent, - topics::{ - EventLenTopics, - EventRespectsTopicLimit, - EventTopics, - RespectTopicLimit, - }, +pub use self::topics::{ + EventLenTopics, + EventRespectsTopicLimit, + EventTopics, + RespectTopicLimit, }; diff --git a/crates/ink/src/codegen/mod.rs b/crates/ink/src/codegen/mod.rs index d53fc3517b..7fcbe98366 100644 --- a/crates/ink/src/codegen/mod.rs +++ b/crates/ink/src/codegen/mod.rs @@ -33,7 +33,6 @@ pub use self::{ StaticEnv, }, event::{ - EmitEvent, EventLenTopics, EventRespectsTopicLimit, EventTopics, diff --git a/crates/ink/src/env_access.rs b/crates/ink/src/env_access.rs index 63c42d01f8..7d2cea9485 100644 --- a/crates/ink/src/env_access.rs +++ b/crates/ink/src/env_access.rs @@ -413,6 +413,14 @@ where ink_env::minimum_balance::() } + /// Emits an event. + pub fn emit_event(self, event: Evt) + where + Evt: ink_env::Topics + scale::Encode, + { + ink_env::emit_event::(event) + } + /// Instantiates another contract. /// /// # Example diff --git a/crates/ink/src/lib.rs b/crates/ink/src/lib.rs index 212ab77aba..c6e46f975e 100644 --- a/crates/ink/src/lib.rs +++ b/crates/ink/src/lib.rs @@ -67,7 +67,6 @@ pub use self::{ prelude::IIP2_WILDCARD_COMPLEMENT_SELECTOR, }; pub use ink_macro::{ - Event, blake2x256, chain_extension, contract, @@ -76,6 +75,7 @@ pub use ink_macro::{ storage_item, test, trait_definition, + Event, }; pub use ink_primitives::{ ConstructorResult, diff --git a/integration-tests/events/event-def/src/lib.rs b/integration-tests/events/event-def/src/lib.rs index a64301d1da..17dd047fdf 100644 --- a/integration-tests/events/event-def/src/lib.rs +++ b/integration-tests/events/event-def/src/lib.rs @@ -1,6 +1,6 @@ #![cfg_attr(not(feature = "std"), no_std, no_main)] -#[derive(ink::Event)] +#[derive(ink::Event, scale::Encode)] pub struct Flipped { pub flipped: bool, } From a75e7f7adde903e082dcaa94f08cdabb23138bb9 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Thu, 4 May 2023 11:49:58 +0100 Subject: [PATCH 013/140] Use bound_impl --- crates/ink/macro/src/event.rs | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/crates/ink/macro/src/event.rs b/crates/ink/macro/src/event.rs index 37d5842d3b..4d0966514e 100644 --- a/crates/ink/macro/src/event.rs +++ b/crates/ink/macro/src/event.rs @@ -97,23 +97,21 @@ fn event_derive_struct(mut s: synstructure::Structure) -> TokenStream2 { } ); - s.gen_impl(quote! { - gen impl ::ink::env::Topics for @Self { - type RemainingTopics = #remaining_topics_ty; + s.bound_impl(quote!(::ink::env::Topics), quote! { + type RemainingTopics = #remaining_topics_ty; - fn topics( - &self, - builder: ::ink::env::topics::TopicsBuilder<::ink::env::topics::state::Uninit, E, B>, - ) -> >::Output - where - E: ::ink::env::Environment, - B: ::ink::env::topics::TopicsBuilderBackend, - { - match self { - #topics_builder - } + fn topics( + &self, + builder: ::ink::env::topics::TopicsBuilder<::ink::env::topics::state::Uninit, E, B>, + ) -> >::Output + where + E: ::ink::env::Environment, + B: ::ink::env::topics::TopicsBuilderBackend, + { + match self { + #topics_builder } - } + } }) } From 5e9239042cf4114f0a47c0a7129a7c032cc1a044 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Thu, 4 May 2023 16:43:26 +0100 Subject: [PATCH 014/140] WIP impl derive EventMetadata --- crates/ink/macro/src/event/metadata.rs | 51 +++++++++++++++++++ .../ink/macro/src/{event.rs => event/mod.rs} | 4 ++ crates/ink/macro/src/lib.rs | 6 +++ crates/ink/src/lib.rs | 1 + crates/metadata/Cargo.toml | 2 + crates/metadata/src/lib.rs | 13 +++++ integration-tests/events/event-def/src/lib.rs | 1 + 7 files changed, 78 insertions(+) create mode 100644 crates/ink/macro/src/event/metadata.rs rename crates/ink/macro/src/{event.rs => event/mod.rs} (98%) diff --git a/crates/ink/macro/src/event/metadata.rs b/crates/ink/macro/src/event/metadata.rs new file mode 100644 index 0000000000..bace4217d5 --- /dev/null +++ b/crates/ink/macro/src/event/metadata.rs @@ -0,0 +1,51 @@ +// Copyright 2018-2022 Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use proc_macro2::TokenStream as TokenStream2; +use quote::{ + quote, + quote_spanned, +}; +use syn::spanned::Spanned; + +/// Derives the `ink::Event` trait for the given `struct`. +pub fn event_metadata_derive(mut s: synstructure::Structure) -> TokenStream2 { + s.bind_with(|_| synstructure::BindStyle::Move) + .add_bounds(synstructure::AddBounds::Fields) + .underscore_const(true); + match &s.ast().data { + syn::Data::Struct(_) => event_metadata_derive_struct(s), + _ => { + panic!("can only derive `EventMetadata` for Rust `struct` items") + } + } +} + +/// `Event` derive implementation for `struct` types. +fn event_metadata_derive_struct(mut s: synstructure::Structure) -> TokenStream2 { + assert_eq!(s.variants().len(), 1, "can only operate on structs"); + let span = s.ast().span(); + let event = &s.ast().ident; + + let _variant = &s.variants()[0]; + + s.bound_impl(quote!(::ink::metadata::EventMetadata), quote! { + fn event_spec() -> ::ink::metadata::EventSpec { + #[::ink::metadata::linkme::distributed_slice(::ink::metadata::EVENTS)] + #[linkme(crate = ::ink::metadata::linkme)] + static EVENT_METADATA: fn() -> ::ink::metadata::EventSpec = <#event as ::ink::metadata::EventMetadata>::event_spec; + todo!() + } + }) +} diff --git a/crates/ink/macro/src/event.rs b/crates/ink/macro/src/event/mod.rs similarity index 98% rename from crates/ink/macro/src/event.rs rename to crates/ink/macro/src/event/mod.rs index 4d0966514e..48f23e1612 100644 --- a/crates/ink/macro/src/event.rs +++ b/crates/ink/macro/src/event/mod.rs @@ -12,6 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. +mod metadata; + +pub use metadata::event_metadata_derive; + use proc_macro2::TokenStream as TokenStream2; use quote::{ quote, diff --git a/crates/ink/macro/src/lib.rs b/crates/ink/macro/src/lib.rs index a5c214b93d..406dd36e92 100644 --- a/crates/ink/macro/src/lib.rs +++ b/crates/ink/macro/src/lib.rs @@ -1289,6 +1289,12 @@ synstructure::decl_derive!( event::event_derive ); +synstructure::decl_derive!( + [EventMetadata] => + /// todo + event::event_metadata_derive +); + synstructure::decl_derive!( [Storable] => /// Derives `ink::storage`'s `Storable` trait for the given `struct`, `enum` or `union`. diff --git a/crates/ink/src/lib.rs b/crates/ink/src/lib.rs index c6e46f975e..66d211ea4f 100644 --- a/crates/ink/src/lib.rs +++ b/crates/ink/src/lib.rs @@ -76,6 +76,7 @@ pub use ink_macro::{ test, trait_definition, Event, + EventMetadata, }; pub use ink_primitives::{ ConstructorResult, diff --git a/crates/metadata/Cargo.toml b/crates/metadata/Cargo.toml index 1b4ef2412d..574d854286 100644 --- a/crates/metadata/Cargo.toml +++ b/crates/metadata/Cargo.toml @@ -15,6 +15,7 @@ categories = ["no-std", "embedded"] include = ["Cargo.toml", "src/**/*.rs", "README.md", "LICENSE"] [dependencies] +ink_env = "4.2.0" ink_prelude = { version = "4.2.0", path = "../prelude/", default-features = false } ink_primitives = { version = "4.2.0", path = "../primitives/", default-features = false } @@ -22,6 +23,7 @@ serde = { version = "1.0", default-features = false, features = ["derive", "allo impl-serde = "0.4.0" derive_more = { version = "0.99", default-features = false, features = ["from"] } scale-info = { version = "2.5", default-features = false, features = ["derive", "serde", "decode"] } +linkme = "0.3.9" [dev-dependencies] pretty_assertions = "1" diff --git a/crates/metadata/src/lib.rs b/crates/metadata/src/lib.rs index 29673caeba..e813f61c25 100644 --- a/crates/metadata/src/lib.rs +++ b/crates/metadata/src/lib.rs @@ -54,6 +54,9 @@ pub use self::specs::{ use impl_serde::serialize as serde_hex; pub use scale_info::TypeInfo; +#[doc(hidden)] +pub use linkme; + #[cfg(feature = "derive")] use scale_info::{ form::PortableForm, @@ -153,3 +156,13 @@ impl InkProject { &self.spec } } + +/// Collects all event definitions in the contract binary at linking time. +#[linkme::distributed_slice] +pub static EVENTS: [fn() -> EventSpec] = [..]; + +// todo: docs +pub trait EventMetadata { + /// Returns the metadata of the event. + fn event_spec() -> EventSpec; +} diff --git a/integration-tests/events/event-def/src/lib.rs b/integration-tests/events/event-def/src/lib.rs index 17dd047fdf..08dea99a1e 100644 --- a/integration-tests/events/event-def/src/lib.rs +++ b/integration-tests/events/event-def/src/lib.rs @@ -1,6 +1,7 @@ #![cfg_attr(not(feature = "std"), no_std, no_main)] #[derive(ink::Event, scale::Encode)] +#[cfg_attr(feature = "std", derive(ink::EventMetadata))] pub struct Flipped { pub flipped: bool, } From fc395893b8ff6cf367cccf700cb89289ce8b7e8e Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Fri, 5 May 2023 11:09:06 +0100 Subject: [PATCH 015/140] WIP add event metadata derive --- crates/ink/macro/src/event/metadata.rs | 19 +++++++-- crates/ink/macro/src/tests/event_metadata.rs | 41 ++++++++++++++++++++ crates/ink/macro/src/tests/mod.rs | 1 + 3 files changed, 57 insertions(+), 4 deletions(-) create mode 100644 crates/ink/macro/src/tests/event_metadata.rs diff --git a/crates/ink/macro/src/event/metadata.rs b/crates/ink/macro/src/event/metadata.rs index bace4217d5..19bb223d6a 100644 --- a/crates/ink/macro/src/event/metadata.rs +++ b/crates/ink/macro/src/event/metadata.rs @@ -36,16 +36,27 @@ pub fn event_metadata_derive(mut s: synstructure::Structure) -> TokenStream2 { fn event_metadata_derive_struct(mut s: synstructure::Structure) -> TokenStream2 { assert_eq!(s.variants().len(), 1, "can only operate on structs"); let span = s.ast().span(); - let event = &s.ast().ident; - let _variant = &s.variants()[0]; + let variant = &s.variants()[0]; + let ident = variant.ast().ident; s.bound_impl(quote!(::ink::metadata::EventMetadata), quote! { fn event_spec() -> ::ink::metadata::EventSpec { + // register this event metadata function in the distributed slice for combining all + // events referenced in the contract binary. #[::ink::metadata::linkme::distributed_slice(::ink::metadata::EVENTS)] #[linkme(crate = ::ink::metadata::linkme)] - static EVENT_METADATA: fn() -> ::ink::metadata::EventSpec = <#event as ::ink::metadata::EventMetadata>::event_spec; - todo!() + static EVENT_METADATA: fn() -> ::ink::metadata::EventSpec = + <#ident as ::ink::metadata::EventMetadata>::event_spec; + + ::ink::metadata::EventSpec::new(::core::stringify!(#ident)) + .args([ + // #( #args ),* + ]) + .docs([ + // #( #docs ),* + ]) + .done() } }) } diff --git a/crates/ink/macro/src/tests/event_metadata.rs b/crates/ink/macro/src/tests/event_metadata.rs new file mode 100644 index 0000000000..0900706052 --- /dev/null +++ b/crates/ink/macro/src/tests/event_metadata.rs @@ -0,0 +1,41 @@ +// Copyright 2018-2022 Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::event::event_metadata_derive; + +#[test] +fn unit_struct_works() { + crate::test_derive! { + event_metadata_derive { + struct UnitStruct; + } + expands to { + const _: () = { + impl ::ink::metadata::EventMetadata for UnitStruct { + fn event_spec() -> ::ink::metadata::EventSpec { + #[::ink::metadata::linkme::distributed_slice(::ink::metadata::EVENTS)] + #[linkme(crate = ::ink::metadata::linkme)] + static EVENT_METADATA: fn() -> ::ink::metadata::EventSpec = + ::event_spec; + + ::ink::metadata::EventSpec::new(::core::stringify!(UnitStruct)) + .args([]) + .docs([]) + .done() + } + } + }; + } + } +} diff --git a/crates/ink/macro/src/tests/mod.rs b/crates/ink/macro/src/tests/mod.rs index 25a42b1d1c..aeac397477 100644 --- a/crates/ink/macro/src/tests/mod.rs +++ b/crates/ink/macro/src/tests/mod.rs @@ -13,6 +13,7 @@ // limitations under the License. mod event; +mod event_metadata; mod storable; mod storable_hint; mod storage_key; From 5fea6ff611a89739eff3fe73fd14ea2f7d05a83b Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Fri, 5 May 2023 11:17:25 +0100 Subject: [PATCH 016/140] Add some metadata derive todos --- crates/ink/macro/src/event/metadata.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/ink/macro/src/event/metadata.rs b/crates/ink/macro/src/event/metadata.rs index 19bb223d6a..2520cbfb20 100644 --- a/crates/ink/macro/src/event/metadata.rs +++ b/crates/ink/macro/src/event/metadata.rs @@ -49,10 +49,14 @@ fn event_metadata_derive_struct(mut s: synstructure::Structure) -> TokenStream2 static EVENT_METADATA: fn() -> ::ink::metadata::EventSpec = <#ident as ::ink::metadata::EventMetadata>::event_spec; + // todo: check that cfg attributes work here ::ink::metadata::EventSpec::new(::core::stringify!(#ident)) + // todo: add signanture topic if not anonymous + // todo: add fields, with topic flag. .args([ // #( #args ),* ]) + // todo: add docs .docs([ // #( #docs ),* ]) From c1ba2fa34a43581c7409587e984dc23f86052e08 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Fri, 5 May 2023 11:44:24 +0100 Subject: [PATCH 017/140] WIP Collect metadata from linked events --- crates/ink/codegen/src/generator/metadata.rs | 9 +++++---- crates/metadata/src/lib.rs | 8 ++++++++ crates/metadata/src/specs.rs | 1 + 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/crates/ink/codegen/src/generator/metadata.rs b/crates/ink/codegen/src/generator/metadata.rs index 0dac717232..7a21a3d63b 100644 --- a/crates/ink/codegen/src/generator/metadata.rs +++ b/crates/ink/codegen/src/generator/metadata.rs @@ -88,7 +88,7 @@ impl Metadata<'_> { fn generate_contract(&self) -> TokenStream2 { let constructors = self.generate_constructors(); let messages = self.generate_messages(); - let events = self.generate_events(); + // let events = self.generate_events(); let docs = self .contract .module() @@ -108,9 +108,10 @@ impl Metadata<'_> { .messages([ #( #messages ),* ]) - .events([ - #( #events ),* - ]) + .events( + // collects metadata for all events linked into the contract. + ::ink::metadata::event_specs() + ) .docs([ #( #docs ),* ]) diff --git a/crates/metadata/src/lib.rs b/crates/metadata/src/lib.rs index e813f61c25..dc31802f97 100644 --- a/crates/metadata/src/lib.rs +++ b/crates/metadata/src/lib.rs @@ -161,6 +161,14 @@ impl InkProject { #[linkme::distributed_slice] pub static EVENTS: [fn() -> EventSpec] = [..]; +/// Load metadata for all events linked into the contract. +pub fn event_specs() -> Vec { + EVENTS + .iter() + .map(|event| event()) + .collect() +} + // todo: docs pub trait EventMetadata { /// Returns the metadata of the event. diff --git a/crates/metadata/src/specs.rs b/crates/metadata/src/specs.rs index e49f4655c7..4c40705b15 100644 --- a/crates/metadata/src/specs.rs +++ b/crates/metadata/src/specs.rs @@ -274,6 +274,7 @@ where { /// Creates a new contract specification. pub fn new() -> ContractSpecBuilder { + ContractSpecBuilder { spec: Self { constructors: Vec::new(), From dc88a3300f7f535fdf8b2b87521cb7d2903c2d47 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Fri, 5 May 2023 11:53:42 +0100 Subject: [PATCH 018/140] Refactor event metadata collection --- crates/ink/codegen/src/generator/metadata.rs | 5 +---- crates/metadata/src/lib.rs | 8 -------- crates/metadata/src/specs.rs | 11 +++++++++++ 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/crates/ink/codegen/src/generator/metadata.rs b/crates/ink/codegen/src/generator/metadata.rs index 7a21a3d63b..3d5965ba0b 100644 --- a/crates/ink/codegen/src/generator/metadata.rs +++ b/crates/ink/codegen/src/generator/metadata.rs @@ -108,10 +108,7 @@ impl Metadata<'_> { .messages([ #( #messages ),* ]) - .events( - // collects metadata for all events linked into the contract. - ::ink::metadata::event_specs() - ) + .collect_events() .docs([ #( #docs ),* ]) diff --git a/crates/metadata/src/lib.rs b/crates/metadata/src/lib.rs index dc31802f97..e813f61c25 100644 --- a/crates/metadata/src/lib.rs +++ b/crates/metadata/src/lib.rs @@ -161,14 +161,6 @@ impl InkProject { #[linkme::distributed_slice] pub static EVENTS: [fn() -> EventSpec] = [..]; -/// Load metadata for all events linked into the contract. -pub fn event_specs() -> Vec { - EVENTS - .iter() - .map(|event| event()) - .collect() -} - // todo: docs pub trait EventMetadata { /// Returns the metadata of the event. diff --git a/crates/metadata/src/specs.rs b/crates/metadata/src/specs.rs index 4c40705b15..3163080f70 100644 --- a/crates/metadata/src/specs.rs +++ b/crates/metadata/src/specs.rs @@ -239,6 +239,17 @@ where } } +impl ContractSpecBuilder { + /// Collect metadata for all events linked into the contract. + pub fn collect_events(self) -> Self { + let events = + crate::EVENTS + .iter() + .map(|event| event()); + self.events(events) + } +} + impl ContractSpecBuilder where F: Form, From 1fcf5e2bf53b83a4c75b2c45f6c52cb3f0f85853 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Fri, 5 May 2023 17:20:38 +0100 Subject: [PATCH 019/140] Add metadata test --- crates/ink/macro/src/event/metadata.rs | 47 +++++++++--------- crates/ink/tests/events_metadata.rs | 67 ++++++++++++++++++++++++++ crates/metadata/src/lib.rs | 2 +- crates/metadata/src/specs.rs | 8 +-- 4 files changed, 95 insertions(+), 29 deletions(-) create mode 100644 crates/ink/tests/events_metadata.rs diff --git a/crates/ink/macro/src/event/metadata.rs b/crates/ink/macro/src/event/metadata.rs index 2520cbfb20..fbd6ff442b 100644 --- a/crates/ink/macro/src/event/metadata.rs +++ b/crates/ink/macro/src/event/metadata.rs @@ -40,27 +40,30 @@ fn event_metadata_derive_struct(mut s: synstructure::Structure) -> TokenStream2 let variant = &s.variants()[0]; let ident = variant.ast().ident; - s.bound_impl(quote!(::ink::metadata::EventMetadata), quote! { - fn event_spec() -> ::ink::metadata::EventSpec { - // register this event metadata function in the distributed slice for combining all - // events referenced in the contract binary. - #[::ink::metadata::linkme::distributed_slice(::ink::metadata::EVENTS)] - #[linkme(crate = ::ink::metadata::linkme)] - static EVENT_METADATA: fn() -> ::ink::metadata::EventSpec = - <#ident as ::ink::metadata::EventMetadata>::event_spec; + s.bound_impl( + quote!(::ink::metadata::EventMetadata), + quote! { + fn event_spec() -> ::ink::metadata::EventSpec { + // register this event metadata function in the distributed slice for combining all + // events referenced in the contract binary. + #[::ink::metadata::linkme::distributed_slice(::ink::metadata::EVENTS)] + #[linkme(crate = ::ink::metadata::linkme)] + static EVENT_METADATA: fn() -> ::ink::metadata::EventSpec = + <#ident as ::ink::metadata::EventMetadata>::event_spec; - // todo: check that cfg attributes work here - ::ink::metadata::EventSpec::new(::core::stringify!(#ident)) - // todo: add signanture topic if not anonymous - // todo: add fields, with topic flag. - .args([ - // #( #args ),* - ]) - // todo: add docs - .docs([ - // #( #docs ),* - ]) - .done() - } - }) + // todo: check that cfg attributes work here + ::ink::metadata::EventSpec::new(::core::stringify!(#ident)) + // todo: add signanture topic if not anonymous + // todo: add fields, with topic flag. + .args([ + // #( #args ),* + ]) + // todo: add docs + .docs([ + // #( #docs ),* + ]) + .done() + } + }, + ) } diff --git a/crates/ink/tests/events_metadata.rs b/crates/ink/tests/events_metadata.rs new file mode 100644 index 0000000000..9a1c50808d --- /dev/null +++ b/crates/ink/tests/events_metadata.rs @@ -0,0 +1,67 @@ +// Copyright 2018-2022 Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![cfg_attr(not(feature = "std"), no_std)] + +#[derive(ink::Event, scale::Encode)] +#[cfg_attr(feature = "std", derive(ink::EventMetadata))] +pub struct EventExternal { + f1: bool, + f2: u32, +} + +#[ink::contract] +mod contract { + #[ink(storage)] + pub struct Contract {} + + #[derive(ink::Event, scale::Encode)] + #[cfg_attr(feature = "std", derive(ink::EventMetadata))] + pub struct EventInline { + f3: bool, + f4: u32, + } + + impl Contract { + #[ink(constructor)] + pub fn new() -> Self { + Self {} + } + } + + impl Contract { + #[ink(message)] + pub fn get_value(&self) -> u32 { + 42 + } + } +} + +#[cfg(test)] +mod tests { + fn generate_metadata() -> ink_metadata::InkProject { + extern "Rust" { + fn __ink_generate_metadata() -> ink_metadata::InkProject; + } + + unsafe { __ink_generate_metadata() } + } + + #[test] + fn collects_all_events() { + let metadata = generate_metadata(); + + assert_eq!(metadata.spec().events().len(), 2); + } +} diff --git a/crates/metadata/src/lib.rs b/crates/metadata/src/lib.rs index e813f61c25..16a2b38586 100644 --- a/crates/metadata/src/lib.rs +++ b/crates/metadata/src/lib.rs @@ -53,9 +53,9 @@ pub use self::specs::{ use impl_serde::serialize as serde_hex; -pub use scale_info::TypeInfo; #[doc(hidden)] pub use linkme; +pub use scale_info::TypeInfo; #[cfg(feature = "derive")] use scale_info::{ diff --git a/crates/metadata/src/specs.rs b/crates/metadata/src/specs.rs index 3163080f70..5a7af5b121 100644 --- a/crates/metadata/src/specs.rs +++ b/crates/metadata/src/specs.rs @@ -239,13 +239,10 @@ where } } -impl ContractSpecBuilder { +impl ContractSpecBuilder { /// Collect metadata for all events linked into the contract. pub fn collect_events(self) -> Self { - let events = - crate::EVENTS - .iter() - .map(|event| event()); + let events = crate::EVENTS.iter().map(|event| event()); self.events(events) } } @@ -285,7 +282,6 @@ where { /// Creates a new contract specification. pub fn new() -> ContractSpecBuilder { - ContractSpecBuilder { spec: Self { constructors: Vec::new(), From 5f7f99fb1f383851c0383ff533d0253e656aef87 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Fri, 5 May 2023 17:22:19 +0100 Subject: [PATCH 020/140] Check for external and Inline events --- crates/ink/tests/events_metadata.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/crates/ink/tests/events_metadata.rs b/crates/ink/tests/events_metadata.rs index 9a1c50808d..04d54e7be6 100644 --- a/crates/ink/tests/events_metadata.rs +++ b/crates/ink/tests/events_metadata.rs @@ -63,5 +63,17 @@ mod tests { let metadata = generate_metadata(); assert_eq!(metadata.spec().events().len(), 2); + assert!(metadata + .spec() + .events() + .iter() + .find(|e| e.label() == "EventExternal") + .is_some()); + assert!(metadata + .spec() + .events() + .iter() + .find(|e| e.label() == "EventInline") + .is_some()); } } From 884e82cf729aa4abcc8b82152b65e493c343e18e Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Tue, 9 May 2023 16:45:05 +0100 Subject: [PATCH 021/140] Add todo for inline events --- crates/ink/tests/events_metadata.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/ink/tests/events_metadata.rs b/crates/ink/tests/events_metadata.rs index 04d54e7be6..52679bfb1c 100644 --- a/crates/ink/tests/events_metadata.rs +++ b/crates/ink/tests/events_metadata.rs @@ -28,6 +28,7 @@ mod contract { #[derive(ink::Event, scale::Encode)] #[cfg_attr(feature = "std", derive(ink::EventMetadata))] + // todo: allow the above to be written as `#[ink(event)]` instead pub struct EventInline { f3: bool, f4: u32, From e2658d64ce0c7022a7b2e357662aa6f3c3f51381 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Mon, 22 May 2023 12:21:09 +0100 Subject: [PATCH 022/140] WIP adding compile time topics check to emit_event. --- crates/env/src/api.rs | 4 +-- crates/env/src/backend.rs | 2 +- crates/env/src/engine/off_chain/impls.rs | 4 +-- crates/env/src/engine/on_chain/impls.rs | 4 +-- crates/env/src/topics.rs | 4 ++- crates/ink/codegen/src/generator/events.rs | 34 ++++++++++++++++++++-- crates/ink/macro/src/event/mod.rs | 17 ++++++++++- crates/ink/src/env_access.rs | 12 ++++---- 8 files changed, 64 insertions(+), 17 deletions(-) diff --git a/crates/env/src/api.rs b/crates/env/src/api.rs index c1a9a660ff..88e3974b26 100644 --- a/crates/env/src/api.rs +++ b/crates/env/src/api.rs @@ -175,13 +175,13 @@ where } /// Emits an event with the given event data. -pub fn emit_event(event: Event) +pub fn emit_event(event: Event) where E: Environment, Event: Topics + scale::Encode, { ::on_instance(|instance| { - TypedEnvBackend::emit_event::(instance, event) + TypedEnvBackend::emit_event::(instance, event) }) } diff --git a/crates/env/src/backend.rs b/crates/env/src/backend.rs index fb6b00350c..9bdaba9a0c 100644 --- a/crates/env/src/backend.rs +++ b/crates/env/src/backend.rs @@ -405,7 +405,7 @@ pub trait TypedEnvBackend: EnvBackend { /// # Note /// /// For more details visit: [`emit_event`][`crate::emit_event`] - fn emit_event(&mut self, event: Event) + fn emit_event(&mut self, event: Event) where E: Environment, Event: Topics + scale::Encode; diff --git a/crates/env/src/engine/off_chain/impls.rs b/crates/env/src/engine/off_chain/impls.rs index 5dbdf6a773..5df02e39bb 100644 --- a/crates/env/src/engine/off_chain/impls.rs +++ b/crates/env/src/engine/off_chain/impls.rs @@ -422,13 +422,13 @@ impl TypedEnvBackend for EnvInstance { }) } - fn emit_event(&mut self, event: Event) + fn emit_event(&mut self, event: Event) where E: Environment, Event: Topics + scale::Encode, { let builder = TopicsBuilder::default(); - let enc_topics = event.topics::(builder.into()); + let enc_topics = event.topics::(builder.into()); let enc_data = &scale::Encode::encode(&event)[..]; self.engine.deposit_event(&enc_topics[..], enc_data); } diff --git a/crates/env/src/engine/on_chain/impls.rs b/crates/env/src/engine/on_chain/impls.rs index 69747bba5c..2cf8067ecd 100644 --- a/crates/env/src/engine/on_chain/impls.rs +++ b/crates/env/src/engine/on_chain/impls.rs @@ -392,13 +392,13 @@ impl TypedEnvBackend for EnvInstance { self.get_property_little_endian::(ext::minimum_balance) } - fn emit_event(&mut self, event: Event) + fn emit_event(&mut self, event: Event) where E: Environment, Event: Topics + scale::Encode, { let (mut scope, enc_topics) = - event.topics::(TopicsBuilder::from(self.scoped_buffer()).into()); + event.topics::(TopicsBuilder::from(self.scoped_buffer()).into()); let enc_data = scope.take_encoded(&event); ext::deposit_event(enc_topics, enc_data); } diff --git a/crates/env/src/topics.rs b/crates/env/src/topics.rs index 98cecda4a3..c2164e99e3 100644 --- a/crates/env/src/topics.rs +++ b/crates/env/src/topics.rs @@ -195,12 +195,14 @@ pub trait Topics { /// builder. type RemainingTopics: EventTopicsAmount; + const TOPICS_LEN: usize; + /// The unique signature topic of the event. `None` for anonymous events. /// todo: document how this is calculated const SIGNATURE_TOPIC: Option<[u8; 32]> = None; /// Guides event topic serialization using the given topics builder. - fn topics( + fn topics( &self, builder: TopicsBuilder, ) -> >::Output diff --git a/crates/ink/codegen/src/generator/events.rs b/crates/ink/codegen/src/generator/events.rs index 6eaf9c1087..c7f14ea466 100644 --- a/crates/ink/codegen/src/generator/events.rs +++ b/crates/ink/codegen/src/generator/events.rs @@ -33,13 +33,13 @@ impl_as_ref_for_generator!(Events); impl GenerateCode for Events<'_> { fn generate_code(&self) -> TokenStream2 { - // let emit_event_trait_impl = self.generate_emit_event_trait_impl(); + let emit_event_trait_impl = self.generate_emit_event_trait_impl(); // let event_base = self.generate_event_base(); // let topic_guards = self.generate_topic_guards(); // let topics_impls = self.generate_topics_impls(); let event_structs = self.generate_event_structs(); quote! { - // #emit_event_trait_impl + #emit_event_trait_impl // #event_base // #( #topic_guards )* #( #event_structs )* @@ -49,6 +49,32 @@ impl GenerateCode for Events<'_> { } impl<'a> Events<'a> { + /// todo: comments + fn generate_emit_event_trait_impl(&self) -> TokenStream2 { + quote! { + /// Local trait for emitting events, allowing extending of `EnvAccess` and to include + /// a compile time check ensuring max number of topics not exceeded. + pub trait __ink_EmitEvent { + fn emit_event(self, event: E) + where + E: ::ink::env::Topics + ::scale::Encode; + } + + impl<'a> __ink_EmitEvent for ::ink::EnvAccess<'a, Environment> { + fn emit_event(self, event: E) + where + E: ::ink::env::Topics + ::scale::Encode, + { + ::ink::env::emit_event::< + { ::MAX_EVENT_TOPICS }, + Environment, + E, + >(event); + } + } + } + } + /// Generates the base event enum that comprises all user defined events. /// All emitted events are converted into a variant of this enum before being /// serialized and emitted to apply their unique event discriminant (ID). @@ -110,6 +136,8 @@ impl<'a> Events<'a> { impl ::ink::env::Topics for #base_event_ident { type RemainingTopics = __ink_UndefinedAmountOfTopics; + const TOPICS_LEN: usize = 4; + fn topics( &self, builder: ::ink::env::topics::TopicsBuilder<::ink::env::topics::state::Uninit, E, B>, @@ -225,6 +253,8 @@ impl<'a> Events<'a> { impl ::ink::env::Topics for #event_ident { type RemainingTopics = #remaining_topics_ty; + const TOPICS_LEN: usize = #len_topics + #anonymous_topics_offset; + fn topics( &self, builder: ::ink::env::topics::TopicsBuilder<::ink::env::topics::state::Uninit, E, B>, diff --git a/crates/ink/macro/src/event/mod.rs b/crates/ink/macro/src/event/mod.rs index 48f23e1612..d035af9906 100644 --- a/crates/ink/macro/src/event/mod.rs +++ b/crates/ink/macro/src/event/mod.rs @@ -104,7 +104,11 @@ fn event_derive_struct(mut s: synstructure::Structure) -> TokenStream2 { s.bound_impl(quote!(::ink::env::Topics), quote! { type RemainingTopics = #remaining_topics_ty; - fn topics( + // todo: bring this back... + // const TOPICS_LEN: usize = #len_topics + #anonymous_topics_offset; + const TOPICS_LEN: usize = 5; + + fn topics( &self, builder: ::ink::env::topics::TopicsBuilder<::ink::env::topics::state::Uninit, E, B>, ) -> >::Output @@ -112,6 +116,17 @@ fn event_derive_struct(mut s: synstructure::Structure) -> TokenStream2 { E: ::ink::env::Environment, B: ::ink::env::topics::TopicsBuilderBackend, { + // todo: debug_assert environment and max topics length. + + // todo: move these out of codegen + // todo: or possibly utilize existing RespectTopicLimit trait + pub struct Assert; + impl Assert { + const LESS_EQ: () = assert!(L <= R, "too many topics!"); + } + + let _ = Assert::<{ Self::TOPICS_LEN }, { MAX_TOPICS }>::LESS_EQ; + match self { #topics_builder } diff --git a/crates/ink/src/env_access.rs b/crates/ink/src/env_access.rs index 7d2cea9485..b0ebc8f7fc 100644 --- a/crates/ink/src/env_access.rs +++ b/crates/ink/src/env_access.rs @@ -414,12 +414,12 @@ where } /// Emits an event. - pub fn emit_event(self, event: Evt) - where - Evt: ink_env::Topics + scale::Encode, - { - ink_env::emit_event::(event) - } + // pub fn emit_event(self, event: Evt) + // where + // Evt: ink_env::Topics + scale::Encode, + // { + // ink_env::emit_event::(event) + // } /// Instantiates another contract. /// From a82a6adb4d303efee8d3979f4f1f51490fa6e366 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Mon, 22 May 2023 13:35:09 +0100 Subject: [PATCH 023/140] fmt --- crates/env/src/engine/on_chain/impls.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/env/src/engine/on_chain/impls.rs b/crates/env/src/engine/on_chain/impls.rs index 2cf8067ecd..9987630653 100644 --- a/crates/env/src/engine/on_chain/impls.rs +++ b/crates/env/src/engine/on_chain/impls.rs @@ -397,8 +397,8 @@ impl TypedEnvBackend for EnvInstance { E: Environment, Event: Topics + scale::Encode, { - let (mut scope, enc_topics) = - event.topics::(TopicsBuilder::from(self.scoped_buffer()).into()); + let (mut scope, enc_topics) = event + .topics::(TopicsBuilder::from(self.scoped_buffer()).into()); let enc_data = scope.take_encoded(&event); ext::deposit_event(enc_topics, enc_data); } From b084d610f222cdd59e852a6e5f7be4a334b1408a Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Mon, 22 May 2023 16:07:45 +0100 Subject: [PATCH 024/140] Refactor EventRespectsTopicsLimit to use const assertion --- crates/ink/macro/src/event/mod.rs | 25 +++----- crates/ink/src/codegen/event.rs | 47 ++++++++++++++ crates/ink/src/codegen/event/mod.rs | 22 ------- crates/ink/src/codegen/event/topics.rs | 88 -------------------------- crates/ink/src/codegen/mod.rs | 7 +- 5 files changed, 57 insertions(+), 132 deletions(-) create mode 100644 crates/ink/src/codegen/event.rs delete mode 100644 crates/ink/src/codegen/event/mod.rs delete mode 100644 crates/ink/src/codegen/event/topics.rs diff --git a/crates/ink/macro/src/event/mod.rs b/crates/ink/macro/src/event/mod.rs index d035af9906..746e4e0c76 100644 --- a/crates/ink/macro/src/event/mod.rs +++ b/crates/ink/macro/src/event/mod.rs @@ -65,15 +65,17 @@ fn event_derive_struct(mut s: synstructure::Structure) -> TokenStream2 { let variant = &s.variants()[0]; - let len_topics = variant.bindings().len(); // Anonymous events require 1 fewer topics since they do not include their signature. let anonymous_topics_offset = usize::from(!anonymous); - let remaining_topics_ty = match len_topics + anonymous_topics_offset { + let len_topics = variant.bindings().len() + anonymous_topics_offset; + + let remaining_topics_ty = match len_topics { 0 => quote_spanned!(span=> ::ink::env::topics::state::NoRemainingTopics), n => quote_spanned!(span=> [::ink::env::topics::state::HasRemainingTopics; #n]), }; - let signature_topic = signature_topic(variant.ast().fields, &variant.ast().ident); + let event_ident = variant.ast().ident; + let signature_topic = signature_topic(variant.ast().fields, event_ident); let event_signature_topic = if anonymous { None } else { @@ -104,9 +106,7 @@ fn event_derive_struct(mut s: synstructure::Structure) -> TokenStream2 { s.bound_impl(quote!(::ink::env::Topics), quote! { type RemainingTopics = #remaining_topics_ty; - // todo: bring this back... - // const TOPICS_LEN: usize = #len_topics + #anonymous_topics_offset; - const TOPICS_LEN: usize = 5; + const TOPICS_LEN: usize = #len_topics; fn topics( &self, @@ -116,16 +116,9 @@ fn event_derive_struct(mut s: synstructure::Structure) -> TokenStream2 { E: ::ink::env::Environment, B: ::ink::env::topics::TopicsBuilderBackend, { - // todo: debug_assert environment and max topics length. - - // todo: move these out of codegen - // todo: or possibly utilize existing RespectTopicLimit trait - pub struct Assert; - impl Assert { - const LESS_EQ: () = assert!(L <= R, "too many topics!"); - } - - let _ = Assert::<{ Self::TOPICS_LEN }, { MAX_TOPICS }>::LESS_EQ; + // assert at compile time the number of topics defined by this event is within the + // given limit. + let _ = ::ink::codegen::EventRespectsTopicLimit::<{ Self::TOPICS_LEN }, { MAX_TOPICS }>::ASSERT; match self { #topics_builder diff --git a/crates/ink/src/codegen/event.rs b/crates/ink/src/codegen/event.rs new file mode 100644 index 0000000000..ec2e837343 --- /dev/null +++ b/crates/ink/src/codegen/event.rs @@ -0,0 +1,47 @@ +// Copyright 2018-2022 Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/// Guards that an ink! event definitions respects the topic limit. +/// todo: update docs +/// +/// # Usage +/// +/// ``` +/// // #[ink(event)] +/// pub struct ExampleEvent {} +/// +/// /// The amount of the topics of the example event struct. +/// const LEN_TOPICS: usize = 3; +/// +/// /// The limit for the amount of topics per ink! event definition. +/// const TOPICS_LIMIT: usize = 4; +/// +/// impl ::ink::codegen::EventLenTopics for ExampleEvent { +/// type LenTopics = ::ink::codegen::EventTopics; +/// } +/// +/// // The below code only compiles successfully if the example ink! event +/// // definitions respects the topic limitation: it must have an amount of +/// // topics less than or equal to the topic limit. +/// const _: () = ::ink::codegen::utils::consume_type::< +/// ::ink::codegen::EventRespectsTopicLimit, +/// >(); +/// ``` +pub struct EventRespectsTopicLimit; + +impl EventRespectsTopicLimit< + LEN_EVENT_TOPICS, + LEN_MAX_TOPICS> { + pub const ASSERT: () = assert!(LEN_EVENT_TOPICS <= LEN_MAX_TOPICS, "The event definition exceeded the maximum number of topics."); +} diff --git a/crates/ink/src/codegen/event/mod.rs b/crates/ink/src/codegen/event/mod.rs deleted file mode 100644 index d2c2bac908..0000000000 --- a/crates/ink/src/codegen/event/mod.rs +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2018-2022 Parity Technologies (UK) Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -mod topics; - -pub use self::topics::{ - EventLenTopics, - EventRespectsTopicLimit, - EventTopics, - RespectTopicLimit, -}; diff --git a/crates/ink/src/codegen/event/topics.rs b/crates/ink/src/codegen/event/topics.rs deleted file mode 100644 index b360c8a75d..0000000000 --- a/crates/ink/src/codegen/event/topics.rs +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2018-2022 Parity Technologies (UK) Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use core::marker::PhantomData; - -/// Guards that an ink! event definitions respects the topic limit. -/// -/// # Usage -/// -/// ``` -/// // #[ink(event)] -/// pub struct ExampleEvent {} -/// -/// /// The amount of the topics of the example event struct. -/// const LEN_TOPICS: usize = 3; -/// -/// /// The limit for the amount of topics per ink! event definition. -/// const TOPICS_LIMIT: usize = 4; -/// -/// impl ::ink::codegen::EventLenTopics for ExampleEvent { -/// type LenTopics = ::ink::codegen::EventTopics; -/// } -/// -/// // The below code only compiles successfully if the example ink! event -/// // definitions respects the topic limitation: it must have an amount of -/// // topics less than or equal to the topic limit. -/// const _: () = ::ink::codegen::utils::consume_type::< -/// ::ink::codegen::EventRespectsTopicLimit, -/// >(); -/// ``` -pub struct EventRespectsTopicLimit -where - Event: EventLenTopics, - ::LenTopics: RespectTopicLimit, -{ - marker: PhantomData Event>, -} - -/// Guards that an amount of event topics respects the event topic limit. -/// -/// # Note -/// -/// Implemented by `EventTopics` if M is less or equal to N. -/// Automatically implemented for up to 12 event topics. -pub trait RespectTopicLimit {} - -/// Represents an the amount of topics for an ink! event definition. -pub struct EventTopics; - -macro_rules! impl_is_smaller_or_equals { - ( $first:literal $( , $rest:literal )* $(,)? ) => { - impl RespectTopicLimit<$first> for EventTopics<$first> {} - $( - impl RespectTopicLimit<$rest> for EventTopics<$first> {} - )* - - impl_is_smaller_or_equals! { $( $rest ),* } - }; - ( ) => {}; -} -impl_is_smaller_or_equals! { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 -} - -/// Stores the number of event topics of the ink! event definition. -pub trait EventLenTopics { - /// Type denoting the number of event topics. - /// - /// # Note - /// - /// We use an associated type instead of an associated constant here - /// because Rust does not yet allow for generics in constant parameter - /// position which would be required in the `EventRespectsTopicLimit` - /// trait definition. - /// As soon as this is possible in Rust we might change this to a constant. - type LenTopics; -} diff --git a/crates/ink/src/codegen/mod.rs b/crates/ink/src/codegen/mod.rs index 7fcbe98366..5013945ce4 100644 --- a/crates/ink/src/codegen/mod.rs +++ b/crates/ink/src/codegen/mod.rs @@ -32,12 +32,7 @@ pub use self::{ Env, StaticEnv, }, - event::{ - EventLenTopics, - EventRespectsTopicLimit, - EventTopics, - RespectTopicLimit, - }, + event::EventRespectsTopicLimit, implies_return::ImpliesReturn, trait_def::{ TraitCallBuilder, From 6c4ad8eb42ee2a8209aadb8ac055f4272f655e1b Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Mon, 22 May 2023 16:08:00 +0100 Subject: [PATCH 025/140] Fmt --- crates/ink/src/codegen/event.rs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/crates/ink/src/codegen/event.rs b/crates/ink/src/codegen/event.rs index ec2e837343..4b19b0dcec 100644 --- a/crates/ink/src/codegen/event.rs +++ b/crates/ink/src/codegen/event.rs @@ -38,10 +38,16 @@ /// ::ink::codegen::EventRespectsTopicLimit, /// >(); /// ``` -pub struct EventRespectsTopicLimit; +pub struct EventRespectsTopicLimit< + const LEN_EVENT_TOPICS: usize, + const LEN_MAX_TOPICS: usize, +>; -impl EventRespectsTopicLimit< - LEN_EVENT_TOPICS, - LEN_MAX_TOPICS> { - pub const ASSERT: () = assert!(LEN_EVENT_TOPICS <= LEN_MAX_TOPICS, "The event definition exceeded the maximum number of topics."); +impl + EventRespectsTopicLimit +{ + pub const ASSERT: () = assert!( + LEN_EVENT_TOPICS <= LEN_MAX_TOPICS, + "The event definition exceeded the maximum number of topics." + ); } From 07d9b85691a8e4e5df41180cecfe65f3b5b32e23 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Mon, 22 May 2023 16:22:38 +0100 Subject: [PATCH 026/140] Remove unused code from legacy events generation --- crates/ink/codegen/src/generator/events.rs | 203 --------------------- 1 file changed, 203 deletions(-) diff --git a/crates/ink/codegen/src/generator/events.rs b/crates/ink/codegen/src/generator/events.rs index c7f14ea466..90a6938701 100644 --- a/crates/ink/codegen/src/generator/events.rs +++ b/crates/ink/codegen/src/generator/events.rs @@ -15,7 +15,6 @@ use crate::GenerateCode; use derive_more::From; use proc_macro2::{ - Span, TokenStream as TokenStream2, }; use quote::{ @@ -75,208 +74,6 @@ impl<'a> Events<'a> { } } - /// Generates the base event enum that comprises all user defined events. - /// All emitted events are converted into a variant of this enum before being - /// serialized and emitted to apply their unique event discriminant (ID). - /// - /// # Developer Note - /// - /// The `__ink_dylint_EventBase` config attribute is used here to convey the - /// information that the generated enum is an ink! event to `dylint`. - fn generate_event_base(&self) -> TokenStream2 { - let storage_ident = &self.contract.module().storage().ident(); - let event_idents = self - .contract - .module() - .events() - .map(|event| event.ident()) - .collect::>(); - let event_idents_cfgs = self - .contract - .module() - .events() - .map(|event| event.get_cfg_attrs(event.span())) - .collect::>(); - let base_event_ident = - proc_macro2::Ident::new("__ink_EventBase", Span::call_site()); - quote! { - #[allow(non_camel_case_types)] - #[derive(::scale::Encode, ::scale::Decode)] - #[cfg(not(feature = "__ink_dylint_EventBase"))] - pub enum #base_event_ident { - #( - #( #event_idents_cfgs )* - #event_idents(#event_idents), - )* - } - - const _: () = { - impl ::ink::reflect::ContractEventBase for #storage_ident { - type Type = #base_event_ident; - } - }; - - #( - #( #event_idents_cfgs )* - const _: () = { - impl From<#event_idents> for #base_event_ident { - fn from(event: #event_idents) -> Self { - Self::#event_idents(event) - } - } - }; - )* - - const _: () = { - pub enum __ink_UndefinedAmountOfTopics {} - impl ::ink::env::topics::EventTopicsAmount for __ink_UndefinedAmountOfTopics { - const AMOUNT: usize = 0; - } - - impl ::ink::env::Topics for #base_event_ident { - type RemainingTopics = __ink_UndefinedAmountOfTopics; - - const TOPICS_LEN: usize = 4; - - fn topics( - &self, - builder: ::ink::env::topics::TopicsBuilder<::ink::env::topics::state::Uninit, E, B>, - ) -> >::Output - where - E: ::ink::env::Environment, - B: ::ink::env::topics::TopicsBuilderBackend, - { - match self { - #( - #( #event_idents_cfgs )* - Self::#event_idents(event) => { - <#event_idents as ::ink::env::Topics>::topics::(event, builder) - } - )*, - _ => panic!("Event does not exist!") - } - } - } - }; - } - } - - /// Generate checks to guard against too many topics in event definitions. - fn generate_topics_guard(&self, event: &ir::Event) -> TokenStream2 { - let span = event.span(); - let storage_ident = self.contract.module().storage().ident(); - let event_ident = event.ident(); - let cfg_attrs = event.get_cfg_attrs(span); - let len_topics = event.fields().filter(|event| event.is_topic).count(); - let max_len_topics = quote_spanned!(span=> - <<#storage_ident as ::ink::env::ContractEnv>::Env - as ::ink::env::Environment>::MAX_EVENT_TOPICS - ); - quote_spanned!(span=> - #( #cfg_attrs )* - impl ::ink::codegen::EventLenTopics for #event_ident { - type LenTopics = ::ink::codegen::EventTopics<#len_topics>; - } - - #( #cfg_attrs )* - const _: () = ::ink::codegen::utils::consume_type::< - ::ink::codegen::EventRespectsTopicLimit< - #event_ident, - { #max_len_topics }, - > - >(); - ) - } - - /// Generates the guard code that protects against having too many topics defined on - /// an ink! event. - fn generate_topic_guards(&'a self) -> impl Iterator + 'a { - self.contract.module().events().map(move |event| { - let span = event.span(); - let topics_guard = self.generate_topics_guard(event); - quote_spanned!(span => - #topics_guard - ) - }) - } - - /// Generates the `Topics` trait implementations for the user defined events. - fn generate_topics_impls(&'a self) -> impl Iterator + 'a { - let contract_ident = self.contract.module().storage().ident(); - self.contract.module().events().map(move |event| { - let span = event.span(); - let event_ident = event.ident(); - let event_signature = syn::LitByteStr::new( - format!("{contract_ident}::{event_ident}" - ).as_bytes(), span); - let len_event_signature = event_signature.value().len(); - let len_topics = event.fields().filter(|field| field.is_topic).count(); - let topic_impls = event - .fields() - .enumerate() - .filter(|(_, field)| field.is_topic) - .map(|(n, topic_field)| { - let span = topic_field.span(); - let field_ident = topic_field - .ident() - .map(quote::ToTokens::into_token_stream) - .unwrap_or_else(|| quote_spanned!(span => #n)); - let field_type = topic_field.ty(); - let signature = syn::LitByteStr::new( - format!("{contract_ident}::{event_ident}::{field_ident}" - ).as_bytes(), span); - quote_spanned!(span => - .push_topic::<::ink::env::topics::PrefixedValue<#field_type>>( - &::ink::env::topics::PrefixedValue { value: &self.#field_ident, prefix: #signature } - ) - ) - }); - // Only include topic for event signature in case of non-anonymous event. - let event_signature_topic = match event.anonymous { - true => None, - false => Some(quote_spanned!(span=> - .push_topic::<::ink::env::topics::PrefixedValue<[u8; #len_event_signature]>>( - &::ink::env::topics::PrefixedValue { value: #event_signature, prefix: b"" } - ) - )) - }; - // Anonymous events require 1 fewer topics since they do not include their signature. - let anonymous_topics_offset = usize::from(!event.anonymous); - let remaining_topics_ty = match len_topics + anonymous_topics_offset { - 0 => quote_spanned!(span=> ::ink::env::topics::state::NoRemainingTopics), - n => quote_spanned!(span=> [::ink::env::topics::state::HasRemainingTopics; #n]), - }; - let cfg_attrs = event.get_cfg_attrs(span); - quote_spanned!(span => - #( #cfg_attrs )* - const _: () = { - impl ::ink::env::Topics for #event_ident { - type RemainingTopics = #remaining_topics_ty; - - const TOPICS_LEN: usize = #len_topics + #anonymous_topics_offset; - - fn topics( - &self, - builder: ::ink::env::topics::TopicsBuilder<::ink::env::topics::state::Uninit, E, B>, - ) -> >::Output - where - E: ::ink::env::Environment, - B: ::ink::env::topics::TopicsBuilderBackend, - { - builder - .build::() - #event_signature_topic - #( - #topic_impls - )* - .finish() - } - } - }; - ) - }) - } - /// Generates all the user defined event struct definitions. fn generate_event_structs(&'a self) -> impl Iterator + 'a { self.contract.module().events().map(move |event| { From b7c7199048724d29b7e278439901799f173f469a Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Mon, 22 May 2023 16:37:23 +0100 Subject: [PATCH 027/140] test and generate SIGNATURE topic --- crates/env/src/topics.rs | 2 +- crates/ink/macro/src/event/mod.rs | 8 +++++++- crates/ink/macro/src/tests/event.rs | 7 ++++++- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/crates/env/src/topics.rs b/crates/env/src/topics.rs index c2164e99e3..b2f3eb1be9 100644 --- a/crates/env/src/topics.rs +++ b/crates/env/src/topics.rs @@ -199,7 +199,7 @@ pub trait Topics { /// The unique signature topic of the event. `None` for anonymous events. /// todo: document how this is calculated - const SIGNATURE_TOPIC: Option<[u8; 32]> = None; + const SIGNATURE_TOPIC: Option<[u8; 32]>; /// Guides event topic serialization using the given topics builder. fn topics( diff --git a/crates/ink/macro/src/event/mod.rs b/crates/ink/macro/src/event/mod.rs index 746e4e0c76..5fd9668300 100644 --- a/crates/ink/macro/src/event/mod.rs +++ b/crates/ink/macro/src/event/mod.rs @@ -75,7 +75,12 @@ fn event_derive_struct(mut s: synstructure::Structure) -> TokenStream2 { }; let event_ident = variant.ast().ident; - let signature_topic = signature_topic(variant.ast().fields, event_ident); + let signature_topic = if !anonymous { + let topic_bytes = signature_topic(variant.ast().fields, event_ident); + quote_spanned!(span=> ::core::option::Option::Some([ #( #topic_bytes ),* ])) + } else { + quote_spanned!(span=> None) + }; let event_signature_topic = if anonymous { None } else { @@ -107,6 +112,7 @@ fn event_derive_struct(mut s: synstructure::Structure) -> TokenStream2 { type RemainingTopics = #remaining_topics_ty; const TOPICS_LEN: usize = #len_topics; + const SIGNATURE_TOPIC: ::core::option::Option<[::core::primitive::u8; 32]> = #signature_topic; fn topics( &self, diff --git a/crates/ink/macro/src/tests/event.rs b/crates/ink/macro/src/tests/event.rs index 073deb61b9..d1c7c9d91a 100644 --- a/crates/ink/macro/src/tests/event.rs +++ b/crates/ink/macro/src/tests/event.rs @@ -33,7 +33,10 @@ fn unit_struct_works() { impl ::ink::env::Topics for UnitStruct { type RemainingTopics = [::ink::env::topics::state::HasRemainingTopics; 1usize]; - fn topics( + const TOPICS_LEN: usize = 1usize; + const SIGNATURE_TOPIC: ::core::option::Option<[::core::primitive::u8; 32]> = None; + + fn topics( &self, builder: ::ink::env::topics::TopicsBuilder<::ink::env::topics::state::Uninit, E, B>, ) -> >::Output @@ -41,6 +44,8 @@ fn unit_struct_works() { E: ::ink::env::Environment, B: ::ink::env::topics::TopicsBuilderBackend, { + let _ = ::ink::codegen::EventRespectsTopicLimit::<{ Self::TOPICS_LEN }, { MAX_TOPICS }>::ASSERT; + match self { UnitStruct => { builder From 7258197d67a22f3131cb1a8d353008ffc0c9e64b Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Tue, 23 May 2023 17:31:26 +0100 Subject: [PATCH 028/140] use built in ink macro for generating signature topic --- crates/ink/codegen/src/generator/events.rs | 4 +--- crates/ink/macro/src/event/mod.rs | 14 ++++---------- crates/ink/macro/src/tests/event.rs | 3 ++- 3 files changed, 7 insertions(+), 14 deletions(-) diff --git a/crates/ink/codegen/src/generator/events.rs b/crates/ink/codegen/src/generator/events.rs index 90a6938701..bc5301181f 100644 --- a/crates/ink/codegen/src/generator/events.rs +++ b/crates/ink/codegen/src/generator/events.rs @@ -14,9 +14,7 @@ use crate::GenerateCode; use derive_more::From; -use proc_macro2::{ - TokenStream as TokenStream2, -}; +use proc_macro2::TokenStream as TokenStream2; use quote::{ quote, quote_spanned, diff --git a/crates/ink/macro/src/event/mod.rs b/crates/ink/macro/src/event/mod.rs index 5fd9668300..a4b365eeb0 100644 --- a/crates/ink/macro/src/event/mod.rs +++ b/crates/ink/macro/src/event/mod.rs @@ -76,8 +76,8 @@ fn event_derive_struct(mut s: synstructure::Structure) -> TokenStream2 { let event_ident = variant.ast().ident; let signature_topic = if !anonymous { - let topic_bytes = signature_topic(variant.ast().fields, event_ident); - quote_spanned!(span=> ::core::option::Option::Some([ #( #topic_bytes ),* ])) + let topic_str = signature_topic(variant.ast().fields, event_ident); + quote_spanned!(span=> ::core::option::Option::Some(::ink::blake2x256!(#topic_str))) } else { quote_spanned!(span=> None) }; @@ -136,10 +136,7 @@ fn event_derive_struct(mut s: synstructure::Structure) -> TokenStream2 { /// The signature topic of an event variant. /// /// Calculated with `blake2b("Event(field1_type,field2_type)")`. -pub fn signature_topic( - fields: &syn::Fields, - event_ident: &syn::Ident, -) -> [syn::LitInt; 32] { +pub fn signature_topic(fields: &syn::Fields, event_ident: &syn::Ident) -> syn::LitStr { let fields = fields .iter() .map(|field| { @@ -150,8 +147,5 @@ pub fn signature_topic( .collect::>() .join(","); let topic_str = format!("{}({fields})", event_ident); - let input = topic_str.as_bytes(); - let mut output = [0; 32]; - ink_ir::blake2b_256(&input, &mut output); - output.map(::hex_padded_suffixed) + syn::parse_quote!( #topic_str ) } diff --git a/crates/ink/macro/src/tests/event.rs b/crates/ink/macro/src/tests/event.rs index d1c7c9d91a..90b136b13d 100644 --- a/crates/ink/macro/src/tests/event.rs +++ b/crates/ink/macro/src/tests/event.rs @@ -34,7 +34,8 @@ fn unit_struct_works() { type RemainingTopics = [::ink::env::topics::state::HasRemainingTopics; 1usize]; const TOPICS_LEN: usize = 1usize; - const SIGNATURE_TOPIC: ::core::option::Option<[::core::primitive::u8; 32]> = None; + const SIGNATURE_TOPIC: ::core::option::Option<[::core::primitive::u8; 32]> = + ::core::option::Option::Some( ::ink::blake2x256!("UnitStruct()") ); fn topics( &self, From 76a176f849168bf4524378c54db86201d7d3b2d4 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Tue, 23 May 2023 18:03:52 +0100 Subject: [PATCH 029/140] Remove all warnings --- crates/ink/codegen/src/generator/metadata.rs | 45 -------------------- crates/ink/macro/src/event/metadata.rs | 13 +++--- 2 files changed, 5 insertions(+), 53 deletions(-) diff --git a/crates/ink/codegen/src/generator/metadata.rs b/crates/ink/codegen/src/generator/metadata.rs index 3d5965ba0b..edd44d1a79 100644 --- a/crates/ink/codegen/src/generator/metadata.rs +++ b/crates/ink/codegen/src/generator/metadata.rs @@ -372,51 +372,6 @@ impl Metadata<'_> { ) } - /// Generates ink! metadata for all user provided ink! event definitions. - fn generate_events(&self) -> impl Iterator + '_ { - self.contract.module().events().map(|event| { - let span = event.span(); - let ident = event.ident(); - let docs = event.attrs().iter().filter_map(|attr| attr.extract_docs()); - let args = Self::generate_event_args(event); - let cfg_attrs = event.get_cfg_attrs(span); - quote_spanned!(span => - #( #cfg_attrs )* - ::ink::metadata::EventSpec::new(::core::stringify!(#ident)) - .args([ - #( #args ),* - ]) - .docs([ - #( #docs ),* - ]) - .done() - ) - }) - } - - /// Generate ink! metadata for a single argument of an ink! event definition. - fn generate_event_args(event: &ir::Event) -> impl Iterator + '_ { - event.fields().map(|event_field| { - let span = event_field.span(); - let ident = event_field.ident(); - let is_topic = event_field.is_topic; - let docs = event_field - .attrs() - .into_iter() - .filter_map(|attr| attr.extract_docs()); - let ty = Self::generate_type_spec(event_field.ty()); - quote_spanned!(span => - ::ink::metadata::EventParamSpec::new(::core::stringify!(#ident)) - .of_type(#ty) - .indexed(#is_topic) - .docs([ - #( #docs ),* - ]) - .done() - ) - }) - } - fn generate_environment(&self) -> TokenStream2 { let span = self.contract.module().span(); diff --git a/crates/ink/macro/src/event/metadata.rs b/crates/ink/macro/src/event/metadata.rs index fbd6ff442b..f8865105a5 100644 --- a/crates/ink/macro/src/event/metadata.rs +++ b/crates/ink/macro/src/event/metadata.rs @@ -13,10 +13,7 @@ // limitations under the License. use proc_macro2::TokenStream as TokenStream2; -use quote::{ - quote, - quote_spanned, -}; +use quote::quote_spanned; use syn::spanned::Spanned; /// Derives the `ink::Event` trait for the given `struct`. @@ -33,7 +30,7 @@ pub fn event_metadata_derive(mut s: synstructure::Structure) -> TokenStream2 { } /// `Event` derive implementation for `struct` types. -fn event_metadata_derive_struct(mut s: synstructure::Structure) -> TokenStream2 { +fn event_metadata_derive_struct(s: synstructure::Structure) -> TokenStream2 { assert_eq!(s.variants().len(), 1, "can only operate on structs"); let span = s.ast().span(); @@ -41,8 +38,8 @@ fn event_metadata_derive_struct(mut s: synstructure::Structure) -> TokenStream2 let ident = variant.ast().ident; s.bound_impl( - quote!(::ink::metadata::EventMetadata), - quote! { + quote_spanned!(span=> ::ink::metadata::EventMetadata), + quote_spanned!(span=> fn event_spec() -> ::ink::metadata::EventSpec { // register this event metadata function in the distributed slice for combining all // events referenced in the contract binary. @@ -64,6 +61,6 @@ fn event_metadata_derive_struct(mut s: synstructure::Structure) -> TokenStream2 ]) .done() } - }, + ), ) } From b3b258718115b483f8040d3434993fcd11cdab4f Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Thu, 25 May 2023 10:48:36 +0100 Subject: [PATCH 030/140] Implement anonymous event --- crates/ink/macro/src/event/mod.rs | 63 +++++++++++++++++++++++++---- crates/ink/macro/src/lib.rs | 2 +- crates/ink/macro/src/tests/event.rs | 40 ++++++++++++++++++ 3 files changed, 97 insertions(+), 8 deletions(-) diff --git a/crates/ink/macro/src/event/mod.rs b/crates/ink/macro/src/event/mod.rs index a4b365eeb0..f119780c75 100644 --- a/crates/ink/macro/src/event/mod.rs +++ b/crates/ink/macro/src/event/mod.rs @@ -29,19 +29,68 @@ pub fn event_derive(mut s: synstructure::Structure) -> TokenStream2 { .add_bounds(synstructure::AddBounds::Fields) .underscore_const(true); match &s.ast().data { - syn::Data::Struct(_) => event_derive_struct(s), + syn::Data::Struct(_) => { + event_derive_struct(s).unwrap_or_else(|err| err.to_compile_error()) + } _ => { - panic!("can only derive `Event` for Rust `struct` items") + syn::Error::new( + s.ast().span(), + "can only derive `Event` for Rust `struct` items", + ) + .to_compile_error() } } } /// `Event` derive implementation for `struct` types. -fn event_derive_struct(mut s: synstructure::Structure) -> TokenStream2 { +fn event_derive_struct(mut s: synstructure::Structure) -> syn::Result { assert_eq!(s.variants().len(), 1, "can only operate on structs"); let span = s.ast().span(); - let anonymous = false; // todo read from struct attribute e.g. #[event(anonymous)] + let ink_attrs = s + .ast() + .attrs + .iter() + .filter_map(|attr| { + if attr.path().is_ident("ink") { + Some(attr) + } else { + None + } + }) + .collect::>(); + + // todo: check for duplicates + // todo: check only anonymous allowed on outer. + + let anonymous_attrs: Vec<_> = ink_attrs + .iter() + .map(|attr| { + attr.parse_nested_meta(|meta| { + if meta.path.is_ident("anonymous") { + Ok(()) + } else { + Err(meta.error("Only `#[ink(anonymous)]` attribute is allowed.")) + } + }) + }) + .collect::>()?; + + // if anonymous_attrs.len() != ink_attrs.len() { + // return syn::Error::new( + // span, + // "Only `#[ink(anonymous)]` attribute is allowed.", + // ).to_compile_error() + // } + // + // if anonymous_attrs.len() > 1 { + // return syn::Error::new( + // span, + // "Only one `#[ink(anonymous)]` attribute is allowed.", + // ).to_compile_error() + // } + + let anonymous = !anonymous_attrs.is_empty(); // let decode_body = variant.construct(|field, _index| { // let ty = &field.ty; @@ -79,7 +128,7 @@ fn event_derive_struct(mut s: synstructure::Structure) -> TokenStream2 { let topic_str = signature_topic(variant.ast().fields, event_ident); quote_spanned!(span=> ::core::option::Option::Some(::ink::blake2x256!(#topic_str))) } else { - quote_spanned!(span=> None) + quote_spanned!(span=> ::core::option::Option::None) }; let event_signature_topic = if anonymous { None @@ -108,7 +157,7 @@ fn event_derive_struct(mut s: synstructure::Structure) -> TokenStream2 { } ); - s.bound_impl(quote!(::ink::env::Topics), quote! { + Ok(s.bound_impl(quote!(::ink::env::Topics), quote! { type RemainingTopics = #remaining_topics_ty; const TOPICS_LEN: usize = #len_topics; @@ -130,7 +179,7 @@ fn event_derive_struct(mut s: synstructure::Structure) -> TokenStream2 { #topics_builder } } - }) + })) } /// The signature topic of an event variant. diff --git a/crates/ink/macro/src/lib.rs b/crates/ink/macro/src/lib.rs index 406dd36e92..4acdedd68d 100644 --- a/crates/ink/macro/src/lib.rs +++ b/crates/ink/macro/src/lib.rs @@ -1284,7 +1284,7 @@ pub fn chain_extension(attr: TokenStream, item: TokenStream) -> TokenStream { } synstructure::decl_derive!( - [Event] => + [Event, attributes(ink)] => /// todo event::event_derive ); diff --git a/crates/ink/macro/src/tests/event.rs b/crates/ink/macro/src/tests/event.rs index 90b136b13d..c903f837dc 100644 --- a/crates/ink/macro/src/tests/event.rs +++ b/crates/ink/macro/src/tests/event.rs @@ -61,3 +61,43 @@ fn unit_struct_works() { } } } + +#[test] +fn unit_struct_anonymous_has_no_topics() { + crate::test_derive! { + event_derive { + #[ink(anonymous)] + struct UnitStruct; + } + expands to { + const _: () = { + impl ::ink::env::Topics for UnitStruct { + type RemainingTopics = ::ink::env::topics::state::NoRemainingTopics; + + const TOPICS_LEN: usize = 0usize; + const SIGNATURE_TOPIC: ::core::option::Option<[::core::primitive::u8; 32]> = + ::core::option::Option::None; + + fn topics( + &self, + builder: ::ink::env::topics::TopicsBuilder<::ink::env::topics::state::Uninit, E, B>, + ) -> >::Output + where + E: ::ink::env::Environment, + B: ::ink::env::topics::TopicsBuilderBackend, + { + let _ = ::ink::codegen::EventRespectsTopicLimit::<{ Self::TOPICS_LEN }, { MAX_TOPICS }>::ASSERT; + + match self { + UnitStruct => { + builder + .build::() + .finish() + } + } + } + } + }; + } no_build + } +} From d0d917b76e4ca68809b5b27988237f3797026d42 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Thu, 25 May 2023 11:49:08 +0100 Subject: [PATCH 031/140] Remove PrefixedValue --- crates/env/src/topics.rs | 33 --------------------------------- 1 file changed, 33 deletions(-) diff --git a/crates/env/src/topics.rs b/crates/env/src/topics.rs index b2f3eb1be9..9d93b27d84 100644 --- a/crates/env/src/topics.rs +++ b/crates/env/src/topics.rs @@ -210,36 +210,3 @@ pub trait Topics { E: Environment, B: TopicsBuilderBackend; } - -/// For each topic a hash is generated. This hash must be unique -/// for a field and its value. The `prefix` is concatenated -/// with the `value`. This result is then hashed. -/// The `prefix` is typically set to the path a field has in -/// an event struct plus the identifier of the event struct. -/// -/// For example, in the case of our ERC-20 example contract the -/// prefix `Erc20::Transfer::from` is concatenated with the -/// field value of `from` and then hashed. -/// In this example `Erc20` would be the contract identified, -/// `Transfer` the event identifier, and `from` the field identifier. -#[doc(hidden)] -pub struct PrefixedValue<'a, 'b, T> { - pub prefix: &'a [u8], - pub value: &'b T, -} - -impl scale::Encode for PrefixedValue<'_, '_, X> -where - X: scale::Encode, -{ - #[inline] - fn size_hint(&self) -> usize { - self.prefix.size_hint() + self.value.size_hint() - } - - #[inline] - fn encode_to(&self, dest: &mut T) { - self.prefix.encode_to(dest); - self.value.encode_to(dest); - } -} From 951543a07a319487c2717298b0c57a34f7769c8b Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Thu, 25 May 2023 14:43:07 +0100 Subject: [PATCH 032/140] Implement field topics --- crates/ink/macro/src/event/mod.rs | 91 +++++++++++++--------------- crates/ink/macro/src/tests/event.rs | 92 +++++++++++++++++++++++++++++ 2 files changed, 133 insertions(+), 50 deletions(-) diff --git a/crates/ink/macro/src/event/mod.rs b/crates/ink/macro/src/event/mod.rs index f119780c75..432013e364 100644 --- a/crates/ink/macro/src/event/mod.rs +++ b/crates/ink/macro/src/event/mod.rs @@ -47,50 +47,7 @@ fn event_derive_struct(mut s: synstructure::Structure) -> syn::Result>(); - - // todo: check for duplicates - // todo: check only anonymous allowed on outer. - - let anonymous_attrs: Vec<_> = ink_attrs - .iter() - .map(|attr| { - attr.parse_nested_meta(|meta| { - if meta.path.is_ident("anonymous") { - Ok(()) - } else { - Err(meta.error("Only `#[ink(anonymous)]` attribute is allowed.")) - } - }) - }) - .collect::>()?; - - // if anonymous_attrs.len() != ink_attrs.len() { - // return syn::Error::new( - // span, - // "Only `#[ink(anonymous)]` attribute is allowed.", - // ).to_compile_error() - // } - // - // if anonymous_attrs.len() > 1 { - // return syn::Error::new( - // span, - // "Only one `#[ink(anonymous)]` attribute is allowed.", - // ).to_compile_error() - // } - - let anonymous = !anonymous_attrs.is_empty(); + let anonymous = has_ink_attribute(&s.ast().attrs, "anonymous")?; // let decode_body = variant.construct(|field, _index| { // let ty = &field.ty; @@ -105,12 +62,23 @@ fn event_derive_struct(mut s: synstructure::Structure) -> syn::Result = None; s.variants_mut()[0].filter(|bi| { - bi.ast() - .attrs - .iter() - .any(|attr| attr.path().is_ident("topic")) + match has_ink_attribute(&bi.ast().attrs, "topic") { + Ok(has_attr) => has_attr, + Err(err) => { + match topic_err { + Some(ref mut topic_err) => topic_err.combine(err), + None => topic_err = Some(err), + } + false + } + } }); + if let Some(err) = topic_err { + return Err(err) + } let variant = &s.variants()[0]; @@ -138,7 +106,6 @@ fn event_derive_struct(mut s: synstructure::Structure) -> syn::Result syn::Result(#field) ) }); + let pat = variant.pat(); let topics_builder = quote!( #pat => { builder @@ -185,7 +153,7 @@ fn event_derive_struct(mut s: synstructure::Structure) -> syn::Result syn::LitStr { +fn signature_topic(fields: &syn::Fields, event_ident: &syn::Ident) -> syn::LitStr { let fields = fields .iter() .map(|field| { @@ -198,3 +166,26 @@ pub fn signature_topic(fields: &syn::Fields, event_ident: &syn::Ident) -> syn::L let topic_str = format!("{}({fields})", event_ident); syn::parse_quote!( #topic_str ) } + +/// Checks if the given attributes contain an `ink` attribute with the given path. +fn has_ink_attribute(attrs: &[syn::Attribute], path: &str) -> syn::Result { + let ink_attrs: Vec<_> = attrs + .iter() + .filter_map(|attr| { + if attr.path().is_ident("ink") { + Some(attr.parse_nested_meta(|meta| { + if meta.path.is_ident(path) { + Ok(()) + } else { + Err(meta + .error(format!("Only `#[ink({path})]` attribute allowed."))) + } + })) + } else { + None + } + }) + .collect::>()?; + // todo: check only one + Ok(!ink_attrs.is_empty()) +} diff --git a/crates/ink/macro/src/tests/event.rs b/crates/ink/macro/src/tests/event.rs index c903f837dc..6a59d9672e 100644 --- a/crates/ink/macro/src/tests/event.rs +++ b/crates/ink/macro/src/tests/event.rs @@ -101,3 +101,95 @@ fn unit_struct_anonymous_has_no_topics() { } no_build } } + +#[test] +fn struct_with_fields_no_topics() { + crate::test_derive! { + event_derive { + struct Event { + field_1: u32, + field_2: u64, + field_3: u128, + } + } + expands to { + const _: () = { + impl ::ink::env::Topics for Event { + type RemainingTopics = [::ink::env::topics::state::HasRemainingTopics; 1usize]; + + const TOPICS_LEN: usize = 1usize; + const SIGNATURE_TOPIC: ::core::option::Option<[::core::primitive::u8; 32]> = + ::core::option::Option::Some( ::ink::blake2x256!("Event(u32,u64,u128)") ); + + fn topics( + &self, + builder: ::ink::env::topics::TopicsBuilder<::ink::env::topics::state::Uninit, E, B>, + ) -> >::Output + where + E: ::ink::env::Environment, + B: ::ink::env::topics::TopicsBuilderBackend, + { + let _ = ::ink::codegen::EventRespectsTopicLimit::<{ Self::TOPICS_LEN }, { MAX_TOPICS }>::ASSERT; + + match self { + Event { .. } => { + builder + .build::() + .push_topic(&Self::SIGNATURE_TOPIC.expect("non-anonymous events must have a signature topic")) + .finish() + } + } + } + } + }; + } + } +} + +#[test] +fn struct_with_fields_and_some_topics() { + crate::test_derive! { + event_derive { + struct Event { + field_1: u32, + #[ink(topic)] + field_2: u64, + #[ink(topic)] + field_3: u128, + } + } + expands to { + const _: () = { + impl ::ink::env::Topics for Event { + type RemainingTopics = [::ink::env::topics::state::HasRemainingTopics; 3usize]; + + const TOPICS_LEN: usize = 3usize; + const SIGNATURE_TOPIC: ::core::option::Option<[::core::primitive::u8; 32]> = + ::core::option::Option::Some( ::ink::blake2x256!("Event(u32,u64,u128)") ); + + fn topics( + &self, + builder: ::ink::env::topics::TopicsBuilder<::ink::env::topics::state::Uninit, E, B>, + ) -> >::Output + where + E: ::ink::env::Environment, + B: ::ink::env::topics::TopicsBuilderBackend, + { + let _ = ::ink::codegen::EventRespectsTopicLimit::<{ Self::TOPICS_LEN }, { MAX_TOPICS }>::ASSERT; + + match self { + Event { field_2 : __binding_1 , field_3 : __binding_2 , .. } => { + builder + .build::() + .push_topic(&Self::SIGNATURE_TOPIC.expect("non-anonymous events must have a signature topic")) + .push_topic::(__binding_1) + .push_topic::(__binding_2) + .finish() + } + } + } + } + }; + } no_build + } +} From c152723ff932a10e775b768925f57cf9897159f0 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Thu, 25 May 2023 14:47:55 +0100 Subject: [PATCH 033/140] Use TOPICS_LEN for topics remaining --- crates/ink/macro/src/event/mod.rs | 4 +++- crates/ink/macro/src/tests/event.rs | 6 +++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/crates/ink/macro/src/event/mod.rs b/crates/ink/macro/src/event/mod.rs index 432013e364..bb5b70f9b8 100644 --- a/crates/ink/macro/src/event/mod.rs +++ b/crates/ink/macro/src/event/mod.rs @@ -88,7 +88,9 @@ fn event_derive_struct(mut s: synstructure::Structure) -> syn::Result quote_spanned!(span=> ::ink::env::topics::state::NoRemainingTopics), - n => quote_spanned!(span=> [::ink::env::topics::state::HasRemainingTopics; #n]), + _ => { + quote_spanned!(span=> [::ink::env::topics::state::HasRemainingTopics; Self::TOPICS_LEN]) + } }; let event_ident = variant.ast().ident; diff --git a/crates/ink/macro/src/tests/event.rs b/crates/ink/macro/src/tests/event.rs index 6a59d9672e..adc96d718e 100644 --- a/crates/ink/macro/src/tests/event.rs +++ b/crates/ink/macro/src/tests/event.rs @@ -31,7 +31,7 @@ fn unit_struct_works() { expands to { const _: () = { impl ::ink::env::Topics for UnitStruct { - type RemainingTopics = [::ink::env::topics::state::HasRemainingTopics; 1usize]; + type RemainingTopics = [::ink::env::topics::state::HasRemainingTopics; Self::TOPICS_LEN]; const TOPICS_LEN: usize = 1usize; const SIGNATURE_TOPIC: ::core::option::Option<[::core::primitive::u8; 32]> = @@ -115,7 +115,7 @@ fn struct_with_fields_no_topics() { expands to { const _: () = { impl ::ink::env::Topics for Event { - type RemainingTopics = [::ink::env::topics::state::HasRemainingTopics; 1usize]; + type RemainingTopics = [::ink::env::topics::state::HasRemainingTopics; Self::TOPICS_LEN]; const TOPICS_LEN: usize = 1usize; const SIGNATURE_TOPIC: ::core::option::Option<[::core::primitive::u8; 32]> = @@ -161,7 +161,7 @@ fn struct_with_fields_and_some_topics() { expands to { const _: () = { impl ::ink::env::Topics for Event { - type RemainingTopics = [::ink::env::topics::state::HasRemainingTopics; 3usize]; + type RemainingTopics = [::ink::env::topics::state::HasRemainingTopics; Self::TOPICS_LEN]; const TOPICS_LEN: usize = 3usize; const SIGNATURE_TOPIC: ::core::option::Option<[::core::primitive::u8; 32]> = From affd57dbdae91d49d050e7f446f652df16c53146 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Thu, 25 May 2023 18:32:59 +0100 Subject: [PATCH 034/140] WIP don't push Option::None topics --- crates/env/src/lib.rs | 3 + crates/env/src/option_info.rs | 118 ++++++++++++++++++ crates/env/src/topics.rs | 4 +- integration-tests/events/event-def/src/lib.rs | 12 +- integration-tests/events/lib.rs | 37 +++++- 5 files changed, 168 insertions(+), 6 deletions(-) create mode 100644 crates/env/src/option_info.rs diff --git a/crates/env/src/lib.rs b/crates/env/src/lib.rs index 80bccd066c..4cc8a4cb39 100644 --- a/crates/env/src/lib.rs +++ b/crates/env/src/lib.rs @@ -86,6 +86,9 @@ mod contract; mod engine; mod error; pub mod hash; +#[macro_use] +#[doc(hidden)] +pub mod option_info; #[doc(hidden)] pub mod topics; mod types; diff --git a/crates/env/src/option_info.rs b/crates/env/src/option_info.rs new file mode 100644 index 0000000000..a3ba3bab88 --- /dev/null +++ b/crates/env/src/option_info.rs @@ -0,0 +1,118 @@ +// Copyright 2018-2022 Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +pub struct IsOptionType { + marker: core::marker::PhantomData T>, +} + +impl IsOptionType<::core::option::Option> { + // We need to allow for dead code at this point because + // the Rust compiler thinks this function is unused even + // though it acts as the specialized case for detection. + #[allow(dead_code)] + pub const VALUE: bool = true; +} + +pub trait IsOptionTypeFallback { + const VALUE: bool = false; +} +impl IsOptionTypeFallback for IsOptionType {} + +/// Returns `true` if the given type is a `Result` type. +#[macro_export] +#[doc(hidden)] +macro_rules! is_option_type { + ( $T:ty $(,)? ) => {{ + #[allow(unused_imports)] + use $crate::option_info::IsOptionTypeFallback as _; + + $crate::option_info::IsOptionType::<$T>::VALUE + }}; +} + +pub struct IsOptionNone<'lt, T>(pub &'lt T); + +impl IsOptionNone<'_, ::core::option::Option> { + #[inline] + // We need to allow for dead code at this point because + // the Rust compiler thinks this function is unused even + // though it acts as the specialized case for detection. + #[allow(dead_code)] + pub fn value(&self) -> bool { + self.0.is_none() + } +} + +pub trait IsOptionNoneFallback { + #[inline] + fn value(&self) -> bool { + false + } +} +impl IsOptionNoneFallback for IsOptionNone<'_, T> {} + +/// Evaluates to `true` if the given expression is a `Option::None`. +/// +/// # Note +/// +/// This given expression is not required to be of type `Result`. +#[macro_export] +#[doc(hidden)] +macro_rules! is_option_none { + ( $e:expr $(,)? ) => {{ + #[allow(unused_imports)] + use $crate::option_info::IsOptionNoneFallback as _; + $crate::option_info::IsOptionNone(&$e).value() + }}; +} + +#[cfg(test)] +mod tests { + #[test] + fn is_option_type_works() { + assert!(!is_option_type!(bool)); + assert!(!is_option_type!(String)); + assert!(!is_option_type!(Result)); + + assert!(is_option_type!(Option<()>)); + assert!(is_option_type!(Option)); + assert!(is_option_type!(Option)); + assert!(is_option_type!(Option<&str>)); + + assert!(is_option_type!(Option>)); + assert!(is_option_type!(Option<(Option<()>, Option<()>)>)); + + // Check that type aliases work, too. + type MyOption = Option<()>; + assert!(is_option_type!(MyOption)); + } + + #[test] + fn is_option_none_works() { + assert!(!is_option_none!(true)); + assert!(!is_option_none!(42)); + assert!(!is_option_none!("Hello, World!")); + + assert!(!is_option_none!(Some(()))); + assert!(!is_option_none!(Some(5))); + assert!(!is_option_none!(Some(true))); + + assert!(is_option_none!(Option::::None)); + { + // Check that we do not simply check against `Option` as identifier. + type Option = Result<(), ()>; + assert!(!is_option_type!(Option)); + } + } +} diff --git a/crates/env/src/topics.rs b/crates/env/src/topics.rs index 9d93b27d84..969f09ca52 100644 --- a/crates/env/src/topics.rs +++ b/crates/env/src/topics.rs @@ -111,7 +111,9 @@ where where T: scale::Encode, { - self.backend.push_topic(value); + if crate::is_option_type!(T) && !crate::is_option_none!(value) { + self.backend.push_topic(value); + } TopicsBuilder { backend: self.backend, state: Default::default(), diff --git a/integration-tests/events/event-def/src/lib.rs b/integration-tests/events/event-def/src/lib.rs index 08dea99a1e..10e1ad00a4 100644 --- a/integration-tests/events/event-def/src/lib.rs +++ b/integration-tests/events/event-def/src/lib.rs @@ -1,7 +1,17 @@ #![cfg_attr(not(feature = "std"), no_std, no_main)] #[derive(ink::Event, scale::Encode)] -#[cfg_attr(feature = "std", derive(ink::EventMetadata))] +#[cfg_attr(feature = "std", derive(ink::EventMetadata, scale::Decode))] pub struct Flipped { pub flipped: bool, } + +#[derive(ink::Event, scale::Encode)] +#[cfg_attr(feature = "std", derive(ink::EventMetadata, scale::Decode))] +pub struct ThirtyTwoByteTopics { + #[ink(topic)] + pub hash: [u8; 32], + #[ink(topic)] + pub maybe_hash: Option<[u8; 32]>, +} + diff --git a/integration-tests/events/lib.rs b/integration-tests/events/lib.rs index a09ff55f04..4c90ff15a8 100644 --- a/integration-tests/events/lib.rs +++ b/integration-tests/events/lib.rs @@ -2,8 +2,6 @@ #[ink::contract] pub mod events { - use event_def::Flipped; - #[ink(storage)] pub struct Events { value: bool, @@ -20,20 +18,51 @@ pub mod events { #[ink(message)] pub fn flip(&mut self) { self.value = !self.value; - self.env().emit_event(Flipped { flipped: self.value }) + self.env().emit_event(event_def::Flipped { flipped: self.value }) + } + + /// Emit an event with a 32 byte topic. + #[ink(message)] + pub fn emit_32_byte_topic_event(&mut self, maybe_hash: Option<[u8; 32]>) { + self.env().emit_event(event_def::ThirtyTwoByteTopics { + hash: [0x42; 32], + maybe_hash, + }) } } #[cfg(test)] mod tests { use super::*; + use scale::Decode as _; #[ink::test] fn it_works() { let mut events = Events::new(false); events.flip(); - // todo: check events. + + let emitted_events = ink::env::test::recorded_events().collect::>(); + assert_eq!(1, emitted_events.len()); + let event = &emitted_events[0]; + + let decoded_event = ::decode(&mut &event.data[..]) + .expect("encountered invalid contract event data buffer"); + assert_eq!(decoded_event.flipped, true); } + + // #[ink::test] + // fn option_topic_some_has_topic() { + // let mut events = Events::new(false); + // events.emit_32_byte_topic_event(); + // // todo: check events. + // } + // + // #[ink::test] + // fn option_topic_none_missing_topic() { + // let mut events = Events::new(false); + // events.emit_32_byte_topic_event(); + // // todo: check events. + // } } #[cfg(all(test, feature = "e2e-tests"))] From e66f697c34577ce9e5d81a2c3d1326646fdf7f53 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Fri, 26 May 2023 10:56:48 +0100 Subject: [PATCH 035/140] Fix only publishing if not `Option::None` --- crates/env/src/option_info.rs | 94 ++++++++--------------------------- crates/env/src/topics.rs | 5 +- 2 files changed, 25 insertions(+), 74 deletions(-) diff --git a/crates/env/src/option_info.rs b/crates/env/src/option_info.rs index a3ba3bab88..10d5206ff3 100644 --- a/crates/env/src/option_info.rs +++ b/crates/env/src/option_info.rs @@ -12,64 +12,37 @@ // See the License for the specific language governing permissions and // limitations under the License. -pub struct IsOptionType { - marker: core::marker::PhantomData T>, -} - -impl IsOptionType<::core::option::Option> { - // We need to allow for dead code at this point because - // the Rust compiler thinks this function is unused even - // though it acts as the specialized case for detection. - #[allow(dead_code)] - pub const VALUE: bool = true; -} - -pub trait IsOptionTypeFallback { - const VALUE: bool = false; -} -impl IsOptionTypeFallback for IsOptionType {} - -/// Returns `true` if the given type is a `Result` type. -#[macro_export] -#[doc(hidden)] -macro_rules! is_option_type { - ( $T:ty $(,)? ) => {{ - #[allow(unused_imports)] - use $crate::option_info::IsOptionTypeFallback as _; - - $crate::option_info::IsOptionType::<$T>::VALUE - }}; -} - -pub struct IsOptionNone<'lt, T>(pub &'lt T); +pub struct AsOption<'lt, T>(pub &'lt T); -impl IsOptionNone<'_, ::core::option::Option> { +impl AsOption<'_, ::core::option::Option> { #[inline] // We need to allow for dead code at this point because // the Rust compiler thinks this function is unused even // though it acts as the specialized case for detection. #[allow(dead_code)] - pub fn value(&self) -> bool { - self.0.is_none() + pub fn value(&self) -> Option<&T> { + self.0.as_ref() } } -pub trait IsOptionNoneFallback { +pub trait AsOptionFallback { + fn value(&self) -> Option<&T>; +} +impl AsOptionFallback for AsOption<'_, T> { #[inline] - fn value(&self) -> bool { - false + fn value(&self) -> Option<&T> { + Some(&self.0) } } -impl IsOptionNoneFallback for IsOptionNone<'_, T> {} -/// Evaluates to `true` if the given expression is a `Option::None`. +/// Evaluates to `None` if the given expression is a `Option::None`. /// /// # Note /// -/// This given expression is not required to be of type `Result`. +/// This given expression is not required to be of type `Option`. #[macro_export] #[doc(hidden)] -macro_rules! is_option_none { +macro_rules! as_option { ( $e:expr $(,)? ) => {{ #[allow(unused_imports)] use $crate::option_info::IsOptionNoneFallback as _; @@ -80,39 +53,16 @@ macro_rules! is_option_none { #[cfg(test)] mod tests { #[test] - fn is_option_type_works() { - assert!(!is_option_type!(bool)); - assert!(!is_option_type!(String)); - assert!(!is_option_type!(Result)); - - assert!(is_option_type!(Option<()>)); - assert!(is_option_type!(Option)); - assert!(is_option_type!(Option)); - assert!(is_option_type!(Option<&str>)); - - assert!(is_option_type!(Option>)); - assert!(is_option_type!(Option<(Option<()>, Option<()>)>)); - - // Check that type aliases work, too. - type MyOption = Option<()>; - assert!(is_option_type!(MyOption)); - } - - #[test] - fn is_option_none_works() { - assert!(!is_option_none!(true)); - assert!(!is_option_none!(42)); - assert!(!is_option_none!("Hello, World!")); + fn as_option_works() { + assert_eq!(Some(&true), as_option!(true)); + assert_eq!(Some(&42), as_option!(42)); + assert_eq!(Some(&"Hello, World!"), as_option!("Hello, World!")); - assert!(!is_option_none!(Some(()))); - assert!(!is_option_none!(Some(5))); - assert!(!is_option_none!(Some(true))); + assert_eq!(Some(&()), as_option!(Some(()))); + assert_eq!(Some(&5), as_option!(Some(5))); + assert_eq!(Some(&true), as_option!(Some(true))); - assert!(is_option_none!(Option::::None)); - { - // Check that we do not simply check against `Option` as identifier. - type Option = Result<(), ()>; - assert!(!is_option_type!(Option)); - } + assert_eq!(None, as_option!(Option::::None)); + assert_eq!(None, as_option!(Option::::None)); } } diff --git a/crates/env/src/topics.rs b/crates/env/src/topics.rs index 969f09ca52..21108ff882 100644 --- a/crates/env/src/topics.rs +++ b/crates/env/src/topics.rs @@ -111,8 +111,9 @@ where where T: scale::Encode, { - if crate::is_option_type!(T) && !crate::is_option_none!(value) { - self.backend.push_topic(value); + // Only publish the topic if it is not an `Option::None`. + if let Some(topic) = crate::as_option!(value) { + self.backend.push_topic(topic); } TopicsBuilder { backend: self.backend, From ef6693c0dd5b60c26c46bf261833fe6b745cf3ec Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Fri, 26 May 2023 10:58:00 +0100 Subject: [PATCH 036/140] Fix only publishing if not `Option::None` --- crates/env/src/option_info.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/env/src/option_info.rs b/crates/env/src/option_info.rs index 10d5206ff3..6da313d1d6 100644 --- a/crates/env/src/option_info.rs +++ b/crates/env/src/option_info.rs @@ -45,8 +45,8 @@ impl AsOptionFallback for AsOption<'_, T> { macro_rules! as_option { ( $e:expr $(,)? ) => {{ #[allow(unused_imports)] - use $crate::option_info::IsOptionNoneFallback as _; - $crate::option_info::IsOptionNone(&$e).value() + use $crate::option_info::AsOptionFallback as _; + $crate::option_info::AsOption(&$e).value() }}; } From cacb540ff35978c21d7579ec54b8aae428ab7b2d Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Fri, 26 May 2023 17:54:12 +0100 Subject: [PATCH 037/140] Only publish value of `Some` topic, not none. --- crates/env/src/lib.rs | 3 -- crates/env/src/topics.rs | 7 ++-- crates/ink/macro/src/event/mod.rs | 4 +-- crates/ink/src/lib.rs | 4 +++ crates/{env => ink}/src/option_info.rs | 11 +++++++ integration-tests/events/event-def/src/lib.rs | 1 - integration-tests/events/lib.rs | 32 ++++++++++++++----- 7 files changed, 46 insertions(+), 16 deletions(-) rename crates/{env => ink}/src/option_info.rs (87%) diff --git a/crates/env/src/lib.rs b/crates/env/src/lib.rs index 4cc8a4cb39..80bccd066c 100644 --- a/crates/env/src/lib.rs +++ b/crates/env/src/lib.rs @@ -86,9 +86,6 @@ mod contract; mod engine; mod error; pub mod hash; -#[macro_use] -#[doc(hidden)] -pub mod option_info; #[doc(hidden)] pub mod topics; mod types; diff --git a/crates/env/src/topics.rs b/crates/env/src/topics.rs index 21108ff882..73da2b4b15 100644 --- a/crates/env/src/topics.rs +++ b/crates/env/src/topics.rs @@ -102,17 +102,20 @@ where { /// Pushes another event topic to be serialized through the topics builder. /// + /// If the `value` of the topic is `None` then the topic will *not* be pushed. + /// /// Returns a topics builder that expects one less event topic for serialization /// than before the call. pub fn push_topic( mut self, - value: &T, + value: Option<&T>, ) -> TopicsBuilder<::Next, E, B> where T: scale::Encode, { // Only publish the topic if it is not an `Option::None`. - if let Some(topic) = crate::as_option!(value) { + if let Some(topic) = value { + println!("pushing topic {:?}", scale::Encode::encode(topic)); self.backend.push_topic(topic); } TopicsBuilder { diff --git a/crates/ink/macro/src/event/mod.rs b/crates/ink/macro/src/event/mod.rs index bb5b70f9b8..7e9e6f0b86 100644 --- a/crates/ink/macro/src/event/mod.rs +++ b/crates/ink/macro/src/event/mod.rs @@ -104,7 +104,7 @@ fn event_derive_struct(mut s: synstructure::Structure) -> syn::Result - .push_topic(&Self::SIGNATURE_TOPIC.expect("non-anonymous events must have a signature topic")) + .push_topic(Self::SIGNATURE_TOPIC.as_ref()) )) }; @@ -113,7 +113,7 @@ fn event_derive_struct(mut s: synstructure::Structure) -> syn::Result #acc - .push_topic::<#field_ty>(#field) + .push_topic(::ink::as_option!(#field)) ) }); let pat = variant.pat(); diff --git a/crates/ink/src/lib.rs b/crates/ink/src/lib.rs index 66d211ea4f..a96bad8a27 100644 --- a/crates/ink/src/lib.rs +++ b/crates/ink/src/lib.rs @@ -18,6 +18,10 @@ )] #![cfg_attr(not(feature = "std"), no_std)] +#[macro_use] +#[doc(hidden)] +pub mod option_info; + #[macro_use] #[doc(hidden)] pub mod result_info; diff --git a/crates/env/src/option_info.rs b/crates/ink/src/option_info.rs similarity index 87% rename from crates/env/src/option_info.rs rename to crates/ink/src/option_info.rs index 6da313d1d6..5c878438fc 100644 --- a/crates/env/src/option_info.rs +++ b/crates/ink/src/option_info.rs @@ -25,6 +25,13 @@ impl AsOption<'_, ::core::option::Option> { } } +impl<'lt, T> AsOption<'lt, &'lt ::core::option::Option> { + #[inline] + pub fn value(&self) -> Option<&T> { + self.0.as_ref() + } +} + pub trait AsOptionFallback { fn value(&self) -> Option<&T>; } @@ -52,6 +59,8 @@ macro_rules! as_option { #[cfg(test)] mod tests { + use std::fmt::Debug; + #[test] fn as_option_works() { assert_eq!(Some(&true), as_option!(true)); @@ -61,8 +70,10 @@ mod tests { assert_eq!(Some(&()), as_option!(Some(()))); assert_eq!(Some(&5), as_option!(Some(5))); assert_eq!(Some(&true), as_option!(Some(true))); + assert_eq!(Some(&true), as_option!(&Some(true))); assert_eq!(None, as_option!(Option::::None)); assert_eq!(None, as_option!(Option::::None)); + assert_eq!(None, as_option!(&Option::::None)); } } diff --git a/integration-tests/events/event-def/src/lib.rs b/integration-tests/events/event-def/src/lib.rs index 10e1ad00a4..eb2681ad94 100644 --- a/integration-tests/events/event-def/src/lib.rs +++ b/integration-tests/events/event-def/src/lib.rs @@ -14,4 +14,3 @@ pub struct ThirtyTwoByteTopics { #[ink(topic)] pub maybe_hash: Option<[u8; 32]>, } - diff --git a/integration-tests/events/lib.rs b/integration-tests/events/lib.rs index 4c90ff15a8..3627de16b9 100644 --- a/integration-tests/events/lib.rs +++ b/integration-tests/events/lib.rs @@ -18,7 +18,9 @@ pub mod events { #[ink(message)] pub fn flip(&mut self) { self.value = !self.value; - self.env().emit_event(event_def::Flipped { flipped: self.value }) + self.env().emit_event(event_def::Flipped { + flipped: self.value, + }) } /// Emit an event with a 32 byte topic. @@ -50,13 +52,27 @@ pub mod events { assert_eq!(decoded_event.flipped, true); } - // #[ink::test] - // fn option_topic_some_has_topic() { - // let mut events = Events::new(false); - // events.emit_32_byte_topic_event(); - // // todo: check events. - // } - // + #[ink::test] + fn option_topic_some_has_topic() { + let mut events = Events::new(false); + events.emit_32_byte_topic_event(Some([0xAA; 32])); + + let emitted_events = ink::env::test::recorded_events().collect::>(); + assert_eq!(1, emitted_events.len()); + let event = &emitted_events[0]; + + assert_eq!(event.topics.len(), 3); + let signature_topic = + ::SIGNATURE_TOPIC + .map(|topic| topic.to_vec()); + assert_eq!(Some(&event.topics[0]), signature_topic.as_ref()); + assert_eq!(event.topics[1], [0x42; 32]); + assert_eq!( + event.topics[2], [0xAA; 32], + "option topic should be published" + ); + } + // #[ink::test] // fn option_topic_none_missing_topic() { // let mut events = Events::new(false); From c2ac4445ca3580ae6d2de4c55bba27504ad57ea5 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Fri, 26 May 2023 17:57:09 +0100 Subject: [PATCH 038/140] Fix event derive codegen tests --- crates/ink/macro/src/tests/event.rs | 10 +++++----- crates/ink/src/option_info.rs | 2 -- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/crates/ink/macro/src/tests/event.rs b/crates/ink/macro/src/tests/event.rs index adc96d718e..fa77d92076 100644 --- a/crates/ink/macro/src/tests/event.rs +++ b/crates/ink/macro/src/tests/event.rs @@ -51,7 +51,7 @@ fn unit_struct_works() { UnitStruct => { builder .build::() - .push_topic(&Self::SIGNATURE_TOPIC.expect("non-anonymous events must have a signature topic")) + .push_topic(Self::SIGNATURE_TOPIC.as_ref()) .finish() } } @@ -135,7 +135,7 @@ fn struct_with_fields_no_topics() { Event { .. } => { builder .build::() - .push_topic(&Self::SIGNATURE_TOPIC.expect("non-anonymous events must have a signature topic")) + .push_topic(Self::SIGNATURE_TOPIC.as_ref()) .finish() } } @@ -181,9 +181,9 @@ fn struct_with_fields_and_some_topics() { Event { field_2 : __binding_1 , field_3 : __binding_2 , .. } => { builder .build::() - .push_topic(&Self::SIGNATURE_TOPIC.expect("non-anonymous events must have a signature topic")) - .push_topic::(__binding_1) - .push_topic::(__binding_2) + .push_topic(Self::SIGNATURE_TOPIC.as_ref()) + .push_topic(::ink::as_option!(__binding_1)) + .push_topic(::ink::as_option!(__binding_2)) .finish() } } diff --git a/crates/ink/src/option_info.rs b/crates/ink/src/option_info.rs index 5c878438fc..90120ea3c1 100644 --- a/crates/ink/src/option_info.rs +++ b/crates/ink/src/option_info.rs @@ -59,8 +59,6 @@ macro_rules! as_option { #[cfg(test)] mod tests { - use std::fmt::Debug; - #[test] fn as_option_works() { assert_eq!(Some(&true), as_option!(true)); From 5a378e76f9d492677d9e2475b2e4bfdb31175726 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Fri, 26 May 2023 18:02:17 +0100 Subject: [PATCH 039/140] Add test for None topic --- integration-tests/events/lib.rs | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/integration-tests/events/lib.rs b/integration-tests/events/lib.rs index 3627de16b9..90d62ae148 100644 --- a/integration-tests/events/lib.rs +++ b/integration-tests/events/lib.rs @@ -73,12 +73,21 @@ pub mod events { ); } - // #[ink::test] - // fn option_topic_none_missing_topic() { - // let mut events = Events::new(false); - // events.emit_32_byte_topic_event(); - // // todo: check events. - // } + #[ink::test] + fn option_topic_none_missing_topic() { + let mut events = Events::new(false); + events.emit_32_byte_topic_event(None); + + let emitted_events = ink::env::test::recorded_events().collect::>(); + assert_eq!(1, emitted_events.len()); + let event = &emitted_events[0]; + + assert_eq!( + event.topics.len(), + 2, + "option topic should *not* be published" + ); + } } #[cfg(all(test, feature = "e2e-tests"))] From bbdb5fb3d3903a9f008789722fdceb8165af2bc8 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Tue, 30 May 2023 17:45:58 +0100 Subject: [PATCH 040/140] Rename Topics trait to Event --- crates/env/src/api.rs | 8 ++--- crates/env/src/backend.rs | 6 ++-- crates/env/src/engine/off_chain/impls.rs | 8 ++--- crates/env/src/engine/off_chain/tests.rs | 2 +- crates/env/src/engine/on_chain/impls.rs | 8 ++--- crates/env/src/{topics.rs => event.rs} | 8 ++--- crates/env/src/lib.rs | 4 +-- crates/ink/codegen/src/generator/events.rs | 4 +-- crates/ink/macro/src/event/mod.rs | 12 +++---- crates/ink/macro/src/tests/event.rs | 40 +++++++++++----------- integration-tests/events/lib.rs | 2 +- 11 files changed, 51 insertions(+), 51 deletions(-) rename crates/env/src/{topics.rs => event.rs} (96%) diff --git a/crates/env/src/api.rs b/crates/env/src/api.rs index 88e3974b26..1cc63205c4 100644 --- a/crates/env/src/api.rs +++ b/crates/env/src/api.rs @@ -36,7 +36,7 @@ use crate::{ CryptoHash, HashOutput, }, - topics::Topics, + event::Event, types::Gas, Environment, Result, @@ -175,13 +175,13 @@ where } /// Emits an event with the given event data. -pub fn emit_event(event: Event) +pub fn emit_event(event: Evt) where E: Environment, - Event: Topics + scale::Encode, + Evt: Event, { ::on_instance(|instance| { - TypedEnvBackend::emit_event::(instance, event) + TypedEnvBackend::emit_event::(instance, event) }) } diff --git a/crates/env/src/backend.rs b/crates/env/src/backend.rs index 9bdaba9a0c..ef5399d661 100644 --- a/crates/env/src/backend.rs +++ b/crates/env/src/backend.rs @@ -25,7 +25,7 @@ use crate::{ CryptoHash, HashOutput, }, - topics::Topics, + event::Event, Environment, Result, }; @@ -405,10 +405,10 @@ pub trait TypedEnvBackend: EnvBackend { /// # Note /// /// For more details visit: [`emit_event`][`crate::emit_event`] - fn emit_event(&mut self, event: Event) + fn emit_event(&mut self, event: Evt) where E: Environment, - Event: Topics + scale::Encode; + Evt: Event; /// Invokes a contract message and returns its result. /// diff --git a/crates/env/src/engine/off_chain/impls.rs b/crates/env/src/engine/off_chain/impls.rs index 5df02e39bb..8992d088aa 100644 --- a/crates/env/src/engine/off_chain/impls.rs +++ b/crates/env/src/engine/off_chain/impls.rs @@ -30,8 +30,8 @@ use crate::{ Keccak256, Sha2x256, }, - topics::{ - Topics, + event::{ + Event, TopicsBuilderBackend, }, Clear, @@ -422,10 +422,10 @@ impl TypedEnvBackend for EnvInstance { }) } - fn emit_event(&mut self, event: Event) + fn emit_event(&mut self, event: Evt) where E: Environment, - Event: Topics + scale::Encode, + Evt: Event, { let builder = TopicsBuilder::default(); let enc_topics = event.topics::(builder.into()); diff --git a/crates/env/src/engine/off_chain/tests.rs b/crates/env/src/engine/off_chain/tests.rs index f94b6001f8..170dab0201 100644 --- a/crates/env/src/engine/off_chain/tests.rs +++ b/crates/env/src/engine/off_chain/tests.rs @@ -14,7 +14,7 @@ use crate::{ engine::off_chain::impls::TopicsBuilder, - topics::TopicsBuilderBackend, + event::TopicsBuilderBackend, Result, }; diff --git a/crates/env/src/engine/on_chain/impls.rs b/crates/env/src/engine/on_chain/impls.rs index 9987630653..797fd6d253 100644 --- a/crates/env/src/engine/on_chain/impls.rs +++ b/crates/env/src/engine/on_chain/impls.rs @@ -35,8 +35,8 @@ use crate::{ Keccak256, Sha2x256, }, - topics::{ - Topics, + event::{ + Event, TopicsBuilderBackend, }, Clear, @@ -392,10 +392,10 @@ impl TypedEnvBackend for EnvInstance { self.get_property_little_endian::(ext::minimum_balance) } - fn emit_event(&mut self, event: Event) + fn emit_event(&mut self, event: Evt) where E: Environment, - Event: Topics + scale::Encode, + Evt: Event, { let (mut scope, enc_topics) = event .topics::(TopicsBuilder::from(self.scoped_buffer()).into()); diff --git a/crates/env/src/topics.rs b/crates/env/src/event.rs similarity index 96% rename from crates/env/src/topics.rs rename to crates/env/src/event.rs index 73da2b4b15..397f4423ff 100644 --- a/crates/env/src/topics.rs +++ b/crates/env/src/event.rs @@ -82,11 +82,11 @@ where /// to serialize. /// /// The number of expected topics is given implicitly by the `E` type parameter. - pub fn build( + pub fn build( mut self, - ) -> TopicsBuilder<::RemainingTopics, E, B> { + ) -> TopicsBuilder<::RemainingTopics, E, B> { self.backend - .expect(<::RemainingTopics as EventTopicsAmount>::AMOUNT); + .expect(<::RemainingTopics as EventTopicsAmount>::AMOUNT); TopicsBuilder { backend: self.backend, state: Default::default(), @@ -196,7 +196,7 @@ impl EventTopicsAmount for state::NoRemainingTopics { /// builder. /// /// Normally this trait should be implemented automatically via the ink! codegen. -pub trait Topics { +pub trait Event: scale::Encode { /// Type state indicating how many event topics are to be expected by the topics /// builder. type RemainingTopics: EventTopicsAmount; diff --git a/crates/env/src/lib.rs b/crates/env/src/lib.rs index 80bccd066c..081e4ffc3a 100644 --- a/crates/env/src/lib.rs +++ b/crates/env/src/lib.rs @@ -87,7 +87,7 @@ mod engine; mod error; pub mod hash; #[doc(hidden)] -pub mod topics; +pub mod event; mod types; #[cfg(test)] @@ -115,7 +115,7 @@ pub use self::{ Error, Result, }, - topics::Topics, + event::Event, types::{ AccountIdGuard, DefaultEnvironment, diff --git a/crates/ink/codegen/src/generator/events.rs b/crates/ink/codegen/src/generator/events.rs index bc5301181f..a291d09f35 100644 --- a/crates/ink/codegen/src/generator/events.rs +++ b/crates/ink/codegen/src/generator/events.rs @@ -54,13 +54,13 @@ impl<'a> Events<'a> { pub trait __ink_EmitEvent { fn emit_event(self, event: E) where - E: ::ink::env::Topics + ::scale::Encode; + E: ::ink::env::Event + ::scale::Encode; } impl<'a> __ink_EmitEvent for ::ink::EnvAccess<'a, Environment> { fn emit_event(self, event: E) where - E: ::ink::env::Topics + ::scale::Encode, + E: ::ink::env::Event + ::scale::Encode, { ::ink::env::emit_event::< { ::MAX_EVENT_TOPICS }, diff --git a/crates/ink/macro/src/event/mod.rs b/crates/ink/macro/src/event/mod.rs index 7e9e6f0b86..f693e88f09 100644 --- a/crates/ink/macro/src/event/mod.rs +++ b/crates/ink/macro/src/event/mod.rs @@ -87,9 +87,9 @@ fn event_derive_struct(mut s: synstructure::Structure) -> syn::Result quote_spanned!(span=> ::ink::env::topics::state::NoRemainingTopics), + 0 => quote_spanned!(span=> ::ink::env::event::state::NoRemainingTopics), _ => { - quote_spanned!(span=> [::ink::env::topics::state::HasRemainingTopics; Self::TOPICS_LEN]) + quote_spanned!(span=> [::ink::env::event::state::HasRemainingTopics; Self::TOPICS_LEN]) } }; @@ -127,7 +127,7 @@ fn event_derive_struct(mut s: synstructure::Structure) -> syn::Result syn::Result( &self, - builder: ::ink::env::topics::TopicsBuilder<::ink::env::topics::state::Uninit, E, B>, - ) -> >::Output + builder: ::ink::env::event::TopicsBuilder<::ink::env::event::state::Uninit, E, B>, + ) -> >::Output where E: ::ink::env::Environment, - B: ::ink::env::topics::TopicsBuilderBackend, + B: ::ink::env::event::TopicsBuilderBackend, { // assert at compile time the number of topics defined by this event is within the // given limit. diff --git a/crates/ink/macro/src/tests/event.rs b/crates/ink/macro/src/tests/event.rs index fa77d92076..d014833b46 100644 --- a/crates/ink/macro/src/tests/event.rs +++ b/crates/ink/macro/src/tests/event.rs @@ -30,8 +30,8 @@ fn unit_struct_works() { } expands to { const _: () = { - impl ::ink::env::Topics for UnitStruct { - type RemainingTopics = [::ink::env::topics::state::HasRemainingTopics; Self::TOPICS_LEN]; + impl ::ink::env::Event for UnitStruct { + type RemainingTopics = [::ink::env::event::state::HasRemainingTopics; Self::TOPICS_LEN]; const TOPICS_LEN: usize = 1usize; const SIGNATURE_TOPIC: ::core::option::Option<[::core::primitive::u8; 32]> = @@ -39,11 +39,11 @@ fn unit_struct_works() { fn topics( &self, - builder: ::ink::env::topics::TopicsBuilder<::ink::env::topics::state::Uninit, E, B>, - ) -> >::Output + builder: ::ink::env::event::TopicsBuilder<::ink::env::event::state::Uninit, E, B>, + ) -> >::Output where E: ::ink::env::Environment, - B: ::ink::env::topics::TopicsBuilderBackend, + B: ::ink::env::event::TopicsBuilderBackend, { let _ = ::ink::codegen::EventRespectsTopicLimit::<{ Self::TOPICS_LEN }, { MAX_TOPICS }>::ASSERT; @@ -71,8 +71,8 @@ fn unit_struct_anonymous_has_no_topics() { } expands to { const _: () = { - impl ::ink::env::Topics for UnitStruct { - type RemainingTopics = ::ink::env::topics::state::NoRemainingTopics; + impl ::ink::env::Event for UnitStruct { + type RemainingTopics = ::ink::env::event::state::NoRemainingTopics; const TOPICS_LEN: usize = 0usize; const SIGNATURE_TOPIC: ::core::option::Option<[::core::primitive::u8; 32]> = @@ -80,11 +80,11 @@ fn unit_struct_anonymous_has_no_topics() { fn topics( &self, - builder: ::ink::env::topics::TopicsBuilder<::ink::env::topics::state::Uninit, E, B>, - ) -> >::Output + builder: ::ink::env::event::TopicsBuilder<::ink::env::event::state::Uninit, E, B>, + ) -> >::Output where E: ::ink::env::Environment, - B: ::ink::env::topics::TopicsBuilderBackend, + B: ::ink::env::event::TopicsBuilderBackend, { let _ = ::ink::codegen::EventRespectsTopicLimit::<{ Self::TOPICS_LEN }, { MAX_TOPICS }>::ASSERT; @@ -114,8 +114,8 @@ fn struct_with_fields_no_topics() { } expands to { const _: () = { - impl ::ink::env::Topics for Event { - type RemainingTopics = [::ink::env::topics::state::HasRemainingTopics; Self::TOPICS_LEN]; + impl ::ink::env::Event for Event { + type RemainingTopics = [::ink::env::event::state::HasRemainingTopics; Self::TOPICS_LEN]; const TOPICS_LEN: usize = 1usize; const SIGNATURE_TOPIC: ::core::option::Option<[::core::primitive::u8; 32]> = @@ -123,11 +123,11 @@ fn struct_with_fields_no_topics() { fn topics( &self, - builder: ::ink::env::topics::TopicsBuilder<::ink::env::topics::state::Uninit, E, B>, - ) -> >::Output + builder: ::ink::env::event::TopicsBuilder<::ink::env::event::state::Uninit, E, B>, + ) -> >::Output where E: ::ink::env::Environment, - B: ::ink::env::topics::TopicsBuilderBackend, + B: ::ink::env::event::TopicsBuilderBackend, { let _ = ::ink::codegen::EventRespectsTopicLimit::<{ Self::TOPICS_LEN }, { MAX_TOPICS }>::ASSERT; @@ -160,8 +160,8 @@ fn struct_with_fields_and_some_topics() { } expands to { const _: () = { - impl ::ink::env::Topics for Event { - type RemainingTopics = [::ink::env::topics::state::HasRemainingTopics; Self::TOPICS_LEN]; + impl ::ink::env::Event for Event { + type RemainingTopics = [::ink::env::event::state::HasRemainingTopics; Self::TOPICS_LEN]; const TOPICS_LEN: usize = 3usize; const SIGNATURE_TOPIC: ::core::option::Option<[::core::primitive::u8; 32]> = @@ -169,11 +169,11 @@ fn struct_with_fields_and_some_topics() { fn topics( &self, - builder: ::ink::env::topics::TopicsBuilder<::ink::env::topics::state::Uninit, E, B>, - ) -> >::Output + builder: ::ink::env::event::TopicsBuilder<::ink::env::event::state::Uninit, E, B>, + ) -> >::Output where E: ::ink::env::Environment, - B: ::ink::env::topics::TopicsBuilderBackend, + B: ::ink::env::event::TopicsBuilderBackend, { let _ = ::ink::codegen::EventRespectsTopicLimit::<{ Self::TOPICS_LEN }, { MAX_TOPICS }>::ASSERT; diff --git a/integration-tests/events/lib.rs b/integration-tests/events/lib.rs index 90d62ae148..34183c979b 100644 --- a/integration-tests/events/lib.rs +++ b/integration-tests/events/lib.rs @@ -63,7 +63,7 @@ pub mod events { assert_eq!(event.topics.len(), 3); let signature_topic = - ::SIGNATURE_TOPIC + ::SIGNATURE_TOPIC .map(|topic| topic.to_vec()); assert_eq!(Some(&event.topics[0]), signature_topic.as_ref()); assert_eq!(event.topics[1], [0x42; 32]); From 45460fa103fe7c79dfe26edb79731a246181154a Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Tue, 30 May 2023 17:46:16 +0100 Subject: [PATCH 041/140] Fmt --- crates/env/src/api.rs | 2 +- crates/env/src/backend.rs | 2 +- crates/env/src/engine/off_chain/impls.rs | 8 ++++---- crates/env/src/engine/on_chain/impls.rs | 8 ++++---- crates/env/src/lib.rs | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/crates/env/src/api.rs b/crates/env/src/api.rs index 1cc63205c4..c2239a4b8e 100644 --- a/crates/env/src/api.rs +++ b/crates/env/src/api.rs @@ -32,11 +32,11 @@ use crate::{ EnvInstance, OnInstance, }, + event::Event, hash::{ CryptoHash, HashOutput, }, - event::Event, types::Gas, Environment, Result, diff --git a/crates/env/src/backend.rs b/crates/env/src/backend.rs index ef5399d661..1f65144749 100644 --- a/crates/env/src/backend.rs +++ b/crates/env/src/backend.rs @@ -21,11 +21,11 @@ use crate::{ DelegateCall, FromAccountId, }, + event::Event, hash::{ CryptoHash, HashOutput, }, - event::Event, Environment, Result, }; diff --git a/crates/env/src/engine/off_chain/impls.rs b/crates/env/src/engine/off_chain/impls.rs index 8992d088aa..8541cc5377 100644 --- a/crates/env/src/engine/off_chain/impls.rs +++ b/crates/env/src/engine/off_chain/impls.rs @@ -22,6 +22,10 @@ use crate::{ DelegateCall, FromAccountId, }, + event::{ + Event, + TopicsBuilderBackend, + }, hash::{ Blake2x128, Blake2x256, @@ -30,10 +34,6 @@ use crate::{ Keccak256, Sha2x256, }, - event::{ - Event, - TopicsBuilderBackend, - }, Clear, EnvBackend, Environment, diff --git a/crates/env/src/engine/on_chain/impls.rs b/crates/env/src/engine/on_chain/impls.rs index 797fd6d253..e078eaeb2d 100644 --- a/crates/env/src/engine/on_chain/impls.rs +++ b/crates/env/src/engine/on_chain/impls.rs @@ -27,6 +27,10 @@ use crate::{ DelegateCall, FromAccountId, }, + event::{ + Event, + TopicsBuilderBackend, + }, hash::{ Blake2x128, Blake2x256, @@ -35,10 +39,6 @@ use crate::{ Keccak256, Sha2x256, }, - event::{ - Event, - TopicsBuilderBackend, - }, Clear, EnvBackend, Environment, diff --git a/crates/env/src/lib.rs b/crates/env/src/lib.rs index 081e4ffc3a..46bf445f06 100644 --- a/crates/env/src/lib.rs +++ b/crates/env/src/lib.rs @@ -85,9 +85,9 @@ pub mod chain_extension; mod contract; mod engine; mod error; -pub mod hash; #[doc(hidden)] pub mod event; +pub mod hash; mod types; #[cfg(test)] From 57e6ac4864c96263477ce431e5da024ab87a0201 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Tue, 30 May 2023 17:52:52 +0100 Subject: [PATCH 042/140] Remove println --- crates/env/src/event.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/env/src/event.rs b/crates/env/src/event.rs index 397f4423ff..0aea3c6270 100644 --- a/crates/env/src/event.rs +++ b/crates/env/src/event.rs @@ -115,7 +115,6 @@ where { // Only publish the topic if it is not an `Option::None`. if let Some(topic) = value { - println!("pushing topic {:?}", scale::Encode::encode(topic)); self.backend.push_topic(topic); } TopicsBuilder { From 4c731ea1c6bdc7fb38be66acf8c2556cd7f52685 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Tue, 30 May 2023 17:56:48 +0100 Subject: [PATCH 043/140] Remove EmitEvent imports --- crates/ink/codegen/src/generator/item_impls.rs | 6 ------ crates/ink/codegen/src/generator/storage.rs | 7 ------- 2 files changed, 13 deletions(-) diff --git a/crates/ink/codegen/src/generator/item_impls.rs b/crates/ink/codegen/src/generator/item_impls.rs index ca472091c8..ab4f239503 100644 --- a/crates/ink/codegen/src/generator/item_impls.rs +++ b/crates/ink/codegen/src/generator/item_impls.rs @@ -46,16 +46,10 @@ impl GenerateCode for ItemImpls<'_> { .map(|item_impl| self.generate_item_impl(item_impl)); let inout_guards = self.generate_input_output_guards(); let trait_message_property_guards = self.generate_trait_message_property_guards(); - let use_emit_event = - self.contract.module().events().next().is_some().then(|| { - // Required to make `self.env().emit_event(...)` syntax available. - quote! { use ::ink::codegen::EmitEvent as _; } - }); quote! { const _: () = { // Required to make `self.env()` and `Self::env()` syntax available. use ::ink::codegen::{Env as _, StaticEnv as _}; - #use_emit_event #( #item_impls )* #inout_guards diff --git a/crates/ink/codegen/src/generator/storage.rs b/crates/ink/codegen/src/generator/storage.rs index a69c05648a..cdc4777681 100644 --- a/crates/ink/codegen/src/generator/storage.rs +++ b/crates/ink/codegen/src/generator/storage.rs @@ -33,12 +33,6 @@ impl GenerateCode for Storage<'_> { let storage_span = self.contract.module().storage().span(); let access_env_impls = self.generate_access_env_trait_impls(); let storage_struct = self.generate_storage_struct(); - let use_emit_event = - self.contract.module().events().next().is_some().then(|| { - // Required to allow for `self.env().emit_event(...)` in messages and - // constructors. - quote! { use ::ink::codegen::EmitEvent as _; } - }); quote_spanned!(storage_span => #storage_struct #access_env_impls @@ -50,7 +44,6 @@ impl GenerateCode for Storage<'_> { Env as _, StaticEnv as _, }; - #use_emit_event }; ) } From 938b36102ef07da526e6d5008bcde3c63425dd7b Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Thu, 1 Jun 2023 10:16:07 +0100 Subject: [PATCH 044/140] WIP adding E2E test for topics --- crates/e2e/src/client.rs | 54 +--------- crates/e2e/src/events.rs | 101 ++++++++++++++++++ crates/e2e/src/lib.rs | 1 + crates/ink/codegen/src/generator/events.rs | 21 ++-- crates/ink/ir/src/ir/item/event.rs | 3 + crates/ink/tests/events_metadata.rs | 4 +- integration-tests/e2e-call-runtime/Cargo.toml | 3 +- integration-tests/events/lib.rs | 43 +++----- 8 files changed, 137 insertions(+), 93 deletions(-) create mode 100644 crates/e2e/src/events.rs diff --git a/crates/e2e/src/client.rs b/crates/e2e/src/client.rs index 088ad922b8..f12bbae2fd 100644 --- a/crates/e2e/src/client.rs +++ b/crates/e2e/src/client.rs @@ -17,6 +17,10 @@ use super::{ constructor_exec_input, CreateBuilderPartial, }, + events::{ + CodeStoredEvent, + ContractInstantiatedEvent, + }, log_error, log_info, sr25519, @@ -55,8 +59,6 @@ use subxt::{ config::ExtrinsicParams, events::EventDetails, ext::{ - scale_decode, - scale_encode, scale_value::{ Composite, Value, @@ -354,54 +356,6 @@ where } } -/// A contract was successfully instantiated. -#[derive( - Debug, - scale::Decode, - scale::Encode, - scale_decode::DecodeAsType, - scale_encode::EncodeAsType, -)] -#[decode_as_type(trait_bounds = "", crate_path = "subxt::ext::scale_decode")] -#[encode_as_type(crate_path = "subxt::ext::scale_encode")] -struct ContractInstantiatedEvent { - /// Account id of the deployer. - pub deployer: E::AccountId, - /// Account id where the contract was instantiated to. - pub contract: E::AccountId, -} - -impl subxt::events::StaticEvent for ContractInstantiatedEvent -where - E: Environment, -{ - const PALLET: &'static str = "Contracts"; - const EVENT: &'static str = "Instantiated"; -} - -/// Code with the specified hash has been stored. -#[derive( - Debug, - scale::Decode, - scale::Encode, - scale_decode::DecodeAsType, - scale_encode::EncodeAsType, -)] -#[decode_as_type(trait_bounds = "", crate_path = "subxt::ext::scale_decode")] -#[encode_as_type(crate_path = "subxt::ext::scale_encode")] -struct CodeStoredEvent { - /// Hash under which the contract code was stored. - pub code_hash: E::Hash, -} - -impl subxt::events::StaticEvent for CodeStoredEvent -where - E: Environment, -{ - const PALLET: &'static str = "Contracts"; - const EVENT: &'static str = "CodeStored"; -} - /// The `Client` takes care of communicating with the node. /// /// This node's RPC interface will be used for instantiating the contract diff --git a/crates/e2e/src/events.rs b/crates/e2e/src/events.rs new file mode 100644 index 0000000000..951fd5af6d --- /dev/null +++ b/crates/e2e/src/events.rs @@ -0,0 +1,101 @@ +// Copyright 2018-2022 Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use ink::codegen::ContractCallBuilder; +use ink_env::{ + call::{ + utils::{ + ReturnType, + Set, + }, + Call, + ExecutionInput, + FromAccountId, + }, + Environment, +}; +use ink_primitives::MessageResult; +use pallet_contracts_primitives::ExecReturnValue; +use sp_core::Pair; +#[cfg(feature = "std")] +use std::{ + collections::BTreeMap, + fmt::Debug, + marker::PhantomData, + path::PathBuf, +}; + +use subxt::{ + blocks::ExtrinsicEvents, + config::ExtrinsicParams, + events::EventDetails, + ext::{ + scale_decode, + scale_encode, + scale_value::{ + Composite, + Value, + ValueDef, + }, + }, + tx::PairSigner, +}; + +/// A contract was successfully instantiated. +#[derive( + Debug, + scale::Decode, + scale::Encode, + scale_decode::DecodeAsType, + scale_encode::EncodeAsType, +)] +#[decode_as_type(trait_bounds = "", crate_path = "subxt::ext::scale_decode")] +#[encode_as_type(crate_path = "subxt::ext::scale_encode")] +pub struct ContractInstantiatedEvent { + /// Account id of the deployer. + pub deployer: E::AccountId, + /// Account id where the contract was instantiated to. + pub contract: E::AccountId, +} + +impl subxt::events::StaticEvent for ContractInstantiatedEvent +where + E: Environment, +{ + const PALLET: &'static str = "Contracts"; + const EVENT: &'static str = "Instantiated"; +} + +/// Code with the specified hash has been stored. +#[derive( + Debug, + scale::Decode, + scale::Encode, + scale_decode::DecodeAsType, + scale_encode::EncodeAsType, +)] +#[decode_as_type(trait_bounds = "", crate_path = "subxt::ext::scale_decode")] +#[encode_as_type(crate_path = "subxt::ext::scale_encode")] +pub struct CodeStoredEvent { + /// Hash under which the contract code was stored. + pub code_hash: E::Hash, +} + +impl subxt::events::StaticEvent for CodeStoredEvent +where + E: Environment, +{ + const PALLET: &'static str = "Contracts"; + const EVENT: &'static str = "CodeStored"; +} diff --git a/crates/e2e/src/lib.rs b/crates/e2e/src/lib.rs index 343361934b..06f2c6e1c2 100644 --- a/crates/e2e/src/lib.rs +++ b/crates/e2e/src/lib.rs @@ -22,6 +22,7 @@ mod builders; mod client; mod default_accounts; +mod events; mod node_proc; mod xts; diff --git a/crates/ink/codegen/src/generator/events.rs b/crates/ink/codegen/src/generator/events.rs index a291d09f35..5f6674291a 100644 --- a/crates/ink/codegen/src/generator/events.rs +++ b/crates/ink/codegen/src/generator/events.rs @@ -76,25 +76,20 @@ impl<'a> Events<'a> { fn generate_event_structs(&'a self) -> impl Iterator + 'a { self.contract.module().events().map(move |event| { let span = event.span(); - let ident = event.ident(); let attrs = event.attrs(); - let fields = event.fields().map(|event_field| { - let span = event_field.span(); - let attrs = event_field.attrs(); - let vis = event_field.vis(); - let ident = event_field.ident(); - let ty = event_field.ty(); - quote_spanned!(span=> - #( #attrs )* - #vis #ident : #ty + // todo: should we just keep this attribute as part of attrs in the first + // place? + let anonymous_attr = event.anonymous.then(|| { + quote_spanned!(span => + #[ink(::anonymous)] ) }); quote_spanned!(span => #( #attrs )* #[derive(::ink::Event, scale::Encode, scale::Decode)] - pub struct #ident { - #( #fields ),* - } + #[cfg_attr(feature = "std", derive(ink::EventMetadata))] + #anonymous_attr + #event ) }) } diff --git a/crates/ink/ir/src/ir/item/event.rs b/crates/ink/ir/src/ir/item/event.rs index 476c6d40b7..388cf279c3 100644 --- a/crates/ink/ir/src/ir/item/event.rs +++ b/crates/ink/ir/src/ir/item/event.rs @@ -96,15 +96,18 @@ impl TryFrom for Event { } }, )?; + // todo: remove generics check and tests. also, how to handle generics? if !item_struct.generics.params.is_empty() { return Err(format_err_spanned!( item_struct.generics.params, "generic ink! event structs are not supported", )) } + // todo: remove this visibility check? utils::ensure_pub_visibility("event structs", struct_span, &item_struct.vis)?; 'repeat: for field in item_struct.fields.iter() { let field_span = field.span(); + // todo: move this check to the new derive macros? let some_cfg_attrs = field .attrs .iter() diff --git a/crates/ink/tests/events_metadata.rs b/crates/ink/tests/events_metadata.rs index 52679bfb1c..8a6057b8a4 100644 --- a/crates/ink/tests/events_metadata.rs +++ b/crates/ink/tests/events_metadata.rs @@ -26,9 +26,7 @@ mod contract { #[ink(storage)] pub struct Contract {} - #[derive(ink::Event, scale::Encode)] - #[cfg_attr(feature = "std", derive(ink::EventMetadata))] - // todo: allow the above to be written as `#[ink(event)]` instead + #[ink(event)] pub struct EventInline { f3: bool, f4: u32, diff --git a/integration-tests/e2e-call-runtime/Cargo.toml b/integration-tests/e2e-call-runtime/Cargo.toml index 7cb65dc428..d4ec118ac8 100644 --- a/integration-tests/e2e-call-runtime/Cargo.toml +++ b/integration-tests/e2e-call-runtime/Cargo.toml @@ -13,7 +13,8 @@ scale-info = { version = "2.6", default-features = false, features = ["derive"], [dev-dependencies] ink_e2e = { path = "../../crates/e2e" } -subxt = { version = "0.28.0", default-features = false } +#subxt = { version = "0.28.0", default-features = false } +subxt = { git = "http://github.com/paritytech/subxt" } [lib] path = "lib.rs" diff --git a/integration-tests/events/lib.rs b/integration-tests/events/lib.rs index 34183c979b..3a51398674 100644 --- a/integration-tests/events/lib.rs +++ b/integration-tests/events/lib.rs @@ -99,32 +99,23 @@ pub mod events { #[ink_e2e::test] async fn it_works(mut client: ink_e2e::Client) -> E2EResult<()> { - // // given - // let constructor = FlipperRef::new(false); - // let contract_acc_id = client - // .instantiate("events", &ink_e2e::alice(), constructor, 0, None) - // .await - // .expect("instantiate failed") - // .account_id; - // - // let get = build_message::(contract_acc_id.clone()) - // .call(|events| events.get()); - // let get_res = client.call_dry_run(&ink_e2e::bob(), &get, 0, None).await; - // assert!(matches!(get_res.return_value(), false)); - // - // // when - // let flip = build_message::(contract_acc_id.clone()) - // .call(|events| events.flip()); - // let _flip_res = client - // .call(&ink_e2e::bob(), flip, 0, None) - // .await - // .expect("flip failed"); - // - // // then - // let get = build_message::(contract_acc_id.clone()) - // .call(|events| events.get()); - // let get_res = client.call_dry_run(&ink_e2e::bob(), &get, 0, None).await; - // assert!(matches!(get_res.return_value(), true)); + // given + let constructor = EventsRef::new(false); + let contract = client + .instantiate("events", &ink_e2e::alice(), constructor, 0, None) + .await + .expect("instantiate failed"); + let mut call = contract.call(); + + // when + let flip = call.flip(); + let flip_res = client + .call(&ink_e2e::bob(), flip, 0, None) + .await + .expect("flip failed"); + + // todo: CotnractEmitted + let events = flip_res.events.find::<>(); // // Ok(()) } From 562b044c09e8960ce77f4f2f70217489e3396c61 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Thu, 1 Jun 2023 10:20:07 +0100 Subject: [PATCH 045/140] Clippy --- crates/ink/macro/src/event/mod.rs | 2 +- crates/ink/src/option_info.rs | 2 +- crates/ink/tests/events_metadata.rs | 6 ++---- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/crates/ink/macro/src/event/mod.rs b/crates/ink/macro/src/event/mod.rs index f693e88f09..0840ff0f74 100644 --- a/crates/ink/macro/src/event/mod.rs +++ b/crates/ink/macro/src/event/mod.rs @@ -161,7 +161,7 @@ fn signature_topic(fields: &syn::Fields, event_ident: &syn::Ident) -> syn::LitSt .map(|field| { quote::ToTokens::to_token_stream(&field.ty) .to_string() - .replace(" ", "") + .replace(' ', "") }) .collect::>() .join(","); diff --git a/crates/ink/src/option_info.rs b/crates/ink/src/option_info.rs index 90120ea3c1..361dc8b612 100644 --- a/crates/ink/src/option_info.rs +++ b/crates/ink/src/option_info.rs @@ -38,7 +38,7 @@ pub trait AsOptionFallback { impl AsOptionFallback for AsOption<'_, T> { #[inline] fn value(&self) -> Option<&T> { - Some(&self.0) + Some(self.0) } } diff --git a/crates/ink/tests/events_metadata.rs b/crates/ink/tests/events_metadata.rs index 8a6057b8a4..ec1368fffd 100644 --- a/crates/ink/tests/events_metadata.rs +++ b/crates/ink/tests/events_metadata.rs @@ -66,13 +66,11 @@ mod tests { .spec() .events() .iter() - .find(|e| e.label() == "EventExternal") - .is_some()); + .any(|e| e.label() == "EventExternal")); assert!(metadata .spec() .events() .iter() - .find(|e| e.label() == "EventInline") - .is_some()); + .any(|e| e.label() == "EventInline")); } } From b83bba90efe10594ca11468db30d40527711bba6 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Thu, 1 Jun 2023 10:23:19 +0100 Subject: [PATCH 046/140] Unnused imports --- crates/e2e/src/client.rs | 10 ++++------ crates/e2e/src/events.rs | 41 +++++----------------------------------- 2 files changed, 9 insertions(+), 42 deletions(-) diff --git a/crates/e2e/src/client.rs b/crates/e2e/src/client.rs index f12bbae2fd..ef499e651e 100644 --- a/crates/e2e/src/client.rs +++ b/crates/e2e/src/client.rs @@ -58,12 +58,10 @@ use subxt::{ blocks::ExtrinsicEvents, config::ExtrinsicParams, events::EventDetails, - ext::{ - scale_value::{ - Composite, - Value, - ValueDef, - }, + ext::scale_value::{ + Composite, + Value, + ValueDef, }, tx::PairSigner, }; diff --git a/crates/e2e/src/events.rs b/crates/e2e/src/events.rs index 951fd5af6d..d8b37cd156 100644 --- a/crates/e2e/src/events.rs +++ b/crates/e2e/src/events.rs @@ -12,44 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -use ink::codegen::ContractCallBuilder; -use ink_env::{ - call::{ - utils::{ - ReturnType, - Set, - }, - Call, - ExecutionInput, - FromAccountId, - }, - Environment, -}; -use ink_primitives::MessageResult; -use pallet_contracts_primitives::ExecReturnValue; -use sp_core::Pair; +use ink_env::Environment; #[cfg(feature = "std")] -use std::{ - collections::BTreeMap, - fmt::Debug, - marker::PhantomData, - path::PathBuf, -}; +use std::fmt::Debug; -use subxt::{ - blocks::ExtrinsicEvents, - config::ExtrinsicParams, - events::EventDetails, - ext::{ - scale_decode, - scale_encode, - scale_value::{ - Composite, - Value, - ValueDef, - }, - }, - tx::PairSigner, +use subxt::ext::{ + scale_decode, + scale_encode, }; /// A contract was successfully instantiated. From 20b2c443b441e5da10286f4ca17c4c00d70124e9 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Thu, 1 Jun 2023 11:12:46 +0100 Subject: [PATCH 047/140] E2E test contract events emitted --- crates/e2e/src/client.rs | 9 +++++ crates/e2e/src/events.rs | 36 ++++++++++++++++--- crates/e2e/src/lib.rs | 2 +- integration-tests/events/event-def/src/lib.rs | 2 +- integration-tests/events/lib.rs | 29 ++++++++------- 5 files changed, 58 insertions(+), 20 deletions(-) diff --git a/crates/e2e/src/client.rs b/crates/e2e/src/client.rs index ef499e651e..a7ca231bfb 100644 --- a/crates/e2e/src/client.rs +++ b/crates/e2e/src/client.rs @@ -54,6 +54,7 @@ use std::{ path::PathBuf, }; +use crate::events; use subxt::{ blocks::ExtrinsicEvents, config::ExtrinsicParams, @@ -201,6 +202,14 @@ where event.pallet_name() == pallet_name && event.variant_name() == variant_name }) } + + /// Returns all the `ContractEmitted` events emitted by the contract. + pub fn contract_emitted_events(&self) -> Vec> { + self.events + .find::>() + .collect::, _>>() + .expect("ContractEmitted events should be decodable") + } } /// We implement a custom `Debug` here, as to avoid requiring the trait diff --git a/crates/e2e/src/events.rs b/crates/e2e/src/events.rs index d8b37cd156..cc923841e0 100644 --- a/crates/e2e/src/events.rs +++ b/crates/e2e/src/events.rs @@ -16,9 +16,12 @@ use ink_env::Environment; #[cfg(feature = "std")] use std::fmt::Debug; -use subxt::ext::{ - scale_decode, - scale_encode, +use subxt::{ + events::StaticEvent, + ext::{ + scale_decode, + scale_encode, + }, }; /// A contract was successfully instantiated. @@ -38,7 +41,7 @@ pub struct ContractInstantiatedEvent { pub contract: E::AccountId, } -impl subxt::events::StaticEvent for ContractInstantiatedEvent +impl StaticEvent for ContractInstantiatedEvent where E: Environment, { @@ -61,10 +64,33 @@ pub struct CodeStoredEvent { pub code_hash: E::Hash, } -impl subxt::events::StaticEvent for CodeStoredEvent +impl StaticEvent for CodeStoredEvent where E: Environment, { const PALLET: &'static str = "Contracts"; const EVENT: &'static str = "CodeStored"; } + +#[derive( + scale::Decode, + scale::Encode, + scale_decode::DecodeAsType, + scale_encode::EncodeAsType, + Debug, +)] +#[decode_as_type(trait_bounds = "", crate_path = "subxt::ext::scale_decode")] +#[encode_as_type(crate_path = "subxt::ext::scale_encode")] +/// A custom event emitted by the contract. +pub struct ContractEmitted { + pub contract: E::AccountId, + pub data: Vec, +} + +impl StaticEvent for ContractEmitted +where + E: Environment, +{ + const PALLET: &'static str = "Contracts"; + const EVENT: &'static str = "ContractEmitted"; +} diff --git a/crates/e2e/src/lib.rs b/crates/e2e/src/lib.rs index 06f2c6e1c2..c56bdd92fc 100644 --- a/crates/e2e/src/lib.rs +++ b/crates/e2e/src/lib.rs @@ -22,7 +22,7 @@ mod builders; mod client; mod default_accounts; -mod events; +pub mod events; mod node_proc; mod xts; diff --git a/integration-tests/events/event-def/src/lib.rs b/integration-tests/events/event-def/src/lib.rs index eb2681ad94..9724bf5b08 100644 --- a/integration-tests/events/event-def/src/lib.rs +++ b/integration-tests/events/event-def/src/lib.rs @@ -3,7 +3,7 @@ #[derive(ink::Event, scale::Encode)] #[cfg_attr(feature = "std", derive(ink::EventMetadata, scale::Decode))] pub struct Flipped { - pub flipped: bool, + pub value: bool, } #[derive(ink::Event, scale::Encode)] diff --git a/integration-tests/events/lib.rs b/integration-tests/events/lib.rs index 3a51398674..d0c4557e92 100644 --- a/integration-tests/events/lib.rs +++ b/integration-tests/events/lib.rs @@ -18,9 +18,8 @@ pub mod events { #[ink(message)] pub fn flip(&mut self) { self.value = !self.value; - self.env().emit_event(event_def::Flipped { - flipped: self.value, - }) + self.env() + .emit_event(event_def::Flipped { value: self.value }) } /// Emit an event with a 32 byte topic. @@ -49,7 +48,7 @@ pub mod events { let decoded_event = ::decode(&mut &event.data[..]) .expect("encountered invalid contract event data buffer"); - assert_eq!(decoded_event.flipped, true); + assert_eq!(decoded_event.value, true); } #[ink::test] @@ -93,31 +92,35 @@ pub mod events { #[cfg(all(test, feature = "e2e-tests"))] mod e2e_tests { use super::*; - use ink_e2e::build_message; type E2EResult = std::result::Result>; #[ink_e2e::test] - async fn it_works(mut client: ink_e2e::Client) -> E2EResult<()> { + async fn emits_shared_event(mut client: ink_e2e::Client) -> E2EResult<()> { // given - let constructor = EventsRef::new(false); + let init_value = false; + let constructor = EventsRef::new(init_value); let contract = client .instantiate("events", &ink_e2e::alice(), constructor, 0, None) .await .expect("instantiate failed"); - let mut call = contract.call(); + let mut call = contract.call::(); // when let flip = call.flip(); let flip_res = client - .call(&ink_e2e::bob(), flip, 0, None) + .call(&ink_e2e::bob(), &flip, 0, None) .await .expect("flip failed"); - // todo: CotnractEmitted - let events = flip_res.events.find::<>(); - // - // Ok(()) + let contract_events = flip_res.contract_emitted_events(); + assert_eq!(1, contract_events.len()); + let flipped: event_def::Flipped = + scale::Decode::decode(&mut &contract_events[0].data[..]) + .expect("encountered invalid contract event data buffer"); + assert_eq!(!init_value, flipped.value); + + Ok(()) } } } From 56ef583b514c7b5f2076280d4a572bb930acc4fa Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Thu, 1 Jun 2023 13:04:10 +0100 Subject: [PATCH 048/140] WIP e2e testing topics --- crates/e2e/Cargo.toml | 3 ++- crates/e2e/src/client.rs | 24 ++++++++++++++----- crates/e2e/src/events.rs | 8 ++++++- crates/env/Cargo.toml | 4 ++-- crates/primitives/Cargo.toml | 4 ++-- integration-tests/e2e-call-runtime/Cargo.toml | 3 +-- integration-tests/events/lib.rs | 4 +++- 7 files changed, 35 insertions(+), 15 deletions(-) diff --git a/crates/e2e/Cargo.toml b/crates/e2e/Cargo.toml index 1e74733c8c..a5876e69b7 100644 --- a/crates/e2e/Cargo.toml +++ b/crates/e2e/Cargo.toml @@ -29,7 +29,8 @@ tokio = { version = "1.18.2", features = ["rt-multi-thread"] } tracing = "0.1.37" tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } scale = { package = "parity-scale-codec", version = "3.4", default-features = false, features = ["derive"] } -subxt = "0.28.0" +#subxt = "0.28.0" +subxt = { git = "http://github.com/paritytech/subxt" } # Substrate pallet-contracts-primitives = "23.0.0" diff --git a/crates/e2e/src/client.rs b/crates/e2e/src/client.rs index a7ca231bfb..d4ca8ff2f6 100644 --- a/crates/e2e/src/client.rs +++ b/crates/e2e/src/client.rs @@ -20,6 +20,7 @@ use super::{ events::{ CodeStoredEvent, ContractInstantiatedEvent, + EventWithTopics, }, log_error, log_info, @@ -204,11 +205,22 @@ where } /// Returns all the `ContractEmitted` events emitted by the contract. - pub fn contract_emitted_events(&self) -> Vec> { - self.events - .find::>() - .collect::, _>>() - .expect("ContractEmitted events should be decodable") + pub fn contract_emitted_events(&self) -> Result, E>>, subxt::Error> + where + E::Hash: From + { + let mut events_with_topics = Vec::new(); + for event in self.events.iter() { + let event = event?; + if let Some(decoded_event) = event.as_event::>()? { + let event_with_topics = EventWithTopics { + event: decoded_event, + topics: event.topics().iter().cloned().map(Into::into).collect(), + }; + events_with_topics.push(event_with_topics); + } + } + Ok(events_with_topics) } } @@ -953,6 +965,6 @@ where } /// Returns true if the give event is System::Extrinsic failed. -fn is_extrinsic_failed_event(event: &EventDetails) -> bool { +fn is_extrinsic_failed_event(event: &EventDetails) -> bool { event.pallet_name() == "System" && event.variant_name() == "ExtrinsicFailed" } diff --git a/crates/e2e/src/events.rs b/crates/e2e/src/events.rs index cc923841e0..1986e89556 100644 --- a/crates/e2e/src/events.rs +++ b/crates/e2e/src/events.rs @@ -87,10 +87,16 @@ pub struct ContractEmitted { pub data: Vec, } -impl StaticEvent for ContractEmitted +impl StaticEvent for ContractEmitted where E: Environment, { const PALLET: &'static str = "Contracts"; const EVENT: &'static str = "ContractEmitted"; } + +/// A decoded event with its associated topics. +pub struct EventWithTopics { + pub topics: Vec, + pub event: T, +} diff --git a/crates/env/Cargo.toml b/crates/env/Cargo.toml index 3dd14ec405..eb10f7b8a9 100644 --- a/crates/env/Cargo.toml +++ b/crates/env/Cargo.toml @@ -47,8 +47,8 @@ secp256k1 = { version = "0.27.0", features = ["recovery", "global-context"], opt # # Sadly couldn't be marked as dev-dependency. # Never use this crate outside the off-chain environment! -scale-decode = { version = "0.5.0", default-features = false, optional = true } -scale-encode = { version = "0.1.0", default-features = false, optional = true } +scale-decode = { version = "0.7.0", default-features = false, optional = true } +scale-encode = { version = "0.3.0", default-features = false, optional = true } scale-info = { version = "2.6", default-features = false, features = ["derive"], optional = true } [dev-dependencies] diff --git a/crates/primitives/Cargo.toml b/crates/primitives/Cargo.toml index 4d1228b02b..b2a3209259 100644 --- a/crates/primitives/Cargo.toml +++ b/crates/primitives/Cargo.toml @@ -18,8 +18,8 @@ include = ["/Cargo.toml", "src/**/*.rs", "/README.md", "/LICENSE"] derive_more = { version = "0.99", default-features = false, features = ["from", "display"] } ink_prelude = { version = "4.2.0", path = "../prelude/", default-features = false } scale = { package = "parity-scale-codec", version = "3.4", default-features = false, features = ["derive"] } -scale-decode = { version = "0.5.0", default-features = false, features = ["derive"], optional = true } -scale-encode = { version = "0.1.0", default-features = false, features = ["derive"], optional = true } +scale-decode = { version = "0.7.0", default-features = false, features = ["derive"], optional = true } +scale-encode = { version = "0.3.0", default-features = false, features = ["derive"], optional = true } scale-info = { version = "2.6", default-features = false, features = ["derive"], optional = true } xxhash-rust = { version = "0.8", features = ["const_xxh32"] } diff --git a/integration-tests/e2e-call-runtime/Cargo.toml b/integration-tests/e2e-call-runtime/Cargo.toml index d4ec118ac8..7cb65dc428 100644 --- a/integration-tests/e2e-call-runtime/Cargo.toml +++ b/integration-tests/e2e-call-runtime/Cargo.toml @@ -13,8 +13,7 @@ scale-info = { version = "2.6", default-features = false, features = ["derive"], [dev-dependencies] ink_e2e = { path = "../../crates/e2e" } -#subxt = { version = "0.28.0", default-features = false } -subxt = { git = "http://github.com/paritytech/subxt" } +subxt = { version = "0.28.0", default-features = false } [lib] path = "lib.rs" diff --git a/integration-tests/events/lib.rs b/integration-tests/events/lib.rs index d0c4557e92..e3ff8dc4bd 100644 --- a/integration-tests/events/lib.rs +++ b/integration-tests/events/lib.rs @@ -113,13 +113,15 @@ pub mod events { .await .expect("flip failed"); - let contract_events = flip_res.contract_emitted_events(); + let contract_events = flip_res.contract_emitted_events()?; assert_eq!(1, contract_events.len()); let flipped: event_def::Flipped = scale::Decode::decode(&mut &contract_events[0].data[..]) .expect("encountered invalid contract event data buffer"); assert_eq!(!init_value, flipped.value); + // todo check topics + Ok(()) } } From dbd0dbeecce927c04435c5fc45078daaa437fda6 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Thu, 1 Jun 2023 13:44:51 +0100 Subject: [PATCH 049/140] Add e2e test which checks topics --- crates/e2e/src/client.rs | 6 ++++-- crates/e2e/src/events.rs | 6 +++--- integration-tests/events/lib.rs | 14 ++++++++++++-- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/crates/e2e/src/client.rs b/crates/e2e/src/client.rs index d4ca8ff2f6..425cf429f5 100644 --- a/crates/e2e/src/client.rs +++ b/crates/e2e/src/client.rs @@ -205,9 +205,11 @@ where } /// Returns all the `ContractEmitted` events emitted by the contract. - pub fn contract_emitted_events(&self) -> Result, E>>, subxt::Error> + pub fn contract_emitted_events( + &self, + ) -> Result>>, subxt::Error> where - E::Hash: From + C::Hash: Into, { let mut events_with_topics = Vec::new(); for event in self.events.iter() { diff --git a/crates/e2e/src/events.rs b/crates/e2e/src/events.rs index 1986e89556..0bf7ff54c7 100644 --- a/crates/e2e/src/events.rs +++ b/crates/e2e/src/events.rs @@ -87,7 +87,7 @@ pub struct ContractEmitted { pub data: Vec, } -impl StaticEvent for ContractEmitted +impl StaticEvent for ContractEmitted where E: Environment, { @@ -96,7 +96,7 @@ where } /// A decoded event with its associated topics. -pub struct EventWithTopics { - pub topics: Vec, +pub struct EventWithTopics { + pub topics: Vec, pub event: T, } diff --git a/integration-tests/events/lib.rs b/integration-tests/events/lib.rs index e3ff8dc4bd..0b598de504 100644 --- a/integration-tests/events/lib.rs +++ b/integration-tests/events/lib.rs @@ -92,6 +92,7 @@ pub mod events { #[cfg(all(test, feature = "e2e-tests"))] mod e2e_tests { use super::*; + use ink_e2e::H256; type E2EResult = std::result::Result>; @@ -114,13 +115,22 @@ pub mod events { .expect("flip failed"); let contract_events = flip_res.contract_emitted_events()?; + + // then assert_eq!(1, contract_events.len()); + let contract_event = &contract_events[0]; let flipped: event_def::Flipped = - scale::Decode::decode(&mut &contract_events[0].data[..]) + scale::Decode::decode(&mut &contract_event.event.data[..]) .expect("encountered invalid contract event data buffer"); assert_eq!(!init_value, flipped.value); - // todo check topics + let signature_topic = + ::SIGNATURE_TOPIC + .map(|topic| H256::from(topic)) + .unwrap(); + + let expected_topics = vec![signature_topic]; + assert_eq!(expected_topics, contract_event.topics); Ok(()) } From 916399f88c85fc0c700df225fc58714056825fe0 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Thu, 8 Jun 2023 15:27:29 +0100 Subject: [PATCH 050/140] Add signature topic to event spec metadata --- crates/ink/macro/src/event/metadata.rs | 4 +- crates/ink/macro/src/tests/event.rs | 4 ++ crates/ink/macro/src/tests/event_metadata.rs | 2 + crates/metadata/src/specs.rs | 43 +++++++++++++++++++- 4 files changed, 51 insertions(+), 2 deletions(-) diff --git a/crates/ink/macro/src/event/metadata.rs b/crates/ink/macro/src/event/metadata.rs index f8865105a5..2516e78337 100644 --- a/crates/ink/macro/src/event/metadata.rs +++ b/crates/ink/macro/src/event/metadata.rs @@ -50,7 +50,9 @@ fn event_metadata_derive_struct(s: synstructure::Structure) -> TokenStream2 { // todo: check that cfg attributes work here ::ink::metadata::EventSpec::new(::core::stringify!(#ident)) - // todo: add signanture topic if not anonymous + .signature_topic( + ::SIGNATURE_TOPIC + ) // todo: add fields, with topic flag. .args([ // #( #args ),* diff --git a/crates/ink/macro/src/tests/event.rs b/crates/ink/macro/src/tests/event.rs index d014833b46..3a8983487e 100644 --- a/crates/ink/macro/src/tests/event.rs +++ b/crates/ink/macro/src/tests/event.rs @@ -26,6 +26,7 @@ use crate::event::event_derive; fn unit_struct_works() { crate::test_derive! { event_derive { + #[derive(scale::Encode)] struct UnitStruct; } expands to { @@ -66,6 +67,7 @@ fn unit_struct_works() { fn unit_struct_anonymous_has_no_topics() { crate::test_derive! { event_derive { + #[derive(scale::Encode)] #[ink(anonymous)] struct UnitStruct; } @@ -106,6 +108,7 @@ fn unit_struct_anonymous_has_no_topics() { fn struct_with_fields_no_topics() { crate::test_derive! { event_derive { + #[derive(scale::Encode)] struct Event { field_1: u32, field_2: u64, @@ -150,6 +153,7 @@ fn struct_with_fields_no_topics() { fn struct_with_fields_and_some_topics() { crate::test_derive! { event_derive { + #[derive(scale::Encode)] struct Event { field_1: u32, #[ink(topic)] diff --git a/crates/ink/macro/src/tests/event_metadata.rs b/crates/ink/macro/src/tests/event_metadata.rs index 0900706052..676cfcbaf9 100644 --- a/crates/ink/macro/src/tests/event_metadata.rs +++ b/crates/ink/macro/src/tests/event_metadata.rs @@ -18,6 +18,7 @@ use crate::event::event_metadata_derive; fn unit_struct_works() { crate::test_derive! { event_metadata_derive { + #[derive(ink::Event, scale::Encode)] struct UnitStruct; } expands to { @@ -30,6 +31,7 @@ fn unit_struct_works() { ::event_spec; ::ink::metadata::EventSpec::new(::core::stringify!(UnitStruct)) + .signature_topic(::SIGNATURE_TOPIC) .args([]) .docs([]) .done() diff --git a/crates/metadata/src/specs.rs b/crates/metadata/src/specs.rs index 3780c0f62a..a896358ab3 100644 --- a/crates/metadata/src/specs.rs +++ b/crates/metadata/src/specs.rs @@ -16,7 +16,11 @@ use crate::{ serde_hex, - utils::trim_extra_whitespace, + utils::{ + deserialize_from_byte_str, + serialize_as_byte_str, + trim_extra_whitespace, + }, }; #[cfg(not(feature = "std"))] use alloc::{ @@ -854,12 +858,36 @@ impl IntoPortable for MessageSpec { pub struct EventSpec { /// The label of the event. label: F::String, + /// The signature topic of the event. `None` if the event is anonymous. + signature_topic: Option, /// The event arguments. args: Vec>, /// The event documentation. docs: Vec, } +/// The value of the signature topic for a non anonymous event. +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] +#[serde(transparent)] +pub struct SignatureTopic { + #[serde( + serialize_with = "serialize_as_byte_str", + deserialize_with = "deserialize_from_byte_str" + )] + bytes: Vec, +} + +impl From for SignatureTopic +where + T: AsRef<[u8]>, +{ + fn from(bytes: T) -> Self { + SignatureTopic { + bytes: bytes.as_ref().to_vec(), + } + } +} + /// An event specification builder. #[must_use] pub struct EventSpecBuilder @@ -884,6 +912,17 @@ where this } + /// Sets the signature topic of the event specification. + pub fn signature_topic(self, topic: Option) -> Self + where + T: AsRef<[u8]>, + { + let mut this = self; + debug_assert!(this.spec.signature_topic.is_none()); + this.spec.signature_topic = topic.as_ref().map(SignatureTopic::from); + this + } + /// Sets the input arguments of the event specification. pub fn docs<'a, D>(self, docs: D) -> Self where @@ -911,6 +950,7 @@ impl IntoPortable for EventSpec { fn into_portable(self, registry: &mut Registry) -> Self::Output { EventSpec { label: self.label.to_string(), + signature_topic: self.signature_topic, args: self .args .into_iter() @@ -930,6 +970,7 @@ where EventSpecBuilder { spec: Self { label, + signature_topic: None, args: Vec::new(), docs: Vec::new(), }, From e1eba71d6408a557588dcf3e25924cfbac03c55c Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Fri, 9 Jun 2023 11:38:35 +0100 Subject: [PATCH 051/140] Use Result in EventMetadata derive --- crates/ink/macro/src/event/metadata.rs | 64 ++++++++++++++++++-------- crates/ink/macro/src/event/mod.rs | 21 +++------ crates/metadata/src/specs.rs | 8 ++-- 3 files changed, 55 insertions(+), 38 deletions(-) diff --git a/crates/ink/macro/src/event/metadata.rs b/crates/ink/macro/src/event/metadata.rs index 2516e78337..48db94868e 100644 --- a/crates/ink/macro/src/event/metadata.rs +++ b/crates/ink/macro/src/event/metadata.rs @@ -22,22 +22,49 @@ pub fn event_metadata_derive(mut s: synstructure::Structure) -> TokenStream2 { .add_bounds(synstructure::AddBounds::Fields) .underscore_const(true); match &s.ast().data { - syn::Data::Struct(_) => event_metadata_derive_struct(s), + syn::Data::Struct(_) => { + event_metadata_derive_struct(s).unwrap_or_else(|err| err.to_compile_error()) + } _ => { - panic!("can only derive `EventMetadata` for Rust `struct` items") + syn::Error::new( + s.ast().span(), + "can only derive `EventMetadata` for Rust `struct` items", + ) + .to_compile_error() } } } /// `Event` derive implementation for `struct` types. -fn event_metadata_derive_struct(s: synstructure::Structure) -> TokenStream2 { +fn event_metadata_derive_struct(s: synstructure::Structure) -> syn::Result { assert_eq!(s.variants().len(), 1, "can only operate on structs"); let span = s.ast().span(); let variant = &s.variants()[0]; let ident = variant.ast().ident; - s.bound_impl( + let args = variant.bindings().iter().map( |field| { + let field_ty = &field.ast().ty; + let field_span = field_ty.span(); + if let Some(field_name) = field.ast().ident.as_ref() { + let indexed = super::has_ink_attribute(&field.ast().attrs, "indexed") + .unwrap(); + Ok(quote_spanned!(field_span => + ::ink::metadata::EventParamSpec::new(::core::stringify!(#field_name)) + .of_type::<#field_ty>() + .indexed(#indexed) + // .docs + .done() + )) + } else { + Err(syn::Error::new( + field_span, + "can only derive `EventMetadata` for Rust `struct` items with named fields", + )) + } + }).collect::>>()?; + + Ok(s.bound_impl( quote_spanned!(span=> ::ink::metadata::EventMetadata), quote_spanned!(span=> fn event_spec() -> ::ink::metadata::EventSpec { @@ -48,21 +75,20 @@ fn event_metadata_derive_struct(s: synstructure::Structure) -> TokenStream2 { static EVENT_METADATA: fn() -> ::ink::metadata::EventSpec = <#ident as ::ink::metadata::EventMetadata>::event_spec; - // todo: check that cfg attributes work here - ::ink::metadata::EventSpec::new(::core::stringify!(#ident)) - .signature_topic( - ::SIGNATURE_TOPIC - ) - // todo: add fields, with topic flag. - .args([ - // #( #args ),* - ]) - // todo: add docs - .docs([ - // #( #docs ),* - ]) - .done() + // todo: check that cfg attributes work here + ::ink::metadata::EventSpec::new(::core::stringify!(#ident)) + .signature_topic( + ::SIGNATURE_TOPIC + ) + .args([ + #( #args ),* + ]) + // todo: add docs + .docs([ + // #( #docs ),* + ]) + .done() } ), - ) + )) } diff --git a/crates/ink/macro/src/event/mod.rs b/crates/ink/macro/src/event/mod.rs index 0840ff0f74..a3fc3888ba 100644 --- a/crates/ink/macro/src/event/mod.rs +++ b/crates/ink/macro/src/event/mod.rs @@ -49,23 +49,9 @@ fn event_derive_struct(mut s: synstructure::Structure) -> syn::Result - // <#ty as ::ink::storage::traits::Storable>::decode(__input)? - // ) - // }); - // let encode_body = variant.each(|binding| { - // let span = binding.ast().ty.span(); - // quote_spanned!(span => - // ::ink::storage::traits::Storable::encode(#binding, __dest); - // ) - // }); - let mut topic_err: Option = None; s.variants_mut()[0].filter(|bi| { - match has_ink_attribute(&bi.ast().attrs, "topic") { + match has_ink_topic_attribute(&bi.ast().attrs) { Ok(has_attr) => has_attr, Err(err) => { match topic_err { @@ -169,6 +155,11 @@ fn signature_topic(fields: &syn::Fields, event_ident: &syn::Ident) -> syn::LitSt syn::parse_quote!( #topic_str ) } +/// Checks if the given attributes contain an `#[ink(topic)]` attribute. +fn has_ink_topic_attribute(attrs: &[syn::Attribute]) -> syn::Result { + has_ink_attribute(attrs, "topic") +} + /// Checks if the given attributes contain an `ink` attribute with the given path. fn has_ink_attribute(attrs: &[syn::Attribute], path: &str) -> syn::Result { let ink_attrs: Vec<_> = attrs diff --git a/crates/metadata/src/specs.rs b/crates/metadata/src/specs.rs index a896358ab3..cec0d14202 100644 --- a/crates/metadata/src/specs.rs +++ b/crates/metadata/src/specs.rs @@ -1203,7 +1203,7 @@ where pub struct EventParamSpec { /// The label of the parameter. label: F::String, - /// If the event parameter is indexed. + /// If the event parameter is indexed as a topic. indexed: bool, /// The type of the parameter. #[serde(rename = "type")] @@ -1235,7 +1235,7 @@ where EventParamSpecBuilder { spec: Self { label, - // By default event parameters are not indexed. + // By default event parameters are not indexed as topics. indexed: false, // We initialize every parameter type as `()`. ty: Default::default(), @@ -1249,7 +1249,7 @@ where &self.label } - /// Returns true if the event parameter is indexed. + /// Returns true if the event parameter is indexed as a topic. pub fn indexed(&self) -> bool { self.indexed } @@ -1286,7 +1286,7 @@ where this } - /// If the event parameter is indexed. + /// If the event parameter is indexed as a topic. pub fn indexed(self, is_indexed: bool) -> Self { let mut this = self; this.spec.indexed = is_indexed; From 39bd2d9dfb07e7b48c221382e5f06f0ea1cd981f Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Fri, 9 Jun 2023 16:05:19 +0100 Subject: [PATCH 052/140] Tests for generating events metadata --- crates/ink/macro/src/event/metadata.rs | 5 +- crates/ink/macro/src/tests/event_metadata.rs | 92 ++++++++++++++++++++ 2 files changed, 94 insertions(+), 3 deletions(-) diff --git a/crates/ink/macro/src/event/metadata.rs b/crates/ink/macro/src/event/metadata.rs index 48db94868e..464a2311c5 100644 --- a/crates/ink/macro/src/event/metadata.rs +++ b/crates/ink/macro/src/event/metadata.rs @@ -47,11 +47,10 @@ fn event_metadata_derive_struct(s: synstructure::Structure) -> syn::Result ::ink::metadata::EventParamSpec::new(::core::stringify!(#field_name)) - .of_type::<#field_ty>() + .of_type(::ink::metadata::TypeSpec::with_name_str::<#field_ty>(::core::stringify!(#field_ty))) .indexed(#indexed) // .docs .done() diff --git a/crates/ink/macro/src/tests/event_metadata.rs b/crates/ink/macro/src/tests/event_metadata.rs index 676cfcbaf9..4f27cf834a 100644 --- a/crates/ink/macro/src/tests/event_metadata.rs +++ b/crates/ink/macro/src/tests/event_metadata.rs @@ -41,3 +41,95 @@ fn unit_struct_works() { } } } + +#[test] +fn struct_with_fields_no_topics() { + crate::test_derive! { + event_metadata_derive { + #[derive(ink::Event, scale::Encode)] + struct Event { + field_1: u32, + field_2: u64, + field_3: u128, + } + } + expands to { + const _: () = { + impl ::ink::metadata::EventMetadata for Event { + fn event_spec() -> ::ink::metadata::EventSpec { + #[::ink::metadata::linkme::distributed_slice(::ink::metadata::EVENTS)] + #[linkme(crate = ::ink::metadata::linkme)] + static EVENT_METADATA: fn() -> ::ink::metadata::EventSpec = + ::event_spec; + + ::ink::metadata::EventSpec::new(::core::stringify!(Event)) + .signature_topic(::SIGNATURE_TOPIC) + .args([ + ::ink::metadata::EventParamSpec::new(::core::stringify!(field_1)) + .of_type(::ink::metadata::TypeSpec::with_name_str::(::core::stringify!(u32))) + .indexed(false) + .done(), + ::ink::metadata::EventParamSpec::new(::core::stringify!(field_2)) + .of_type(::ink::metadata::TypeSpec::with_name_str::(::core::stringify!(u64))) + .indexed(false) + .done(), + ::ink::metadata::EventParamSpec::new(::core::stringify!(field_3)) + .of_type(::ink::metadata::TypeSpec::with_name_str::(::core::stringify!(u128))) + .indexed(false) + .done() + ]) + .docs([]) + .done() + } + } + }; + } + } +} + +#[test] +fn struct_with_fields_and_some_topics() { + crate::test_derive! { + event_metadata_derive { + #[derive(ink::Event, scale::Encode)] + struct Event { + field_1: u32, + #[ink(topic)] + field_2: u64, + #[ink(topic)] + field_3: u128, + } + } + expands to { + const _: () = { + impl ::ink::metadata::EventMetadata for Event { + fn event_spec() -> ::ink::metadata::EventSpec { + #[::ink::metadata::linkme::distributed_slice(::ink::metadata::EVENTS)] + #[linkme(crate = ::ink::metadata::linkme)] + static EVENT_METADATA: fn() -> ::ink::metadata::EventSpec = + ::event_spec; + + ::ink::metadata::EventSpec::new(::core::stringify!(Event)) + .signature_topic(::SIGNATURE_TOPIC) + .args([ + ::ink::metadata::EventParamSpec::new(::core::stringify!(field_1)) + .of_type(::ink::metadata::TypeSpec::with_name_str::(::core::stringify!(u32))) + .indexed(false) + .done(), + ::ink::metadata::EventParamSpec::new(::core::stringify!(field_2)) + .of_type(::ink::metadata::TypeSpec::with_name_str::(::core::stringify!(u64))) + .indexed(true) + .done(), + ::ink::metadata::EventParamSpec::new(::core::stringify!(field_3)) + .of_type(::ink::metadata::TypeSpec::with_name_str::(::core::stringify!(u128))) + .indexed(true) + .done() + ]) + .docs([]) + .done() + } + } + }; + } + } +} From d97c51ebc9c11c4432cb4a8f40c2ea10aceeba7c Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Fri, 9 Jun 2023 17:37:40 +0100 Subject: [PATCH 053/140] Remove max topics len compile time check --- crates/env/src/api.rs | 4 ++-- crates/env/src/backend.rs | 2 +- crates/env/src/engine/off_chain/impls.rs | 4 ++-- crates/env/src/engine/on_chain/impls.rs | 2 +- crates/env/src/event.rs | 4 +--- crates/ink/codegen/src/generator/events.rs | 28 ---------------------- crates/ink/macro/src/event/mod.rs | 9 ++----- crates/ink/macro/src/tests/event.rs | 26 ++++++-------------- crates/ink/src/env_access.rs | 12 +++++----- 9 files changed, 22 insertions(+), 69 deletions(-) diff --git a/crates/env/src/api.rs b/crates/env/src/api.rs index 9ffe0ad2af..b66eda4bfb 100644 --- a/crates/env/src/api.rs +++ b/crates/env/src/api.rs @@ -175,13 +175,13 @@ where } /// Emits an event with the given event data. -pub fn emit_event(event: Evt) +pub fn emit_event(event: Evt) where E: Environment, Evt: Event, { ::on_instance(|instance| { - TypedEnvBackend::emit_event::(instance, event) + TypedEnvBackend::emit_event::(instance, event) }) } diff --git a/crates/env/src/backend.rs b/crates/env/src/backend.rs index 4a59ed7344..f9227bc163 100644 --- a/crates/env/src/backend.rs +++ b/crates/env/src/backend.rs @@ -405,7 +405,7 @@ pub trait TypedEnvBackend: EnvBackend { /// # Note /// /// For more details visit: [`emit_event`][`crate::emit_event`] - fn emit_event(&mut self, event: Evt) + fn emit_event(&mut self, event: Evt) where E: Environment, Evt: Event; diff --git a/crates/env/src/engine/off_chain/impls.rs b/crates/env/src/engine/off_chain/impls.rs index c7e9253995..a18ad0652d 100644 --- a/crates/env/src/engine/off_chain/impls.rs +++ b/crates/env/src/engine/off_chain/impls.rs @@ -422,13 +422,13 @@ impl TypedEnvBackend for EnvInstance { }) } - fn emit_event(&mut self, event: Evt) + fn emit_event(&mut self, event: Evt) where E: Environment, Evt: Event, { let builder = TopicsBuilder::default(); - let enc_topics = event.topics::(builder.into()); + let enc_topics = event.topics::(builder.into()); let enc_data = &scale::Encode::encode(&event)[..]; self.engine.deposit_event(&enc_topics[..], enc_data); } diff --git a/crates/env/src/engine/on_chain/impls.rs b/crates/env/src/engine/on_chain/impls.rs index 98dc8f806e..a1d06b569c 100644 --- a/crates/env/src/engine/on_chain/impls.rs +++ b/crates/env/src/engine/on_chain/impls.rs @@ -392,7 +392,7 @@ impl TypedEnvBackend for EnvInstance { self.get_property_little_endian::(ext::minimum_balance) } - fn emit_event(&mut self, event: Evt) + fn emit_event(&mut self, event: Evt) where E: Environment, Evt: Event, diff --git a/crates/env/src/event.rs b/crates/env/src/event.rs index 0aea3c6270..614025ed98 100644 --- a/crates/env/src/event.rs +++ b/crates/env/src/event.rs @@ -200,14 +200,12 @@ pub trait Event: scale::Encode { /// builder. type RemainingTopics: EventTopicsAmount; - const TOPICS_LEN: usize; - /// The unique signature topic of the event. `None` for anonymous events. /// todo: document how this is calculated const SIGNATURE_TOPIC: Option<[u8; 32]>; /// Guides event topic serialization using the given topics builder. - fn topics( + fn topics( &self, builder: TopicsBuilder, ) -> >::Output diff --git a/crates/ink/codegen/src/generator/events.rs b/crates/ink/codegen/src/generator/events.rs index 5f6674291a..48f162a2cb 100644 --- a/crates/ink/codegen/src/generator/events.rs +++ b/crates/ink/codegen/src/generator/events.rs @@ -30,13 +30,11 @@ impl_as_ref_for_generator!(Events); impl GenerateCode for Events<'_> { fn generate_code(&self) -> TokenStream2 { - let emit_event_trait_impl = self.generate_emit_event_trait_impl(); // let event_base = self.generate_event_base(); // let topic_guards = self.generate_topic_guards(); // let topics_impls = self.generate_topics_impls(); let event_structs = self.generate_event_structs(); quote! { - #emit_event_trait_impl // #event_base // #( #topic_guards )* #( #event_structs )* @@ -46,32 +44,6 @@ impl GenerateCode for Events<'_> { } impl<'a> Events<'a> { - /// todo: comments - fn generate_emit_event_trait_impl(&self) -> TokenStream2 { - quote! { - /// Local trait for emitting events, allowing extending of `EnvAccess` and to include - /// a compile time check ensuring max number of topics not exceeded. - pub trait __ink_EmitEvent { - fn emit_event(self, event: E) - where - E: ::ink::env::Event + ::scale::Encode; - } - - impl<'a> __ink_EmitEvent for ::ink::EnvAccess<'a, Environment> { - fn emit_event(self, event: E) - where - E: ::ink::env::Event + ::scale::Encode, - { - ::ink::env::emit_event::< - { ::MAX_EVENT_TOPICS }, - Environment, - E, - >(event); - } - } - } - } - /// Generates all the user defined event struct definitions. fn generate_event_structs(&'a self) -> impl Iterator + 'a { self.contract.module().events().map(move |event| { diff --git a/crates/ink/macro/src/event/mod.rs b/crates/ink/macro/src/event/mod.rs index a3fc3888ba..a39e74a142 100644 --- a/crates/ink/macro/src/event/mod.rs +++ b/crates/ink/macro/src/event/mod.rs @@ -75,7 +75,7 @@ fn event_derive_struct(mut s: synstructure::Structure) -> syn::Result quote_spanned!(span=> ::ink::env::event::state::NoRemainingTopics), _ => { - quote_spanned!(span=> [::ink::env::event::state::HasRemainingTopics; Self::TOPICS_LEN]) + quote_spanned!(span=> [::ink::env::event::state::HasRemainingTopics; #len_topics]) } }; @@ -116,10 +116,9 @@ fn event_derive_struct(mut s: synstructure::Structure) -> syn::Result = #signature_topic; - fn topics( + fn topics( &self, builder: ::ink::env::event::TopicsBuilder<::ink::env::event::state::Uninit, E, B>, ) -> >::Output @@ -127,10 +126,6 @@ fn event_derive_struct(mut s: synstructure::Structure) -> syn::Result, { - // assert at compile time the number of topics defined by this event is within the - // given limit. - let _ = ::ink::codegen::EventRespectsTopicLimit::<{ Self::TOPICS_LEN }, { MAX_TOPICS }>::ASSERT; - match self { #topics_builder } diff --git a/crates/ink/macro/src/tests/event.rs b/crates/ink/macro/src/tests/event.rs index 3a8983487e..924564fc4c 100644 --- a/crates/ink/macro/src/tests/event.rs +++ b/crates/ink/macro/src/tests/event.rs @@ -32,13 +32,12 @@ fn unit_struct_works() { expands to { const _: () = { impl ::ink::env::Event for UnitStruct { - type RemainingTopics = [::ink::env::event::state::HasRemainingTopics; Self::TOPICS_LEN]; + type RemainingTopics = [::ink::env::event::state::HasRemainingTopics; 1usize]; - const TOPICS_LEN: usize = 1usize; const SIGNATURE_TOPIC: ::core::option::Option<[::core::primitive::u8; 32]> = ::core::option::Option::Some( ::ink::blake2x256!("UnitStruct()") ); - fn topics( + fn topics( &self, builder: ::ink::env::event::TopicsBuilder<::ink::env::event::state::Uninit, E, B>, ) -> >::Output @@ -46,8 +45,6 @@ fn unit_struct_works() { E: ::ink::env::Environment, B: ::ink::env::event::TopicsBuilderBackend, { - let _ = ::ink::codegen::EventRespectsTopicLimit::<{ Self::TOPICS_LEN }, { MAX_TOPICS }>::ASSERT; - match self { UnitStruct => { builder @@ -76,11 +73,10 @@ fn unit_struct_anonymous_has_no_topics() { impl ::ink::env::Event for UnitStruct { type RemainingTopics = ::ink::env::event::state::NoRemainingTopics; - const TOPICS_LEN: usize = 0usize; const SIGNATURE_TOPIC: ::core::option::Option<[::core::primitive::u8; 32]> = ::core::option::Option::None; - fn topics( + fn topics( &self, builder: ::ink::env::event::TopicsBuilder<::ink::env::event::state::Uninit, E, B>, ) -> >::Output @@ -88,8 +84,6 @@ fn unit_struct_anonymous_has_no_topics() { E: ::ink::env::Environment, B: ::ink::env::event::TopicsBuilderBackend, { - let _ = ::ink::codegen::EventRespectsTopicLimit::<{ Self::TOPICS_LEN }, { MAX_TOPICS }>::ASSERT; - match self { UnitStruct => { builder @@ -118,13 +112,12 @@ fn struct_with_fields_no_topics() { expands to { const _: () = { impl ::ink::env::Event for Event { - type RemainingTopics = [::ink::env::event::state::HasRemainingTopics; Self::TOPICS_LEN]; + type RemainingTopics = [::ink::env::event::state::HasRemainingTopics; 1usize]; - const TOPICS_LEN: usize = 1usize; const SIGNATURE_TOPIC: ::core::option::Option<[::core::primitive::u8; 32]> = ::core::option::Option::Some( ::ink::blake2x256!("Event(u32,u64,u128)") ); - fn topics( + fn topics( &self, builder: ::ink::env::event::TopicsBuilder<::ink::env::event::state::Uninit, E, B>, ) -> >::Output @@ -132,8 +125,6 @@ fn struct_with_fields_no_topics() { E: ::ink::env::Environment, B: ::ink::env::event::TopicsBuilderBackend, { - let _ = ::ink::codegen::EventRespectsTopicLimit::<{ Self::TOPICS_LEN }, { MAX_TOPICS }>::ASSERT; - match self { Event { .. } => { builder @@ -165,13 +156,12 @@ fn struct_with_fields_and_some_topics() { expands to { const _: () = { impl ::ink::env::Event for Event { - type RemainingTopics = [::ink::env::event::state::HasRemainingTopics; Self::TOPICS_LEN]; + type RemainingTopics = [::ink::env::event::state::HasRemainingTopics; 3usize]; - const TOPICS_LEN: usize = 3usize; const SIGNATURE_TOPIC: ::core::option::Option<[::core::primitive::u8; 32]> = ::core::option::Option::Some( ::ink::blake2x256!("Event(u32,u64,u128)") ); - fn topics( + fn topics( &self, builder: ::ink::env::event::TopicsBuilder<::ink::env::event::state::Uninit, E, B>, ) -> >::Output @@ -179,8 +169,6 @@ fn struct_with_fields_and_some_topics() { E: ::ink::env::Environment, B: ::ink::env::event::TopicsBuilderBackend, { - let _ = ::ink::codegen::EventRespectsTopicLimit::<{ Self::TOPICS_LEN }, { MAX_TOPICS }>::ASSERT; - match self { Event { field_2 : __binding_1 , field_3 : __binding_2 , .. } => { builder diff --git a/crates/ink/src/env_access.rs b/crates/ink/src/env_access.rs index f4b0b8c6f1..f8772b67e4 100644 --- a/crates/ink/src/env_access.rs +++ b/crates/ink/src/env_access.rs @@ -414,12 +414,12 @@ where } /// Emits an event. - // pub fn emit_event(self, event: Evt) - // where - // Evt: ink_env::Topics + scale::Encode, - // { - // ink_env::emit_event::(event) - // } + pub fn emit_event(self, event: Evt) + where + Evt: ink_env::Event + { + ink_env::emit_event::(event) + } /// Instantiates another contract. /// From c74da8c4b3aa5268f2728584ba67f074d4f1d5cc Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Mon, 12 Jun 2023 17:47:31 +0100 Subject: [PATCH 054/140] Add failing test for topics len validation at metadata generation time --- crates/ink/macro/src/event/metadata.rs | 6 +- crates/ink/macro/src/tests/event_metadata.rs | 6 ++ crates/ink/src/env_access.rs | 2 +- crates/metadata/src/lib.rs | 3 + crates/metadata/src/specs.rs | 15 +++++ crates/metadata/src/tests.rs | 69 ++++++++++++++++++++ 6 files changed, 98 insertions(+), 3 deletions(-) diff --git a/crates/ink/macro/src/event/metadata.rs b/crates/ink/macro/src/event/metadata.rs index 464a2311c5..237236c45b 100644 --- a/crates/ink/macro/src/event/metadata.rs +++ b/crates/ink/macro/src/event/metadata.rs @@ -66,7 +66,9 @@ fn event_metadata_derive_struct(s: synstructure::Structure) -> syn::Result ::ink::metadata::EventMetadata), quote_spanned!(span=> - fn event_spec() -> ::ink::metadata::EventSpec { + const MODULE_PATH: &'static str = ::core::module_path!(); + + fn event_spec() -> ::ink::metadata::EventSpec { // register this event metadata function in the distributed slice for combining all // events referenced in the contract binary. #[::ink::metadata::linkme::distributed_slice(::ink::metadata::EVENTS)] @@ -87,7 +89,7 @@ fn event_metadata_derive_struct(s: synstructure::Structure) -> syn::Result ::ink::metadata::EventSpec { #[::ink::metadata::linkme::distributed_slice(::ink::metadata::EVENTS)] #[linkme(crate = ::ink::metadata::linkme)] @@ -56,6 +58,8 @@ fn struct_with_fields_no_topics() { expands to { const _: () = { impl ::ink::metadata::EventMetadata for Event { + const MODULE_PATH: &'static str = ::core::module_path!(); + fn event_spec() -> ::ink::metadata::EventSpec { #[::ink::metadata::linkme::distributed_slice(::ink::metadata::EVENTS)] #[linkme(crate = ::ink::metadata::linkme)] @@ -103,6 +107,8 @@ fn struct_with_fields_and_some_topics() { expands to { const _: () = { impl ::ink::metadata::EventMetadata for Event { + const MODULE_PATH: &'static str = ::core::module_path!(); + fn event_spec() -> ::ink::metadata::EventSpec { #[::ink::metadata::linkme::distributed_slice(::ink::metadata::EVENTS)] #[linkme(crate = ::ink::metadata::linkme)] diff --git a/crates/ink/src/env_access.rs b/crates/ink/src/env_access.rs index f8772b67e4..da8be6846b 100644 --- a/crates/ink/src/env_access.rs +++ b/crates/ink/src/env_access.rs @@ -416,7 +416,7 @@ where /// Emits an event. pub fn emit_event(self, event: Evt) where - Evt: ink_env::Event + Evt: ink_env::Event, { ink_env::emit_event::(event) } diff --git a/crates/metadata/src/lib.rs b/crates/metadata/src/lib.rs index 6325b38d47..929aa6a320 100644 --- a/crates/metadata/src/lib.rs +++ b/crates/metadata/src/lib.rs @@ -164,6 +164,9 @@ pub static EVENTS: [fn() -> EventSpec] = [..]; // todo: docs pub trait EventMetadata { + /// The full path to the event type, usually provided by [`core::module_path`]. + const MODULE_PATH: &'static str; + /// Returns the metadata of the event. fn event_spec() -> EventSpec; } diff --git a/crates/metadata/src/specs.rs b/crates/metadata/src/specs.rs index cec0d14202..9e28c994af 100644 --- a/crates/metadata/src/specs.rs +++ b/crates/metadata/src/specs.rs @@ -858,6 +858,8 @@ impl IntoPortable for MessageSpec { pub struct EventSpec { /// The label of the event. label: F::String, + /// The module path to the event type definition. + module_path: F::String, /// The signature topic of the event. `None` if the event is anonymous. signature_topic: Option, /// The event arguments. @@ -901,6 +903,16 @@ impl EventSpecBuilder where F: Form, { + /// Sets the module path to the event type definition. + pub fn module_path<'a>(self, path: &'a str) -> Self + where + F::String: From<&'a str>, + { + let mut this = self; + this.spec.module_path = path.into(); + this + } + /// Sets the input arguments of the event specification. pub fn args(self, args: A) -> Self where @@ -950,6 +962,7 @@ impl IntoPortable for EventSpec { fn into_portable(self, registry: &mut Registry) -> Self::Output { EventSpec { label: self.label.to_string(), + module_path: self.module_path.to_string(), signature_topic: self.signature_topic, args: self .args @@ -964,12 +977,14 @@ impl IntoPortable for EventSpec { impl EventSpec where F: Form, + F::String: Default, { /// Creates a new event specification builder. pub fn new(label: ::String) -> EventSpecBuilder { EventSpecBuilder { spec: Self { label, + module_path: Default::default(), signature_topic: None, args: Vec::new(), docs: Vec::new(), diff --git a/crates/metadata/src/tests.rs b/crates/metadata/src/tests.rs index 9beee133c0..46c03c8a96 100644 --- a/crates/metadata/src/tests.rs +++ b/crates/metadata/src/tests.rs @@ -151,6 +151,75 @@ fn spec_contract_only_one_default_constructor_allowed() { .done(); } +#[test] +#[should_panic( + expected = "the event `path::to::Event` defines 3 topics, exceeding the limit of 2 topics" +)] +fn spec_contract_event_definition_exceeds_environment_topics_limit() { + const MAX_EVENT_TOPICS: usize = 2; + + ContractSpec::new() + .constructors(vec![ConstructorSpec::from_label("new") + .selector([94u8, 189u8, 136u8, 214u8]) + .payable(true) + .args(vec![MessageParamSpec::new("init_value") + .of_type(TypeSpec::with_name_segs::( + vec!["i32"].into_iter().map(AsRef::as_ref), + )) + .done()]) + .returns(ReturnTypeSpec::new(None)) + .docs(Vec::new()) + .default(true) + .done()]) + .messages(vec![MessageSpec::from_label("inc") + .selector([231u8, 208u8, 89u8, 15u8]) + .mutates(true) + .payable(true) + .args(vec![MessageParamSpec::new("by") + .of_type(TypeSpec::with_name_segs::( + vec!["i32"].into_iter().map(AsRef::as_ref), + )) + .done()]) + .returns(ReturnTypeSpec::new(None)) + .default(true) + .done()]) + .events(vec![EventSpec::new("Event") + .module_path("path::to") + .args([ + EventParamSpec::new("field1_no_topic") + .of_type(TypeSpec::of_type::()) + .indexed(false) + .done(), + EventParamSpec::new("field2_topic") + .of_type(TypeSpec::of_type::()) + .indexed(true) + .done(), + EventParamSpec::new("field3_topic") + .of_type(TypeSpec::of_type::()) + .indexed(true) + .done(), + ]) + .done()]) + .lang_error(TypeSpec::with_name_segs::( + ::core::iter::Iterator::map( + ::core::iter::IntoIterator::into_iter(["ink", "LangError"]), + ::core::convert::AsRef::as_ref, + ), + )) + .environment( + EnvironmentSpec::new() + .account_id(TypeSpec::of_type::()) + .balance(TypeSpec::of_type::()) + .hash(TypeSpec::of_type::()) + .timestamp(TypeSpec::of_type::()) + .block_number(TypeSpec::of_type::()) + .chain_extension(TypeSpec::of_type::<()>()) + .max_event_topics(MAX_EVENT_TOPICS) + .done(), + ) + .done(); +} + #[test] fn spec_contract_json() { #[cfg_attr(feature = "std", derive(scale_info::TypeInfo))] From ca861ae7eab19625e3e212be329b185a6219521f Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Tue, 13 Jun 2023 09:26:48 +0100 Subject: [PATCH 055/140] Check for max topics limit breach in metadata generation --- crates/metadata/src/lib.rs | 1 + crates/metadata/src/specs.rs | 29 ++++++++++++++++++++++++++++- crates/metadata/src/tests.rs | 3 ++- 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/crates/metadata/src/lib.rs b/crates/metadata/src/lib.rs index 929aa6a320..4924e21999 100644 --- a/crates/metadata/src/lib.rs +++ b/crates/metadata/src/lib.rs @@ -20,6 +20,7 @@ #[cfg(not(feature = "std"))] extern crate alloc; +extern crate core; #[cfg(test)] mod tests; diff --git a/crates/metadata/src/specs.rs b/crates/metadata/src/specs.rs index 9e28c994af..7aa9aff7e5 100644 --- a/crates/metadata/src/specs.rs +++ b/crates/metadata/src/specs.rs @@ -28,7 +28,10 @@ use alloc::{ vec, vec::Vec, }; -use core::marker::PhantomData; +use core::{ + fmt::Display, + marker::PhantomData, +}; use scale_info::{ form::{ Form, @@ -255,6 +258,7 @@ impl ContractSpecBuilder { impl ContractSpecBuilder where F: Form, + F::String: Display, TypeSpec: Default, { /// Finalizes construction of the contract specification. @@ -275,6 +279,29 @@ where self.spec.messages.iter().filter(|m| m.default).count() < 2, "only one default message is allowed" ); + let events_exceeding_max_topics_limit = self + .spec + .events + .iter() + .filter_map(|e| { + let signature_topic = if e.signature_topic.is_some() { 1 } else { 0 }; + let topics_count = + signature_topic + e.args.iter().filter(|a| a.indexed).count(); + if topics_count > self.spec.environment.max_event_topics { + Some(format!( + "`{}::{}` ({} topics)", + e.module_path, e.label, topics_count + )) + } else { + None + } + }) + .collect::>(); + assert!( + events_exceeding_max_topics_limit.is_empty(), + "maximum of 2 event topics exceeded: {}", + events_exceeding_max_topics_limit.join(", ") + ); self.spec } } diff --git a/crates/metadata/src/tests.rs b/crates/metadata/src/tests.rs index 46c03c8a96..d621413dde 100644 --- a/crates/metadata/src/tests.rs +++ b/crates/metadata/src/tests.rs @@ -153,7 +153,7 @@ fn spec_contract_only_one_default_constructor_allowed() { #[test] #[should_panic( - expected = "the event `path::to::Event` defines 3 topics, exceeding the limit of 2 topics" + expected = "maximum of 2 event topics exceeded: `path::to::Event` (3 topics)" )] fn spec_contract_event_definition_exceeds_environment_topics_limit() { const MAX_EVENT_TOPICS: usize = 2; @@ -185,6 +185,7 @@ fn spec_contract_event_definition_exceeds_environment_topics_limit() { .done()]) .events(vec![EventSpec::new("Event") .module_path("path::to") + .signature_topic(Some([0u8; 32])) .args([ EventParamSpec::new("field1_no_topic") .of_type(TypeSpec::of_type::()) From 39e6e2832d41cfff9c3b81a09ded98043416c955 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Tue, 13 Jun 2023 10:03:08 +0100 Subject: [PATCH 056/140] Add extra event with no signature topic --- crates/metadata/src/tests.rs | 60 ++++++++++++++++++++++++------------ 1 file changed, 41 insertions(+), 19 deletions(-) diff --git a/crates/metadata/src/tests.rs b/crates/metadata/src/tests.rs index d621413dde..42f1b488e0 100644 --- a/crates/metadata/src/tests.rs +++ b/crates/metadata/src/tests.rs @@ -153,7 +153,7 @@ fn spec_contract_only_one_default_constructor_allowed() { #[test] #[should_panic( - expected = "maximum of 2 event topics exceeded: `path::to::Event` (3 topics)" + expected = "maximum of 2 event topics exceeded: `path::to::Event` (3 topics), `path::to::Event2` (3 topics)" )] fn spec_contract_event_definition_exceeds_environment_topics_limit() { const MAX_EVENT_TOPICS: usize = 2; @@ -183,24 +183,46 @@ fn spec_contract_event_definition_exceeds_environment_topics_limit() { .returns(ReturnTypeSpec::new(None)) .default(true) .done()]) - .events(vec![EventSpec::new("Event") - .module_path("path::to") - .signature_topic(Some([0u8; 32])) - .args([ - EventParamSpec::new("field1_no_topic") - .of_type(TypeSpec::of_type::()) - .indexed(false) - .done(), - EventParamSpec::new("field2_topic") - .of_type(TypeSpec::of_type::()) - .indexed(true) - .done(), - EventParamSpec::new("field3_topic") - .of_type(TypeSpec::of_type::()) - .indexed(true) - .done(), - ]) - .done()]) + .events(vec![ + EventSpec::new("Event") + .module_path("path::to") + // has a signature topic which counts towards the limit + .signature_topic(Some([0u8; 32])) + .args([ + EventParamSpec::new("field1_no_topic") + .of_type(TypeSpec::of_type::()) + .indexed(false) + .done(), + EventParamSpec::new("field2_topic") + .of_type(TypeSpec::of_type::()) + .indexed(true) + .done(), + EventParamSpec::new("field3_topic") + .of_type(TypeSpec::of_type::()) + .indexed(true) + .done(), + ]) + .done(), + EventSpec::new("Event2") + .module_path("path::to") + // is an anonymous event with no signature topic + .signature_topic::<[u8; 32]>(None) + .args([ + EventParamSpec::new("field1_topic") + .of_type(TypeSpec::of_type::()) + .indexed(true) + .done(), + EventParamSpec::new("field2_topic") + .of_type(TypeSpec::of_type::()) + .indexed(true) + .done(), + EventParamSpec::new("field3_topic") + .of_type(TypeSpec::of_type::()) + .indexed(true) + .done(), + ]) + .done(), + ]) .lang_error(TypeSpec::with_name_segs::( ::core::iter::Iterator::map( ::core::iter::IntoIterator::into_iter(["ink", "LangError"]), From fc40629da6c5466bfff5c0ed1015abd538df15d0 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Tue, 13 Jun 2023 12:25:53 +0100 Subject: [PATCH 057/140] Remove checks for generics and pub visibility --- crates/ink/ir/src/ir/item/event.rs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/crates/ink/ir/src/ir/item/event.rs b/crates/ink/ir/src/ir/item/event.rs index 388cf279c3..c9a184946b 100644 --- a/crates/ink/ir/src/ir/item/event.rs +++ b/crates/ink/ir/src/ir/item/event.rs @@ -16,7 +16,6 @@ use crate::{ error::ExtError as _, ir::{ self, - utils, utils::extract_cfg_attributes, CFG_IDENT, }, @@ -96,15 +95,6 @@ impl TryFrom for Event { } }, )?; - // todo: remove generics check and tests. also, how to handle generics? - if !item_struct.generics.params.is_empty() { - return Err(format_err_spanned!( - item_struct.generics.params, - "generic ink! event structs are not supported", - )) - } - // todo: remove this visibility check? - utils::ensure_pub_visibility("event structs", struct_span, &item_struct.vis)?; 'repeat: for field in item_struct.fields.iter() { let field_span = field.span(); // todo: move this check to the new derive macros? From bbbee5e4d45aa7aa872de2b7aee399f1d4fb4572 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Tue, 13 Jun 2023 12:33:17 +0100 Subject: [PATCH 058/140] Fix anonymous attr --- crates/ink/codegen/src/generator/events.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ink/codegen/src/generator/events.rs b/crates/ink/codegen/src/generator/events.rs index 48f162a2cb..fabc334a28 100644 --- a/crates/ink/codegen/src/generator/events.rs +++ b/crates/ink/codegen/src/generator/events.rs @@ -53,7 +53,7 @@ impl<'a> Events<'a> { // place? let anonymous_attr = event.anonymous.then(|| { quote_spanned!(span => - #[ink(::anonymous)] + #[ink(anonymous)] ) }); quote_spanned!(span => From 43e3ab07224c1491b12a7479cfc487af2dde21d8 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Tue, 13 Jun 2023 15:34:47 +0100 Subject: [PATCH 059/140] Move topics attr validation to derive macro --- crates/ink/ir/src/ir/item/event.rs | 38 ------------------------------ crates/ink/macro/src/event/mod.rs | 17 ++++++++++++- 2 files changed, 16 insertions(+), 39 deletions(-) diff --git a/crates/ink/ir/src/ir/item/event.rs b/crates/ink/ir/src/ir/item/event.rs index c9a184946b..fad5db61ec 100644 --- a/crates/ink/ir/src/ir/item/event.rs +++ b/crates/ink/ir/src/ir/item/event.rs @@ -13,11 +13,9 @@ // limitations under the License. use crate::{ - error::ExtError as _, ir::{ self, utils::extract_cfg_attributes, - CFG_IDENT, }, }; use proc_macro2::{ @@ -95,42 +93,6 @@ impl TryFrom for Event { } }, )?; - 'repeat: for field in item_struct.fields.iter() { - let field_span = field.span(); - // todo: move this check to the new derive macros? - let some_cfg_attrs = field - .attrs - .iter() - .find(|attr| attr.path().is_ident(CFG_IDENT)); - if some_cfg_attrs.is_some() { - return Err(format_err!( - field_span, - "conditional compilation is not allowed for event field" - )) - } - let (ink_attrs, _) = ir::partition_attributes(field.attrs.clone())?; - if ink_attrs.is_empty() { - continue 'repeat - } - let normalized = - ir::InkAttribute::from_expanded(ink_attrs).map_err(|err| { - err.into_combine(format_err!(field_span, "at this invocation",)) - })?; - if !matches!(normalized.first().kind(), ir::AttributeArg::Topic) { - return Err(format_err!( - field_span, - "first optional ink! attribute of an event field must be #[ink(topic)]", - )); - } - for arg in normalized.args() { - if !matches!(arg.kind(), ir::AttributeArg::Topic) { - return Err(format_err!( - arg.span(), - "encountered conflicting ink! attribute for event field", - )) - } - } - } Ok(Self { item: syn::ItemStruct { attrs: other_attrs, diff --git a/crates/ink/macro/src/event/mod.rs b/crates/ink/macro/src/event/mod.rs index a39e74a142..46236f6714 100644 --- a/crates/ink/macro/src/event/mod.rs +++ b/crates/ink/macro/src/event/mod.rs @@ -49,6 +49,7 @@ fn event_derive_struct(mut s: synstructure::Structure) -> syn::Result = None; s.variants_mut()[0].filter(|bi| { match has_ink_topic_attribute(&bi.ast().attrs) { @@ -151,8 +152,22 @@ fn signature_topic(fields: &syn::Fields, event_ident: &syn::Ident) -> syn::LitSt } /// Checks if the given attributes contain an `#[ink(topic)]` attribute. +/// +/// Returns `Err` if: +/// - the given attributes contain a `#[cfg(...)]` attribute +/// - there are `ink` attributes other than a single `#[ink(topic)]` fn has_ink_topic_attribute(attrs: &[syn::Attribute]) -> syn::Result { - has_ink_attribute(attrs, "topic") + let some_cfg_attrs = attrs + .iter() + .find(|attr| attr.path().is_ident("cfg")); + if let Some(attr) = some_cfg_attrs { + return Err(syn::Error::new( + attr.span(), + "conditional compilation is not allowed for event fields", + )) + } else { + has_ink_attribute(attrs, "topic") + } } /// Checks if the given attributes contain an `ink` attribute with the given path. From 6e93717b66ae97700ab659843ff7470672d289b3 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Tue, 13 Jun 2023 15:36:23 +0100 Subject: [PATCH 060/140] Fmt and remove remaining MAX_TOPICS check --- crates/env/src/engine/on_chain/impls.rs | 4 ++-- crates/ink/ir/src/ir/item/event.rs | 8 +++----- crates/ink/macro/src/event/mod.rs | 4 +--- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/crates/env/src/engine/on_chain/impls.rs b/crates/env/src/engine/on_chain/impls.rs index a1d06b569c..e140549778 100644 --- a/crates/env/src/engine/on_chain/impls.rs +++ b/crates/env/src/engine/on_chain/impls.rs @@ -397,8 +397,8 @@ impl TypedEnvBackend for EnvInstance { E: Environment, Evt: Event, { - let (mut scope, enc_topics) = event - .topics::(TopicsBuilder::from(self.scoped_buffer()).into()); + let (mut scope, enc_topics) = + event.topics::(TopicsBuilder::from(self.scoped_buffer()).into()); let enc_data = scope.take_encoded(&event); ext::deposit_event(enc_topics, enc_data); } diff --git a/crates/ink/ir/src/ir/item/event.rs b/crates/ink/ir/src/ir/item/event.rs index fad5db61ec..0d90dd9251 100644 --- a/crates/ink/ir/src/ir/item/event.rs +++ b/crates/ink/ir/src/ir/item/event.rs @@ -12,11 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::{ - ir::{ - self, - utils::extract_cfg_attributes, - }, +use crate::ir::{ + self, + utils::extract_cfg_attributes, }; use proc_macro2::{ Ident, diff --git a/crates/ink/macro/src/event/mod.rs b/crates/ink/macro/src/event/mod.rs index 46236f6714..5a6fe83f64 100644 --- a/crates/ink/macro/src/event/mod.rs +++ b/crates/ink/macro/src/event/mod.rs @@ -157,9 +157,7 @@ fn signature_topic(fields: &syn::Fields, event_ident: &syn::Ident) -> syn::LitSt /// - the given attributes contain a `#[cfg(...)]` attribute /// - there are `ink` attributes other than a single `#[ink(topic)]` fn has_ink_topic_attribute(attrs: &[syn::Attribute]) -> syn::Result { - let some_cfg_attrs = attrs - .iter() - .find(|attr| attr.path().is_ident("cfg")); + let some_cfg_attrs = attrs.iter().find(|attr| attr.path().is_ident("cfg")); if let Some(attr) = some_cfg_attrs { return Err(syn::Error::new( attr.span(), From 28b2d305d2a9f71a7eca470440d7c36d01f08dbb Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Tue, 13 Jun 2023 17:18:00 +0100 Subject: [PATCH 061/140] Remove event spec field type name --- crates/ink/macro/src/event/metadata.rs | 2 +- crates/ink/macro/src/tests/event_metadata.rs | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/crates/ink/macro/src/event/metadata.rs b/crates/ink/macro/src/event/metadata.rs index 237236c45b..8aed7bc812 100644 --- a/crates/ink/macro/src/event/metadata.rs +++ b/crates/ink/macro/src/event/metadata.rs @@ -50,7 +50,7 @@ fn event_metadata_derive_struct(s: synstructure::Structure) -> syn::Result ::ink::metadata::EventParamSpec::new(::core::stringify!(#field_name)) - .of_type(::ink::metadata::TypeSpec::with_name_str::<#field_ty>(::core::stringify!(#field_ty))) + .of_type(::ink::metadata::TypeSpec::of_type::<#field_ty>()) .indexed(#indexed) // .docs .done() diff --git a/crates/ink/macro/src/tests/event_metadata.rs b/crates/ink/macro/src/tests/event_metadata.rs index 0f8ce7f940..06479d3933 100644 --- a/crates/ink/macro/src/tests/event_metadata.rs +++ b/crates/ink/macro/src/tests/event_metadata.rs @@ -70,15 +70,15 @@ fn struct_with_fields_no_topics() { .signature_topic(::SIGNATURE_TOPIC) .args([ ::ink::metadata::EventParamSpec::new(::core::stringify!(field_1)) - .of_type(::ink::metadata::TypeSpec::with_name_str::(::core::stringify!(u32))) + .of_type(::ink::metadata::TypeSpec::of_type::()) .indexed(false) .done(), ::ink::metadata::EventParamSpec::new(::core::stringify!(field_2)) - .of_type(::ink::metadata::TypeSpec::with_name_str::(::core::stringify!(u64))) + .of_type(::ink::metadata::TypeSpec::of_type::()) .indexed(false) .done(), ::ink::metadata::EventParamSpec::new(::core::stringify!(field_3)) - .of_type(::ink::metadata::TypeSpec::with_name_str::(::core::stringify!(u128))) + .of_type(::ink::metadata::TypeSpec::of_type::()) .indexed(false) .done() ]) @@ -119,15 +119,15 @@ fn struct_with_fields_and_some_topics() { .signature_topic(::SIGNATURE_TOPIC) .args([ ::ink::metadata::EventParamSpec::new(::core::stringify!(field_1)) - .of_type(::ink::metadata::TypeSpec::with_name_str::(::core::stringify!(u32))) + .of_type(::ink::metadata::TypeSpec::of_type::()) .indexed(false) .done(), ::ink::metadata::EventParamSpec::new(::core::stringify!(field_2)) - .of_type(::ink::metadata::TypeSpec::with_name_str::(::core::stringify!(u64))) + .of_type(::ink::metadata::TypeSpec::of_type::()) .indexed(true) .done(), ::ink::metadata::EventParamSpec::new(::core::stringify!(field_3)) - .of_type(::ink::metadata::TypeSpec::with_name_str::(::core::stringify!(u128))) + .of_type(::ink::metadata::TypeSpec::of_type::()) .indexed(true) .done() ]) From 298cfa8904476a8598d0a5ea88e1adbe3f3e38ae Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Mon, 19 Jun 2023 16:26:20 +0100 Subject: [PATCH 062/140] Clippy --- crates/ink/macro/src/event/mod.rs | 2 +- crates/ink/tests/events_metadata.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/ink/macro/src/event/mod.rs b/crates/ink/macro/src/event/mod.rs index 5a6fe83f64..c484e0e30e 100644 --- a/crates/ink/macro/src/event/mod.rs +++ b/crates/ink/macro/src/event/mod.rs @@ -159,7 +159,7 @@ fn signature_topic(fields: &syn::Fields, event_ident: &syn::Ident) -> syn::LitSt fn has_ink_topic_attribute(attrs: &[syn::Attribute]) -> syn::Result { let some_cfg_attrs = attrs.iter().find(|attr| attr.path().is_ident("cfg")); if let Some(attr) = some_cfg_attrs { - return Err(syn::Error::new( + Err(syn::Error::new( attr.span(), "conditional compilation is not allowed for event fields", )) diff --git a/crates/ink/tests/events_metadata.rs b/crates/ink/tests/events_metadata.rs index ec1368fffd..c55fd3ff2f 100644 --- a/crates/ink/tests/events_metadata.rs +++ b/crates/ink/tests/events_metadata.rs @@ -34,7 +34,7 @@ mod contract { impl Contract { #[ink(constructor)] - pub fn new() -> Self { + pub fn new(_x: u8) -> Self { Self {} } } From e7e18379a4720242a054ffd79ac68d14190e093d Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Mon, 19 Jun 2023 17:55:50 +0100 Subject: [PATCH 063/140] WIP add `#[ink::event]` which expands to derives --- crates/ink/codegen/src/generator/events.rs | 16 +- crates/ink/ir/src/ir/item/event.rs | 198 +----------------- crates/ink/macro/src/event/mod.rs | 15 ++ crates/ink/macro/src/lib.rs | 6 + crates/ink/src/lib.rs | 1 + integration-tests/events/event-def/src/lib.rs | 6 +- 6 files changed, 30 insertions(+), 212 deletions(-) diff --git a/crates/ink/codegen/src/generator/events.rs b/crates/ink/codegen/src/generator/events.rs index fabc334a28..55c76afd01 100644 --- a/crates/ink/codegen/src/generator/events.rs +++ b/crates/ink/codegen/src/generator/events.rs @@ -30,22 +30,16 @@ impl_as_ref_for_generator!(Events); impl GenerateCode for Events<'_> { fn generate_code(&self) -> TokenStream2 { - // let event_base = self.generate_event_base(); - // let topic_guards = self.generate_topic_guards(); - // let topics_impls = self.generate_topics_impls(); - let event_structs = self.generate_event_structs(); + let event_items = self.generate_event_items(); quote! { - // #event_base - // #( #topic_guards )* - #( #event_structs )* - // #( #topics_impls )* + #( #event_items )* } } } impl<'a> Events<'a> { /// Generates all the user defined event struct definitions. - fn generate_event_structs(&'a self) -> impl Iterator + 'a { + fn generate_event_items(&'a self) -> impl Iterator + 'a { self.contract.module().events().map(move |event| { let span = event.span(); let attrs = event.attrs(); @@ -58,8 +52,8 @@ impl<'a> Events<'a> { }); quote_spanned!(span => #( #attrs )* - #[derive(::ink::Event, scale::Encode, scale::Decode)] - #[cfg_attr(feature = "std", derive(ink::EventMetadata))] + #[derive(::ink::Event, ::scale::Encode, ::scale::Decode)] + #[cfg_attr(feature = "std", derive(::ink::EventMetadata))] #anonymous_attr #event ) diff --git a/crates/ink/ir/src/ir/item/event.rs b/crates/ink/ir/src/ir/item/event.rs index 0d90dd9251..0ca2c454d4 100644 --- a/crates/ink/ir/src/ir/item/event.rs +++ b/crates/ink/ir/src/ir/item/event.rs @@ -107,12 +107,6 @@ impl Event { &self.item.ident } - /// Returns an iterator yielding all the `#[ink(topic)]` annotated fields - /// of the event struct. - pub fn fields(&self) -> EventFieldsIter { - EventFieldsIter::new(self) - } - /// Returns all non-ink! attributes. pub fn attrs(&self) -> &[syn::Attribute] { &self.item.attrs @@ -124,77 +118,6 @@ impl Event { } } -/// An event field with a flag indicating if this field is an event topic. -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub struct EventField<'a> { - /// The associated `field` is an event topic if this is `true`. - pub is_topic: bool, - /// The event field. - field: &'a syn::Field, -} - -impl<'a> EventField<'a> { - /// Returns the span of the event field. - pub fn span(self) -> Span { - self.field.span() - } - - /// Returns all non-ink! attributes of the event field. - pub fn attrs(self) -> Vec { - let (_, non_ink_attrs) = ir::partition_attributes(self.field.attrs.clone()) - .unwrap_or_else(|err| { - panic!("encountered invalid event field attributes: {err}") - }); - non_ink_attrs - } - - /// Returns the visibility of the event field. - pub fn vis(self) -> &'a syn::Visibility { - &self.field.vis - } - - /// Returns the identifier of the event field if any. - pub fn ident(self) -> Option<&'a Ident> { - self.field.ident.as_ref() - } - - /// Returns the type of the event field. - pub fn ty(self) -> &'a syn::Type { - &self.field.ty - } -} - -/// Iterator yielding all `#[ink(topic)]` annotated fields of an event struct. -pub struct EventFieldsIter<'a> { - iter: syn::punctuated::Iter<'a, syn::Field>, -} - -impl<'a> EventFieldsIter<'a> { - /// Creates a new topics fields iterator for the given ink! event struct. - fn new(event: &'a Event) -> Self { - Self { - iter: event.item.fields.iter(), - } - } -} - -impl<'a> Iterator for EventFieldsIter<'a> { - type Item = EventField<'a>; - - fn next(&mut self) -> Option { - match self.iter.next() { - None => None, - Some(field) => { - let is_topic = ir::first_ink_attribute(&field.attrs) - .unwrap_or_default() - .map(|attr| matches!(attr.first().kind(), ir::AttributeArg::Topic)) - .unwrap_or_default(); - Some(EventField { is_topic, field }) - } - } - } -} - #[cfg(test)] mod tests { use super::*; @@ -268,7 +191,7 @@ mod tests { } #[test] - fn missing_storage_attribute_fails() { + fn missing_event_attribute_fails() { assert_try_from_fails( syn::parse_quote! { pub struct MyEvent { @@ -281,83 +204,6 @@ mod tests { ) } - #[test] - fn generic_event_fails() { - assert_try_from_fails( - syn::parse_quote! { - #[ink(event)] - pub struct GenericEvent { - #[ink(topic)] - field_1: T, - field_2: bool, - } - }, - "generic ink! event structs are not supported", - ) - } - - #[test] - fn non_pub_event_struct() { - assert_try_from_fails( - syn::parse_quote! { - #[ink(event)] - struct PrivateEvent { - #[ink(topic)] - field_1: i32, - field_2: bool, - } - }, - "non `pub` ink! event structs are not supported", - ) - } - - #[test] - fn duplicate_field_attributes_fails() { - assert_try_from_fails( - syn::parse_quote! { - #[ink(event)] - pub struct MyEvent { - #[ink(topic)] - #[ink(topic)] - field_1: i32, - field_2: bool, - } - }, - "encountered duplicate ink! attribute", - ) - } - - #[test] - fn invalid_field_attributes_fails() { - assert_try_from_fails( - syn::parse_quote! { - #[ink(event)] - pub struct MyEvent { - #[ink(message)] - field_1: i32, - field_2: bool, - } - }, - "first optional ink! attribute of an event field must be #[ink(topic)]", - ) - } - - #[test] - fn conflicting_field_attributes_fails() { - assert_try_from_fails( - syn::parse_quote! { - #[ink(event)] - pub struct MyEvent { - #[ink(topic)] - #[ink(payable)] - field_1: i32, - field_2: bool, - } - }, - "encountered conflicting ink! attribute for event field", - ) - } - #[test] fn cfg_marked_field_attribute_fails() { assert_try_from_fails( @@ -397,48 +243,6 @@ mod tests { } } - #[test] - fn event_fields_iter_works() { - let expected_fields: Vec<(bool, NamedField)> = vec![ - ( - true, - syn::parse_quote! { - field_1: i32 - }, - ), - ( - false, - syn::parse_quote! { - field_2: u64 - }, - ), - ( - true, - syn::parse_quote! { - field_3: [u8; 32] - }, - ), - ]; - let input = >::try_from(syn::parse_quote! { - #[ink(event)] - pub struct MyEvent { - #[ink(topic)] - field_1: i32, - field_2: u64, - #[ink(topic)] - field_3: [u8; 32], - } - }) - .unwrap(); - let mut fields_iter = input.fields(); - for (is_topic, expected_field) in expected_fields { - let field = fields_iter.next().unwrap(); - assert_eq!(field.is_topic, is_topic); - assert_eq!(field.ident(), Some(expected_field.ident())); - assert_eq!(field.ty(), expected_field.ty()); - } - } - #[test] fn anonymous_event_works() { fn assert_anonymous_event(event: syn::ItemStruct) { diff --git a/crates/ink/macro/src/event/mod.rs b/crates/ink/macro/src/event/mod.rs index c484e0e30e..b010ca9dc8 100644 --- a/crates/ink/macro/src/event/mod.rs +++ b/crates/ink/macro/src/event/mod.rs @@ -23,6 +23,21 @@ use quote::{ }; use syn::spanned::Spanned; +/// todo: docs +pub fn generate(_config: TokenStream2, input: TokenStream2) -> TokenStream2 { + // todo: check for duplicate attributes + match syn::parse2::(input) { + Ok(ast) => { + quote::quote! ( + #[derive(::ink::Event, ::scale::Encode, ::scale::Decode)] + #[cfg_attr(feature = "std", derive(::ink::EventMetadata))] + #ast + ) + } + Err(err) => err.to_compile_error(), + } +} + /// Derives the `ink::Event` trait for the given `struct`. pub fn event_derive(mut s: synstructure::Structure) -> TokenStream2 { s.bind_with(|_| synstructure::BindStyle::Move) diff --git a/crates/ink/macro/src/lib.rs b/crates/ink/macro/src/lib.rs index 4acdedd68d..c8e7a5eeb1 100644 --- a/crates/ink/macro/src/lib.rs +++ b/crates/ink/macro/src/lib.rs @@ -650,6 +650,12 @@ pub fn trait_definition(attr: TokenStream, item: TokenStream) -> TokenStream { trait_def::analyze(attr.into(), item.into()).into() } +/// todo: docs +#[proc_macro_attribute] +pub fn event(attr: TokenStream, item: TokenStream) -> TokenStream { + event::generate(attr.into(), item.into()).into() +} + /// Prepares the type to be fully compatible and usable with the storage. /// It implements all necessary traits and calculates the storage key for types. /// `Packed` types don't have a storage key, but non-packed types (like `Mapping`, `Lazy` diff --git a/crates/ink/src/lib.rs b/crates/ink/src/lib.rs index a96bad8a27..69ba6a4977 100644 --- a/crates/ink/src/lib.rs +++ b/crates/ink/src/lib.rs @@ -74,6 +74,7 @@ pub use ink_macro::{ blake2x256, chain_extension, contract, + event, selector_bytes, selector_id, storage_item, diff --git a/integration-tests/events/event-def/src/lib.rs b/integration-tests/events/event-def/src/lib.rs index 9724bf5b08..13cb7aa34e 100644 --- a/integration-tests/events/event-def/src/lib.rs +++ b/integration-tests/events/event-def/src/lib.rs @@ -1,13 +1,11 @@ #![cfg_attr(not(feature = "std"), no_std, no_main)] -#[derive(ink::Event, scale::Encode)] -#[cfg_attr(feature = "std", derive(ink::EventMetadata, scale::Decode))] +#[ink::event] pub struct Flipped { pub value: bool, } -#[derive(ink::Event, scale::Encode)] -#[cfg_attr(feature = "std", derive(ink::EventMetadata, scale::Decode))] +#[ink::event] pub struct ThirtyTwoByteTopics { #[ink(topic)] pub hash: [u8; 32], From e8f186b9de2ca6331d9e947d7f7a732e6a19d96b Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Thu, 22 Jun 2023 14:52:15 +0100 Subject: [PATCH 064/140] Check for signature topics collisions when building metadata --- crates/metadata/src/specs.rs | 34 +++++++++++++++++++ crates/metadata/src/tests.rs | 65 ++++++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+) diff --git a/crates/metadata/src/specs.rs b/crates/metadata/src/specs.rs index 7aa9aff7e5..67579bb543 100644 --- a/crates/metadata/src/specs.rs +++ b/crates/metadata/src/specs.rs @@ -24,7 +24,9 @@ use crate::{ }; #[cfg(not(feature = "std"))] use alloc::{ + collections::BTreeMap, format, + string::String, vec, vec::Vec, }; @@ -49,6 +51,8 @@ use serde::{ Deserialize, Serialize, }; +#[cfg(feature = "std")] +use std::collections::BTreeMap; /// Describes a contract. #[derive(Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] @@ -279,6 +283,7 @@ where self.spec.messages.iter().filter(|m| m.default).count() < 2, "only one default message is allowed" ); + let events_exceeding_max_topics_limit = self .spec .events @@ -302,6 +307,35 @@ where "maximum of 2 event topics exceeded: {}", events_exceeding_max_topics_limit.join(", ") ); + + let mut signature_topics: BTreeMap, Vec> = BTreeMap::new(); + for e in self.spec.events.iter() { + if let Some(signature_topic) = &e.signature_topic { + signature_topics + .entry(signature_topic.bytes.clone()) + .or_default() + .push(format!("`{}::{}`", e.module_path, e.label)); + } + } + let signature_topic_collisions = signature_topics + .iter() + .filter_map(|(_, topics)| { + if topics.len() > 1 { + Some(format!( + "event signature topic collision: {}", + topics.join(", ") + )) + } else { + None + } + }) + .collect::>(); + assert!( + signature_topic_collisions.is_empty(), + "{}", + signature_topic_collisions.join("\n") + ); + self.spec } } diff --git a/crates/metadata/src/tests.rs b/crates/metadata/src/tests.rs index 42f1b488e0..eaadb9ebde 100644 --- a/crates/metadata/src/tests.rs +++ b/crates/metadata/src/tests.rs @@ -243,6 +243,71 @@ fn spec_contract_event_definition_exceeds_environment_topics_limit() { .done(); } +#[test] +#[should_panic( + expected = "event signature topic collision: `path::to::Event`, `path::to::Event2`" +)] +fn spec_contract_event_definition_signature_topic_collision() { + const SIGNATURE_TOPIC: Option<[u8; 32]> = Some([42u8; 32]); + + ContractSpec::new() + .constructors(vec![ConstructorSpec::from_label("new") + .selector([94u8, 189u8, 136u8, 214u8]) + .payable(true) + .args(vec![MessageParamSpec::new("init_value") + .of_type(TypeSpec::with_name_segs::( + vec!["i32"].into_iter().map(AsRef::as_ref), + )) + .done()]) + .returns(ReturnTypeSpec::new(None)) + .docs(Vec::new()) + .default(true) + .done()]) + .messages(vec![MessageSpec::from_label("inc") + .selector([231u8, 208u8, 89u8, 15u8]) + .mutates(true) + .payable(true) + .args(vec![MessageParamSpec::new("by") + .of_type(TypeSpec::with_name_segs::( + vec!["i32"].into_iter().map(AsRef::as_ref), + )) + .done()]) + .returns(ReturnTypeSpec::new(None)) + .default(true) + .done()]) + .events(vec![ + EventSpec::new("Event") + .module_path("path::to") + // has a signature topic which counts towards the limit + .signature_topic(SIGNATURE_TOPIC) + .args([]) + .done(), + EventSpec::new("Event2") + .module_path("path::to") + .signature_topic::<[u8; 32]>(SIGNATURE_TOPIC) + .args([]) + .done(), + ]) + .lang_error(TypeSpec::with_name_segs::( + ::core::iter::Iterator::map( + ::core::iter::IntoIterator::into_iter(["ink", "LangError"]), + ::core::convert::AsRef::as_ref, + ), + )) + .environment( + EnvironmentSpec::new() + .account_id(TypeSpec::of_type::()) + .balance(TypeSpec::of_type::()) + .hash(TypeSpec::of_type::()) + .timestamp(TypeSpec::of_type::()) + .block_number(TypeSpec::of_type::()) + .chain_extension(TypeSpec::of_type::<()>()) + .max_event_topics(2) + .done(), + ) + .done(); +} + #[test] fn spec_contract_json() { #[cfg_attr(feature = "std", derive(scale_info::TypeInfo))] From 309e30c8a90dcb595bb09db0fc034fa22acacec9 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Fri, 23 Jun 2023 16:49:23 +0100 Subject: [PATCH 065/140] Commentl --- crates/ink/codegen/src/generator/events.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/ink/codegen/src/generator/events.rs b/crates/ink/codegen/src/generator/events.rs index 55c76afd01..dbe51ccd33 100644 --- a/crates/ink/codegen/src/generator/events.rs +++ b/crates/ink/codegen/src/generator/events.rs @@ -43,8 +43,8 @@ impl<'a> Events<'a> { self.contract.module().events().map(move |event| { let span = event.span(); let attrs = event.attrs(); - // todo: should we just keep this attribute as part of attrs in the first - // place? + // add back the `#[ink(anonymous)]` attribute if it was present, for parsing by the + // derive macros. let anonymous_attr = event.anonymous.then(|| { quote_spanned!(span => #[ink(anonymous)] From 03f7e7ca031181881b962b0038dc0d4e6dfd1db5 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Mon, 26 Jun 2023 10:14:42 +0100 Subject: [PATCH 066/140] Remove EventRespectsTopicLimits --- crates/ink/src/codegen/event.rs | 53 ---------------- crates/ink/src/codegen/mod.rs | 2 - .../fail/event-too-many-topics-anonymous.rs | 60 ------------------- .../event-too-many-topics-anonymous.stderr | 21 ------- .../ui/contract/fail/event-too-many-topics.rs | 56 ----------------- .../fail/event-too-many-topics.stderr | 21 ------- 6 files changed, 213 deletions(-) delete mode 100644 crates/ink/src/codegen/event.rs delete mode 100644 crates/ink/tests/ui/contract/fail/event-too-many-topics-anonymous.rs delete mode 100644 crates/ink/tests/ui/contract/fail/event-too-many-topics-anonymous.stderr delete mode 100644 crates/ink/tests/ui/contract/fail/event-too-many-topics.rs delete mode 100644 crates/ink/tests/ui/contract/fail/event-too-many-topics.stderr diff --git a/crates/ink/src/codegen/event.rs b/crates/ink/src/codegen/event.rs deleted file mode 100644 index 4b19b0dcec..0000000000 --- a/crates/ink/src/codegen/event.rs +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2018-2022 Parity Technologies (UK) Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -/// Guards that an ink! event definitions respects the topic limit. -/// todo: update docs -/// -/// # Usage -/// -/// ``` -/// // #[ink(event)] -/// pub struct ExampleEvent {} -/// -/// /// The amount of the topics of the example event struct. -/// const LEN_TOPICS: usize = 3; -/// -/// /// The limit for the amount of topics per ink! event definition. -/// const TOPICS_LIMIT: usize = 4; -/// -/// impl ::ink::codegen::EventLenTopics for ExampleEvent { -/// type LenTopics = ::ink::codegen::EventTopics; -/// } -/// -/// // The below code only compiles successfully if the example ink! event -/// // definitions respects the topic limitation: it must have an amount of -/// // topics less than or equal to the topic limit. -/// const _: () = ::ink::codegen::utils::consume_type::< -/// ::ink::codegen::EventRespectsTopicLimit, -/// >(); -/// ``` -pub struct EventRespectsTopicLimit< - const LEN_EVENT_TOPICS: usize, - const LEN_MAX_TOPICS: usize, ->; - -impl - EventRespectsTopicLimit -{ - pub const ASSERT: () = assert!( - LEN_EVENT_TOPICS <= LEN_MAX_TOPICS, - "The event definition exceeded the maximum number of topics." - ); -} diff --git a/crates/ink/src/codegen/mod.rs b/crates/ink/src/codegen/mod.rs index 5013945ce4..d7ebf7e84e 100644 --- a/crates/ink/src/codegen/mod.rs +++ b/crates/ink/src/codegen/mod.rs @@ -16,7 +16,6 @@ mod dispatch; mod env; -mod event; mod implies_return; mod trait_def; pub mod utils; @@ -32,7 +31,6 @@ pub use self::{ Env, StaticEnv, }, - event::EventRespectsTopicLimit, implies_return::ImpliesReturn, trait_def::{ TraitCallBuilder, diff --git a/crates/ink/tests/ui/contract/fail/event-too-many-topics-anonymous.rs b/crates/ink/tests/ui/contract/fail/event-too-many-topics-anonymous.rs deleted file mode 100644 index 440ba81c41..0000000000 --- a/crates/ink/tests/ui/contract/fail/event-too-many-topics-anonymous.rs +++ /dev/null @@ -1,60 +0,0 @@ -use ink_env::{ - DefaultEnvironment, - Environment, -}; - -pub struct EnvironmentMoreTopics; - -impl ink_env::Environment for EnvironmentMoreTopics { - const MAX_EVENT_TOPICS: usize = 2; - - type AccountId = ::AccountId; - type Balance = ::Balance; - type Hash = ::Hash; - type Timestamp = ::Timestamp; - type BlockNumber = ::BlockNumber; - type ChainExtension = (); -} - -#[ink::contract(env = super::EnvironmentMoreTopics)] -mod contract { - #[ink(storage)] - pub struct Contract {} - - #[ink(event, anonymous)] - pub struct Event { - #[ink(topic)] - arg_1: i8, - #[ink(topic)] - arg_2: i16, - #[ink(topic)] - arg_3: i32, - #[ink(topic)] - arg_4: i32, - } - - impl Contract { - #[ink(constructor)] - pub fn constructor() -> Self { - Self::env().emit_event(Event { - arg_1: 1, - arg_2: 2, - arg_3: 3, - arg_4: 4, - }); - Self {} - } - - #[ink(message)] - pub fn message(&self) { - self.env().emit_event(Event { - arg_1: 1, - arg_2: 2, - arg_3: 3, - arg_4: 4, - }); - } - } -} - -fn main() {} diff --git a/crates/ink/tests/ui/contract/fail/event-too-many-topics-anonymous.stderr b/crates/ink/tests/ui/contract/fail/event-too-many-topics-anonymous.stderr deleted file mode 100644 index b378115967..0000000000 --- a/crates/ink/tests/ui/contract/fail/event-too-many-topics-anonymous.stderr +++ /dev/null @@ -1,21 +0,0 @@ -error[E0277]: the trait bound `EventTopics<4>: RespectTopicLimit<2>` is not satisfied - --> tests/ui/contract/fail/event-too-many-topics-anonymous.rs:25:5 - | -25 | pub struct Event { - | ^^^ the trait `RespectTopicLimit<2>` is not implemented for `EventTopics<4>` - | - = help: the following other types implement trait `RespectTopicLimit`: - as RespectTopicLimit<10>> - as RespectTopicLimit<11>> - as RespectTopicLimit<12>> - as RespectTopicLimit<4>> - as RespectTopicLimit<5>> - as RespectTopicLimit<6>> - as RespectTopicLimit<7>> - as RespectTopicLimit<8>> - as RespectTopicLimit<9>> -note: required by a bound in `EventRespectsTopicLimit` - --> src/codegen/event/topics.rs - | - | ::LenTopics: RespectTopicLimit, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `EventRespectsTopicLimit` diff --git a/crates/ink/tests/ui/contract/fail/event-too-many-topics.rs b/crates/ink/tests/ui/contract/fail/event-too-many-topics.rs deleted file mode 100644 index 4b7be58401..0000000000 --- a/crates/ink/tests/ui/contract/fail/event-too-many-topics.rs +++ /dev/null @@ -1,56 +0,0 @@ -use ink_env::{ - DefaultEnvironment, - Environment, -}; - -pub struct EnvironmentMoreTopics; - -impl ink_env::Environment for EnvironmentMoreTopics { - const MAX_EVENT_TOPICS: usize = 2; - - type AccountId = ::AccountId; - type Balance = ::Balance; - type Hash = ::Hash; - type Timestamp = ::Timestamp; - type BlockNumber = ::BlockNumber; - type ChainExtension = (); -} - -#[ink::contract(env = super::EnvironmentMoreTopics)] -mod contract { - #[ink(storage)] - pub struct Contract {} - - #[ink(event)] - pub struct Event { - #[ink(topic)] - arg_1: i8, - #[ink(topic)] - arg_2: i16, - #[ink(topic)] - arg_3: i32, - } - - impl Contract { - #[ink(constructor)] - pub fn constructor() -> Self { - Self::env().emit_event(Event { - arg_1: 1, - arg_2: 2, - arg_3: 3, - }); - Self {} - } - - #[ink(message)] - pub fn message(&self) { - self.env().emit_event(Event { - arg_1: 1, - arg_2: 2, - arg_3: 3, - }); - } - } -} - -fn main() {} diff --git a/crates/ink/tests/ui/contract/fail/event-too-many-topics.stderr b/crates/ink/tests/ui/contract/fail/event-too-many-topics.stderr deleted file mode 100644 index e542e880c0..0000000000 --- a/crates/ink/tests/ui/contract/fail/event-too-many-topics.stderr +++ /dev/null @@ -1,21 +0,0 @@ -error[E0277]: the trait bound `EventTopics<3>: RespectTopicLimit<2>` is not satisfied - --> tests/ui/contract/fail/event-too-many-topics.rs:25:5 - | -25 | pub struct Event { - | ^^^ the trait `RespectTopicLimit<2>` is not implemented for `EventTopics<3>` - | - = help: the following other types implement trait `RespectTopicLimit`: - as RespectTopicLimit<10>> - as RespectTopicLimit<11>> - as RespectTopicLimit<12>> - as RespectTopicLimit<3>> - as RespectTopicLimit<4>> - as RespectTopicLimit<5>> - as RespectTopicLimit<6>> - as RespectTopicLimit<7>> - and $N others -note: required by a bound in `EventRespectsTopicLimit` - --> src/codegen/event/topics.rs - | - | ::LenTopics: RespectTopicLimit, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `EventRespectsTopicLimit` From eeecbc71958e8a4d24e5fa514bb52e6f21c7ddbe Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Mon, 26 Jun 2023 11:48:07 +0100 Subject: [PATCH 067/140] Remove cfg attr test --- crates/ink/ir/src/ir/item/event.rs | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/crates/ink/ir/src/ir/item/event.rs b/crates/ink/ir/src/ir/item/event.rs index 0ca2c454d4..483f5db8e5 100644 --- a/crates/ink/ir/src/ir/item/event.rs +++ b/crates/ink/ir/src/ir/item/event.rs @@ -204,22 +204,6 @@ mod tests { ) } - #[test] - fn cfg_marked_field_attribute_fails() { - assert_try_from_fails( - syn::parse_quote! { - #[ink(event)] - pub struct MyEvent { - #[ink(topic)] - field_1: i32, - #[cfg(unix)] - field_2: bool, - } - }, - "conditional compilation is not allowed for event field", - ) - } - /// Used for the event fields iterator unit test because `syn::Field` does /// not provide a `syn::parse::Parse` implementation. #[derive(Debug, PartialEq, Eq)] From 5391171fdbd27a885f8e15fef6d09a63f2b297e6 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Mon, 26 Jun 2023 11:48:31 +0100 Subject: [PATCH 068/140] Remove unused topics attr test --- crates/ink/tests/unique_topics.rs | 101 ------------------------------ 1 file changed, 101 deletions(-) delete mode 100644 crates/ink/tests/unique_topics.rs diff --git a/crates/ink/tests/unique_topics.rs b/crates/ink/tests/unique_topics.rs deleted file mode 100644 index 8234d8470f..0000000000 --- a/crates/ink/tests/unique_topics.rs +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2018-2022 Parity Technologies (UK) Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#![cfg_attr(not(feature = "std"), no_std)] - -#[ink::contract] -mod my_contract { - #[ink(storage)] - pub struct MyContract {} - - /// Exemplary event - #[ink(event)] - pub struct MyEvent { - #[ink(topic)] - v0: Option, - #[ink(topic)] - v1: Balance, - #[ink(topic)] - v2: bool, - #[ink(topic)] - v3: bool, - } - - impl MyContract { - /// Creates a new `MyContract` instance. - #[ink(constructor)] - pub fn new() -> Self { - MyContract {} - } - - /// Emits a `MyEvent`. - #[ink(message)] - pub fn emit_my_event(&self) { - self.env().emit_event(MyEvent { - v0: None, - v1: 0, - v2: false, - v3: false, - }); - } - } - - impl Default for MyContract { - fn default() -> Self { - Self::new() - } - } - - #[cfg(test)] - mod tests { - use super::*; - use ink_env::test::EmittedEvent; - - #[ink::test] - fn event_must_have_unique_topics() { - // given - let my_contract = MyContract::new(); - - // when - MyContract::emit_my_event(&my_contract); - - // then - // all topics must be unique - let emitted_events = - ink_env::test::recorded_events().collect::>(); - let mut encoded_topics: std::vec::Vec<&[u8]> = emitted_events[0] - .topics - .iter() - .map(|topic| topic.as_slice()) - .collect(); - assert!(!has_duplicates(&mut encoded_topics)); - } - - /// Finds duplicates in a given vector. - /// - /// This function has complexity of `O(n * log n)` and no additional memory - /// is required, although the order of items is not preserved. - fn has_duplicates>(items: &mut [T]) -> bool { - // Sort the vector - items.sort_by(|a, b| Ord::cmp(a.as_ref(), b.as_ref())); - // And then find any two consecutive equal elements. - items.windows(2).any(|w| { - match w { - [ref a, ref b] => a == b, - _ => false, - } - }) - } - } -} From 0891fd359a62f727f413129771e1f22562581a92 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Mon, 26 Jun 2023 11:51:15 +0100 Subject: [PATCH 069/140] Fmt --- crates/ink/codegen/src/generator/events.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/ink/codegen/src/generator/events.rs b/crates/ink/codegen/src/generator/events.rs index dbe51ccd33..153c4bea49 100644 --- a/crates/ink/codegen/src/generator/events.rs +++ b/crates/ink/codegen/src/generator/events.rs @@ -43,8 +43,8 @@ impl<'a> Events<'a> { self.contract.module().events().map(move |event| { let span = event.span(); let attrs = event.attrs(); - // add back the `#[ink(anonymous)]` attribute if it was present, for parsing by the - // derive macros. + // add back the `#[ink(anonymous)]` attribute if it was present, for parsing + // by the derive macros. let anonymous_attr = event.anonymous.then(|| { quote_spanned!(span => #[ink(anonymous)] From 81d62ae12691c8dada5398802b16ff99bcd7f52c Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Mon, 26 Jun 2023 17:07:56 +0100 Subject: [PATCH 070/140] Fix trait erc20 tests --- integration-tests/trait-erc20/lib.rs | 67 +++++++--------------------- 1 file changed, 16 insertions(+), 51 deletions(-) diff --git a/integration-tests/trait-erc20/lib.rs b/integration-tests/trait-erc20/lib.rs index 30f5517b72..790c628d9b 100644 --- a/integration-tests/trait-erc20/lib.rs +++ b/integration-tests/trait-erc20/lib.rs @@ -269,25 +269,20 @@ mod erc20 { primitives::Clear, }; - type Event = ::Type; - fn assert_transfer_event( event: &ink::env::test::EmittedEvent, expected_from: Option, expected_to: Option, expected_value: Balance, ) { - let decoded_event = ::decode(&mut &event.data[..]) + let decoded_event = ::decode(&mut &event.data[..]) .expect("encountered invalid contract event data buffer"); - if let Event::Transfer(Transfer { from, to, value }) = decoded_event { - assert_eq!(from, expected_from, "encountered invalid Transfer.from"); - assert_eq!(to, expected_to, "encountered invalid Transfer.to"); - assert_eq!(value, expected_value, "encountered invalid Trasfer.value"); - } else { - panic!("encountered unexpected event kind: expected a Transfer event") - } + let Transfer { from, to, value } = decoded_event; + assert_eq!(from, expected_from, "encountered invalid Transfer.from"); + assert_eq!(to, expected_to, "encountered invalid Transfer.to"); + assert_eq!(value, expected_value, "encountered invalid Trasfer.value"); - fn encoded_into_hash(entity: &T) -> Hash + fn encoded_into_hash(entity: T) -> Hash where T: scale::Encode, { @@ -307,24 +302,16 @@ mod erc20 { result } - let expected_topics = [ - encoded_into_hash(&PrefixedValue { - prefix: b"", - value: b"Erc20::Transfer", - }), - encoded_into_hash(&PrefixedValue { - prefix: b"Erc20::Transfer::from", - value: &expected_from, - }), - encoded_into_hash(&PrefixedValue { - prefix: b"Erc20::Transfer::to", - value: &expected_to, - }), - encoded_into_hash(&PrefixedValue { - prefix: b"Erc20::Transfer::value", - value: &expected_value, - }), - ]; + let mut expected_topics = Vec::new(); + expected_topics.push(ink::blake2x256!("Transfer(Option,Option,Balance)").into()); + if let Some(from) = expected_from { + expected_topics.push(encoded_into_hash(&from)); + } + if let Some(to) = expected_to { + expected_topics.push(encoded_into_hash(&to)); + } + expected_topics.push(encoded_into_hash(&value)); + for (n, (actual_topic, expected_topic)) in event.topics.iter().zip(expected_topics).enumerate() { @@ -546,27 +533,5 @@ mod erc20 { fn set_caller(sender: AccountId) { ink::env::test::set_caller::(sender); } - - /// For calculating the event topic hash. - struct PrefixedValue<'a, 'b, T> { - pub prefix: &'a [u8], - pub value: &'b T, - } - - impl scale::Encode for PrefixedValue<'_, '_, X> - where - X: scale::Encode, - { - #[inline] - fn size_hint(&self) -> usize { - self.prefix.size_hint() + self.value.size_hint() - } - - #[inline] - fn encode_to(&self, dest: &mut T) { - self.prefix.encode_to(dest); - self.value.encode_to(dest); - } - } } } From 4073c7efb30bcef4d64ad28b09525e35b20726e8 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Mon, 26 Jun 2023 17:11:05 +0100 Subject: [PATCH 071/140] Fix erc20 tests and fmt --- integration-tests/erc20/lib.rs | 68 ++++++++-------------------- integration-tests/trait-erc20/lib.rs | 5 +- 2 files changed, 22 insertions(+), 51 deletions(-) diff --git a/integration-tests/erc20/lib.rs b/integration-tests/erc20/lib.rs index f00c2eb25e..7ebb393d8e 100644 --- a/integration-tests/erc20/lib.rs +++ b/integration-tests/erc20/lib.rs @@ -223,41 +223,31 @@ mod erc20 { Hash, }; - type Event = ::Type; - fn assert_transfer_event( event: &ink::env::test::EmittedEvent, expected_from: Option, expected_to: Option, expected_value: Balance, ) { - let decoded_event = ::decode(&mut &event.data[..]) + let decoded_event = ::decode(&mut &event.data[..]) .expect("encountered invalid contract event data buffer"); - if let Event::Transfer(Transfer { from, to, value }) = decoded_event { - assert_eq!(from, expected_from, "encountered invalid Transfer.from"); - assert_eq!(to, expected_to, "encountered invalid Transfer.to"); - assert_eq!(value, expected_value, "encountered invalid Trasfer.value"); - } else { - panic!("encountered unexpected event kind: expected a Transfer event") + let Transfer { from, to, value } = decoded_event; + assert_eq!(from, expected_from, "encountered invalid Transfer.from"); + assert_eq!(to, expected_to, "encountered invalid Transfer.to"); + assert_eq!(value, expected_value, "encountered invalid Trasfer.value"); + + let mut expected_topics = Vec::new(); + expected_topics.push( + ink::blake2x256!("Transfer(Option,Option,Balance)") + .into(), + ); + if let Some(from) = expected_from { + expected_topics.push(encoded_into_hash(&from)); + } + if let Some(to) = expected_to { + expected_topics.push(encoded_into_hash(&to)); } - let expected_topics = vec![ - encoded_into_hash(&PrefixedValue { - value: b"Erc20::Transfer", - prefix: b"", - }), - encoded_into_hash(&PrefixedValue { - prefix: b"Erc20::Transfer::from", - value: &expected_from, - }), - encoded_into_hash(&PrefixedValue { - prefix: b"Erc20::Transfer::to", - value: &expected_to, - }), - encoded_into_hash(&PrefixedValue { - prefix: b"Erc20::Transfer::value", - value: &expected_value, - }), - ]; + expected_topics.push(encoded_into_hash(&value)); let topics = event.topics.clone(); for (n, (actual_topic, expected_topic)) in @@ -482,29 +472,7 @@ mod erc20 { ) } - /// For calculating the event topic hash. - struct PrefixedValue<'a, 'b, T> { - pub prefix: &'a [u8], - pub value: &'b T, - } - - impl scale::Encode for PrefixedValue<'_, '_, X> - where - X: scale::Encode, - { - #[inline] - fn size_hint(&self) -> usize { - self.prefix.size_hint() + self.value.size_hint() - } - - #[inline] - fn encode_to(&self, dest: &mut T) { - self.prefix.encode_to(dest); - self.value.encode_to(dest); - } - } - - fn encoded_into_hash(entity: &T) -> Hash + fn encoded_into_hash(entity: T) -> Hash where T: scale::Encode, { diff --git a/integration-tests/trait-erc20/lib.rs b/integration-tests/trait-erc20/lib.rs index 790c628d9b..bf6c80df59 100644 --- a/integration-tests/trait-erc20/lib.rs +++ b/integration-tests/trait-erc20/lib.rs @@ -303,7 +303,10 @@ mod erc20 { } let mut expected_topics = Vec::new(); - expected_topics.push(ink::blake2x256!("Transfer(Option,Option,Balance)").into()); + expected_topics.push( + ink::blake2x256!("Transfer(Option,Option,Balance)") + .into(), + ); if let Some(from) = expected_from { expected_topics.push(encoded_into_hash(&from)); } From b423df0f7aafc1571aa30b5d538a846710da3a68 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Mon, 26 Jun 2023 17:26:44 +0100 Subject: [PATCH 072/140] Fix duplicate inline attrs. Remove offchain duplicate topics check. --- crates/env/src/engine/off_chain/impls.rs | 4 ---- crates/ink/codegen/src/generator/events.rs | 2 -- crates/ink/ir/src/ir/item/event.rs | 21 ++------------------- integration-tests/custom-environment/lib.rs | 10 ++-------- 4 files changed, 4 insertions(+), 33 deletions(-) diff --git a/crates/env/src/engine/off_chain/impls.rs b/crates/env/src/engine/off_chain/impls.rs index 6d7736518d..1cb8444c83 100644 --- a/crates/env/src/engine/off_chain/impls.rs +++ b/crates/env/src/engine/off_chain/impls.rs @@ -150,10 +150,6 @@ where } let off_hash = result.as_ref(); let off_hash = off_hash.to_vec(); - debug_assert!( - !self.topics.contains(&off_hash), - "duplicate topic hash discovered!" - ); self.topics.push(off_hash); } diff --git a/crates/ink/codegen/src/generator/events.rs b/crates/ink/codegen/src/generator/events.rs index 153c4bea49..d9dd7dca0d 100644 --- a/crates/ink/codegen/src/generator/events.rs +++ b/crates/ink/codegen/src/generator/events.rs @@ -42,7 +42,6 @@ impl<'a> Events<'a> { fn generate_event_items(&'a self) -> impl Iterator + 'a { self.contract.module().events().map(move |event| { let span = event.span(); - let attrs = event.attrs(); // add back the `#[ink(anonymous)]` attribute if it was present, for parsing // by the derive macros. let anonymous_attr = event.anonymous.then(|| { @@ -51,7 +50,6 @@ impl<'a> Events<'a> { ) }); quote_spanned!(span => - #( #attrs )* #[derive(::ink::Event, ::scale::Encode, ::scale::Decode)] #[cfg_attr(feature = "std", derive(::ink::EventMetadata))] #anonymous_attr diff --git a/crates/ink/ir/src/ir/item/event.rs b/crates/ink/ir/src/ir/item/event.rs index 483f5db8e5..aa9f9898f6 100644 --- a/crates/ink/ir/src/ir/item/event.rs +++ b/crates/ink/ir/src/ir/item/event.rs @@ -12,15 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::ir::{ - self, - utils::extract_cfg_attributes, -}; -use proc_macro2::{ - Ident, - Span, - TokenStream, -}; +use crate::ir; +use proc_macro2::Ident; use syn::spanned::Spanned as _; /// An ink! event struct definition. @@ -106,16 +99,6 @@ impl Event { pub fn ident(&self) -> &Ident { &self.item.ident } - - /// Returns all non-ink! attributes. - pub fn attrs(&self) -> &[syn::Attribute] { - &self.item.attrs - } - - /// Returns a list of `cfg` attributes if any. - pub fn get_cfg_attrs(&self, span: Span) -> Vec { - extract_cfg_attributes(self.attrs(), span) - } } #[cfg(test)] diff --git a/integration-tests/custom-environment/lib.rs b/integration-tests/custom-environment/lib.rs index 971485a993..6a703cbb00 100644 --- a/integration-tests/custom-environment/lib.rs +++ b/integration-tests/custom-environment/lib.rs @@ -67,8 +67,6 @@ mod runtime_call { mod tests { use super::*; - type Event = ::Type; - #[ink::test] fn emits_event_with_many_topics() { let mut contract = Topics::new(); @@ -78,13 +76,9 @@ mod runtime_call { assert_eq!(emitted_events.len(), 1); let emitted_event = - ::decode(&mut &emitted_events[0].data[..]) - .expect("encountered invalid contract event data buffer"); + ::decode(&mut &emitted_events[0].data[..]); - assert!(matches!( - emitted_event, - Event::EventWithTopics(EventWithTopics { .. }) - )); + assert!(emitted_event.is_ok()); } } From 5414c95fdc8031f8b1ecf5d0eb93b08d84a601f2 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Mon, 26 Jun 2023 17:27:14 +0100 Subject: [PATCH 073/140] Fmt --- integration-tests/custom-environment/lib.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/integration-tests/custom-environment/lib.rs b/integration-tests/custom-environment/lib.rs index 6a703cbb00..b75a0d372d 100644 --- a/integration-tests/custom-environment/lib.rs +++ b/integration-tests/custom-environment/lib.rs @@ -75,8 +75,9 @@ mod runtime_call { let emitted_events = ink::env::test::recorded_events().collect::>(); assert_eq!(emitted_events.len(), 1); - let emitted_event = - ::decode(&mut &emitted_events[0].data[..]); + let emitted_event = ::decode( + &mut &emitted_events[0].data[..], + ); assert!(emitted_event.is_ok()); } From a0c0218424de4f06c33fb64b7f8302543ab16da2 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Mon, 26 Jun 2023 17:46:45 +0100 Subject: [PATCH 074/140] Clippy --- integration-tests/erc20/lib.rs | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/integration-tests/erc20/lib.rs b/integration-tests/erc20/lib.rs index 7ebb393d8e..258c36d0da 100644 --- a/integration-tests/erc20/lib.rs +++ b/integration-tests/erc20/lib.rs @@ -242,12 +242,12 @@ mod erc20 { .into(), ); if let Some(from) = expected_from { - expected_topics.push(encoded_into_hash(&from)); + expected_topics.push(encoded_into_hash(from)); } if let Some(to) = expected_to { - expected_topics.push(encoded_into_hash(&to)); + expected_topics.push(encoded_into_hash(to)); } - expected_topics.push(encoded_into_hash(&value)); + expected_topics.push(encoded_into_hash(value)); let topics = event.topics.clone(); for (n, (actual_topic, expected_topic)) in @@ -526,7 +526,7 @@ mod erc20 { let bob_account = ink_e2e::account_id(ink_e2e::AccountKeyring::Bob); let transfer_to_bob = 500_000_000u128; - let transfer = call.transfer(bob_account.clone(), transfer_to_bob); + let transfer = call.transfer(bob_account, transfer_to_bob); let _transfer_res = client .call(&ink_e2e::alice(), &transfer, 0, None) .await @@ -566,8 +566,7 @@ mod erc20 { let amount = 500_000_000u128; // tx - let transfer_from = - call.transfer_from(bob_account.clone(), charlie_account.clone(), amount); + let transfer_from = call.transfer_from(bob_account, charlie_account, amount); let transfer_from_result = client .call(&ink_e2e::charlie(), &transfer_from, 0, None) .await; @@ -579,18 +578,15 @@ mod erc20 { // Bob approves Charlie to transfer up to amount on his behalf let approved_value = 1_000u128; - let approve_call = call.approve(charlie_account.clone(), approved_value); + let approve_call = call.approve(charlie_account, approved_value); client .call(&ink_e2e::bob(), &approve_call, 0, None) .await .expect("approve failed"); // `transfer_from` the approved amount - let transfer_from = call.transfer_from( - bob_account.clone(), - charlie_account.clone(), - approved_value, - ); + let transfer_from = + call.transfer_from(bob_account, charlie_account, approved_value); let transfer_from_result = client .call(&ink_e2e::charlie(), &transfer_from, 0, None) .await; @@ -605,8 +601,7 @@ mod erc20 { .await; // `transfer_from` again, this time exceeding the approved amount - let transfer_from = - call.transfer_from(bob_account.clone(), charlie_account.clone(), 1); + let transfer_from = call.transfer_from(bob_account, charlie_account, 1); let transfer_from_result = client .call(&ink_e2e::charlie(), &transfer_from, 0, None) .await; From cb52b1af4c32a62e70577660426028d2ac54eb17 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Mon, 26 Jun 2023 17:47:31 +0100 Subject: [PATCH 075/140] clippy --- integration-tests/trait-erc20/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/integration-tests/trait-erc20/lib.rs b/integration-tests/trait-erc20/lib.rs index bf6c80df59..17bb3994fd 100644 --- a/integration-tests/trait-erc20/lib.rs +++ b/integration-tests/trait-erc20/lib.rs @@ -308,12 +308,12 @@ mod erc20 { .into(), ); if let Some(from) = expected_from { - expected_topics.push(encoded_into_hash(&from)); + expected_topics.push(encoded_into_hash(from)); } if let Some(to) = expected_to { - expected_topics.push(encoded_into_hash(&to)); + expected_topics.push(encoded_into_hash(to)); } - expected_topics.push(encoded_into_hash(&value)); + expected_topics.push(encoded_into_hash(value)); for (n, (actual_topic, expected_topic)) in event.topics.iter().zip(expected_topics).enumerate() From 4eb0e81d985aaa6854597be18d1203e783f90bfd Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Mon, 26 Jun 2023 17:53:43 +0100 Subject: [PATCH 076/140] events clippy --- integration-tests/events/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/integration-tests/events/lib.rs b/integration-tests/events/lib.rs index 0b598de504..4db8f6cdfe 100644 --- a/integration-tests/events/lib.rs +++ b/integration-tests/events/lib.rs @@ -48,7 +48,7 @@ pub mod events { let decoded_event = ::decode(&mut &event.data[..]) .expect("encountered invalid contract event data buffer"); - assert_eq!(decoded_event.value, true); + assert!(decoded_event.value); } #[ink::test] @@ -126,7 +126,7 @@ pub mod events { let signature_topic = ::SIGNATURE_TOPIC - .map(|topic| H256::from(topic)) + .map(H256::from) .unwrap(); let expected_topics = vec![signature_topic]; From 7bf21ccc389f69fc1803c7182e3ff016a0cea803 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Wed, 28 Jun 2023 11:37:46 +0100 Subject: [PATCH 077/140] Push 0 topic if `None` --- crates/env/src/event.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/env/src/event.rs b/crates/env/src/event.rs index 614025ed98..22b7c13b9a 100644 --- a/crates/env/src/event.rs +++ b/crates/env/src/event.rs @@ -102,8 +102,6 @@ where { /// Pushes another event topic to be serialized through the topics builder. /// - /// If the `value` of the topic is `None` then the topic will *not* be pushed. - /// /// Returns a topics builder that expects one less event topic for serialization /// than before the call. pub fn push_topic( @@ -115,7 +113,9 @@ where { // Only publish the topic if it is not an `Option::None`. if let Some(topic) = value { - self.backend.push_topic(topic); + self.backend.push_topic::(topic); + } else { + self.backend.push_topic::(&0u8); } TopicsBuilder { backend: self.backend, From 7fef4a61b169c9e3ad48f6a13466a72de6b254f3 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Wed, 28 Jun 2023 11:38:18 +0100 Subject: [PATCH 078/140] Expose signature_topic --- crates/metadata/src/specs.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/crates/metadata/src/specs.rs b/crates/metadata/src/specs.rs index 67579bb543..674110ced8 100644 --- a/crates/metadata/src/specs.rs +++ b/crates/metadata/src/specs.rs @@ -951,6 +951,12 @@ where } } +impl AsRef<[u8]> for SignatureTopic { + fn as_ref(&self) -> &[u8] { + &self.bytes + } +} + /// An event specification builder. #[must_use] pub struct EventSpecBuilder @@ -1068,6 +1074,11 @@ where &self.args } + /// The signature topic of the event. `None` if the event is anonymous. + pub fn signature_topic(&self) -> Option<&SignatureTopic> { + self.signature_topic.as_ref() + } + /// The event documentation. pub fn docs(&self) -> &[F::String] { &self.docs From ef91f80190c6a619dfe5578a8d8946e8b019c01a Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Wed, 28 Jun 2023 14:29:44 +0100 Subject: [PATCH 079/140] Change SignatureTopic to expose as_bytes --- crates/metadata/src/specs.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/metadata/src/specs.rs b/crates/metadata/src/specs.rs index 674110ced8..7ce560586c 100644 --- a/crates/metadata/src/specs.rs +++ b/crates/metadata/src/specs.rs @@ -951,8 +951,9 @@ where } } -impl AsRef<[u8]> for SignatureTopic { - fn as_ref(&self) -> &[u8] { +impl SignatureTopic { + /// Returns the bytes of the signature topic. + pub fn as_bytes(&self) -> &[u8] { &self.bytes } } From 7f5f6d8c2d0ad5b505dc244b467dffb1aaa1a217 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Wed, 28 Jun 2023 14:30:32 +0100 Subject: [PATCH 080/140] Add test for None topic value --- integration-tests/events/lib.rs | 63 +++++++++++++++++++++++++++++---- 1 file changed, 57 insertions(+), 6 deletions(-) diff --git a/integration-tests/events/lib.rs b/integration-tests/events/lib.rs index 4db8f6cdfe..7ee836f2cd 100644 --- a/integration-tests/events/lib.rs +++ b/integration-tests/events/lib.rs @@ -73,7 +73,7 @@ pub mod events { } #[ink::test] - fn option_topic_none_missing_topic() { + fn option_topic_none_encoded_as_0() { let mut events = Events::new(false); events.emit_32_byte_topic_event(None); @@ -81,11 +81,17 @@ pub mod events { assert_eq!(1, emitted_events.len()); let event = &emitted_events[0]; - assert_eq!( - event.topics.len(), - 2, - "option topic should *not* be published" - ); + let signature_topic = + ::SIGNATURE_TOPIC + .map(|topic| topic.to_vec()) + .unwrap(); + + let expected_topics = vec![ + signature_topic, + [0x42; 32].to_vec(), + [0x00; 32].to_vec(), // None is encoded as 0x00 + ]; + assert_eq!(expected_topics, event.topics); } } @@ -134,5 +140,50 @@ pub mod events { Ok(()) } + + #[ink_e2e::test] + async fn emits_event_with_option_topic_none( + mut client: ink_e2e::Client, + ) -> E2EResult<()> { + // given + let init_value = false; + let constructor = EventsRef::new(init_value); + let contract = client + .instantiate("events", &ink_e2e::alice(), constructor, 0, None) + .await + .expect("instantiate failed"); + let mut call = contract.call::(); + + // when + let call = call.emit_32_byte_topic_event(None); + let call_res = client + .call(&ink_e2e::bob(), &call, 0, None) + .await + .expect("emit_32_byte_topic_event failed"); + + let contract_events = call_res.contract_emitted_events()?; + + // then + assert_eq!(1, contract_events.len()); + let contract_event = &contract_events[0]; + let event: event_def::ThirtyTwoByteTopics = + scale::Decode::decode(&mut &contract_event.event.data[..]) + .expect("encountered invalid contract event data buffer"); + assert!(event.maybe_hash.is_none()); + + let signature_topic = + ::SIGNATURE_TOPIC + .map(H256::from) + .unwrap(); + + let expected_topics = vec![ + signature_topic, + [0x42; 32].into(), + [0x00; 32].into(), // None is encoded as 0x00 + ]; + assert_eq!(expected_topics, contract_event.topics); + + Ok(()) + } } } From 4334aac3f30ba1a5574d714147f27f9152a7dd45 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Wed, 28 Jun 2023 15:07:45 +0100 Subject: [PATCH 081/140] Fix erc20 topic tests --- integration-tests/erc20/lib.rs | 4 ++++ integration-tests/trait-erc20/lib.rs | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/integration-tests/erc20/lib.rs b/integration-tests/erc20/lib.rs index 258c36d0da..668ba3859f 100644 --- a/integration-tests/erc20/lib.rs +++ b/integration-tests/erc20/lib.rs @@ -243,9 +243,13 @@ mod erc20 { ); if let Some(from) = expected_from { expected_topics.push(encoded_into_hash(from)); + } else { + expected_topics.push(Hash::CLEAR_HASH); } if let Some(to) = expected_to { expected_topics.push(encoded_into_hash(to)); + } else { + expected_topics.push(Hash::CLEAR_HASH); } expected_topics.push(encoded_into_hash(value)); diff --git a/integration-tests/trait-erc20/lib.rs b/integration-tests/trait-erc20/lib.rs index 17bb3994fd..4fb73f9ea0 100644 --- a/integration-tests/trait-erc20/lib.rs +++ b/integration-tests/trait-erc20/lib.rs @@ -309,9 +309,13 @@ mod erc20 { ); if let Some(from) = expected_from { expected_topics.push(encoded_into_hash(from)); + } else { + expected_topics.push(Hash::CLEAR_HASH); } if let Some(to) = expected_to { expected_topics.push(encoded_into_hash(to)); + } else { + expected_topics.push(Hash::CLEAR_HASH); } expected_topics.push(encoded_into_hash(value)); From 37115d9e9f75b1c31ccf01d8fb12d05d3c314b06 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Thu, 29 Jun 2023 10:45:16 +0100 Subject: [PATCH 082/140] Add module_path --- crates/ink/macro/src/event/metadata.rs | 1 + crates/ink/macro/src/tests/event_metadata.rs | 3 +++ 2 files changed, 4 insertions(+) diff --git a/crates/ink/macro/src/event/metadata.rs b/crates/ink/macro/src/event/metadata.rs index 8aed7bc812..e023cc1a19 100644 --- a/crates/ink/macro/src/event/metadata.rs +++ b/crates/ink/macro/src/event/metadata.rs @@ -78,6 +78,7 @@ fn event_metadata_derive_struct(s: synstructure::Structure) -> syn::Result::SIGNATURE_TOPIC ) diff --git a/crates/ink/macro/src/tests/event_metadata.rs b/crates/ink/macro/src/tests/event_metadata.rs index 06479d3933..65f25e5741 100644 --- a/crates/ink/macro/src/tests/event_metadata.rs +++ b/crates/ink/macro/src/tests/event_metadata.rs @@ -33,6 +33,7 @@ fn unit_struct_works() { ::event_spec; ::ink::metadata::EventSpec::new(::core::stringify!(UnitStruct)) + .module_path(::core::module_path!()) .signature_topic(::SIGNATURE_TOPIC) .args([]) .docs([]) @@ -67,6 +68,7 @@ fn struct_with_fields_no_topics() { ::event_spec; ::ink::metadata::EventSpec::new(::core::stringify!(Event)) + .module_path(::core::module_path!()) .signature_topic(::SIGNATURE_TOPIC) .args([ ::ink::metadata::EventParamSpec::new(::core::stringify!(field_1)) @@ -116,6 +118,7 @@ fn struct_with_fields_and_some_topics() { ::event_spec; ::ink::metadata::EventSpec::new(::core::stringify!(Event)) + .module_path(::core::module_path!()) .signature_topic(::SIGNATURE_TOPIC) .args([ ::ink::metadata::EventParamSpec::new(::core::stringify!(field_1)) From f8bbb3b61285a856e29ca7318155a0f0bfa99b2b Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Thu, 29 Jun 2023 11:04:45 +0100 Subject: [PATCH 083/140] Fix max topics in tests --- crates/metadata/src/specs.rs | 5 +++-- crates/metadata/src/tests.rs | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/crates/metadata/src/specs.rs b/crates/metadata/src/specs.rs index 7ce560586c..5d8c740e7b 100644 --- a/crates/metadata/src/specs.rs +++ b/crates/metadata/src/specs.rs @@ -284,6 +284,7 @@ where "only one default message is allowed" ); + let max_topics = self.spec.environment.max_event_topics; let events_exceeding_max_topics_limit = self .spec .events @@ -292,7 +293,7 @@ where let signature_topic = if e.signature_topic.is_some() { 1 } else { 0 }; let topics_count = signature_topic + e.args.iter().filter(|a| a.indexed).count(); - if topics_count > self.spec.environment.max_event_topics { + if topics_count > max_topics { Some(format!( "`{}::{}` ({} topics)", e.module_path, e.label, topics_count @@ -304,7 +305,7 @@ where .collect::>(); assert!( events_exceeding_max_topics_limit.is_empty(), - "maximum of 2 event topics exceeded: {}", + "maximum of {max_topics} event topics exceeded: {}", events_exceeding_max_topics_limit.join(", ") ); diff --git a/crates/metadata/src/tests.rs b/crates/metadata/src/tests.rs index eaadb9ebde..3584b780de 100644 --- a/crates/metadata/src/tests.rs +++ b/crates/metadata/src/tests.rs @@ -700,6 +700,19 @@ fn should_trim_whitespaces_in_events_docs() { assert_eq!(event_spec_name, expected_event_spec); } +/// Create a default environment spec with the `max_event_topics` set to `4`. +fn environment_spec() -> EnvironmentSpec { + EnvironmentSpec::new() + .account_id(Default::default()) + .balance(Default::default()) + .hash(Default::default()) + .timestamp(Default::default()) + .block_number(Default::default()) + .chain_extension(Default::default()) + .max_event_topics(4) + .done() +} + /// Helper for creating a constructor spec at runtime fn runtime_constructor_spec() -> ConstructorSpec { let path: Path = Path::from_segments_unchecked(["FooType".to_string()]); @@ -745,6 +758,8 @@ fn runtime_event_spec() -> EventSpec { .docs(vec![]) .done()]; EventSpec::new("foobar".into()) + .signature_topic(Some([42u8; 32])) + .module_path("foo".into()) .args(args) .docs(["foobar event"]) .done() @@ -754,6 +769,7 @@ fn runtime_event_spec() -> EventSpec { #[test] fn construct_runtime_contract_spec() { let spec = ContractSpec::new() + .environment(environment_spec()) .constructors([runtime_constructor_spec()]) .messages([runtime_message_spec()]) .events([runtime_event_spec()]) @@ -824,6 +840,8 @@ fn construct_runtime_contract_spec() { let expected_event_spec = serde_json::json!( { "label": "foobar", + "module_path": "foo", + "signature_topic": "0x2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a", "args": [ { "label": "something", From ad144ca6ed9407ac8a478fe6cfd2769a0b5dc5e7 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Thu, 29 Jun 2023 11:06:58 +0100 Subject: [PATCH 084/140] Fix event docs test --- crates/metadata/src/tests.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/metadata/src/tests.rs b/crates/metadata/src/tests.rs index 3584b780de..0f3d1ddaf2 100644 --- a/crates/metadata/src/tests.rs +++ b/crates/metadata/src/tests.rs @@ -668,6 +668,8 @@ fn should_trim_whitespaces_in_events_docs() { .docs(vec!["test".to_string()]) .done()]; let es = EventSpec::new("foobar".into()) + .module_path("foo") + .signature_topic(Some([0u8; 32])) .args(args) .docs([" FooBarEvent "]) .done(); @@ -677,6 +679,8 @@ fn should_trim_whitespaces_in_events_docs() { // when let expected_event_spec = serde_json::json!( { + "module_path": "foo", + "signature_topic": "0x0000000000000000000000000000000000000000000000000000000000000000", "args": [ { "docs": ["test"], From 4af676f71809158299610b6d82b0cf6643131d3a Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Thu, 29 Jun 2023 11:13:12 +0100 Subject: [PATCH 085/140] Clippy --- crates/metadata/src/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/metadata/src/tests.rs b/crates/metadata/src/tests.rs index 0f3d1ddaf2..a6d2a47491 100644 --- a/crates/metadata/src/tests.rs +++ b/crates/metadata/src/tests.rs @@ -763,7 +763,7 @@ fn runtime_event_spec() -> EventSpec { .done()]; EventSpec::new("foobar".into()) .signature_topic(Some([42u8; 32])) - .module_path("foo".into()) + .module_path("foo") .args(args) .docs(["foobar event"]) .done() From 8908f10d9d7a06eb0510c02ece9034acd2448a71 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Thu, 29 Jun 2023 12:00:20 +0100 Subject: [PATCH 086/140] Use path dependency for ink_env --- crates/metadata/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/metadata/Cargo.toml b/crates/metadata/Cargo.toml index 304a5e396c..99b2339652 100644 --- a/crates/metadata/Cargo.toml +++ b/crates/metadata/Cargo.toml @@ -15,7 +15,7 @@ categories = ["no-std", "embedded"] include = ["Cargo.toml", "src/**/*.rs", "README.md", "LICENSE"] [dependencies] -ink_env = "4.2.0" +ink_env = { version = "4.2.0", path = "../env/", default-features = false } ink_prelude = { version = "4.2.0", path = "../prelude/", default-features = false } ink_primitives = { version = "4.2.0", path = "../primitives/", default-features = false } From d63afe51c6935c5898d1775748c93caa6347fb4b Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Thu, 29 Jun 2023 12:04:44 +0100 Subject: [PATCH 087/140] Remove ink_env dependency --- crates/metadata/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/metadata/Cargo.toml b/crates/metadata/Cargo.toml index 99b2339652..0254c3dbc7 100644 --- a/crates/metadata/Cargo.toml +++ b/crates/metadata/Cargo.toml @@ -15,7 +15,6 @@ categories = ["no-std", "embedded"] include = ["Cargo.toml", "src/**/*.rs", "README.md", "LICENSE"] [dependencies] -ink_env = { version = "4.2.0", path = "../env/", default-features = false } ink_prelude = { version = "4.2.0", path = "../prelude/", default-features = false } ink_primitives = { version = "4.2.0", path = "../primitives/", default-features = false } From c7636fb4abca5dfa637a61edaed964ca1c3f2d54 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Thu, 29 Jun 2023 15:43:10 +0100 Subject: [PATCH 088/140] RUSTFLAGS for test to fix linking issue --- .gitlab-ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 43c55a0b2a..416a08e0e8 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -272,6 +272,8 @@ test: - job: check-std artifacts: false variables: + # Fix for linking of `linkme` for `cargo test`: https://github.com/dtolnay/linkme/issues/49 + RUSTFLAGS: "-Clink-arg=-z -Clink-arg=nostart-stop-gc" # Since we run the tests with `--all-features` this implies the feature # `ink-fuzz-tests` as well -- i.e. the fuzz tests are run. # There's no way to disable a single feature while enabling all features From 4ca3065d2afc114030b2cf38588ec633b3bd5a8f Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Thu, 29 Jun 2023 16:23:45 +0100 Subject: [PATCH 089/140] RUSTFLAGS for test to fix linking issue --- .gitlab-ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 416a08e0e8..0d25a4b610 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -403,6 +403,9 @@ examples-contract-build: stage: examples <<: *docker-env <<: *test-refs + variables: + # Fix for linking of `linkme` for `cargo contract build`: https://github.com/dtolnay/linkme/issues/49 + RUSTFLAGS: "-Clink-arg=-z -Clink-arg=nostart-stop-gc" script: - rustup component add rust-src --toolchain stable - cargo contract -V From 87a965f08f992b9fcf095ecb9f95843fbc9e78bf Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Thu, 29 Jun 2023 17:17:12 +0100 Subject: [PATCH 090/140] Fix custom environment, remove extra topic --- integration-tests/custom-environment/lib.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/integration-tests/custom-environment/lib.rs b/integration-tests/custom-environment/lib.rs index b75a0d372d..e14b9f6424 100644 --- a/integration-tests/custom-environment/lib.rs +++ b/integration-tests/custom-environment/lib.rs @@ -46,8 +46,6 @@ mod runtime_call { third_topic: Balance, #[ink(topic)] fourth_topic: Balance, - #[ink(topic)] - fifth_topic: Balance, } impl Topics { From 346b38084ad424d695f77968a5a67b4e949f815d Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Tue, 4 Jul 2023 13:36:26 +0100 Subject: [PATCH 091/140] Use ink::event syntax --- crates/ink/tests/events_metadata.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/ink/tests/events_metadata.rs b/crates/ink/tests/events_metadata.rs index c55fd3ff2f..5d6ce19458 100644 --- a/crates/ink/tests/events_metadata.rs +++ b/crates/ink/tests/events_metadata.rs @@ -14,8 +14,7 @@ #![cfg_attr(not(feature = "std"), no_std)] -#[derive(ink::Event, scale::Encode)] -#[cfg_attr(feature = "std", derive(ink::EventMetadata))] +#[ink::event] pub struct EventExternal { f1: bool, f2: u32, From f1bd57f647e381fcb7f3afbcacfad16fd79c7a6e Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Tue, 4 Jul 2023 16:05:45 +0100 Subject: [PATCH 092/140] Add ui test for cfg attributes --- crates/ink/macro/src/event/metadata.rs | 1 - crates/ink/tests/compile_tests.rs | 3 +++ crates/ink/tests/ui/event/fail/cfg_attr_on_topic.rs | 8 ++++++++ crates/ink/tests/ui/event/fail/cfg_attr_on_topic.stderr | 5 +++++ 4 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 crates/ink/tests/ui/event/fail/cfg_attr_on_topic.rs create mode 100644 crates/ink/tests/ui/event/fail/cfg_attr_on_topic.stderr diff --git a/crates/ink/macro/src/event/metadata.rs b/crates/ink/macro/src/event/metadata.rs index e023cc1a19..3865379e76 100644 --- a/crates/ink/macro/src/event/metadata.rs +++ b/crates/ink/macro/src/event/metadata.rs @@ -76,7 +76,6 @@ fn event_metadata_derive_struct(s: synstructure::Structure) -> syn::Result ::ink::metadata::EventSpec = <#ident as ::ink::metadata::EventMetadata>::event_spec; - // todo: check that cfg attributes work here ::ink::metadata::EventSpec::new(::core::stringify!(#ident)) .module_path(::core::module_path!()) .signature_topic( diff --git a/crates/ink/tests/compile_tests.rs b/crates/ink/tests/compile_tests.rs index 78635475b0..c38ed7414b 100644 --- a/crates/ink/tests/compile_tests.rs +++ b/crates/ink/tests/compile_tests.rs @@ -28,6 +28,9 @@ fn ui_tests() { t.pass("tests/ui/contract/pass/*.rs"); t.compile_fail("tests/ui/contract/fail/*.rs"); + t.pass("tests/ui/event/pass/*.rs"); + t.compile_fail("tests/ui/event/fail/*.rs"); + t.pass("tests/ui/storage_item/pass/*.rs"); t.compile_fail("tests/ui/storage_item/fail/*.rs"); diff --git a/crates/ink/tests/ui/event/fail/cfg_attr_on_topic.rs b/crates/ink/tests/ui/event/fail/cfg_attr_on_topic.rs new file mode 100644 index 0000000000..4528625c54 --- /dev/null +++ b/crates/ink/tests/ui/event/fail/cfg_attr_on_topic.rs @@ -0,0 +1,8 @@ +#[ink::event] +pub struct Event { + #[cfg(feature = "std")] + #[ink(topic)] + pub topic: [u8; 32], +} + +fn main() {} \ No newline at end of file diff --git a/crates/ink/tests/ui/event/fail/cfg_attr_on_topic.stderr b/crates/ink/tests/ui/event/fail/cfg_attr_on_topic.stderr new file mode 100644 index 0000000000..57fdb45b1f --- /dev/null +++ b/crates/ink/tests/ui/event/fail/cfg_attr_on_topic.stderr @@ -0,0 +1,5 @@ +error: conditional compilation is not allowed for event fields + --> tests/ui/event/fail/cfg_attr_on_topic.rs:3:5 + | +3 | #[cfg(feature = "std")] + | ^ From 1ed1e88b02951f640325516b61507bb1e63b2f32 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Tue, 4 Jul 2023 16:10:18 +0100 Subject: [PATCH 093/140] Add success ui tests for ink::event --- crates/ink/tests/ui/event/pass/event_attribute_works.rs | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 crates/ink/tests/ui/event/pass/event_attribute_works.rs diff --git a/crates/ink/tests/ui/event/pass/event_attribute_works.rs b/crates/ink/tests/ui/event/pass/event_attribute_works.rs new file mode 100644 index 0000000000..6e22adbb2c --- /dev/null +++ b/crates/ink/tests/ui/event/pass/event_attribute_works.rs @@ -0,0 +1,8 @@ +#[ink::event] +pub struct Event { + #[ink(topic)] + pub topic: [u8; 32], + pub field_1: u32, +} + +fn main() {} \ No newline at end of file From bb263124861dfd3d0fae6374647f0ab6b4735336 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Tue, 4 Jul 2023 16:12:16 +0100 Subject: [PATCH 094/140] UI test for `enum` should fail --- crates/ink/tests/ui/event/fail/event_enum.rs | 10 ++++++++++ crates/ink/tests/ui/event/fail/event_enum.stderr | 13 +++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 crates/ink/tests/ui/event/fail/event_enum.rs create mode 100644 crates/ink/tests/ui/event/fail/event_enum.stderr diff --git a/crates/ink/tests/ui/event/fail/event_enum.rs b/crates/ink/tests/ui/event/fail/event_enum.rs new file mode 100644 index 0000000000..f068725beb --- /dev/null +++ b/crates/ink/tests/ui/event/fail/event_enum.rs @@ -0,0 +1,10 @@ +#[ink::event] +pub enum Event { + Variant1 { + field_1: i8, + #[ink(topic)] + field_2: i16, + } +} + +fn main() {} \ No newline at end of file diff --git a/crates/ink/tests/ui/event/fail/event_enum.stderr b/crates/ink/tests/ui/event/fail/event_enum.stderr new file mode 100644 index 0000000000..3f350c1164 --- /dev/null +++ b/crates/ink/tests/ui/event/fail/event_enum.stderr @@ -0,0 +1,13 @@ +error: can only derive `Event` for Rust `struct` items + --> tests/ui/event/fail/event_enum.rs:1:1 + | +1 | #[ink::event] + | ^^^^^^^^^^^^^ + | + = note: this error originates in the attribute macro `ink::event` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: can only derive `EventMetadata` for Rust `struct` items + --> tests/ui/event/fail/event_enum.rs:2:1 + | +2 | pub enum Event { + | ^^^ From 3d3c89843f534453a25e735a552740ac59fe632d Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Tue, 4 Jul 2023 16:23:22 +0100 Subject: [PATCH 095/140] Refactor topic attribute fn to accept BindingInfo --- crates/ink/macro/src/event/metadata.rs | 2 +- crates/ink/macro/src/event/mod.rs | 14 +++++++------- .../ink/tests/ui/event/fail/cfg_attr_on_field.rs | 7 +++++++ .../tests/ui/event/fail/cfg_attr_on_field.stderr | 5 +++++ 4 files changed, 20 insertions(+), 8 deletions(-) create mode 100644 crates/ink/tests/ui/event/fail/cfg_attr_on_field.rs create mode 100644 crates/ink/tests/ui/event/fail/cfg_attr_on_field.stderr diff --git a/crates/ink/macro/src/event/metadata.rs b/crates/ink/macro/src/event/metadata.rs index 3865379e76..612b2838d4 100644 --- a/crates/ink/macro/src/event/metadata.rs +++ b/crates/ink/macro/src/event/metadata.rs @@ -47,7 +47,7 @@ fn event_metadata_derive_struct(s: synstructure::Structure) -> syn::Result ::ink::metadata::EventParamSpec::new(::core::stringify!(#field_name)) .of_type(::ink::metadata::TypeSpec::of_type::<#field_ty>()) diff --git a/crates/ink/macro/src/event/mod.rs b/crates/ink/macro/src/event/mod.rs index b010ca9dc8..f8414bbeb6 100644 --- a/crates/ink/macro/src/event/mod.rs +++ b/crates/ink/macro/src/event/mod.rs @@ -67,7 +67,7 @@ fn event_derive_struct(mut s: synstructure::Structure) -> syn::Result = None; s.variants_mut()[0].filter(|bi| { - match has_ink_topic_attribute(&bi.ast().attrs) { + match has_ink_topic_attribute(bi) { Ok(has_attr) => has_attr, Err(err) => { match topic_err { @@ -166,20 +166,20 @@ fn signature_topic(fields: &syn::Fields, event_ident: &syn::Ident) -> syn::LitSt syn::parse_quote!( #topic_str ) } -/// Checks if the given attributes contain an `#[ink(topic)]` attribute. +/// Checks if the given field's attributes contain an `#[ink(topic)]` attribute. /// /// Returns `Err` if: /// - the given attributes contain a `#[cfg(...)]` attribute /// - there are `ink` attributes other than a single `#[ink(topic)]` -fn has_ink_topic_attribute(attrs: &[syn::Attribute]) -> syn::Result { - let some_cfg_attrs = attrs.iter().find(|attr| attr.path().is_ident("cfg")); - if let Some(attr) = some_cfg_attrs { +fn has_ink_topic_attribute(field: &synstructure::BindingInfo) -> syn::Result { + let some_cfg_attrs = field.ast().attrs.iter().find(|attr| attr.path().is_ident("cfg")); + if some_cfg_attrs.is_some() { Err(syn::Error::new( - attr.span(), + field.ast().span(), "conditional compilation is not allowed for event fields", )) } else { - has_ink_attribute(attrs, "topic") + has_ink_attribute(&field.ast().attrs, "topic") } } diff --git a/crates/ink/tests/ui/event/fail/cfg_attr_on_field.rs b/crates/ink/tests/ui/event/fail/cfg_attr_on_field.rs new file mode 100644 index 0000000000..12c546de40 --- /dev/null +++ b/crates/ink/tests/ui/event/fail/cfg_attr_on_field.rs @@ -0,0 +1,7 @@ +#[ink::event] +pub struct Event { + #[cfg(feature = "std")] + pub topic: [u8; 32], +} + +fn main() {} \ No newline at end of file diff --git a/crates/ink/tests/ui/event/fail/cfg_attr_on_field.stderr b/crates/ink/tests/ui/event/fail/cfg_attr_on_field.stderr new file mode 100644 index 0000000000..e3b4bdb2f4 --- /dev/null +++ b/crates/ink/tests/ui/event/fail/cfg_attr_on_field.stderr @@ -0,0 +1,5 @@ +error: conditional compilation is not allowed for event fields + --> tests/ui/event/fail/cfg_attr_on_field.rs:3:5 + | +3 | #[cfg(feature = "std")] + | ^ From 7b0abe9e65a0183456b7aade9010156c709b8c37 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Tue, 4 Jul 2023 16:34:19 +0100 Subject: [PATCH 096/140] Fmt --- crates/ink/macro/src/event/metadata.rs | 3 +-- crates/ink/macro/src/event/mod.rs | 6 +++++- crates/ink/tests/events_metadata.rs | 20 ++++++++++++++++---- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/crates/ink/macro/src/event/metadata.rs b/crates/ink/macro/src/event/metadata.rs index 612b2838d4..4d3b84c0f2 100644 --- a/crates/ink/macro/src/event/metadata.rs +++ b/crates/ink/macro/src/event/metadata.rs @@ -84,9 +84,8 @@ fn event_metadata_derive_struct(s: synstructure::Structure) -> syn::Result syn::LitSt /// - the given attributes contain a `#[cfg(...)]` attribute /// - there are `ink` attributes other than a single `#[ink(topic)]` fn has_ink_topic_attribute(field: &synstructure::BindingInfo) -> syn::Result { - let some_cfg_attrs = field.ast().attrs.iter().find(|attr| attr.path().is_ident("cfg")); + let some_cfg_attrs = field + .ast() + .attrs + .iter() + .find(|attr| attr.path().is_ident("cfg")); if some_cfg_attrs.is_some() { Err(syn::Error::new( field.ast().span(), diff --git a/crates/ink/tests/events_metadata.rs b/crates/ink/tests/events_metadata.rs index 5d6ce19458..31408b1271 100644 --- a/crates/ink/tests/events_metadata.rs +++ b/crates/ink/tests/events_metadata.rs @@ -15,7 +15,9 @@ #![cfg_attr(not(feature = "std"), no_std)] #[ink::event] +/// EventExternal docs pub struct EventExternal { + /// f1 docs f1: bool, f2: u32, } @@ -26,8 +28,10 @@ mod contract { pub struct Contract {} #[ink(event)] + /// EventInline docs pub struct EventInline { f3: bool, + /// f4 docs f4: u32, } @@ -61,15 +65,23 @@ mod tests { let metadata = generate_metadata(); assert_eq!(metadata.spec().events().len(), 2); - assert!(metadata + + let event_external = metadata .spec() .events() .iter() - .any(|e| e.label() == "EventExternal")); - assert!(metadata + .find(|e| e.label() == "EventExternal") + .expect("EventExternal should be present"); + + assert_eq!(event_external.docs(), &["EventExternal docs"]); + + let event_inline = metadata .spec() .events() .iter() - .any(|e| e.label() == "EventInline")); + .find(|e| e.label() == "EventInline") + .expect("EventInline should be present"); + + assert_eq!(event_inline.docs(), &["EventInline docs"]); } } From 4fd7cf6dcf1b78aca4c64e600a0f6902532d2689 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Tue, 4 Jul 2023 16:35:19 +0100 Subject: [PATCH 097/140] Remove commented out code --- crates/ink/codegen/src/generator/metadata.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/ink/codegen/src/generator/metadata.rs b/crates/ink/codegen/src/generator/metadata.rs index ba97ad68ce..83f1f26c47 100644 --- a/crates/ink/codegen/src/generator/metadata.rs +++ b/crates/ink/codegen/src/generator/metadata.rs @@ -88,7 +88,6 @@ impl Metadata<'_> { fn generate_contract(&self) -> TokenStream2 { let constructors = self.generate_constructors(); let messages = self.generate_messages(); - // let events = self.generate_events(); let docs = self .contract .module() From 5aa63668ebe5bcefd2f5e4c897df991b000b4505 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Tue, 4 Jul 2023 16:42:37 +0100 Subject: [PATCH 098/140] Event docs --- crates/ink/macro/src/event/metadata.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/crates/ink/macro/src/event/metadata.rs b/crates/ink/macro/src/event/metadata.rs index 4d3b84c0f2..70e82bb423 100644 --- a/crates/ink/macro/src/event/metadata.rs +++ b/crates/ink/macro/src/event/metadata.rs @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +use ink_ir::IsDocAttribute; use proc_macro2::TokenStream as TokenStream2; use quote::quote_spanned; use syn::spanned::Spanned; @@ -43,6 +44,12 @@ fn event_metadata_derive_struct(s: synstructure::Structure) -> syn::Result Date: Tue, 4 Jul 2023 16:55:59 +0100 Subject: [PATCH 099/140] Add event field docs --- crates/ink/macro/src/event/metadata.rs | 7 ++++++- crates/ink/tests/events_metadata.rs | 22 +++++++++++++++++++++- crates/metadata/src/specs.rs | 10 +++++++--- 3 files changed, 34 insertions(+), 5 deletions(-) diff --git a/crates/ink/macro/src/event/metadata.rs b/crates/ink/macro/src/event/metadata.rs index 70e82bb423..c65061c719 100644 --- a/crates/ink/macro/src/event/metadata.rs +++ b/crates/ink/macro/src/event/metadata.rs @@ -55,11 +55,16 @@ fn event_metadata_derive_struct(s: synstructure::Structure) -> syn::Result ::ink::metadata::EventParamSpec::new(::core::stringify!(#field_name)) .of_type(::ink::metadata::TypeSpec::of_type::<#field_ty>()) .indexed(#indexed) - // .docs + .docs([ #( #docs ),* ]) .done() )) } else { diff --git a/crates/ink/tests/events_metadata.rs b/crates/ink/tests/events_metadata.rs index 31408b1271..61caf6ee9a 100644 --- a/crates/ink/tests/events_metadata.rs +++ b/crates/ink/tests/events_metadata.rs @@ -17,8 +17,9 @@ #[ink::event] /// EventExternal docs pub struct EventExternal { - /// f1 docs f1: bool, + /// f2 docs + #[ink(topic)] f2: u32, } @@ -30,6 +31,7 @@ mod contract { #[ink(event)] /// EventInline docs pub struct EventInline { + #[ink(topic)] f3: bool, /// f4 docs f4: u32, @@ -74,6 +76,15 @@ mod tests { .expect("EventExternal should be present"); assert_eq!(event_external.docs(), &["EventExternal docs"]); + assert_eq!(event_external.args().len(), 2); + + let arg_f2 = event_external + .args() + .iter() + .find(|a| a.label() == "f2") + .expect("f2 should be present"); + assert_eq!(arg_f2.docs(), &["f2 docs"]); + assert!(arg_f2.indexed()); let event_inline = metadata .spec() @@ -83,5 +94,14 @@ mod tests { .expect("EventInline should be present"); assert_eq!(event_inline.docs(), &["EventInline docs"]); + assert_eq!(event_inline.args().len(), 2); + + let arg_f4 = event_inline + .args() + .iter() + .find(|a| a.label() == "f4") + .expect("f4 should be present"); + assert_eq!(arg_f4.docs(), &["f4 docs"]); + assert_eq!(arg_f4.indexed(), false); } } diff --git a/crates/metadata/src/specs.rs b/crates/metadata/src/specs.rs index 5d8c740e7b..5ca20bb151 100644 --- a/crates/metadata/src/specs.rs +++ b/crates/metadata/src/specs.rs @@ -1383,14 +1383,18 @@ where } /// Sets the documentation of the event parameter. - pub fn docs(self, docs: D) -> Self + pub fn docs<'a, D>(self, docs: D) -> Self where - D: IntoIterator::String>, + D: IntoIterator, + F::String: From<&'a str>, { debug_assert!(self.spec.docs.is_empty()); Self { spec: EventParamSpec { - docs: docs.into_iter().collect::>(), + docs: docs + .into_iter() + .map(|s| trim_extra_whitespace(s).into()) + .collect::>(), ..self.spec }, } From 4327ce07dce700e6a2b70adb042334fdad281e00 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Tue, 4 Jul 2023 17:23:47 +0100 Subject: [PATCH 100/140] Fix event param spec doc tests --- crates/ink/tests/events_metadata.rs | 2 +- crates/metadata/src/tests.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/ink/tests/events_metadata.rs b/crates/ink/tests/events_metadata.rs index 61caf6ee9a..77a682a9a3 100644 --- a/crates/ink/tests/events_metadata.rs +++ b/crates/ink/tests/events_metadata.rs @@ -102,6 +102,6 @@ mod tests { .find(|a| a.label() == "f4") .expect("f4 should be present"); assert_eq!(arg_f4.docs(), &["f4 docs"]); - assert_eq!(arg_f4.indexed(), false); + assert!(!arg_f4.indexed()); } } diff --git a/crates/metadata/src/tests.rs b/crates/metadata/src/tests.rs index a6d2a47491..2866c94fba 100644 --- a/crates/metadata/src/tests.rs +++ b/crates/metadata/src/tests.rs @@ -665,7 +665,7 @@ fn should_trim_whitespaces_in_events_docs() { let args = [EventParamSpec::new("something".into()) .of_type(spec) .indexed(true) - .docs(vec!["test".to_string()]) + .docs(["test"]) .done()]; let es = EventSpec::new("foobar".into()) .module_path("foo") From f6d1953dc6e129e56f081e54ea421e1a8ef31165 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Tue, 4 Jul 2023 17:45:56 +0100 Subject: [PATCH 101/140] Fix metadata codegen tests --- crates/ink/macro/src/tests/event_metadata.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/crates/ink/macro/src/tests/event_metadata.rs b/crates/ink/macro/src/tests/event_metadata.rs index 65f25e5741..ff919b5550 100644 --- a/crates/ink/macro/src/tests/event_metadata.rs +++ b/crates/ink/macro/src/tests/event_metadata.rs @@ -74,14 +74,17 @@ fn struct_with_fields_no_topics() { ::ink::metadata::EventParamSpec::new(::core::stringify!(field_1)) .of_type(::ink::metadata::TypeSpec::of_type::()) .indexed(false) + .docs([]) .done(), ::ink::metadata::EventParamSpec::new(::core::stringify!(field_2)) .of_type(::ink::metadata::TypeSpec::of_type::()) .indexed(false) + .docs([]) .done(), ::ink::metadata::EventParamSpec::new(::core::stringify!(field_3)) .of_type(::ink::metadata::TypeSpec::of_type::()) .indexed(false) + .docs([]) .done() ]) .docs([]) @@ -124,14 +127,17 @@ fn struct_with_fields_and_some_topics() { ::ink::metadata::EventParamSpec::new(::core::stringify!(field_1)) .of_type(::ink::metadata::TypeSpec::of_type::()) .indexed(false) + .docs([]) .done(), ::ink::metadata::EventParamSpec::new(::core::stringify!(field_2)) .of_type(::ink::metadata::TypeSpec::of_type::()) .indexed(true) + .docs([]) .done(), ::ink::metadata::EventParamSpec::new(::core::stringify!(field_3)) .of_type(::ink::metadata::TypeSpec::of_type::()) .indexed(true) + .docs([]) .done() ]) .docs([]) From e4c35724b733e66d36fd0e4fd3f52aec96547a4c Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Tue, 4 Jul 2023 18:04:32 +0100 Subject: [PATCH 102/140] Implement docs for `Event` derive macro --- crates/ink/macro/src/lib.rs | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/crates/ink/macro/src/lib.rs b/crates/ink/macro/src/lib.rs index c8e7a5eeb1..3d31be28b5 100644 --- a/crates/ink/macro/src/lib.rs +++ b/crates/ink/macro/src/lib.rs @@ -1291,7 +1291,29 @@ pub fn chain_extension(attr: TokenStream, item: TokenStream) -> TokenStream { synstructure::decl_derive!( [Event, attributes(ink)] => - /// todo + /// Derives an impl of the [`ink::Event`] trait for the given `struct`. + /// + /// **Note** [`ink::Event`] requires a [`scale::Encode`] impl, it is up to the user to provide + /// that: usually via the derive. + /// + /// # Example + /// + /// ``` + /// use ink::{ + /// Event, + /// env::DefaultEnvironment, + /// }; + /// use scale::Encode; + /// + /// #[derive(Event, Encode)] + /// struct MyEvent { + /// a: u32, + /// #[ink(topic)] + /// b: [u8; 32], + /// } + /// + /// ink_env::emit_event::(MyEvent { a: 42, b: [0x42; 32] }); + /// ``` event::event_derive ); From 4d486694940ab7fcf7e97b7880a6fb88fd0407a4 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Wed, 5 Jul 2023 09:07:00 +0100 Subject: [PATCH 103/140] Spellcheck --- crates/ink/macro/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ink/macro/src/lib.rs b/crates/ink/macro/src/lib.rs index 3d31be28b5..7ce46055df 100644 --- a/crates/ink/macro/src/lib.rs +++ b/crates/ink/macro/src/lib.rs @@ -1291,7 +1291,7 @@ pub fn chain_extension(attr: TokenStream, item: TokenStream) -> TokenStream { synstructure::decl_derive!( [Event, attributes(ink)] => - /// Derives an impl of the [`ink::Event`] trait for the given `struct`. + /// Derives an implementation of the [`ink::Event`] trait for the given `struct`. /// /// **Note** [`ink::Event`] requires a [`scale::Encode`] impl, it is up to the user to provide /// that: usually via the derive. From 3dcc12f0e3534691e3118c4b1ff469516d0fd70c Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Wed, 5 Jul 2023 09:16:32 +0100 Subject: [PATCH 104/140] Check for multiple ink attributes --- crates/ink/macro/src/event/mod.rs | 16 +++++++++++----- .../ink/tests/ui/event/fail/cfg_attr_on_field.rs | 2 +- .../ui/event/fail/multiple_anonymous_args.rs | 9 +++++++++ .../ui/event/fail/multiple_anonymous_args.stderr | 11 +++++++++++ .../tests/ui/event/fail/multiple_topic_args.rs | 8 ++++++++ .../ui/event/fail/multiple_topic_args.stderr | 5 +++++ 6 files changed, 45 insertions(+), 6 deletions(-) create mode 100644 crates/ink/tests/ui/event/fail/multiple_anonymous_args.rs create mode 100644 crates/ink/tests/ui/event/fail/multiple_anonymous_args.stderr create mode 100644 crates/ink/tests/ui/event/fail/multiple_topic_args.rs create mode 100644 crates/ink/tests/ui/event/fail/multiple_topic_args.stderr diff --git a/crates/ink/macro/src/event/mod.rs b/crates/ink/macro/src/event/mod.rs index 159c154b7d..a25b62a4dc 100644 --- a/crates/ink/macro/src/event/mod.rs +++ b/crates/ink/macro/src/event/mod.rs @@ -189,23 +189,29 @@ fn has_ink_topic_attribute(field: &synstructure::BindingInfo) -> syn::Result syn::Result { - let ink_attrs: Vec<_> = attrs + let ink_attrs = attrs .iter() .filter_map(|attr| { if attr.path().is_ident("ink") { - Some(attr.parse_nested_meta(|meta| { + let parse_result = attr.parse_nested_meta(|meta| { if meta.path.is_ident(path) { Ok(()) } else { Err(meta .error(format!("Only `#[ink({path})]` attribute allowed."))) } - })) + }); + Some(parse_result.map(|_| attr)) } else { None } }) - .collect::>()?; - // todo: check only one + .collect::>>()?; + if ink_attrs.len() > 1 { + return Err(syn::Error::new( + ink_attrs[1].span(), + format!("Only a single `#[ink({})]` attribute allowed.", path), + )) + } Ok(!ink_attrs.is_empty()) } diff --git a/crates/ink/tests/ui/event/fail/cfg_attr_on_field.rs b/crates/ink/tests/ui/event/fail/cfg_attr_on_field.rs index 12c546de40..615e0ad374 100644 --- a/crates/ink/tests/ui/event/fail/cfg_attr_on_field.rs +++ b/crates/ink/tests/ui/event/fail/cfg_attr_on_field.rs @@ -1,7 +1,7 @@ #[ink::event] pub struct Event { #[cfg(feature = "std")] - pub topic: [u8; 32], + pub a: [u8; 32], } fn main() {} \ No newline at end of file diff --git a/crates/ink/tests/ui/event/fail/multiple_anonymous_args.rs b/crates/ink/tests/ui/event/fail/multiple_anonymous_args.rs new file mode 100644 index 0000000000..4c6c86b3c8 --- /dev/null +++ b/crates/ink/tests/ui/event/fail/multiple_anonymous_args.rs @@ -0,0 +1,9 @@ +#[ink::event] +#[ink(anonymous)] +#[ink(anonymous)] +pub struct Event { + #[ink(topic)] + pub topic: [u8; 32], +} + +fn main() {} \ No newline at end of file diff --git a/crates/ink/tests/ui/event/fail/multiple_anonymous_args.stderr b/crates/ink/tests/ui/event/fail/multiple_anonymous_args.stderr new file mode 100644 index 0000000000..975e3e0528 --- /dev/null +++ b/crates/ink/tests/ui/event/fail/multiple_anonymous_args.stderr @@ -0,0 +1,11 @@ +error: Only a single `#[ink(anonymous)]` attribute allowed. + --> tests/ui/event/fail/multiple_anonymous_args.rs:3:1 + | +3 | #[ink(anonymous)] + | ^ + +error[E0277]: the trait bound `Event: ink::ink_env::Event` is not satisfied + --> tests/ui/event/fail/multiple_anonymous_args.rs:2:1 + | +2 | #[ink(anonymous)] + | ^ the trait `ink::ink_env::Event` is not implemented for `Event` diff --git a/crates/ink/tests/ui/event/fail/multiple_topic_args.rs b/crates/ink/tests/ui/event/fail/multiple_topic_args.rs new file mode 100644 index 0000000000..fa05b66025 --- /dev/null +++ b/crates/ink/tests/ui/event/fail/multiple_topic_args.rs @@ -0,0 +1,8 @@ +#[ink::event] +pub struct Event { + #[ink(topic)] + #[ink(topic)] + pub topic: [u8; 32], +} + +fn main() {} \ No newline at end of file diff --git a/crates/ink/tests/ui/event/fail/multiple_topic_args.stderr b/crates/ink/tests/ui/event/fail/multiple_topic_args.stderr new file mode 100644 index 0000000000..cbe9999135 --- /dev/null +++ b/crates/ink/tests/ui/event/fail/multiple_topic_args.stderr @@ -0,0 +1,5 @@ +error: Only a single `#[ink(topic)]` attribute allowed. + --> tests/ui/event/fail/multiple_topic_args.rs:4:5 + | +4 | #[ink(topic)] + | ^ From 1defd8128c1ccd066a77df3a2bd1774b81a9d0c6 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Wed, 5 Jul 2023 09:19:40 +0100 Subject: [PATCH 105/140] Spellcheck --- crates/ink/macro/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/ink/macro/src/lib.rs b/crates/ink/macro/src/lib.rs index 7ce46055df..fae06bd6cc 100644 --- a/crates/ink/macro/src/lib.rs +++ b/crates/ink/macro/src/lib.rs @@ -1293,8 +1293,8 @@ synstructure::decl_derive!( [Event, attributes(ink)] => /// Derives an implementation of the [`ink::Event`] trait for the given `struct`. /// - /// **Note** [`ink::Event`] requires a [`scale::Encode`] impl, it is up to the user to provide - /// that: usually via the derive. + /// **Note** [`ink::Event`] requires a [`scale::Encode`] implementation, it is up to + /// the user to provide that: usually via the derive. /// /// # Example /// From a1e3879a5d13a761a4cd80dc89c3e35b43511868 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Wed, 5 Jul 2023 10:04:48 +0100 Subject: [PATCH 106/140] Ink attribute validation --- crates/ink/macro/src/event/mod.rs | 13 ++++++++++++- .../ui/event/fail/multiple_anonymous_args_inline.rs | 8 ++++++++ .../fail/multiple_anonymous_args_inline.stderr | 11 +++++++++++ .../ui/event/fail/multiple_topic_args_inline.rs | 7 +++++++ .../ui/event/fail/multiple_topic_args_inline.stderr | 5 +++++ 5 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 crates/ink/tests/ui/event/fail/multiple_anonymous_args_inline.rs create mode 100644 crates/ink/tests/ui/event/fail/multiple_anonymous_args_inline.stderr create mode 100644 crates/ink/tests/ui/event/fail/multiple_topic_args_inline.rs create mode 100644 crates/ink/tests/ui/event/fail/multiple_topic_args_inline.stderr diff --git a/crates/ink/macro/src/event/mod.rs b/crates/ink/macro/src/event/mod.rs index a25b62a4dc..7c86a74dd6 100644 --- a/crates/ink/macro/src/event/mod.rs +++ b/crates/ink/macro/src/event/mod.rs @@ -188,6 +188,11 @@ fn has_ink_topic_attribute(field: &synstructure::BindingInfo) -> syn::Result syn::Result { let ink_attrs = attrs .iter() @@ -195,7 +200,13 @@ fn has_ink_attribute(attrs: &[syn::Attribute], path: &str) -> syn::Result if attr.path().is_ident("ink") { let parse_result = attr.parse_nested_meta(|meta| { if meta.path.is_ident(path) { - Ok(()) + if meta.input.is_empty() { + Ok(()) + } else { + Err(meta.error(format!( + "Invalid `#[ink({path})]` attribute: multiple arguments not allowed.", + ))) + } } else { Err(meta .error(format!("Only `#[ink({path})]` attribute allowed."))) diff --git a/crates/ink/tests/ui/event/fail/multiple_anonymous_args_inline.rs b/crates/ink/tests/ui/event/fail/multiple_anonymous_args_inline.rs new file mode 100644 index 0000000000..4dc2ea5d40 --- /dev/null +++ b/crates/ink/tests/ui/event/fail/multiple_anonymous_args_inline.rs @@ -0,0 +1,8 @@ +#[ink::event] +#[ink(anonymous, anonymous)] +pub struct Event { + #[ink(topic)] + pub topic: [u8; 32], +} + +fn main() {} \ No newline at end of file diff --git a/crates/ink/tests/ui/event/fail/multiple_anonymous_args_inline.stderr b/crates/ink/tests/ui/event/fail/multiple_anonymous_args_inline.stderr new file mode 100644 index 0000000000..1a323b24cf --- /dev/null +++ b/crates/ink/tests/ui/event/fail/multiple_anonymous_args_inline.stderr @@ -0,0 +1,11 @@ +error: Invalid `#[ink(anonymous)]` attribute: multiple arguments not allowed. + --> tests/ui/event/fail/multiple_anonymous_args_inline.rs:2:7 + | +2 | #[ink(anonymous, anonymous)] + | ^^^^^^^^^ + +error[E0277]: the trait bound `Event: ink::ink_env::Event` is not satisfied + --> tests/ui/event/fail/multiple_anonymous_args_inline.rs:2:1 + | +2 | #[ink(anonymous, anonymous)] + | ^ the trait `ink::ink_env::Event` is not implemented for `Event` diff --git a/crates/ink/tests/ui/event/fail/multiple_topic_args_inline.rs b/crates/ink/tests/ui/event/fail/multiple_topic_args_inline.rs new file mode 100644 index 0000000000..e5974cd6ab --- /dev/null +++ b/crates/ink/tests/ui/event/fail/multiple_topic_args_inline.rs @@ -0,0 +1,7 @@ +#[ink::event] +pub struct Event { + #[ink(topic, topic)] + pub topic: [u8; 32], +} + +fn main() {} \ No newline at end of file diff --git a/crates/ink/tests/ui/event/fail/multiple_topic_args_inline.stderr b/crates/ink/tests/ui/event/fail/multiple_topic_args_inline.stderr new file mode 100644 index 0000000000..92e8159645 --- /dev/null +++ b/crates/ink/tests/ui/event/fail/multiple_topic_args_inline.stderr @@ -0,0 +1,5 @@ +error: Invalid `#[ink(topic)]` attribute: multiple arguments not allowed. + --> tests/ui/event/fail/multiple_topic_args_inline.rs:3:11 + | +3 | #[ink(topic, topic)] + | ^^^^^ From 89e3df68e8c1c6b0e28bafba84e4d52d35684650 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Wed, 5 Jul 2023 10:30:40 +0100 Subject: [PATCH 107/140] Remove Topic --- crates/ink/ir/src/ir/attrs.rs | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/crates/ink/ir/src/ir/attrs.rs b/crates/ink/ir/src/ir/attrs.rs index 0ae6a7f11c..fd312c38e6 100644 --- a/crates/ink/ir/src/ir/attrs.rs +++ b/crates/ink/ir/src/ir/attrs.rs @@ -350,8 +350,6 @@ pub enum AttributeArgKind { Event, /// `#[ink(anonymous)]` Anonymous, - /// `#[ink(topic)]` - Topic, /// `#[ink(message)]` Message, /// `#[ink(constructor)]` @@ -393,10 +391,6 @@ pub enum AttributeArg { /// to reduce event emitting overhead. This is especially useful for user /// defined events. Anonymous, - /// `#[ink(topic)]` - /// - /// Applied on fields of ink! event types to indicate that they are topics. - Topic, /// `#[ink(message)]` /// /// Applied on `&self` or `&mut self` methods to flag them for being an ink! @@ -460,7 +454,6 @@ impl core::fmt::Display for AttributeArgKind { Self::Storage => write!(f, "storage"), Self::Event => write!(f, "event"), Self::Anonymous => write!(f, "anonymous"), - Self::Topic => write!(f, "topic"), Self::Message => write!(f, "message"), Self::Constructor => write!(f, "constructor"), Self::Payable => write!(f, "payable"), @@ -487,7 +480,6 @@ impl AttributeArg { Self::Storage => AttributeArgKind::Storage, Self::Event => AttributeArgKind::Event, Self::Anonymous => AttributeArgKind::Anonymous, - Self::Topic => AttributeArgKind::Topic, Self::Message => AttributeArgKind::Message, Self::Constructor => AttributeArgKind::Constructor, Self::Payable => AttributeArgKind::Payable, @@ -507,7 +499,6 @@ impl core::fmt::Display for AttributeArg { Self::Storage => write!(f, "storage"), Self::Event => write!(f, "event"), Self::Anonymous => write!(f, "anonymous"), - Self::Topic => write!(f, "topic"), Self::Message => write!(f, "message"), Self::Constructor => write!(f, "constructor"), Self::Payable => write!(f, "payable"), @@ -1420,8 +1411,7 @@ mod tests { message, constructor, event, - topic, - payable, +j payable, impl, )] }, @@ -1430,7 +1420,6 @@ mod tests { AttributeArg::Message, AttributeArg::Constructor, AttributeArg::Event, - AttributeArg::Topic, AttributeArg::Payable, AttributeArg::Implementation, ])), From a80bb74e4e68c23ecb1a03a3f89d3c73245aa060 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Wed, 5 Jul 2023 10:31:00 +0100 Subject: [PATCH 108/140] Oops --- crates/ink/ir/src/ir/attrs.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ink/ir/src/ir/attrs.rs b/crates/ink/ir/src/ir/attrs.rs index fd312c38e6..34ad9fdd44 100644 --- a/crates/ink/ir/src/ir/attrs.rs +++ b/crates/ink/ir/src/ir/attrs.rs @@ -1411,7 +1411,7 @@ mod tests { message, constructor, event, -j payable, + payable, impl, )] }, From 15bc04aeb34adaef6b011d6c5bca816a30ac282a Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Wed, 5 Jul 2023 10:31:28 +0100 Subject: [PATCH 109/140] Oops again --- crates/ink/ir/src/ir/attrs.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/ink/ir/src/ir/attrs.rs b/crates/ink/ir/src/ir/attrs.rs index 34ad9fdd44..77d1966c6b 100644 --- a/crates/ink/ir/src/ir/attrs.rs +++ b/crates/ink/ir/src/ir/attrs.rs @@ -971,7 +971,6 @@ impl Parse for AttributeFrag { "constructor" => Ok(AttributeArg::Constructor), "event" => Ok(AttributeArg::Event), "anonymous" => Ok(AttributeArg::Anonymous), - "topic" => Ok(AttributeArg::Topic), "payable" => Ok(AttributeArg::Payable), "default" => Ok(AttributeArg::Default), "impl" => Ok(AttributeArg::Implementation), From 802c79944b6bb322d7cc1676af03774993ecf42f Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Wed, 5 Jul 2023 12:04:49 +0100 Subject: [PATCH 110/140] Test for events in different crates being used in the metadata. --- crates/metadata/src/lib.rs | 9 ++++++++- crates/metadata/src/specs.rs | 3 +-- integration-tests/events/Cargo.toml | 2 ++ integration-tests/events/event-def2/Cargo.toml | 17 +++++++++++++++++ integration-tests/events/event-def2/src/lib.rs | 9 +++++++++ integration-tests/events/lib.rs | 15 +++++++++++++++ 6 files changed, 52 insertions(+), 3 deletions(-) create mode 100644 integration-tests/events/event-def2/Cargo.toml create mode 100644 integration-tests/events/event-def2/src/lib.rs diff --git a/crates/metadata/src/lib.rs b/crates/metadata/src/lib.rs index 4924e21999..90b61b54b3 100644 --- a/crates/metadata/src/lib.rs +++ b/crates/metadata/src/lib.rs @@ -159,10 +159,17 @@ impl InkProject { } } -/// Collects all event definitions in the contract binary at linking time. +/// Any event which derives `#[derive(ink::EventMetadata)]` and is used in the contract +/// binary will have its implementation added to this distributed slice at linking time. #[linkme::distributed_slice] pub static EVENTS: [fn() -> EventSpec] = [..]; +/// Collect the [`EventSpec`] metadata of all event definitions linked and used in the +/// binary. +pub fn collect_events() -> Vec { + EVENTS.iter().map(|event| event()).collect() +} + // todo: docs pub trait EventMetadata { /// The full path to the event type, usually provided by [`core::module_path`]. diff --git a/crates/metadata/src/specs.rs b/crates/metadata/src/specs.rs index 5ca20bb151..84b4bafe91 100644 --- a/crates/metadata/src/specs.rs +++ b/crates/metadata/src/specs.rs @@ -254,8 +254,7 @@ where impl ContractSpecBuilder { /// Collect metadata for all events linked into the contract. pub fn collect_events(self) -> Self { - let events = crate::EVENTS.iter().map(|event| event()); - self.events(events) + self.events(crate::collect_events()) } } diff --git a/integration-tests/events/Cargo.toml b/integration-tests/events/Cargo.toml index 5d37dd87e0..a64fb50377 100644 --- a/integration-tests/events/Cargo.toml +++ b/integration-tests/events/Cargo.toml @@ -11,6 +11,7 @@ scale = { package = "parity-scale-codec", version = "3", default-features = fals scale-info = { version = "2.5", default-features = false, features = ["derive"], optional = true } event-def = { path = "event-def", default-features = false } +event-def2 = { path = "event-def2", default-features = false } [dev-dependencies] ink_e2e = { path = "../../crates/e2e" } @@ -25,6 +26,7 @@ std = [ "scale/std", "scale-info/std", "event-def/std", + "event-def2/std", ] ink-as-dependency = [] e2e-tests = [] diff --git a/integration-tests/events/event-def2/Cargo.toml b/integration-tests/events/event-def2/Cargo.toml new file mode 100644 index 0000000000..beda2da2e5 --- /dev/null +++ b/integration-tests/events/event-def2/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "event-def2" +version = "0.1.0" +edition = "2021" + +[dependencies] +ink = { path = "../../../crates/ink", default-features = false } +scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] } +scale-info = { version = "2.5", default-features = false, features = ["derive"], optional = true } + +[features] +default = ["std"] +std = [ + "ink/std", + "scale/std", + "scale-info/std", +] diff --git a/integration-tests/events/event-def2/src/lib.rs b/integration-tests/events/event-def2/src/lib.rs new file mode 100644 index 0000000000..f27050f895 --- /dev/null +++ b/integration-tests/events/event-def2/src/lib.rs @@ -0,0 +1,9 @@ +#![cfg_attr(not(feature = "std"), no_std, no_main)] + +#[ink::event] +pub struct EventDefAnotherCrate { + #[ink(topic)] + pub hash: [u8; 32], + #[ink(topic)] + pub maybe_hash: Option<[u8; 32]>, +} diff --git a/integration-tests/events/lib.rs b/integration-tests/events/lib.rs index 7ee836f2cd..d0c1103b71 100644 --- a/integration-tests/events/lib.rs +++ b/integration-tests/events/lib.rs @@ -30,6 +30,15 @@ pub mod events { maybe_hash, }) } + + /// Emit an event from a different crate. + #[ink(message)] + pub fn emit_event_from_a_different_crate(&mut self, maybe_hash: Option<[u8; 32]>) { + self.env().emit_event(event_def2::EventDefAnotherCrate { + hash: [0x42; 32], + maybe_hash, + }) + } } #[cfg(test)] @@ -37,6 +46,12 @@ pub mod events { use super::*; use scale::Decode as _; + #[ink::test] + fn collects_specs_for_all_linked_events() { + let event_specs = ink::metadata::collect_events(); + assert_eq!(3, event_specs.len()) + } + #[ink::test] fn it_works() { let mut events = Events::new(false); From ad8b3e779607f954ee5214f67b2cc2c95d363d3d Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Wed, 5 Jul 2023 12:12:23 +0100 Subject: [PATCH 111/140] Test for unused event not included in metadata --- integration-tests/events/Cargo.toml | 2 ++ .../events/event-def-unused/Cargo.toml | 17 +++++++++++++++++ .../events/event-def-unused/src/lib.rs | 9 +++++++++ integration-tests/events/lib.rs | 10 ++++++++-- 4 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 integration-tests/events/event-def-unused/Cargo.toml create mode 100644 integration-tests/events/event-def-unused/src/lib.rs diff --git a/integration-tests/events/Cargo.toml b/integration-tests/events/Cargo.toml index a64fb50377..0b0225bf93 100644 --- a/integration-tests/events/Cargo.toml +++ b/integration-tests/events/Cargo.toml @@ -12,6 +12,7 @@ scale-info = { version = "2.5", default-features = false, features = ["derive"], event-def = { path = "event-def", default-features = false } event-def2 = { path = "event-def2", default-features = false } +event-def-unused = { path = "event-def-unused", default-features = false } [dev-dependencies] ink_e2e = { path = "../../crates/e2e" } @@ -27,6 +28,7 @@ std = [ "scale-info/std", "event-def/std", "event-def2/std", + "event-def-unused/std", ] ink-as-dependency = [] e2e-tests = [] diff --git a/integration-tests/events/event-def-unused/Cargo.toml b/integration-tests/events/event-def-unused/Cargo.toml new file mode 100644 index 0000000000..eb344647fb --- /dev/null +++ b/integration-tests/events/event-def-unused/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "event-def-unused" +version = "0.1.0" +edition = "2021" + +[dependencies] +ink = { path = "../../../crates/ink", default-features = false } +scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] } +scale-info = { version = "2.5", default-features = false, features = ["derive"], optional = true } + +[features] +default = ["std"] +std = [ + "ink/std", + "scale/std", + "scale-info/std", +] diff --git a/integration-tests/events/event-def-unused/src/lib.rs b/integration-tests/events/event-def-unused/src/lib.rs new file mode 100644 index 0000000000..9fc3065ab8 --- /dev/null +++ b/integration-tests/events/event-def-unused/src/lib.rs @@ -0,0 +1,9 @@ +#![cfg_attr(not(feature = "std"), no_std, no_main)] + +#[ink::event] +pub struct EventDefUnused { + #[ink(topic)] + pub hash: [u8; 32], + #[ink(topic)] + pub maybe_hash: Option<[u8; 32]>, +} diff --git a/integration-tests/events/lib.rs b/integration-tests/events/lib.rs index d0c1103b71..991873bf52 100644 --- a/integration-tests/events/lib.rs +++ b/integration-tests/events/lib.rs @@ -47,9 +47,15 @@ pub mod events { use scale::Decode as _; #[ink::test] - fn collects_specs_for_all_linked_events() { + fn collects_specs_for_all_linked_and_used_events() { let event_specs = ink::metadata::collect_events(); - assert_eq!(3, event_specs.len()) + assert_eq!(3, event_specs.len()); + + assert!(event_specs.iter().any(|evt| evt.label() == &"Flipped")); + assert!(event_specs.iter().any(|evt| evt.label() == &"ThirtyTwoByteTopics")); + assert!(event_specs.iter().any(|evt| evt.label() == &"EventDefAnotherCrate")); + + assert!(!event_specs.iter().any(|evt| evt.label() == &"EventDefUnused")); } #[ink::test] From 4dcc77139b01c669f2a7cf152853ebc7c0601616 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Wed, 5 Jul 2023 12:17:51 +0100 Subject: [PATCH 112/140] Add test for inline event definition metadata. --- integration-tests/events/event-def/src/lib.rs | 2 +- integration-tests/events/lib.rs | 26 ++++++++++++++----- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/integration-tests/events/event-def/src/lib.rs b/integration-tests/events/event-def/src/lib.rs index 13cb7aa34e..4f9e8ffac1 100644 --- a/integration-tests/events/event-def/src/lib.rs +++ b/integration-tests/events/event-def/src/lib.rs @@ -1,7 +1,7 @@ #![cfg_attr(not(feature = "std"), no_std, no_main)] #[ink::event] -pub struct Flipped { +pub struct ForeignFlipped { pub value: bool, } diff --git a/integration-tests/events/lib.rs b/integration-tests/events/lib.rs index 991873bf52..651de73905 100644 --- a/integration-tests/events/lib.rs +++ b/integration-tests/events/lib.rs @@ -7,6 +7,11 @@ pub mod events { value: bool, } + #[ink(event)] + pub struct InlineFlipped { + value: bool + } + impl Events { /// Creates a new events smart contract initialized with the given value. #[ink(constructor)] @@ -19,7 +24,15 @@ pub mod events { pub fn flip(&mut self) { self.value = !self.value; self.env() - .emit_event(event_def::Flipped { value: self.value }) + .emit_event(event_def::ForeignFlipped { value: self.value }) + } + + /// Flips the current value of the boolean. + #[ink(message)] + pub fn flip_with_inline_event(&mut self) { + self.value = !self.value; + self.env() + .emit_event(InlineFlipped { value: self.value }) } /// Emit an event with a 32 byte topic. @@ -49,9 +62,10 @@ pub mod events { #[ink::test] fn collects_specs_for_all_linked_and_used_events() { let event_specs = ink::metadata::collect_events(); - assert_eq!(3, event_specs.len()); + assert_eq!(4, event_specs.len()); - assert!(event_specs.iter().any(|evt| evt.label() == &"Flipped")); + assert!(event_specs.iter().any(|evt| evt.label() == &"ForeignFlipped")); + assert!(event_specs.iter().any(|evt| evt.label() == &"InlineFlipped")); assert!(event_specs.iter().any(|evt| evt.label() == &"ThirtyTwoByteTopics")); assert!(event_specs.iter().any(|evt| evt.label() == &"EventDefAnotherCrate")); @@ -67,7 +81,7 @@ pub mod events { assert_eq!(1, emitted_events.len()); let event = &emitted_events[0]; - let decoded_event = ::decode(&mut &event.data[..]) + let decoded_event = ::decode(&mut &event.data[..]) .expect("encountered invalid contract event data buffer"); assert!(decoded_event.value); } @@ -146,13 +160,13 @@ pub mod events { // then assert_eq!(1, contract_events.len()); let contract_event = &contract_events[0]; - let flipped: event_def::Flipped = + let flipped: event_def::ForeignFlipped = scale::Decode::decode(&mut &contract_event.event.data[..]) .expect("encountered invalid contract event data buffer"); assert_eq!(!init_value, flipped.value); let signature_topic = - ::SIGNATURE_TOPIC + ::SIGNATURE_TOPIC .map(H256::from) .unwrap(); From 47b8e70ffc5b71e66776ffd65fa3b6b90512d8b9 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Wed, 5 Jul 2023 12:22:54 +0100 Subject: [PATCH 113/140] Add test for emitting inline event --- integration-tests/events/lib.rs | 47 ++++++++++++++++++++++++++++++--- 1 file changed, 43 insertions(+), 4 deletions(-) diff --git a/integration-tests/events/lib.rs b/integration-tests/events/lib.rs index 651de73905..24fcbd3049 100644 --- a/integration-tests/events/lib.rs +++ b/integration-tests/events/lib.rs @@ -21,7 +21,7 @@ pub mod events { /// Flips the current value of the boolean. #[ink(message)] - pub fn flip(&mut self) { + pub fn flip_with_foreign_event(&mut self) { self.value = !self.value; self.env() .emit_event(event_def::ForeignFlipped { value: self.value }) @@ -75,7 +75,7 @@ pub mod events { #[ink::test] fn it_works() { let mut events = Events::new(false); - events.flip(); + events.flip_with_foreign_event(); let emitted_events = ink::env::test::recorded_events().collect::>(); assert_eq!(1, emitted_events.len()); @@ -138,7 +138,7 @@ pub mod events { type E2EResult = std::result::Result>; #[ink_e2e::test] - async fn emits_shared_event(mut client: ink_e2e::Client) -> E2EResult<()> { + async fn emits_foreign_event(mut client: ink_e2e::Client) -> E2EResult<()> { // given let init_value = false; let constructor = EventsRef::new(init_value); @@ -149,7 +149,7 @@ pub mod events { let mut call = contract.call::(); // when - let flip = call.flip(); + let flip = call.flip_with_foreign_event(); let flip_res = client .call(&ink_e2e::bob(), &flip, 0, None) .await @@ -176,6 +176,45 @@ pub mod events { Ok(()) } + #[ink_e2e::test] + async fn emits_inline_event(mut client: ink_e2e::Client) -> E2EResult<()> { + // given + let init_value = false; + let constructor = EventsRef::new(init_value); + let contract = client + .instantiate("events", &ink_e2e::alice(), constructor, 0, None) + .await + .expect("instantiate failed"); + let mut call = contract.call::(); + + // when + let flip = call.flip_with_inline_event(); + let flip_res = client + .call(&ink_e2e::bob(), &flip, 0, None) + .await + .expect("flip failed"); + + let contract_events = flip_res.contract_emitted_events()?; + + // then + assert_eq!(1, contract_events.len()); + let contract_event = &contract_events[0]; + let flipped: InlineFlipped = + scale::Decode::decode(&mut &contract_event.event.data[..]) + .expect("encountered invalid contract event data buffer"); + assert_eq!(!init_value, flipped.value); + + let signature_topic = + ::SIGNATURE_TOPIC + .map(H256::from) + .unwrap(); + + let expected_topics = vec![signature_topic]; + assert_eq!(expected_topics, contract_event.topics); + + Ok(()) + } + #[ink_e2e::test] async fn emits_event_with_option_topic_none( mut client: ink_e2e::Client, From 4a6431b7aa0f207bb123ecc968765afa94e349db Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Wed, 5 Jul 2023 12:24:19 +0100 Subject: [PATCH 114/140] Fmt --- integration-tests/events/lib.rs | 39 +++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/integration-tests/events/lib.rs b/integration-tests/events/lib.rs index 24fcbd3049..e71fd86e9e 100644 --- a/integration-tests/events/lib.rs +++ b/integration-tests/events/lib.rs @@ -9,7 +9,7 @@ pub mod events { #[ink(event)] pub struct InlineFlipped { - value: bool + value: bool, } impl Events { @@ -31,8 +31,7 @@ pub mod events { #[ink(message)] pub fn flip_with_inline_event(&mut self) { self.value = !self.value; - self.env() - .emit_event(InlineFlipped { value: self.value }) + self.env().emit_event(InlineFlipped { value: self.value }) } /// Emit an event with a 32 byte topic. @@ -46,7 +45,10 @@ pub mod events { /// Emit an event from a different crate. #[ink(message)] - pub fn emit_event_from_a_different_crate(&mut self, maybe_hash: Option<[u8; 32]>) { + pub fn emit_event_from_a_different_crate( + &mut self, + maybe_hash: Option<[u8; 32]>, + ) { self.env().emit_event(event_def2::EventDefAnotherCrate { hash: [0x42; 32], maybe_hash, @@ -64,12 +66,22 @@ pub mod events { let event_specs = ink::metadata::collect_events(); assert_eq!(4, event_specs.len()); - assert!(event_specs.iter().any(|evt| evt.label() == &"ForeignFlipped")); - assert!(event_specs.iter().any(|evt| evt.label() == &"InlineFlipped")); - assert!(event_specs.iter().any(|evt| evt.label() == &"ThirtyTwoByteTopics")); - assert!(event_specs.iter().any(|evt| evt.label() == &"EventDefAnotherCrate")); - - assert!(!event_specs.iter().any(|evt| evt.label() == &"EventDefUnused")); + assert!(event_specs + .iter() + .any(|evt| evt.label() == &"ForeignFlipped")); + assert!(event_specs + .iter() + .any(|evt| evt.label() == &"InlineFlipped")); + assert!(event_specs + .iter() + .any(|evt| evt.label() == &"ThirtyTwoByteTopics")); + assert!(event_specs + .iter() + .any(|evt| evt.label() == &"EventDefAnotherCrate")); + + assert!(!event_specs + .iter() + .any(|evt| evt.label() == &"EventDefUnused")); } #[ink::test] @@ -204,10 +216,9 @@ pub mod events { .expect("encountered invalid contract event data buffer"); assert_eq!(!init_value, flipped.value); - let signature_topic = - ::SIGNATURE_TOPIC - .map(H256::from) - .unwrap(); + let signature_topic = ::SIGNATURE_TOPIC + .map(H256::from) + .unwrap(); let expected_topics = vec![signature_topic]; assert_eq!(expected_topics, contract_event.topics); From 9dea0aa901670ac1bc259a69385fcbfd4277105f Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Wed, 5 Jul 2023 12:25:21 +0100 Subject: [PATCH 115/140] Remove todo, duplicate attributes are checked in the derive impls --- crates/ink/macro/src/event/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/ink/macro/src/event/mod.rs b/crates/ink/macro/src/event/mod.rs index 7c86a74dd6..829c6cbcde 100644 --- a/crates/ink/macro/src/event/mod.rs +++ b/crates/ink/macro/src/event/mod.rs @@ -25,7 +25,6 @@ use syn::spanned::Spanned; /// todo: docs pub fn generate(_config: TokenStream2, input: TokenStream2) -> TokenStream2 { - // todo: check for duplicate attributes match syn::parse2::(input) { Ok(ast) => { quote::quote! ( From 96125152dd0b71664e7ba07e83085fee342a59bf Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Wed, 5 Jul 2023 12:31:51 +0100 Subject: [PATCH 116/140] Add docs --- crates/ink/macro/src/event/mod.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/ink/macro/src/event/mod.rs b/crates/ink/macro/src/event/mod.rs index 829c6cbcde..1f92957383 100644 --- a/crates/ink/macro/src/event/mod.rs +++ b/crates/ink/macro/src/event/mod.rs @@ -23,7 +23,8 @@ use quote::{ }; use syn::spanned::Spanned; -/// todo: docs +/// Generate code from the `#[ink::event]` attribute. This simply expands to the required +/// derive macros to satisfy an event implementation. pub fn generate(_config: TokenStream2, input: TokenStream2) -> TokenStream2 { match syn::parse2::(input) { Ok(ast) => { From ce7c4e005cc093082f0b2abe7b840c2ec079964d Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Wed, 5 Jul 2023 13:40:15 +0100 Subject: [PATCH 117/140] Fix examples-test --- .gitlab-ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 0d25a4b610..f34683436c 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -374,6 +374,9 @@ examples-test: needs: - job: clippy-std artifacts: false + variables: + # Fix linking of `linkme`: https://github.com/dtolnay/linkme/issues/49 + RUSTFLAGS: "-Clink-arg=-z -Clink-arg=nostart-stop-gc" script: - for example in integration-tests/*/; do if [ "$example" = "integration-tests/lang-err-integration-tests/" ]; then continue; fi; From 0b2d2dee19afd041b4a8bb30e619a93209293dc7 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Wed, 5 Jul 2023 14:01:51 +0100 Subject: [PATCH 118/140] Add docs for `#[ink::event]` --- crates/ink/macro/src/lib.rs | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/crates/ink/macro/src/lib.rs b/crates/ink/macro/src/lib.rs index fae06bd6cc..ce1b51b7eb 100644 --- a/crates/ink/macro/src/lib.rs +++ b/crates/ink/macro/src/lib.rs @@ -650,7 +650,32 @@ pub fn trait_definition(attr: TokenStream, item: TokenStream) -> TokenStream { trait_def::analyze(attr.into(), item.into()).into() } -/// todo: docs +/// Implements the necessary traits for a `struct` to be emitted as an event from a +/// contract. +/// +/// By default, a signature topic will be generated for the event. This allows consumers +/// to filter and identify events of this type. Marking an event with `#[ink(anonymous)]` +/// means no signature topic will be generated or emitted. +/// +/// # Examples +/// +/// ``` +/// #[ink::event] +/// pub struct MyEvent { +/// pub field: u32, +/// #[ink(topic)] +/// pub topic: [u8; 32], +/// } +/// +/// // The `#[ink(anonymous)]` attribute means no signature topic will be emitted for the event. +/// #[ink::event] +/// #[ink(anonymous)] +/// pub struct MyAnonEvent { +/// pub field: u32, +/// #[ink(topic)] +/// pub topic: [u8; 32], +/// } +/// ``` #[proc_macro_attribute] pub fn event(attr: TokenStream, item: TokenStream) -> TokenStream { event::generate(attr.into(), item.into()).into() From 7bc0183eff9099c9d24d6ff4948781e5fa11746c Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Wed, 5 Jul 2023 14:48:45 +0100 Subject: [PATCH 119/140] Add docs for `EventMetadata` trait --- crates/metadata/src/lib.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/crates/metadata/src/lib.rs b/crates/metadata/src/lib.rs index 90b61b54b3..75cd440d33 100644 --- a/crates/metadata/src/lib.rs +++ b/crates/metadata/src/lib.rs @@ -170,9 +170,13 @@ pub fn collect_events() -> Vec { EVENTS.iter().map(|event| event()).collect() } -// todo: docs +/// Provides metadata about an ink! event. +/// +/// Implementations must be registered into the [`EVENTS`] distributed slice, in order to +/// be included in the contract metadata. This is done automatically by the +/// `#[derive(ink::EventMetadata)]` pub trait EventMetadata { - /// The full path to the event type, usually provided by [`core::module_path`]. + /// The full path to the event type, usually provided by [`module_path`]. const MODULE_PATH: &'static str; /// Returns the metadata of the event. From 100cbaa2bed22c3459d61cd47850a81c4e67927e Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Wed, 5 Jul 2023 14:51:46 +0100 Subject: [PATCH 120/140] Update Event trait comments --- crates/env/src/event.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/env/src/event.rs b/crates/env/src/event.rs index 22b7c13b9a..e740ac2c1e 100644 --- a/crates/env/src/event.rs +++ b/crates/env/src/event.rs @@ -194,14 +194,13 @@ impl EventTopicsAmount for state::NoRemainingTopics { /// Implemented by event types to guide the event topic serialization using the topics /// builder. /// -/// Normally this trait should be implemented automatically via the ink! codegen. +/// Normally this trait should be implemented automatically via `#[derive(ink::Event)`. pub trait Event: scale::Encode { /// Type state indicating how many event topics are to be expected by the topics /// builder. type RemainingTopics: EventTopicsAmount; /// The unique signature topic of the event. `None` for anonymous events. - /// todo: document how this is calculated const SIGNATURE_TOPIC: Option<[u8; 32]>; /// Guides event topic serialization using the given topics builder. From 850c38a4990a9b3f25780007ef1f7565fdf52e7f Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Wed, 5 Jul 2023 15:14:50 +0100 Subject: [PATCH 121/140] Docs --- crates/ink/macro/src/lib.rs | 43 ++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/crates/ink/macro/src/lib.rs b/crates/ink/macro/src/lib.rs index ce1b51b7eb..a34d1ee034 100644 --- a/crates/ink/macro/src/lib.rs +++ b/crates/ink/macro/src/lib.rs @@ -1321,6 +1321,11 @@ synstructure::decl_derive!( /// **Note** [`ink::Event`] requires a [`scale::Encode`] implementation, it is up to /// the user to provide that: usually via the derive. /// + /// Usually this is used in conjunction with the [`EventMetadata`] derive. + /// + /// For convenience there is the [`event`] attribute macro that will expand to all the necessary + /// derives for an event implementation, including this one. + /// /// # Example /// /// ``` @@ -1344,7 +1349,43 @@ synstructure::decl_derive!( synstructure::decl_derive!( [EventMetadata] => - /// todo + /// Derives the [`ink::EventMetadata`] trait for the given `struct`, which provides metadata + /// about the event definition. + /// + /// Requires that the `struct` also implements the [`ink::Event`] trait, so this derive is + /// usually used in combination with the [`Event`] derive. + /// + /// Metadata is not embedded into the contract binary, it is generated from a separate + /// compilation of the contract with the `std` feature, therefore this derive must be + /// conditionally compiled e.g. `#[cfg_attr(feature = "std", derive(::ink::EventMetadata))]` + /// (see example below). + /// + /// For convenience there is the [`event`] attribute macro that will expand to all the necessary + /// derives for an event implementation, including this one. + /// + /// # Example + /// + /// ``` + /// use ink::{ + /// Event, + /// env::DefaultEnvironment, + /// }; + /// use scale::Encode; + /// + /// #[cfg_attr(feature = "std", derive(::ink::EventMetadata))] + /// #[derive(Event, Encode)] + /// struct MyEvent { + /// a: u32, + /// #[ink(topic)] + /// b: [u8; 32], + /// } + /// + /// assert_eq!(::event_spec().args().len(), 2); + /// ``` + /// + /// The generated code will also register this implementation with the global static distributed + /// slice [`ink::metadata::EVENTS`], in order that the metadata of all events used in a contract + /// can be collected. event::event_metadata_derive ); From d8185cc6d09765016e436793a0b2a6ee56e17a21 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Wed, 5 Jul 2023 15:23:18 +0100 Subject: [PATCH 122/140] SIGNATURE_TOPIC docs --- crates/env/src/event.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crates/env/src/event.rs b/crates/env/src/event.rs index e740ac2c1e..cda111d739 100644 --- a/crates/env/src/event.rs +++ b/crates/env/src/event.rs @@ -201,6 +201,9 @@ pub trait Event: scale::Encode { type RemainingTopics: EventTopicsAmount; /// The unique signature topic of the event. `None` for anonymous events. + /// + /// Usually this is calculated using the `#[derive(ink::Event)]` derive, which by + /// default calculates this as `blake2b("Event(field1_type,field2_type)")` const SIGNATURE_TOPIC: Option<[u8; 32]>; /// Guides event topic serialization using the given topics builder. From b7376e6c0eeca4983126c0d37df7874340d15001 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Wed, 5 Jul 2023 15:48:34 +0100 Subject: [PATCH 123/140] Fix docs --- crates/metadata/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/metadata/src/lib.rs b/crates/metadata/src/lib.rs index 75cd440d33..cad33d8769 100644 --- a/crates/metadata/src/lib.rs +++ b/crates/metadata/src/lib.rs @@ -172,7 +172,7 @@ pub fn collect_events() -> Vec { /// Provides metadata about an ink! event. /// -/// Implementations must be registered into the [`EVENTS`] distributed slice, in order to +/// Implementations must be registered into the [`static@EVENTS`] distributed slice, in order to /// be included in the contract metadata. This is done automatically by the /// `#[derive(ink::EventMetadata)]` pub trait EventMetadata { From 205f876284e4c8448879624b1e6eccb9ff1aa904 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Wed, 5 Jul 2023 15:50:56 +0100 Subject: [PATCH 124/140] fmt --- crates/metadata/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/metadata/src/lib.rs b/crates/metadata/src/lib.rs index cad33d8769..0f044eab8d 100644 --- a/crates/metadata/src/lib.rs +++ b/crates/metadata/src/lib.rs @@ -172,8 +172,8 @@ pub fn collect_events() -> Vec { /// Provides metadata about an ink! event. /// -/// Implementations must be registered into the [`static@EVENTS`] distributed slice, in order to -/// be included in the contract metadata. This is done automatically by the +/// Implementations must be registered into the [`static@EVENTS`] distributed slice, in +/// order to be included in the contract metadata. This is done automatically by the /// `#[derive(ink::EventMetadata)]` pub trait EventMetadata { /// The full path to the event type, usually provided by [`module_path`]. From c558972172b0668e2d8c74ff722a092f77996c65 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Wed, 5 Jul 2023 21:59:19 +0100 Subject: [PATCH 125/140] Try setting lto = "thin" for metadata crate to fix met --- crates/metadata/Cargo.toml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/crates/metadata/Cargo.toml b/crates/metadata/Cargo.toml index 0254c3dbc7..7d35a9eded 100644 --- a/crates/metadata/Cargo.toml +++ b/crates/metadata/Cargo.toml @@ -41,3 +41,8 @@ std = [ "scale-info/std", ] derive = [] + +[profile.release] +# Need this for linkme crate to work +# See https://github.com/dtolnay/linkme/issues/61#issuecomment-1503653702 +lto = "thin" From 083fad88fb9f43c0a2b716efd1149a17529ddfe0 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Thu, 6 Jul 2023 07:47:28 +0100 Subject: [PATCH 126/140] Add lto to test profile of unit test --- crates/metadata/Cargo.toml | 5 ----- integration-tests/events/Cargo.toml | 5 +++++ integration-tests/events/lib.rs | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/metadata/Cargo.toml b/crates/metadata/Cargo.toml index 7d35a9eded..0254c3dbc7 100644 --- a/crates/metadata/Cargo.toml +++ b/crates/metadata/Cargo.toml @@ -41,8 +41,3 @@ std = [ "scale-info/std", ] derive = [] - -[profile.release] -# Need this for linkme crate to work -# See https://github.com/dtolnay/linkme/issues/61#issuecomment-1503653702 -lto = "thin" diff --git a/integration-tests/events/Cargo.toml b/integration-tests/events/Cargo.toml index 0b0225bf93..cc45c92161 100644 --- a/integration-tests/events/Cargo.toml +++ b/integration-tests/events/Cargo.toml @@ -32,3 +32,8 @@ std = [ ] ink-as-dependency = [] e2e-tests = [] + +[profile.test] +# Need this for linkme crate to work for the event metadata unit test. +# See https://github.com/dtolnay/linkme/issues/61#issuecomment-1503653702 +lto = "thin" diff --git a/integration-tests/events/lib.rs b/integration-tests/events/lib.rs index e71fd86e9e..e91de9d30b 100644 --- a/integration-tests/events/lib.rs +++ b/integration-tests/events/lib.rs @@ -61,7 +61,7 @@ pub mod events { use super::*; use scale::Decode as _; - #[ink::test] + #[test] fn collects_specs_for_all_linked_and_used_events() { let event_specs = ink::metadata::collect_events(); assert_eq!(4, event_specs.len()); From df91ae55040571daebb268dfdd6ff12966a16d04 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Fri, 7 Jul 2023 16:25:46 +0100 Subject: [PATCH 127/140] Add test to check disallow generics for Event derive --- crates/ink/macro/src/event/mod.rs | 9 ++++++++- crates/ink/tests/ui/event/fail/generics.rs | 7 +++++++ crates/ink/tests/ui/event/fail/generics.stderr | 5 +++++ 3 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 crates/ink/tests/ui/event/fail/generics.rs create mode 100644 crates/ink/tests/ui/event/fail/generics.stderr diff --git a/crates/ink/macro/src/event/mod.rs b/crates/ink/macro/src/event/mod.rs index 1f92957383..6181d6648f 100644 --- a/crates/ink/macro/src/event/mod.rs +++ b/crates/ink/macro/src/event/mod.rs @@ -60,8 +60,15 @@ pub fn event_derive(mut s: synstructure::Structure) -> TokenStream2 { /// `Event` derive implementation for `struct` types. fn event_derive_struct(mut s: synstructure::Structure) -> syn::Result { assert_eq!(s.variants().len(), 1, "can only operate on structs"); - let span = s.ast().span(); + if !s.ast().generics.params.is_empty() { + return Err(syn::Error::new( + s.ast().generics.params.span(), + "can only derive `Event` for structs without generics", + )); + } + + let span = s.ast().span(); let anonymous = has_ink_attribute(&s.ast().attrs, "anonymous")?; // filter field bindings to those marked as topics diff --git a/crates/ink/tests/ui/event/fail/generics.rs b/crates/ink/tests/ui/event/fail/generics.rs new file mode 100644 index 0000000000..72487d3eeb --- /dev/null +++ b/crates/ink/tests/ui/event/fail/generics.rs @@ -0,0 +1,7 @@ +#[derive(ink::Event, scale::Encode)] +pub struct Event { + #[ink(topic)] + pub topic: T, +} + +fn main() {} \ No newline at end of file diff --git a/crates/ink/tests/ui/event/fail/generics.stderr b/crates/ink/tests/ui/event/fail/generics.stderr new file mode 100644 index 0000000000..0b790c8cf1 --- /dev/null +++ b/crates/ink/tests/ui/event/fail/generics.stderr @@ -0,0 +1,5 @@ +error: can only derive `Event` for structs without generics + --> tests/ui/event/fail/generics.rs:2:18 + | +2 | pub struct Event { + | ^ From f7a4f4fc55b36eac92c4de6c7aaf41623dd1ef20 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Fri, 7 Jul 2023 16:26:12 +0100 Subject: [PATCH 128/140] Fmt --- crates/ink/macro/src/event/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ink/macro/src/event/mod.rs b/crates/ink/macro/src/event/mod.rs index 6181d6648f..b37761a53a 100644 --- a/crates/ink/macro/src/event/mod.rs +++ b/crates/ink/macro/src/event/mod.rs @@ -65,7 +65,7 @@ fn event_derive_struct(mut s: synstructure::Structure) -> syn::Result Date: Mon, 10 Jul 2023 15:32:56 +0100 Subject: [PATCH 129/140] Change to `#[ink::event(anonymous = true)]` syntax --- crates/ink/codegen/src/generator/contract.rs | 7 +- crates/ink/codegen/src/generator/event.rs | 36 ++++++ crates/ink/codegen/src/generator/events.rs | 60 ---------- crates/ink/codegen/src/generator/mod.rs | 4 +- crates/ink/codegen/src/lib.rs | 4 + crates/ink/ir/src/ir/event/config.rs | 70 +++++++++++ .../ir/src/ir/{item/event.rs => event/mod.rs} | 110 +++++++++--------- crates/ink/ir/src/ir/item/mod.rs | 6 +- crates/ink/ir/src/ir/mod.rs | 3 +- crates/ink/macro/src/event/mod.rs | 18 +-- .../ui/event/fail/multiple_anonymous_args.rs | 9 -- .../event/fail/multiple_anonymous_args.stderr | 11 -- .../multiple_anonymous_args_inline.stderr | 11 -- .../anonymous_flag_works.rs} | 4 +- 14 files changed, 180 insertions(+), 173 deletions(-) create mode 100644 crates/ink/codegen/src/generator/event.rs delete mode 100644 crates/ink/codegen/src/generator/events.rs create mode 100644 crates/ink/ir/src/ir/event/config.rs rename crates/ink/ir/src/ir/{item/event.rs => event/mod.rs} (79%) delete mode 100644 crates/ink/tests/ui/event/fail/multiple_anonymous_args.rs delete mode 100644 crates/ink/tests/ui/event/fail/multiple_anonymous_args.stderr delete mode 100644 crates/ink/tests/ui/event/fail/multiple_anonymous_args_inline.stderr rename crates/ink/tests/ui/event/{fail/multiple_anonymous_args_inline.rs => pass/anonymous_flag_works.rs} (58%) diff --git a/crates/ink/codegen/src/generator/contract.rs b/crates/ink/codegen/src/generator/contract.rs index 1662b82642..cb3ca9727c 100644 --- a/crates/ink/codegen/src/generator/contract.rs +++ b/crates/ink/codegen/src/generator/contract.rs @@ -38,7 +38,10 @@ impl GenerateCode for Contract<'_> { let vis = module.vis(); let env = self.generate_code_using::(); let storage = self.generate_code_using::(); - let events = self.generate_code_using::(); + let events = module.events().map(move |event| { + let event_generator = generator::Event::from(event); + event_generator.generate_code() + }); let dispatch2 = self.generate_code_using::(); let item_impls = self.generate_code_using::(); let metadata = self.generate_code_using::(); @@ -55,7 +58,7 @@ impl GenerateCode for Contract<'_> { #vis mod #ident { #env #storage - #events + #( #events )* #dispatch2 #item_impls #contract_reference diff --git a/crates/ink/codegen/src/generator/event.rs b/crates/ink/codegen/src/generator/event.rs new file mode 100644 index 0000000000..002d7bbebb --- /dev/null +++ b/crates/ink/codegen/src/generator/event.rs @@ -0,0 +1,36 @@ +// Copyright 2018-2022 Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::GenerateCode; +use derive_more::From; +use proc_macro2::TokenStream as TokenStream2; + +/// Generates code for the storage item. +#[derive(From, Copy, Clone)] +pub struct Event<'a> { + /// The storage item to generate code for. + item: &'a ir::Event, +} + +impl GenerateCode for Event<'_> { + /// Generates ink! storage item code. + fn generate_code(&self) -> TokenStream2 { + let item = self.item.item(); + quote::quote! ( + #[derive(::ink::Event, ::scale::Encode, ::scale::Decode)] + #[cfg_attr(feature = "std", derive(::ink::EventMetadata))] + #item + ) + } +} diff --git a/crates/ink/codegen/src/generator/events.rs b/crates/ink/codegen/src/generator/events.rs deleted file mode 100644 index d9dd7dca0d..0000000000 --- a/crates/ink/codegen/src/generator/events.rs +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2018-2022 Parity Technologies (UK) Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use crate::GenerateCode; -use derive_more::From; -use proc_macro2::TokenStream as TokenStream2; -use quote::{ - quote, - quote_spanned, -}; -use syn::spanned::Spanned as _; - -/// Generates code for the ink! event structs of the contract. -#[derive(From)] -pub struct Events<'a> { - contract: &'a ir::Contract, -} -impl_as_ref_for_generator!(Events); - -impl GenerateCode for Events<'_> { - fn generate_code(&self) -> TokenStream2 { - let event_items = self.generate_event_items(); - quote! { - #( #event_items )* - } - } -} - -impl<'a> Events<'a> { - /// Generates all the user defined event struct definitions. - fn generate_event_items(&'a self) -> impl Iterator + 'a { - self.contract.module().events().map(move |event| { - let span = event.span(); - // add back the `#[ink(anonymous)]` attribute if it was present, for parsing - // by the derive macros. - let anonymous_attr = event.anonymous.then(|| { - quote_spanned!(span => - #[ink(anonymous)] - ) - }); - quote_spanned!(span => - #[derive(::ink::Event, ::scale::Encode, ::scale::Decode)] - #[cfg_attr(feature = "std", derive(::ink::EventMetadata))] - #anonymous_attr - #event - ) - }) - } -} diff --git a/crates/ink/codegen/src/generator/mod.rs b/crates/ink/codegen/src/generator/mod.rs index 960a1b58dd..453dbc6fbb 100644 --- a/crates/ink/codegen/src/generator/mod.rs +++ b/crates/ink/codegen/src/generator/mod.rs @@ -33,7 +33,7 @@ mod chain_extension; mod contract; mod dispatch; mod env; -mod events; +mod event; mod ink_test; mod item_impls; mod metadata; @@ -58,7 +58,7 @@ pub use self::{ contract::Contract, dispatch::Dispatch, env::Env, - events::Events, + event::Event, ink_test::InkTest, item_impls::ItemImpls, metadata::Metadata, diff --git a/crates/ink/codegen/src/lib.rs b/crates/ink/codegen/src/lib.rs index b5d9631c07..78f020ff4c 100644 --- a/crates/ink/codegen/src/lib.rs +++ b/crates/ink/codegen/src/lib.rs @@ -58,6 +58,10 @@ impl<'a> CodeGenerator for &'a ir::Contract { type Generator = generator::Contract<'a>; } +impl<'a> CodeGenerator for &'a ir::Event { + type Generator = generator::Event<'a>; +} + impl<'a> CodeGenerator for &'a ir::StorageItem { type Generator = generator::StorageItem<'a>; } diff --git a/crates/ink/ir/src/ir/event/config.rs b/crates/ink/ir/src/ir/event/config.rs new file mode 100644 index 0000000000..f717cbb6bb --- /dev/null +++ b/crates/ink/ir/src/ir/event/config.rs @@ -0,0 +1,70 @@ +// Copyright 2018-2022 Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::{ + ast, + utils::duplicate_config_err, +}; + +/// The configuration arguments to the `#[ink::event(..)]` attribute macro. +#[derive(Debug, PartialEq, Eq)] +pub struct EventConfig { + /// If set to `false`, a signature topic is generated and emitted for this event. + /// If set to `true`, **no** signature topic is generated or emitted for this event., + /// This is the default value. + anonymous: bool, +} + +impl TryFrom for EventConfig { + type Error = syn::Error; + + fn try_from(args: ast::AttributeArgs) -> Result { + let mut anonymous: Option = None; + for arg in args.into_iter() { + if arg.name.is_ident("derive") { + if let Some(lit_bool) = anonymous { + return Err(duplicate_config_err(lit_bool, arg, "anonymous", "event")) + } + if let ast::MetaValue::Lit(syn::Lit::Bool(lit_bool)) = &arg.value { + anonymous = Some(lit_bool.clone()) + } else { + return Err(format_err_spanned!( + arg, + "expected a bool literal for `anonymous` ink! event item configuration argument", + )); + } + } else { + return Err(format_err_spanned!( + arg, + "encountered unknown or unsupported ink! storage item configuration argument", + )); + } + } + Ok(EventConfig::new( + anonymous.map(|lit_bool| lit_bool.value).unwrap_or(false), + )) + } +} + +impl EventConfig { + /// Construct a new [`EventConfig`]. + pub fn new(anonymous: bool) -> Self { + Self { anonymous } + } + + /// Returns the anonymous configuration argument. + pub fn anonymous(&self) -> bool { + self.anonymous + } +} diff --git a/crates/ink/ir/src/ir/item/event.rs b/crates/ink/ir/src/ir/event/mod.rs similarity index 79% rename from crates/ink/ir/src/ir/item/event.rs rename to crates/ink/ir/src/ir/event/mod.rs index aa9f9898f6..8671534991 100644 --- a/crates/ink/ir/src/ir/item/event.rs +++ b/crates/ink/ir/src/ir/event/mod.rs @@ -12,47 +12,57 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::ir; -use proc_macro2::Ident; +mod config; + +use config::EventConfig; +use proc_macro2::TokenStream as TokenStream2; +use quote::ToTokens; use syn::spanned::Spanned as _; -/// An ink! event struct definition. -/// -/// # Example -/// -/// ``` -/// # let event = >::try_from(syn::parse_quote! { -/// #[ink(event)] -/// pub struct Transaction { -/// #[ink(topic)] -/// from: AccountId, -/// #[ink(topic)] -/// to: AccountId, -/// value: Balance, -/// } -/// # }).unwrap(); -/// ``` +use crate::ir; + +/// A checked ink! event with its configuration. #[derive(Debug, PartialEq, Eq)] pub struct Event { item: syn::ItemStruct, - pub anonymous: bool, + config: EventConfig, } -impl quote::ToTokens for Event { - /// We mainly implement this trait for this ink! type to have a derived - /// [`Spanned`](`syn::spanned::Spanned`) implementation for it. - fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { - self.item.to_tokens(tokens) +impl Event { + /// Returns `Ok` if the input matches all requirements for an ink! event. + pub fn new(config: TokenStream2, item: TokenStream2) -> Result { + let item = syn::parse2::(item)?; + let parsed_config = syn::parse2::(config)?; + let config = EventConfig::try_from(parsed_config)?; + + for attr in &item.attrs { + if attr.path().to_token_stream().to_string().contains("event") { + return Err(format_err_spanned!( + attr, + "only one `ink::event` is allowed", + )) + } + } + + Ok(Self { item, config }) + } + + /// Returns the event definition . + pub fn item(&self) -> &syn::ItemStruct { + &self.item } -} -impl Event { /// Returns `true` if the first ink! annotation on the given struct is /// `#[ink(event)]`. /// /// # Errors /// /// If the first found ink! attribute is malformed. + /// + /// # Note + /// + /// This is used for legacy "inline" event definitions, i.e. event definitions that + /// are defined within a module annotated with `#[ink::contract]`. pub(super) fn is_ink_event( item_struct: &syn::ItemStruct, ) -> Result { @@ -66,6 +76,20 @@ impl Event { .expect("missing expected ink! attribute for struct"); Ok(matches!(attr.first().kind(), ir::AttributeArg::Event)) } + + /// Returns if the event is marked as anonymous, if true then no signature topic is + /// generated or emitted. + pub fn anonymous(&self) -> bool { + self.config.anonymous() + } +} + +impl ToTokens for Event { + /// We mainly implement this trait for this ink! type to have a derived + /// [`Spanned`](`syn::spanned::Spanned`) implementation for it. + fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { + self.item.to_tokens(tokens) + } } impl TryFrom for Event { @@ -89,18 +113,11 @@ impl TryFrom for Event { attrs: other_attrs, ..item_struct }, - anonymous: ink_attrs.is_anonymous(), + config: EventConfig::new(ink_attrs.is_anonymous()), }) } } -impl Event { - /// Returns the identifier of the event struct. - pub fn ident(&self) -> &Ident { - &self.item.ident - } -} - #[cfg(test)] mod tests { use super::*; @@ -187,35 +204,12 @@ mod tests { ) } - /// Used for the event fields iterator unit test because `syn::Field` does - /// not provide a `syn::parse::Parse` implementation. - #[derive(Debug, PartialEq, Eq)] - struct NamedField(syn::Field); - - impl syn::parse::Parse for NamedField { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - Ok(Self(syn::Field::parse_named(input)?)) - } - } - - impl NamedField { - /// Returns the identifier of the named field. - pub fn ident(&self) -> &Ident { - self.0.ident.as_ref().unwrap() - } - - /// Returns the type of the named field. - pub fn ty(&self) -> &syn::Type { - &self.0.ty - } - } - #[test] fn anonymous_event_works() { fn assert_anonymous_event(event: syn::ItemStruct) { match Event::try_from(event) { Ok(event) => { - assert!(event.anonymous); + assert!(event.anonymous()); } Err(_) => panic!("encountered unexpected invalid anonymous event"), } diff --git a/crates/ink/ir/src/ir/item/mod.rs b/crates/ink/ir/src/ir/item/mod.rs index 5bb3d6b83e..766e5baecd 100644 --- a/crates/ink/ir/src/ir/item/mod.rs +++ b/crates/ink/ir/src/ir/item/mod.rs @@ -12,16 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -mod event; mod storage; #[cfg(test)] mod tests; -pub use self::{ - event::Event, - storage::Storage, -}; +pub use self::storage::Storage; use crate::{ error::ExtError as _, diff --git a/crates/ink/ir/src/ir/mod.rs b/crates/ink/ir/src/ir/mod.rs index a2e8ea0949..8d47fd535f 100644 --- a/crates/ink/ir/src/ir/mod.rs +++ b/crates/ink/ir/src/ir/mod.rs @@ -19,6 +19,7 @@ mod blake2; mod chain_extension; mod config; mod contract; +mod event; mod idents_lint; mod ink_test; mod item; @@ -69,9 +70,9 @@ pub use self::{ }, config::Config, contract::Contract, + event::Event, ink_test::InkTest, item::{ - Event, InkItem, Item, Storage, diff --git a/crates/ink/macro/src/event/mod.rs b/crates/ink/macro/src/event/mod.rs index b37761a53a..185f7e07b6 100644 --- a/crates/ink/macro/src/event/mod.rs +++ b/crates/ink/macro/src/event/mod.rs @@ -16,6 +16,7 @@ mod metadata; pub use metadata::event_metadata_derive; +use ink_codegen::generate_code; use proc_macro2::TokenStream as TokenStream2; use quote::{ quote, @@ -23,19 +24,12 @@ use quote::{ }; use syn::spanned::Spanned; -/// Generate code from the `#[ink::event]` attribute. This simply expands to the required +/// Generate code from the `#[ink::event]` attribute. This expands to the required /// derive macros to satisfy an event implementation. -pub fn generate(_config: TokenStream2, input: TokenStream2) -> TokenStream2 { - match syn::parse2::(input) { - Ok(ast) => { - quote::quote! ( - #[derive(::ink::Event, ::scale::Encode, ::scale::Decode)] - #[cfg_attr(feature = "std", derive(::ink::EventMetadata))] - #ast - ) - } - Err(err) => err.to_compile_error(), - } +pub fn generate(config: TokenStream2, input: TokenStream2) -> TokenStream2 { + ink_ir::Event::new(config, input) + .map(|event| generate_code(&event)) + .unwrap_or_else(|err| err.to_compile_error()) } /// Derives the `ink::Event` trait for the given `struct`. diff --git a/crates/ink/tests/ui/event/fail/multiple_anonymous_args.rs b/crates/ink/tests/ui/event/fail/multiple_anonymous_args.rs deleted file mode 100644 index 4c6c86b3c8..0000000000 --- a/crates/ink/tests/ui/event/fail/multiple_anonymous_args.rs +++ /dev/null @@ -1,9 +0,0 @@ -#[ink::event] -#[ink(anonymous)] -#[ink(anonymous)] -pub struct Event { - #[ink(topic)] - pub topic: [u8; 32], -} - -fn main() {} \ No newline at end of file diff --git a/crates/ink/tests/ui/event/fail/multiple_anonymous_args.stderr b/crates/ink/tests/ui/event/fail/multiple_anonymous_args.stderr deleted file mode 100644 index 975e3e0528..0000000000 --- a/crates/ink/tests/ui/event/fail/multiple_anonymous_args.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error: Only a single `#[ink(anonymous)]` attribute allowed. - --> tests/ui/event/fail/multiple_anonymous_args.rs:3:1 - | -3 | #[ink(anonymous)] - | ^ - -error[E0277]: the trait bound `Event: ink::ink_env::Event` is not satisfied - --> tests/ui/event/fail/multiple_anonymous_args.rs:2:1 - | -2 | #[ink(anonymous)] - | ^ the trait `ink::ink_env::Event` is not implemented for `Event` diff --git a/crates/ink/tests/ui/event/fail/multiple_anonymous_args_inline.stderr b/crates/ink/tests/ui/event/fail/multiple_anonymous_args_inline.stderr deleted file mode 100644 index 1a323b24cf..0000000000 --- a/crates/ink/tests/ui/event/fail/multiple_anonymous_args_inline.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error: Invalid `#[ink(anonymous)]` attribute: multiple arguments not allowed. - --> tests/ui/event/fail/multiple_anonymous_args_inline.rs:2:7 - | -2 | #[ink(anonymous, anonymous)] - | ^^^^^^^^^ - -error[E0277]: the trait bound `Event: ink::ink_env::Event` is not satisfied - --> tests/ui/event/fail/multiple_anonymous_args_inline.rs:2:1 - | -2 | #[ink(anonymous, anonymous)] - | ^ the trait `ink::ink_env::Event` is not implemented for `Event` diff --git a/crates/ink/tests/ui/event/fail/multiple_anonymous_args_inline.rs b/crates/ink/tests/ui/event/pass/anonymous_flag_works.rs similarity index 58% rename from crates/ink/tests/ui/event/fail/multiple_anonymous_args_inline.rs rename to crates/ink/tests/ui/event/pass/anonymous_flag_works.rs index 4dc2ea5d40..34a6e705d2 100644 --- a/crates/ink/tests/ui/event/fail/multiple_anonymous_args_inline.rs +++ b/crates/ink/tests/ui/event/pass/anonymous_flag_works.rs @@ -1,8 +1,8 @@ -#[ink::event] -#[ink(anonymous, anonymous)] +#[ink::event(anonymous = true)] pub struct Event { #[ink(topic)] pub topic: [u8; 32], + pub field_1: u32, } fn main() {} \ No newline at end of file From 26245ac36bda975804bc38a114e7ce1131167500 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Mon, 10 Jul 2023 21:16:32 +0100 Subject: [PATCH 130/140] Fix test --- crates/ink/ir/src/ir/event/config.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ink/ir/src/ir/event/config.rs b/crates/ink/ir/src/ir/event/config.rs index f717cbb6bb..181910c1fe 100644 --- a/crates/ink/ir/src/ir/event/config.rs +++ b/crates/ink/ir/src/ir/event/config.rs @@ -32,7 +32,7 @@ impl TryFrom for EventConfig { fn try_from(args: ast::AttributeArgs) -> Result { let mut anonymous: Option = None; for arg in args.into_iter() { - if arg.name.is_ident("derive") { + if arg.name.is_ident("anonymous") { if let Some(lit_bool) = anonymous { return Err(duplicate_config_err(lit_bool, arg, "anonymous", "event")) } From f912b7bfdac5f232736636f7714815adad387fb6 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Mon, 10 Jul 2023 21:39:50 +0100 Subject: [PATCH 131/140] Annotate derived event struct with anon --- crates/ink/codegen/src/generator/event.rs | 7 ++- integration-tests/events/lib.rs | 63 +++++++++++++++++++---- 2 files changed, 60 insertions(+), 10 deletions(-) diff --git a/crates/ink/codegen/src/generator/event.rs b/crates/ink/codegen/src/generator/event.rs index 002d7bbebb..7cf0e9e20a 100644 --- a/crates/ink/codegen/src/generator/event.rs +++ b/crates/ink/codegen/src/generator/event.rs @@ -27,9 +27,14 @@ impl GenerateCode for Event<'_> { /// Generates ink! storage item code. fn generate_code(&self) -> TokenStream2 { let item = self.item.item(); + let anonymous = self + .item + .anonymous() + .then(|| quote::quote! { #[ink(anonymous)] }); quote::quote! ( - #[derive(::ink::Event, ::scale::Encode, ::scale::Decode)] #[cfg_attr(feature = "std", derive(::ink::EventMetadata))] + #[derive(::ink::Event, ::scale::Encode, ::scale::Decode)] + #anonymous #item ) } diff --git a/integration-tests/events/lib.rs b/integration-tests/events/lib.rs index e91de9d30b..aee5a60076 100644 --- a/integration-tests/events/lib.rs +++ b/integration-tests/events/lib.rs @@ -1,5 +1,12 @@ #![cfg_attr(not(feature = "std"), no_std, no_main)] +#[ink::event(anonymous = true)] +pub struct AnonymousEvent { + #[ink(topic)] + pub topic: [u8; 32], + pub field_1: u32, +} + #[ink::contract] pub mod events { #[ink(storage)] @@ -12,6 +19,14 @@ pub mod events { value: bool, } + #[ink(event)] + #[ink(anonymous)] + pub struct InlineAnonymousEvent { + #[ink(topic)] + pub topic: [u8; 32], + pub field_1: u32, + } + impl Events { /// Creates a new events smart contract initialized with the given value. #[ink(constructor)] @@ -36,7 +51,7 @@ pub mod events { /// Emit an event with a 32 byte topic. #[ink(message)] - pub fn emit_32_byte_topic_event(&mut self, maybe_hash: Option<[u8; 32]>) { + pub fn emit_32_byte_topic_event(&self, maybe_hash: Option<[u8; 32]>) { self.env().emit_event(event_def::ThirtyTwoByteTopics { hash: [0x42; 32], maybe_hash, @@ -45,15 +60,21 @@ pub mod events { /// Emit an event from a different crate. #[ink(message)] - pub fn emit_event_from_a_different_crate( - &mut self, - maybe_hash: Option<[u8; 32]>, - ) { + pub fn emit_event_from_a_different_crate(&self, maybe_hash: Option<[u8; 32]>) { self.env().emit_event(event_def2::EventDefAnotherCrate { hash: [0x42; 32], maybe_hash, }) } + + /// Emit a inline and standalone anonymous events + #[ink(message)] + pub fn emit_anonymous_events(&self, topic: [u8; 32]) { + self.env() + .emit_event(InlineAnonymousEvent { topic, field_1: 42 }); + self.env() + .emit_event(super::AnonymousEvent { topic, field_1: 42 }); + } } #[cfg(test)] @@ -64,7 +85,7 @@ pub mod events { #[test] fn collects_specs_for_all_linked_and_used_events() { let event_specs = ink::metadata::collect_events(); - assert_eq!(4, event_specs.len()); + assert_eq!(6, event_specs.len()); assert!(event_specs .iter() @@ -78,6 +99,12 @@ pub mod events { assert!(event_specs .iter() .any(|evt| evt.label() == &"EventDefAnotherCrate")); + assert!(event_specs + .iter() + .any(|evt| evt.label() == &"AnonymousEvent")); + assert!(event_specs + .iter() + .any(|evt| evt.label() == &"InlineAnonymousEvent")); assert!(!event_specs .iter() @@ -100,7 +127,7 @@ pub mod events { #[ink::test] fn option_topic_some_has_topic() { - let mut events = Events::new(false); + let events = Events::new(false); events.emit_32_byte_topic_event(Some([0xAA; 32])); let emitted_events = ink::env::test::recorded_events().collect::>(); @@ -121,7 +148,7 @@ pub mod events { #[ink::test] fn option_topic_none_encoded_as_0() { - let mut events = Events::new(false); + let events = Events::new(false); events.emit_32_byte_topic_event(None); let emitted_events = ink::env::test::recorded_events().collect::>(); @@ -140,6 +167,24 @@ pub mod events { ]; assert_eq!(expected_topics, event.topics); } + + #[ink::test] + fn anonymous_events_emit_no_signature_topics() { + let events = Events::new(false); + let topic = [0x42; 32]; + events.emit_anonymous_events(topic); + + let emitted_events = ink::env::test::recorded_events().collect::>(); + assert_eq!(2, emitted_events.len()); + + let event = &emitted_events[0]; + assert_eq!(event.topics.len(), 1); + assert_eq!(event.topics[0], topic); + + let event = &emitted_events[1]; + assert_eq!(event.topics.len(), 1); + assert_eq!(event.topics[0], topic); + } } #[cfg(all(test, feature = "e2e-tests"))] @@ -237,7 +282,7 @@ pub mod events { .instantiate("events", &ink_e2e::alice(), constructor, 0, None) .await .expect("instantiate failed"); - let mut call = contract.call::(); + let call = contract.call::(); // when let call = call.emit_32_byte_topic_event(None); From 68688b12bf0960c879eba6ba9b9e4d2f0a1a17cb Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Mon, 10 Jul 2023 22:06:15 +0100 Subject: [PATCH 132/140] UI test --- crates/ink/ir/src/ir/event/mod.rs | 12 ++++++++-- .../ink/tests/ui/event/fail/event_enum.stderr | 22 +++++++++++-------- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/crates/ink/ir/src/ir/event/mod.rs b/crates/ink/ir/src/ir/event/mod.rs index 8671534991..a559971b8e 100644 --- a/crates/ink/ir/src/ir/event/mod.rs +++ b/crates/ink/ir/src/ir/event/mod.rs @@ -19,7 +19,10 @@ use proc_macro2::TokenStream as TokenStream2; use quote::ToTokens; use syn::spanned::Spanned as _; -use crate::ir; +use crate::{ + error::ExtError, + ir, +}; /// A checked ink! event with its configuration. #[derive(Debug, PartialEq, Eq)] @@ -31,7 +34,12 @@ pub struct Event { impl Event { /// Returns `Ok` if the input matches all requirements for an ink! event. pub fn new(config: TokenStream2, item: TokenStream2) -> Result { - let item = syn::parse2::(item)?; + let item = syn::parse2::(item.clone()).map_err(|err| { + err.into_combine(format_err_spanned!( + item, + "event definition must be a `struct`", + )) + })?; let parsed_config = syn::parse2::(config)?; let config = EventConfig::try_from(parsed_config)?; diff --git a/crates/ink/tests/ui/event/fail/event_enum.stderr b/crates/ink/tests/ui/event/fail/event_enum.stderr index 3f350c1164..db73d82b46 100644 --- a/crates/ink/tests/ui/event/fail/event_enum.stderr +++ b/crates/ink/tests/ui/event/fail/event_enum.stderr @@ -1,13 +1,17 @@ -error: can only derive `Event` for Rust `struct` items - --> tests/ui/event/fail/event_enum.rs:1:1 +error: expected `struct` + --> tests/ui/event/fail/event_enum.rs:2:5 | -1 | #[ink::event] - | ^^^^^^^^^^^^^ - | - = note: this error originates in the attribute macro `ink::event` (in Nightly builds, run with -Z macro-backtrace for more info) +2 | pub enum Event { + | ^^^^ -error: can only derive `EventMetadata` for Rust `struct` items +error: event definition must be a `struct` --> tests/ui/event/fail/event_enum.rs:2:1 | -2 | pub enum Event { - | ^^^ +2 | / pub enum Event { +3 | | Variant1 { +4 | | field_1: i8, +5 | | #[ink(topic)] +6 | | field_2: i16, +7 | | } +8 | | } + | |_^ From 1a10613e7a10cb74f6a2ab0c6ace97949646b90e Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Thu, 13 Jul 2023 13:08:09 +0100 Subject: [PATCH 133/140] Document limitation of signature topic derive --- crates/ink/macro/src/lib.rs | 47 +++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/crates/ink/macro/src/lib.rs b/crates/ink/macro/src/lib.rs index a34d1ee034..d77911087e 100644 --- a/crates/ink/macro/src/lib.rs +++ b/crates/ink/macro/src/lib.rs @@ -1342,8 +1342,55 @@ synstructure::decl_derive!( /// b: [u8; 32], /// } /// + /// #[derive(Event, Encode)] + /// #[ink(anonymous)] // anonymous events do not have a signature topic + /// struct MyAnonEvent { + /// a: u32, + /// #[ink(topic)] + /// b: [u8; 32], + /// } + /// /// ink_env::emit_event::(MyEvent { a: 42, b: [0x42; 32] }); + /// ink_env::emit_event::(MyAnonEvent { a: 42, b: [0x42; 32] }); + /// ``` + /// + /// # The Signature Topic + /// + /// By default, the [`ink::Event::SIGNATURE_TOPIC`] is calculated as follows: + /// + /// `blake2b("EventStructName(field1_type_name,field2_type_name)")` + /// + /// The hashing of the topic is done at codegen time in the derive macro, and as such only has + /// access to the **names** of the field types as they appear in the code. As such, if the + /// name of a field of a struct changes, the signature topic will change too, even if the + /// concrete type itself has not changed. This can happen with type aliases, generics, or a + /// change in the use of a `path::to::Type` qualification. + /// + /// Practically this means that two otherwise identical event definitions will have different + /// signature topics if the name of a field type differs. For example, the following two events + /// will have different signature topics: + /// /// ``` + /// #[derive(ink::Event, scale::Encode)] + /// pub struct MyEvent { + /// a: u32, + /// } + /// + /// mod other_event { + /// type MyU32 = u32; + /// + /// #[derive(ink::Event, scale::Encode)] + /// pub struct MyEvent { + /// a: MyU32, + /// } + /// } + /// + /// assert_ne!(::SIGNATURE_TOPIC, ::SIGNATURE_TOPIC); + /// ``` + /// + /// ## Anonymous Events + /// + /// If the event is annotated with `#[ink(anonymous)]` then no signature topic is generated. event::event_derive ); From c60a7866b5c991d6dd222ad52bd449af7ef35aef Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Thu, 13 Jul 2023 13:10:19 +0100 Subject: [PATCH 134/140] Fix doctest --- crates/ink/macro/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/ink/macro/src/lib.rs b/crates/ink/macro/src/lib.rs index d77911087e..77448b37a0 100644 --- a/crates/ink/macro/src/lib.rs +++ b/crates/ink/macro/src/lib.rs @@ -1385,7 +1385,8 @@ synstructure::decl_derive!( /// } /// } /// - /// assert_ne!(::SIGNATURE_TOPIC, ::SIGNATURE_TOPIC); + /// use ink::env::Event; + /// assert_ne!(::SIGNATURE_TOPIC, ::SIGNATURE_TOPIC); /// ``` /// /// ## Anonymous Events From dd8d861e5290a306fd3646ead259bdc03adaabb4 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Thu, 13 Jul 2023 13:26:30 +0100 Subject: [PATCH 135/140] Fix up errors after merge --- crates/e2e/src/client.rs | 69 ++++++++++++---------------------------- 1 file changed, 21 insertions(+), 48 deletions(-) diff --git a/crates/e2e/src/client.rs b/crates/e2e/src/client.rs index 263e02ac72..7d4c56f3af 100644 --- a/crates/e2e/src/client.rs +++ b/crates/e2e/src/client.rs @@ -82,54 +82,6 @@ pub type CallBuilderFinal = ink_env::call::CallBuilder< Set>, >; -/// A contract was successfully instantiated. -#[derive( - Debug, - scale::Decode, - scale::Encode, - scale_decode::DecodeAsType, - scale_encode::EncodeAsType, -)] -#[decode_as_type(trait_bounds = "", crate_path = "subxt::ext::scale_decode")] -#[encode_as_type(crate_path = "subxt::ext::scale_encode")] -struct ContractInstantiatedEvent { - /// Account id of the deployer. - pub deployer: E::AccountId, - /// Account id where the contract was instantiated to. - pub contract: E::AccountId, -} - -impl subxt::events::StaticEvent for ContractInstantiatedEvent -where - E: Environment, -{ - const PALLET: &'static str = "Contracts"; - const EVENT: &'static str = "Instantiated"; -} - -/// Code with the specified hash has been stored. -#[derive( - Debug, - scale::Decode, - scale::Encode, - scale_decode::DecodeAsType, - scale_encode::EncodeAsType, -)] -#[decode_as_type(trait_bounds = "", crate_path = "subxt::ext::scale_decode")] -#[encode_as_type(crate_path = "subxt::ext::scale_encode")] -struct CodeStoredEvent { - /// Hash under which the contract code was stored. - pub code_hash: E::Hash, -} - -impl subxt::events::StaticEvent for CodeStoredEvent -where - E: Environment, -{ - const PALLET: &'static str = "Contracts"; - const EVENT: &'static str = "CodeStored"; -} - /// The `Client` takes care of communicating with the node. /// /// This node's RPC interface will be used for instantiating the contract @@ -728,4 +680,25 @@ impl CallResult> { event.pallet_name() == pallet_name && event.variant_name() == variant_name }) } + + /// Returns all the `ContractEmitted` events emitted by the contract. + pub fn contract_emitted_events( + &self, + ) -> Result>>, subxt::Error> + where + C::Hash: Into, + { + let mut events_with_topics = Vec::new(); + for event in self.events.iter() { + let event = event?; + if let Some(decoded_event) = event.as_event::>()? { + let event_with_topics = EventWithTopics { + event: decoded_event, + topics: event.topics().iter().cloned().map(Into::into).collect(), + }; + events_with_topics.push(event_with_topics); + } + } + Ok(events_with_topics) + } } From 1a8b1575da5b6c1ee18fb2a96715652f8a64f56f Mon Sep 17 00:00:00 2001 From: Green Baneling Date: Fri, 14 Jul 2023 12:24:45 +0300 Subject: [PATCH 136/140] Usage of the crate includes defined there events (#1842) * Usage of the crate includes defined there events * Inclusion of the event from `event_def_unused` is expected --- .../events/event-def-unused/src/lib.rs | 6 ++++++ integration-tests/events/lib.rs | 15 +++++++++++++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/integration-tests/events/event-def-unused/src/lib.rs b/integration-tests/events/event-def-unused/src/lib.rs index 9fc3065ab8..cbf2d719fd 100644 --- a/integration-tests/events/event-def-unused/src/lib.rs +++ b/integration-tests/events/event-def-unused/src/lib.rs @@ -1,5 +1,11 @@ #![cfg_attr(not(feature = "std"), no_std, no_main)] +#[ink::trait_definition] +pub trait FlipperTrait { + #[ink(message)] + fn flip(&mut self); +} + #[ink::event] pub struct EventDefUnused { #[ink(topic)] diff --git a/integration-tests/events/lib.rs b/integration-tests/events/lib.rs index aee5a60076..aa88c7af2f 100644 --- a/integration-tests/events/lib.rs +++ b/integration-tests/events/lib.rs @@ -77,6 +77,15 @@ pub mod events { } } + /// Implementing the trait from the `event_def_unused` crate includes all defined + /// events there. + impl event_def_unused::FlipperTrait for Events { + #[ink(message)] + fn flip(&mut self) { + self.value = !self.value; + } + } + #[cfg(test)] mod tests { use super::*; @@ -85,7 +94,7 @@ pub mod events { #[test] fn collects_specs_for_all_linked_and_used_events() { let event_specs = ink::metadata::collect_events(); - assert_eq!(6, event_specs.len()); + assert_eq!(7, event_specs.len()); assert!(event_specs .iter() @@ -106,7 +115,9 @@ pub mod events { .iter() .any(|evt| evt.label() == &"InlineAnonymousEvent")); - assert!(!event_specs + // The event is not used in the code by being included in the metadata + // because we implement trait form `event_def_unused` crate. + assert!(event_specs .iter() .any(|evt| evt.label() == &"EventDefUnused")); } From cac0f95e022c99ed8093d06f0d576c4ec33c64e4 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Mon, 17 Jul 2023 10:20:08 +0100 Subject: [PATCH 137/140] Update anonymous syntax --- crates/ink/macro/src/lib.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/ink/macro/src/lib.rs b/crates/ink/macro/src/lib.rs index 77448b37a0..53c2ec7958 100644 --- a/crates/ink/macro/src/lib.rs +++ b/crates/ink/macro/src/lib.rs @@ -667,9 +667,8 @@ pub fn trait_definition(attr: TokenStream, item: TokenStream) -> TokenStream { /// pub topic: [u8; 32], /// } /// -/// // The `#[ink(anonymous)]` attribute means no signature topic will be emitted for the event. -/// #[ink::event] -/// #[ink(anonymous)] +/// // Setting `anonymous = true` means no signature topic will be emitted for the event. +/// #[ink::event(anonymous = true)] /// pub struct MyAnonEvent { /// pub field: u32, /// #[ink(topic)] From 4d97e78d2c42106cd68e9bb731697f4386f5e6ef Mon Sep 17 00:00:00 2001 From: andrew Date: Wed, 19 Jul 2023 09:47:16 +0100 Subject: [PATCH 138/140] Anonymous comment --- crates/ink/macro/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ink/macro/src/lib.rs b/crates/ink/macro/src/lib.rs index 53c2ec7958..f0945a1876 100644 --- a/crates/ink/macro/src/lib.rs +++ b/crates/ink/macro/src/lib.rs @@ -654,7 +654,7 @@ pub fn trait_definition(attr: TokenStream, item: TokenStream) -> TokenStream { /// contract. /// /// By default, a signature topic will be generated for the event. This allows consumers -/// to filter and identify events of this type. Marking an event with `#[ink(anonymous)]` +/// to filter and identify events of this type. Marking an event with `anonymous = true` /// means no signature topic will be generated or emitted. /// /// # Examples From 1371e3556226672edd59d14f41f93a9dc55d3534 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Mon, 24 Jul 2023 14:24:42 +0100 Subject: [PATCH 139/140] Move haashing macro inline signature topic gen fn --- crates/ink/macro/src/event/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/ink/macro/src/event/mod.rs b/crates/ink/macro/src/event/mod.rs index 185f7e07b6..54aa625868 100644 --- a/crates/ink/macro/src/event/mod.rs +++ b/crates/ink/macro/src/event/mod.rs @@ -98,8 +98,8 @@ fn event_derive_struct(mut s: synstructure::Structure) -> syn::Result ::core::option::Option::Some(::ink::blake2x256!(#topic_str))) + let signature_topic = signature_topic(variant.ast().fields, event_ident); + quote_spanned!(span=> ::core::option::Option::Some(#signature_topic)) } else { quote_spanned!(span=> ::core::option::Option::None) }; @@ -164,7 +164,7 @@ fn signature_topic(fields: &syn::Fields, event_ident: &syn::Ident) -> syn::LitSt .collect::>() .join(","); let topic_str = format!("{}({fields})", event_ident); - syn::parse_quote!( #topic_str ) + syn::parse_quote!(::ink::blake2x256!(#topic_str)) } /// Checks if the given field's attributes contain an `#[ink(topic)]` attribute. From 9fb00fceb3440a6e05b9d2d15f0dc879c0e7b994 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Mon, 24 Jul 2023 14:30:53 +0100 Subject: [PATCH 140/140] Fix signature_topic return type --- crates/ink/macro/src/event/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/ink/macro/src/event/mod.rs b/crates/ink/macro/src/event/mod.rs index 54aa625868..b358212440 100644 --- a/crates/ink/macro/src/event/mod.rs +++ b/crates/ink/macro/src/event/mod.rs @@ -153,7 +153,7 @@ fn event_derive_struct(mut s: synstructure::Structure) -> syn::Result syn::LitStr { +fn signature_topic(fields: &syn::Fields, event_ident: &syn::Ident) -> TokenStream2 { let fields = fields .iter() .map(|field| { @@ -164,7 +164,7 @@ fn signature_topic(fields: &syn::Fields, event_ident: &syn::Ident) -> syn::LitSt .collect::>() .join(","); let topic_str = format!("{}({fields})", event_ident); - syn::parse_quote!(::ink::blake2x256!(#topic_str)) + quote!(::ink::blake2x256!(#topic_str)) } /// Checks if the given field's attributes contain an `#[ink(topic)]` attribute.