diff --git a/crates/lang/codegen/src/enforced_error.rs b/crates/lang/codegen/src/enforced_error.rs deleted file mode 100644 index dc116b21596..00000000000 --- a/crates/lang/codegen/src/enforced_error.rs +++ /dev/null @@ -1,68 +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 impl_serde::serialize as serde_hex; -use quote::format_ident; - -/// Errors which may occur when forwarding a call is not allowed. -/// -/// We insert markers for these errors in the generated contract code. -/// This is necessary since we can't check these errors at compile time -/// of the contract. -/// `cargo-contract` checks the contract code for these error markers -/// when building a contract and fails if it finds markers. -#[derive(scale::Encode, scale::Decode)] -pub enum EnforcedErrors { - /// The below error represents calling a `&mut self` message in a context that - /// only allows for `&self` messages. This may happen under certain circumstances - /// when ink! trait implementations are involved with long-hand calling notation. - #[codec(index = 1)] - CannotCallTraitMessage { - /// The trait that defines the called message. - trait_ident: String, - /// The name of the called message. - message_ident: String, - /// The selector of the called message. - message_selector: [u8; 4], - /// Is `true` if the `self` receiver of the ink! message is `&mut self`. - message_is_mut: bool, - }, -} - -impl EnforcedErrors { - /// Create the identifier of an enforced ink! compilation error. - fn into_ident(self) -> syn::Ident { - format_ident!( - "__ink_enforce_error_{}", - serde_hex::to_hex(&scale::Encode::encode(&self), false) - ) - } - - /// Creates an enforced linker error to signal that an invalid - /// implementation of an ink! trait message has been called. - pub fn cannot_call_trait_message( - trait_ident: &syn::Ident, - message_ident: &syn::Ident, - message_selector: ir::Selector, - message_is_mut: bool, - ) -> syn::Ident { - Self::CannotCallTraitMessage { - trait_ident: trait_ident.to_string(), - message_ident: message_ident.to_string(), - message_selector: message_selector.to_bytes(), - message_is_mut, - } - .into_ident() - } -} diff --git a/crates/lang/codegen/src/generator/arg_list.rs b/crates/lang/codegen/src/generator/arg_list.rs index bcc771a46cb..a97db6e1e5d 100644 --- a/crates/lang/codegen/src/generator/arg_list.rs +++ b/crates/lang/codegen/src/generator/arg_list.rs @@ -13,10 +13,7 @@ // limitations under the License. use heck::ToLowerCamelCase as _; -use proc_macro2::{ - Span, - TokenStream as TokenStream2, -}; +use proc_macro2::TokenStream as TokenStream2; use quote::{ format_ident, quote, @@ -86,19 +83,3 @@ where } ) } - -/// Generates code to uniquely identify a trait by its unique ID given only its identifier. -/// -/// # Note -/// -/// As with all Rust macros identifiers can shadow each other so the given identifier -/// needs to be valid for the scope in which the returned code is generated. -pub fn generate_reference_to_trait_info( - span: Span, - trait_path: &syn::Path, -) -> TokenStream2 { - quote_spanned!(span=> - <::ink_lang::reflect::TraitDefinitionRegistry - as #trait_path>::__ink_TraitInfo - ) -} diff --git a/crates/lang/codegen/src/generator/as_dependency/call_builder.rs b/crates/lang/codegen/src/generator/as_dependency/call_builder.rs index 7860f510d94..053eec232e7 100644 --- a/crates/lang/codegen/src/generator/as_dependency/call_builder.rs +++ b/crates/lang/codegen/src/generator/as_dependency/call_builder.rs @@ -46,13 +46,11 @@ impl GenerateCode for CallBuilder<'_> { fn generate_code(&self) -> TokenStream2 { let call_builder_struct = self.generate_struct(); let auxiliary_trait_impls = self.generate_auxiliary_trait_impls(); - // let call_builder_impls = self.generate_call_forwarder_impls(); let call_builder_inherent_impls = self.generate_call_builder_inherent_impls(); quote! { const _: () = { #call_builder_struct #auxiliary_trait_impls - // #call_builder_impls #call_builder_inherent_impls }; } @@ -110,7 +108,7 @@ impl CallBuilder<'_> { type Env = <#storage_ident as ::ink_lang::reflect::ContractEnv>::Env; } - impl ::ink_lang::reflect::InkStruct for #cb_ident {} + impl ::ink_lang::reflect::CallBuilder for #cb_ident {} }; ) } @@ -139,170 +137,6 @@ impl CallBuilder<'_> { ) } - /// Generate the `TraitCallForwarder` trait implementations for the call builder - /// for every ink! trait implemented by the associated ink! smart contract. - /// - /// These call forwarder trait implementations are used to dispatch to the global - /// call builder for the respective ink! trait definition that is being called. - /// The call builder only forwards the actual calls to those global call builders - /// and does not have its own calling logic. - fn generate_call_forwarder_impls(&self) -> TokenStream2 { - self.contract - .module() - .impls() - .filter_map(|impl_block| { - // We are only interested in ink! trait implementation block. - impl_block.trait_path().map(|trait_path| { - self.generate_code_for_trait_impl(trait_path, impl_block) - }) - }) - .collect() - } - - /// Generates code required by the ink! call builder of an ink! smart contract - /// for a single ink! trait definition that the contract implements. - fn generate_code_for_trait_impl( - &self, - trait_path: &syn::Path, - impl_block: &ir::ItemImpl, - ) -> TokenStream2 { - let call_forwarder_impl = - self.generate_call_forwarder_for_trait_impl(trait_path, impl_block); - let ink_trait_impl = self.generate_ink_trait_impl(trait_path, impl_block); - quote! { - #call_forwarder_impl - #ink_trait_impl - } - } - - /// Generates code for a single ink! trait implementation to forward calls for - /// the associated ink! smart contract call builder. - fn generate_call_forwarder_for_trait_impl( - &self, - trait_path: &syn::Path, - impl_block: &ir::ItemImpl, - ) -> TokenStream2 { - let span = impl_block.span(); - let cb_ident = Self::call_builder_ident(); - let trait_info = generator::generate_reference_to_trait_info(span, trait_path); - quote_spanned!(span=> - #[doc(hidden)] - impl ::ink_lang::codegen::TraitCallForwarderFor<#trait_info> for #cb_ident { - type Forwarder = <::__ink_TraitInfo as ::ink_lang::codegen::TraitCallForwarder>::Forwarder; - - #[inline] - fn forward(&self) -> &Self::Forwarder { - // SAFETY: - // - // We convert from a shared reference to a type that thinly wraps - // only an `AccountId` to a shared reference to another type of which - // we know that it also thinly wraps an `AccountId`. - // Furthermore both types use `repr(transparent)`. - unsafe { - &*(&self.account_id as *const AccountId as *const Self::Forwarder) - } - } - - #[inline] - fn forward_mut(&mut self) -> &mut Self::Forwarder { - // SAFETY: - // - // We convert from a exclusive reference to a type that thinly wraps - // only an `AccountId` to a exclusive reference to another type of which - // we know that it also thinly wraps an `AccountId`. - // Furthermore both types use `repr(transparent)`. - unsafe { - &mut *(&mut self.account_id as *mut AccountId as *mut Self::Forwarder) - } - } - - #[inline] - fn build(&self) -> &::Builder { - <_ as ::ink_lang::codegen::TraitCallBuilder>::call( - >::forward(self) - ) - } - - #[inline] - fn build_mut(&mut self) - -> &mut ::Builder - { - <_ as ::ink_lang::codegen::TraitCallBuilder>::call_mut( - >::forward_mut(self) - ) - } - } - ) - } - - /// Generates the actual ink! trait implementation for the generated call builder. - fn generate_ink_trait_impl( - &self, - trait_path: &syn::Path, - impl_block: &ir::ItemImpl, - ) -> TokenStream2 { - let span = impl_block.span(); - let cb_ident = Self::call_builder_ident(); - let messages = impl_block - .iter_messages() - .map(|message| self.generate_ink_trait_impl_for_message(trait_path, message)); - quote_spanned!(span=> - impl #trait_path for #cb_ident { - type __ink_TraitInfo = <::ink_lang::reflect::TraitDefinitionRegistry - as #trait_path>::__ink_TraitInfo; - - #( #messages )* - } - ) - } - - /// Generates the code for the ink! trait implementation of the call builder - /// of a single ink! trait message and its associated output type. - fn generate_ink_trait_impl_for_message( - &self, - trait_path: &syn::Path, - message: ir::CallableWithSelector, - ) -> TokenStream2 { - use ir::Callable as _; - let span = message.span(); - let message_ident = message.ident(); - let output_ident = generator::output_ident(message_ident); - let trait_info = generator::generate_reference_to_trait_info(span, trait_path); - let (input_bindings, input_types): (Vec<_>, Vec<_>) = message - .callable() - .inputs() - .map(|input| (&input.pat, &input.ty)) - .unzip(); - let mut_token = message - .receiver() - .is_ref_mut() - .then(|| Some(quote! { mut })); - let build_cmd = match message.receiver() { - ir::Receiver::Ref => quote! { build }, - ir::Receiver::RefMut => quote! { build_mut }, - }; - let attrs = message.attrs(); - quote_spanned!(span=> - type #output_ident = <<< - Self - as ::ink_lang::codegen::TraitCallForwarderFor<#trait_info>>::Forwarder - as ::ink_lang::codegen::TraitCallBuilder>::Builder - as #trait_path>::#output_ident; - - #[inline] - #( #attrs )* - fn #message_ident( - & #mut_token self - #( , #input_bindings: #input_types )* - ) -> Self::#output_ident { - <_ as #trait_path>::#message_ident( - >::#build_cmd(self) - #( , #input_bindings )* - ) - } - ) - } - /// Generate call builder code for all ink! inherent ink! implementation blocks. /// /// # Note diff --git a/crates/lang/codegen/src/generator/as_dependency/contract_ref.rs b/crates/lang/codegen/src/generator/as_dependency/contract_ref.rs index e25c3d35cbe..379ec09390e 100644 --- a/crates/lang/codegen/src/generator/as_dependency/contract_ref.rs +++ b/crates/lang/codegen/src/generator/as_dependency/contract_ref.rs @@ -47,13 +47,11 @@ pub struct ContractRef<'a> { impl GenerateCode for ContractRef<'_> { fn generate_code(&self) -> TokenStream2 { let contract_ref = self.generate_struct(); - // let contract_ref_trait_impls = self.generate_contract_trait_impls(); let contract_ref_inherent_impls = self.generate_contract_inherent_impls(); let call_builder_trait_impl = self.generate_call_builder_trait_impl(); let auxiliary_trait_impls = self.generate_auxiliary_trait_impls(); quote! { #contract_ref - // #contract_ref_trait_impls #contract_ref_inherent_impls #call_builder_trait_impl #auxiliary_trait_impls @@ -115,7 +113,7 @@ impl ContractRef<'_> { type Env = <#storage_ident as ::ink_lang::reflect::ContractEnv>::Env; } - impl ::ink_lang::reflect::InkStruct for #ref_ident {} + impl ::ink_lang::reflect::CallBuilder for #ref_ident{} }; ) } @@ -176,107 +174,6 @@ impl ContractRef<'_> { ) } - /// Generates the code for all ink! trait implementations of the contract itself. - /// - /// # Note - /// - /// The generated implementations must live outside of an artificial `const` block - /// in order to properly show their documentation using `rustdoc`. - fn generate_contract_trait_impls(&self) -> TokenStream2 { - self.contract - .module() - .impls() - .filter_map(|impl_block| { - // We are only interested in ink! trait implementation block. - impl_block.trait_path().map(|trait_path| { - self.generate_contract_trait_impl(trait_path, impl_block) - }) - }) - .collect() - } - - /// Generates the code for a single ink! trait implementation of the contract itself. - /// - /// The generated implementation mainly forwards the calls to the previously generated - /// associated call builder that implements each respective ink! trait. - fn generate_contract_trait_impl( - &self, - trait_path: &syn::Path, - impl_block: &ir::ItemImpl, - ) -> TokenStream2 { - let span = impl_block.span(); - let attrs = impl_block.attrs(); - let forwarder_ident = self.generate_contract_ref_ident(); - let messages = self.generate_contract_trait_impl_messages(trait_path, impl_block); - quote_spanned!(span=> - #( #attrs )* - impl #trait_path for #forwarder_ident { - #[doc(hidden)] - type __ink_TraitInfo = <::ink_lang::reflect::TraitDefinitionRegistry - as #trait_path>::__ink_TraitInfo; - - #messages - } - ) - } - - /// Generates the code for all messages of a single ink! trait implementation of - /// the ink! smart contract. - fn generate_contract_trait_impl_messages( - &self, - trait_path: &syn::Path, - impl_block: &ir::ItemImpl, - ) -> TokenStream2 { - impl_block - .iter_messages() - .map(|message| { - self.generate_contract_trait_impl_for_message(trait_path, message) - }) - .collect() - } - - /// Generates the code for a single message of a single ink! trait implementation - /// that is implemented by the ink! smart contract. - fn generate_contract_trait_impl_for_message( - &self, - trait_path: &syn::Path, - message: ir::CallableWithSelector, - ) -> TokenStream2 { - use ir::Callable as _; - let span = message.span(); - let trait_info = generator::generate_reference_to_trait_info(span, trait_path); - let message_ident = message.ident(); - let output_ident = generator::output_ident(message_ident); - let call_operator = match message.receiver() { - ir::Receiver::Ref => quote! { call }, - ir::Receiver::RefMut => quote! { call_mut }, - }; - let forward_operator = match message.receiver() { - ir::Receiver::Ref => quote! { forward }, - ir::Receiver::RefMut => quote! { forward_mut }, - }; - let mut_token = message.receiver().is_ref_mut().then(|| quote! { mut }); - let input_bindings = message.inputs().map(|input| &input.pat).collect::>(); - let input_types = message.inputs().map(|input| &input.ty).collect::>(); - quote_spanned!(span=> - type #output_ident = - <::Forwarder as #trait_path>::#output_ident; - - #[inline] - fn #message_ident( - & #mut_token self - #( , #input_bindings : #input_types )* - ) -> Self::#output_ident { - <_ as #trait_path>::#message_ident( - <_ as ::ink_lang::codegen::TraitCallForwarderFor<#trait_info>>::#forward_operator( - ::#call_operator(self), - ) - #( , #input_bindings )* - ) - } - ) - } - /// Generates the code for all ink! inherent implementations of the contract itself. /// /// # Note diff --git a/crates/lang/codegen/src/generator/mod.rs b/crates/lang/codegen/src/generator/mod.rs index def2f2d3c83..bc94d44e2f8 100644 --- a/crates/lang/codegen/src/generator/mod.rs +++ b/crates/lang/codegen/src/generator/mod.rs @@ -44,7 +44,6 @@ mod trait_def; pub use self::{ arg_list::{ generate_argument_list, - generate_reference_to_trait_info, input_bindings, input_bindings_tuple, input_types, diff --git a/crates/lang/codegen/src/generator/trait_def/call_forwarder.rs b/crates/lang/codegen/src/generator/trait_def/call_forwarder.rs index 1ebda080771..1911d6d8182 100644 --- a/crates/lang/codegen/src/generator/trait_def/call_forwarder.rs +++ b/crates/lang/codegen/src/generator/trait_def/call_forwarder.rs @@ -13,10 +13,7 @@ // limitations under the License. use super::TraitDefinition; -use crate::{ - generator, - traits::GenerateCode, -}; +use crate::traits::GenerateCode; use derive_more::From; use proc_macro2::{ Span, @@ -338,10 +335,7 @@ impl CallForwarder<'_> { /// of the same ink! trait definition. fn generate_ink_trait_impl(&self) -> TokenStream2 { let span = self.trait_def.span(); - let trait_ident = self.trait_def.trait_def.item().ident(); - let trait_info_ident = self.trait_def.trait_info_ident(); let forwarder_ident = self.ident(); - let message_impls = self.generate_ink_trait_impl_messages(); quote_spanned!(span=> impl ::ink_lang::reflect::ContractEnv for #forwarder_ident where @@ -350,79 +344,10 @@ impl CallForwarder<'_> { type Env = E; } - impl #trait_ident for #forwarder_ident + impl ::ink_lang::reflect::CallBuilder for #forwarder_ident where E: ::ink_env::Environment, - { - #[doc(hidden)] - #[allow(non_camel_case_types)] - type __ink_TraitInfo = #trait_info_ident; - - #message_impls - } - ) - } - - /// Generate the code for all ink! trait messages implemented by the trait call forwarder. - fn generate_ink_trait_impl_messages(&self) -> TokenStream2 { - let messages = - self.trait_def - .trait_def - .item() - .iter_items() - .filter_map(|(item, _)| { - item.filter_map_message() - .map(|message| self.generate_ink_trait_impl_for_message(&message)) - }); - quote! { - #( #messages )* - } - } - - /// Generate the code for a single ink! trait message implemented by the trait call forwarder. - fn generate_ink_trait_impl_for_message( - &self, - message: &ir::InkTraitMessage, - ) -> TokenStream2 { - let span = message.span(); - let trait_ident = self.trait_def.trait_def.item().ident(); - let forwarder_ident = self.ident(); - let message_ident = message.ident(); - let attrs = message.attrs(); - let output_ident = generator::output_ident(message_ident); - let output_type = message - .output() - .cloned() - .unwrap_or_else(|| syn::parse_quote!(())); - let input_bindings = message.inputs().map(|input| &input.pat).collect::>(); - let input_types = message.inputs().map(|input| &input.ty).collect::>(); - let call_op = match message.receiver() { - ir::Receiver::Ref => quote! { call }, - ir::Receiver::RefMut => quote! { call_mut }, - }; - let mut_tok = message.mutates().then(|| quote! { mut }); - let panic_str = format!( - "encountered error while calling <{} as {}>::{}", - forwarder_ident, trait_ident, message_ident, - ); - quote_spanned!(span => - type #output_ident = #output_type; - - #( #attrs )* - #[inline] - fn #message_ident( - & #mut_tok self - #( , #input_bindings : #input_types )* - ) -> Self::#output_ident { - <::Builder as #trait_ident>::#message_ident( - ::#call_op(self) - #( - , #input_bindings - )* - ) - .fire() - .unwrap_or_else(|err| ::core::panic!("{}: {:?}", #panic_str, err)) - } + {} ) } } diff --git a/crates/lang/codegen/src/generator/trait_def/trait_registry.rs b/crates/lang/codegen/src/generator/trait_def/trait_registry.rs index dafc934ce1f..88141f369e5 100644 --- a/crates/lang/codegen/src/generator/trait_def/trait_registry.rs +++ b/crates/lang/codegen/src/generator/trait_def/trait_registry.rs @@ -21,9 +21,8 @@ use super::TraitDefinition; use crate::{ - generator::{self,}, + generator, traits::GenerateCode, - EnforcedErrors, }; use derive_more::From; use proc_macro2::{ @@ -31,14 +30,10 @@ use proc_macro2::{ TokenStream as TokenStream2, }; use quote::{ - format_ident, quote, quote_spanned, }; -use syn::{ - parse_quote, - spanned::Spanned, -}; +use syn::spanned::Spanned; impl<'a> TraitDefinition<'a> { /// Generates the code for the global trait registry implementation. @@ -98,142 +93,31 @@ impl TraitRegistry<'_> { /// associated type to refer back to the actual call forwarder and call builder types. fn generate_registry_impl(&self) -> TokenStream2 { let span = self.span(); - let name = self.trait_ident(); + let trait_ident = self.trait_ident(); let trait_info_ident = self.trait_def.trait_info_ident(); - let messages = self.generate_registry_messages(); + let message_as_dependency_impls = self.generate_ink_trait_impl_messages(); quote_spanned!(span=> - impl #name for ::ink_lang::reflect::TraitDefinitionRegistry + /// The blanket implementation of the forwarder to do cross-contract + /// calls without customization. It only fires the call via SEAL host function + /// and checks that dispatching result is not error. + /// + /// # Note + /// + /// That implementation is used by builder generated in the body of the contract + /// and by reference to contract(aka `ContractRef` in case of `Contract`). + impl #trait_ident for T where - E: ::ink_env::Environment, + T: ::ink_lang::reflect::CallBuilder, { - /// Holds general and global information about the trait. #[doc(hidden)] #[allow(non_camel_case_types)] - type __ink_TraitInfo = #trait_info_ident; - - #messages - } - ) - } - - /// Generate the code for all ink! trait messages implemented by the trait registry. - fn generate_registry_messages(&self) -> TokenStream2 { - let messages = self.trait_def.trait_def.item().iter_items().filter_map( - |(item, selector)| { - item.filter_map_message() - .map(|message| self.generate_registry_for_message(&message, selector)) - }, - ); - quote! { - #( #messages )* - } - } - - /// Generates code to assert that ink! input and output types meet certain properties. - fn generate_inout_guards_for_message(message: &ir::InkTraitMessage) -> TokenStream2 { - let message_span = message.span(); - let message_inputs = message.inputs().map(|input| { - let input_span = input.span(); - let input_type = &*input.ty; - quote_spanned!(input_span=> - let _: () = ::ink_lang::codegen::utils::consume_type::< - ::ink_lang::codegen::DispatchInput<#input_type> - >(); - ) - }); - let message_output = message.output().map(|output_type| { - let output_span = output_type.span(); - quote_spanned!(output_span=> - let _: () = ::ink_lang::codegen::utils::consume_type::< - ::ink_lang::codegen::DispatchOutput<#output_type> - >(); - ) - }); - quote_spanned!(message_span=> - #( #message_inputs )* - #message_output - ) - } - - /// Generate the code for a single ink! trait message implemented by the trait registry. - /// - /// Generally the implementation of any ink! trait of the ink! trait registry - fn generate_registry_for_message( - &self, - message: &ir::InkTraitMessage, - selector: ir::Selector, - ) -> TokenStream2 { - let span = message.span(); - let ident = message.ident(); - let attrs = message.attrs(); - let output_ident = generator::output_ident(message.ident()); - let output_type = message - .output() - .cloned() - .unwrap_or_else(|| parse_quote! { () }); - let mut_token = message.receiver().is_ref_mut().then(|| quote! { mut }); - let (input_bindings, input_types) = - Self::input_bindings_and_types(message.inputs()); - let linker_error_ident = EnforcedErrors::cannot_call_trait_message( - self.trait_ident(), - message.ident(), - selector, - message.mutates(), - ); - let inout_guards = Self::generate_inout_guards_for_message(message); - let impl_body = match option_env!("INK_COVERAGE_REPORTING") { - Some("true") => { - quote! { - // The code coverage reporting CI stage links dead code, - // hence we have to provide an `unreachable!` here. If - // the invalid implementation above is linked this results - // in a linker error. - ::core::unreachable!( - "this is an invalid ink! message call which should never be possible." - ); - } - } - _ => { - quote! { - /// We enforce linking errors in case this is ever actually called. - /// These linker errors are properly resolved by the cargo-contract tool. - extern { - fn #linker_error_ident() -> !; - } - unsafe { #linker_error_ident() } - } - } - }; - quote_spanned!(span=> - type #output_ident = #output_type; + type __ink_TraitInfo = #trait_info_ident<::Env>; - #( #attrs )* - #[cold] - #[doc(hidden)] - fn #ident( - & #mut_token self - #( , #input_bindings : #input_types )* - ) -> Self::#output_ident { - #inout_guards - #impl_body + #message_as_dependency_impls } ) } - /// Returns a pair of input bindings `__ink_bindings_N` and types. - fn input_bindings_and_types( - inputs: ir::InputsIter, - ) -> (Vec, Vec<&syn::Type>) { - inputs - .enumerate() - .map(|(n, pat_type)| { - let binding = format_ident!("__ink_binding_{}", n); - let ty = &*pat_type.ty; - (binding, ty) - }) - .unzip() - } - /// Phantom type that implements the following traits for every ink! trait: /// /// - `ink_lang::TraitCallForwarder` @@ -308,4 +192,98 @@ impl TraitRegistry<'_> { } ) } + + /// Generate the code for all ink! trait messages implemented by the trait. + fn generate_ink_trait_impl_messages(&self) -> TokenStream2 { + let messages = + self.trait_def + .trait_def + .item() + .iter_items() + .filter_map(|(item, _)| { + item.filter_map_message() + .map(|message| self.generate_ink_trait_impl_for_message(&message)) + }); + quote! { + #( #messages )* + } + } + + /// Generate the code for a single ink! trait message implemented by the trait. + fn generate_ink_trait_impl_for_message( + &self, + message: &ir::InkTraitMessage, + ) -> TokenStream2 { + let span = message.span(); + let trait_ident = self.trait_ident(); + let forwarder_ident = self.trait_def.call_forwarder_ident(); + let message_ident = message.ident(); + let attrs = message.attrs(); + let output_ident = generator::output_ident(message_ident); + let output_type = message + .output() + .cloned() + .unwrap_or_else(|| syn::parse_quote!(())); + + let input_bindings = message.inputs().map(|input| &input.pat).collect::>(); + let input_types = message.inputs().map(|input| &input.ty).collect::>(); + + let inout_guards = Self::generate_inout_guards_for_message(message); + + let mut_tok = message.mutates().then(|| quote! { mut }); + let panic_str = format!( + "encountered error while calling <{} as {}>::{}", + forwarder_ident, trait_ident, message_ident, + ); + let builder_ident = self.trait_def.call_builder_ident(); + let env_type = quote! { ::Env }; + quote_spanned!(span => + type #output_ident = #output_type; + + #( #attrs )* + #[inline] + fn #message_ident( + & #mut_tok self + #( , #input_bindings : #input_types )* + ) -> Self::#output_ident { + #inout_guards + <#builder_ident<#env_type> as #trait_ident>::#message_ident( + & #mut_tok <#builder_ident<#env_type> as ::ink_env::call::FromAccountId<#env_type>>::from_account_id( + >::to_account_id(self) + ) + #( + , #input_bindings + )* + ) + .fire() + .unwrap_or_else(|err| ::core::panic!("{}: {:?}", #panic_str, err)) + } + ) + } + + /// Generates code to assert that ink! input and output types meet certain properties. + fn generate_inout_guards_for_message(message: &ir::InkTraitMessage) -> TokenStream2 { + let message_span = message.span(); + let message_inputs = message.inputs().map(|input| { + let input_span = input.span(); + let input_type = &*input.ty; + quote_spanned!(input_span=> + let _: () = ::ink_lang::codegen::utils::consume_type::< + ::ink_lang::codegen::DispatchInput<#input_type> + >(); + ) + }); + let message_output = message.output().map(|output_type| { + let output_span = output_type.span(); + quote_spanned!(output_span=> + let _: () = ::ink_lang::codegen::utils::consume_type::< + ::ink_lang::codegen::DispatchOutput<#output_type> + >(); + ) + }); + quote_spanned!(message_span=> + #( #message_inputs )* + #message_output + ) + } } diff --git a/crates/lang/codegen/src/lib.rs b/crates/lang/codegen/src/lib.rs index 27b82bec914..07dee6674d7 100644 --- a/crates/lang/codegen/src/lib.rs +++ b/crates/lang/codegen/src/lib.rs @@ -12,16 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -mod enforced_error; mod generator; mod traits; -use self::{ - enforced_error::EnforcedErrors, - traits::{ - GenerateCode, - GenerateCodeUsing, - }, +use self::traits::{ + GenerateCode, + GenerateCodeUsing, }; use proc_macro2::TokenStream as TokenStream2; diff --git a/crates/lang/src/codegen/mod.rs b/crates/lang/src/codegen/mod.rs index 59db390da57..6f4d145ab28 100644 --- a/crates/lang/src/codegen/mod.rs +++ b/crates/lang/src/codegen/mod.rs @@ -50,7 +50,6 @@ pub use self::{ trait_def::{ TraitCallBuilder, TraitCallForwarder, - TraitCallForwarderFor, TraitMessagePayable, TraitMessageSelector, }, diff --git a/crates/lang/src/codegen/trait_def/call_builder.rs b/crates/lang/src/codegen/trait_def/call_builder.rs index c1622b3c200..868bf995622 100644 --- a/crates/lang/src/codegen/trait_def/call_builder.rs +++ b/crates/lang/src/codegen/trait_def/call_builder.rs @@ -38,44 +38,3 @@ pub trait TraitCallForwarder { /// The call forwarder type. type Forwarder: TraitCallBuilder; } - -/// Implemented by call builders of smart contracts. -/// -/// These might be implementing multiple different ink! traits. -/// The codegen makes them implement this trait once for every -/// ink! trait they have to implement. -/// -/// While the trait is not necessary it encapsulates a lot of -/// utility and auxiliary code required for the actual ink! trait -/// implementations. -pub trait TraitCallForwarderFor { - type Forwarder: TraitCallBuilder; - - /// Forwards the `&self` call. - /// - /// # Note - /// - /// This is used for the short-hand calling syntax. - fn forward(&self) -> &Self::Forwarder; - - /// Forwards the `&mut self` call. - /// - /// # Note - /// - /// This is used for the short-hand calling syntax. - fn forward_mut(&mut self) -> &mut Self::Forwarder; - - /// Builds up the `&self` call. - /// - /// # Note - /// - /// This is used for the long-hand calling syntax. - fn build(&self) -> &::Builder; - - /// Builds up the `&mut self` call. - /// - /// # Note - /// - /// This is used for the long-hand calling syntax. - fn build_mut(&mut self) -> &mut ::Builder; -} diff --git a/crates/lang/src/codegen/trait_def/mod.rs b/crates/lang/src/codegen/trait_def/mod.rs index 1383002d058..b3328349016 100644 --- a/crates/lang/src/codegen/trait_def/mod.rs +++ b/crates/lang/src/codegen/trait_def/mod.rs @@ -19,7 +19,6 @@ pub use self::{ call_builder::{ TraitCallBuilder, TraitCallForwarder, - TraitCallForwarderFor, }, trait_message::{ TraitMessagePayable, diff --git a/crates/lang/src/reflect/mod.rs b/crates/lang/src/reflect/mod.rs index 6dd9d44dcb8..802382eb12c 100644 --- a/crates/lang/src/reflect/mod.rs +++ b/crates/lang/src/reflect/mod.rs @@ -48,7 +48,7 @@ pub use self::{ }, event::ContractEventBase, trait_def::{ - InkStruct, + CallBuilder, TraitDefinitionRegistry, TraitMessageInfo, TraitModulePath, diff --git a/crates/lang/src/reflect/trait_def/info.rs b/crates/lang/src/reflect/trait_def/info.rs index e2e4515593b..89fd7f0b768 100644 --- a/crates/lang/src/reflect/trait_def/info.rs +++ b/crates/lang/src/reflect/trait_def/info.rs @@ -12,6 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. +use crate::{ + reflect::ContractEnv, + ToAccountId, +}; + /// Stores information for every ink! trait message of an ink! trait definition. /// /// This information includes if the ink! trait message is payable @@ -153,5 +158,4 @@ pub trait TraitModulePath { const NAME: &'static str; } -/// Used to identify ink! structs. -pub trait InkStruct {} +pub trait CallBuilder: ContractEnv + ToAccountId<::Env> {} diff --git a/crates/lang/src/reflect/trait_def/mod.rs b/crates/lang/src/reflect/trait_def/mod.rs index f5bbfec2980..33cfd1967be 100644 --- a/crates/lang/src/reflect/trait_def/mod.rs +++ b/crates/lang/src/reflect/trait_def/mod.rs @@ -17,7 +17,7 @@ mod registry; pub use self::{ info::{ - InkStruct, + CallBuilder, TraitMessageInfo, TraitModulePath, }, diff --git a/crates/lang/src/reflect/trait_def/registry.rs b/crates/lang/src/reflect/trait_def/registry.rs index eba84d2d88d..5a5de64e1c4 100644 --- a/crates/lang/src/reflect/trait_def/registry.rs +++ b/crates/lang/src/reflect/trait_def/registry.rs @@ -12,9 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -use super::InkStruct; use crate::{ - reflect::ContractEnv, + reflect::{ + CallBuilder, + ContractEnv, + }, ToAccountId, }; use core::marker::PhantomData; @@ -59,8 +61,6 @@ pub struct TraitDefinitionRegistry { marker: PhantomData E>, } -// Inheritance of three traits below allows us to use default implementation of traits define via our macro - impl ContractEnv for TraitDefinitionRegistry where E: ink_env::Environment, @@ -68,14 +68,25 @@ where type Env = E; } -impl InkStruct for TraitDefinitionRegistry where E: ink_env::Environment {} - impl ToAccountId for TraitDefinitionRegistry where E: ink_env::Environment, { - #[inline] fn to_account_id(&self) -> E::AccountId { - unimplemented!() + ::core::unreachable!( + "this is an invalid ink! message call which should never be possible." + ); } } + +/// Generates the global trait registry implementation for the ink! trait. +/// +/// This makes it possible to refer back to the global call forwarder and +/// call builder specific to this ink! trait from anywhere with just the Rust +/// trait identifier which allows for type safe access. +/// +/// # Note +/// +/// The actual implementation of the registration is done in the blanket implementation +/// during definition of the trait. +impl CallBuilder for TraitDefinitionRegistry where E: ink_env::Environment {} diff --git a/crates/lang/tests/ui/trait_def/fail/message_input_non_codec.stderr b/crates/lang/tests/ui/trait_def/fail/message_input_non_codec.stderr index 2b0fe21ef79..d0af8c3415c 100644 --- a/crates/lang/tests/ui/trait_def/fail/message_input_non_codec.stderr +++ b/crates/lang/tests/ui/trait_def/fail/message_input_non_codec.stderr @@ -11,31 +11,31 @@ note: required by a bound in `DispatchInput` | T: scale::Decode + 'static; | ^^^^^^^^^^^^^ required by this bound in `DispatchInput` -error[E0277]: the trait bound `NonCodec: WrapperTypeEncode` is not satisfied +error[E0599]: the method `fire` exists for struct `ink_env::call::CallBuilder<::Env, Set<<::Env as Environment>::AccountId>, Unset, Unset<<::Env as Environment>::Balance>, Set, ArgumentList>>>, Set<()>>`, but its trait bounds were not satisfied --> tests/ui/trait_def/fail/message_input_non_codec.rs:7:5 | 7 | / #[ink(message)] 8 | | fn message(&self, input: NonCodec); - | |_______________________________________^ the trait `WrapperTypeEncode` is not implemented for `NonCodec` + | |_______________________________________^ method cannot be called on `ink_env::call::CallBuilder<::Env, Set<<::Env as Environment>::AccountId>, Unset, Unset<<::Env as Environment>::Balance>, Set, ArgumentList>>>, Set<()>>` due to unsatisfied trait bounds | - = note: required because of the requirements on the impl of `Encode` for `NonCodec` -note: required by a bound in `ExecutionInput::>::push_arg` - --> $WORKSPACE/crates/env/src/call/execution_input.rs + ::: $WORKSPACE/crates/env/src/call/execution_input.rs | - | T: scale::Encode, - | ^^^^^^^^^^^^^ required by this bound in `ExecutionInput::>::push_arg` + | pub struct ArgumentList { + | ----------------------------------- doesn't satisfy `_: Encode` + | + = note: the following trait bounds were not satisfied: + `ArgumentList, ArgumentList>: Encode` -error[E0599]: the method `fire` exists for struct `CallBuilder::AccountId>, Unset, Unset<::Balance>, Set, ArgumentList>>>, Set<()>>`, but its trait bounds were not satisfied +error[E0277]: the trait bound `NonCodec: WrapperTypeEncode` is not satisfied --> tests/ui/trait_def/fail/message_input_non_codec.rs:7:5 | 7 | / #[ink(message)] 8 | | fn message(&self, input: NonCodec); - | |_______________________________________^ method cannot be called on `CallBuilder::AccountId>, Unset, Unset<::Balance>, Set, ArgumentList>>>, Set<()>>` due to unsatisfied trait bounds - | - ::: $WORKSPACE/crates/env/src/call/execution_input.rs + | |_______________________________________^ the trait `WrapperTypeEncode` is not implemented for `NonCodec` | - | pub struct ArgumentList { - | ----------------------------------- doesn't satisfy `_: Encode` + = note: required because of the requirements on the impl of `Encode` for `NonCodec` +note: required by a bound in `ExecutionInput::>::push_arg` + --> $WORKSPACE/crates/env/src/call/execution_input.rs | - = note: the following trait bounds were not satisfied: - `ArgumentList, ArgumentList>: Encode` + | T: scale::Encode, + | ^^^^^^^^^^^^^ required by this bound in `ExecutionInput::>::push_arg` diff --git a/crates/lang/tests/ui/trait_def/fail/message_output_non_codec.stderr b/crates/lang/tests/ui/trait_def/fail/message_output_non_codec.stderr index ffdb0343f25..48e65c8cf8e 100644 --- a/crates/lang/tests/ui/trait_def/fail/message_output_non_codec.stderr +++ b/crates/lang/tests/ui/trait_def/fail/message_output_non_codec.stderr @@ -6,12 +6,12 @@ error[E0277]: the trait bound `NonCodec: WrapperTypeEncode` is not satisfied | = note: required because of the requirements on the impl of `Encode` for `NonCodec` note: required by a bound in `DispatchOutput` - --> src/codegen/dispatch/type_check.rs:69:8 + --> src/codegen/dispatch/type_check.rs | -69 | T: scale::Encode + 'static; + | T: scale::Encode + 'static; | ^^^^^^^^^^^^^ required by this bound in `DispatchOutput` -error[E0599]: the method `fire` exists for struct `CallBuilder::AccountId>, Unset, Unset<::Balance>, Set>>, Set>>`, but its trait bounds were not satisfied +error[E0599]: the method `fire` exists for struct `ink_env::call::CallBuilder<::Env, Set<<::Env as Environment>::AccountId>, Unset, Unset<<::Env as Environment>::Balance>, Set>>, Set>>`, but its trait bounds were not satisfied --> tests/ui/trait_def/fail/message_output_non_codec.rs:7:5 | 3 | pub struct NonCodec; @@ -19,7 +19,7 @@ error[E0599]: the method `fire` exists for struct `CallBuilder NonCodec; - | |__________________________________^ method cannot be called on `CallBuilder::AccountId>, Unset, Unset<::Balance>, Set>>, Set>>` due to unsatisfied trait bounds + | |__________________________________^ method cannot be called on `ink_env::call::CallBuilder<::Env, Set<<::Env as Environment>::AccountId>, Unset, Unset<<::Env as Environment>::Balance>, Set>>, Set>>` due to unsatisfied trait bounds | = note: the following trait bounds were not satisfied: `NonCodec: parity_scale_codec::Decode` diff --git a/examples/recovery/.gitignore b/examples/recovery/.gitignore new file mode 100644 index 00000000000..bf910de10af --- /dev/null +++ b/examples/recovery/.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/examples/recovery/Cargo.toml b/examples/recovery/Cargo.toml new file mode 100644 index 00000000000..ca9291f8e5a --- /dev/null +++ b/examples/recovery/Cargo.toml @@ -0,0 +1,37 @@ +[package] +name = "recovery" +version = "3.0.0-rc8" +authors = ["Parity Technologies "] +edition = "2021" + +[dependencies] +ink_primitives = { version = "3.0.0-rc8", path = "../../crates/primitives", default-features = false } +ink_metadata = { version = "3.0.0-rc8", path = "../../crates/metadata", default-features = false, features = ["derive"], optional = true } +ink_env = { version = "3.0.0-rc8", path = "../../crates/env", default-features = false } +ink_storage = { version = "3.0.0-rc8", path = "../../crates/storage", default-features = false } +ink_lang = { version = "3.0.0-rc8", path = "../../crates/lang", default-features = false } +ink_eth_compatibility = { version = "3.0.0-rc8", path = "../../crates/eth_compatibility", default-features = false } + +scale = { package = "parity-scale-codec", version = "2", default-features = false, features = ["derive"] } +scale-info = { version = "1", default-features = false, features = ["derive"], optional = true } + +[lib] +name = "recovery" +path = "lib.rs" +crate-type = ["cdylib"] + +[features] +default = ["std"] +std = [ + "ink_primitives/std", + "ink_metadata", + "ink_metadata/std", + "ink_env/std", + "ink_storage/std", + "ink_lang/std", + "ink_eth_compatibility/std", + "scale/std", + "scale-info", + "scale-info/std", +] +ink-as-dependency = [] diff --git a/examples/recovery/lib.rs b/examples/recovery/lib.rs new file mode 100644 index 00000000000..e3202d775d0 --- /dev/null +++ b/examples/recovery/lib.rs @@ -0,0 +1,49 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +use ink_lang as ink; + +#[ink::contract] +pub mod recovery { + #[ink(storage)] + pub struct Recovery; + + impl Recovery { + #[ink(constructor)] + pub fn new() -> Self { + Self {} + } + + #[ink(message)] + pub fn ecdsa_recovery(&self) { + let signature: [u8; 65] = [ + 161, 234, 203, 74, 147, 96, 51, 212, 5, 174, 231, 9, 142, 48, 137, 201, + 162, 118, 192, 67, 239, 16, 71, 216, 125, 86, 167, 139, 70, 7, 86, 241, + 33, 87, 154, 251, 81, 29, 160, 4, 176, 239, 88, 211, 244, 232, 232, 52, + 211, 234, 100, 115, 230, 47, 80, 44, 152, 166, 62, 50, 8, 13, 86, 175, + 33, + ]; + let message_hash: [u8; 32] = [ + 162, 28, 244, 179, 96, 76, 244, 178, 188, 83, 230, 248, 143, 106, 77, + 117, 239, 95, 244, 171, 65, 95, 62, 153, 174, 166, 182, 28, 130, 73, 196, + 208, + ]; + const EXPECTED_COMPRESSED_PUBLIC_KEY: [u8; 33] = [ + 2, 121, 190, 102, 126, 249, 220, 187, 172, 85, 160, 98, 149, 206, 135, + 11, 7, 2, 155, 252, 219, 45, 206, 40, 217, 89, 242, 129, 91, 22, 248, 23, + 152, + ]; + let pub_key = self.env().ecdsa_recover(&signature, &message_hash); + ink_env::debug_print!("{:?}", pub_key); + assert!(pub_key.is_ok()); + let pub_key = pub_key.unwrap(); + assert_eq!(pub_key.as_ref(), &EXPECTED_COMPRESSED_PUBLIC_KEY); + + const EXPECTED_ETH_ADDRESS: [u8; 20] = [ + 126, 95, 69, 82, 9, 26, 105, 18, 93, 93, 252, 183, 184, 194, 101, 144, + 41, 57, 91, 223, + ]; + + assert_eq!(pub_key.to_eth_address().as_ref(), &EXPECTED_ETH_ADDRESS); + } + } +} diff --git a/examples/trait-incrementer/Cargo.toml b/examples/trait-incrementer/Cargo.toml index ad3de41f05a..5e4f8df2288 100644 --- a/examples/trait-incrementer/Cargo.toml +++ b/examples/trait-incrementer/Cargo.toml @@ -13,7 +13,7 @@ ink_lang = { version = "3.0.0-rc8", path = "../../crates/lang", default-features scale = { package = "parity-scale-codec", version = "2", default-features = false, features = ["derive"] } scale-info = { version = "1", default-features = false, features = ["derive"], optional = true } -increment-traits = { path = "./traits", default-features = false } +traits = { path = "./traits", default-features = false } [lib] name = "trait_incrementer" @@ -32,6 +32,6 @@ std = [ "scale/std", "scale-info", "scale-info/std", - "increment-traits/std", + "traits/std", ] ink-as-dependency = [] diff --git a/examples/trait-incrementer/lib.rs b/examples/trait-incrementer/lib.rs index a6646de2171..af21de87f83 100644 --- a/examples/trait-incrementer/lib.rs +++ b/examples/trait-incrementer/lib.rs @@ -5,7 +5,7 @@ use ink_lang as ink; #[ink::contract] pub mod incrementer { - use increment_traits::{ + use traits::{ Increment, Reset, }; diff --git a/examples/trait-incrementer/traits/Cargo.toml b/examples/trait-incrementer/traits/Cargo.toml index a17487ae563..a9474c2f87a 100644 --- a/examples/trait-incrementer/traits/Cargo.toml +++ b/examples/trait-incrementer/traits/Cargo.toml @@ -1,21 +1,21 @@ [package] -name = "increment-traits" -version = "3.0.0-rc6" +name = "traits" +version = "3.0.0-rc8" authors = ["Parity Technologies "] edition = "2021" [dependencies] -ink_primitives = { version = "3.0.0-rc6", path = "../../../crates/primitives", default-features = false } -ink_metadata = { version = "3.0.0-rc6", path = "../../../crates/metadata", default-features = false, features = ["derive"], optional = true } -ink_env = { version = "3.0.0-rc6", path = "../../../crates/env", default-features = false } -ink_storage = { version = "3.0.0-rc6", path = "../../../crates/storage", default-features = false } -ink_lang = { version = "3.0.0-rc6", path = "../../../crates/lang", default-features = false } +ink_primitives = { version = "3.0.0-rc8", path = "../../../crates/primitives", default-features = false } +ink_metadata = { version = "3.0.0-rc8", path = "../../../crates/metadata", default-features = false, features = ["derive"], optional = true } +ink_env = { version = "3.0.0-rc8", path = "../../../crates/env", default-features = false } +ink_storage = { version = "3.0.0-rc8", path = "../../../crates/storage", default-features = false } +ink_lang = { version = "3.0.0-rc8", path = "../../../crates/lang", default-features = false } scale = { package = "parity-scale-codec", version = "2", default-features = false, features = ["derive"] } scale-info = { version = "1", default-features = false, features = ["derive"], optional = true } [lib] -name = "increment_traits" +name = "traits" path = "lib.rs" crate-type = ["rlib"] diff --git a/examples/trait-incrementer/traits/lib.rs b/examples/trait-incrementer/traits/lib.rs index 8a1d58a3f18..2874e0b9134 100644 --- a/examples/trait-incrementer/traits/lib.rs +++ b/examples/trait-incrementer/traits/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2021 Parity Technologies (UK) Ltd. +// 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. @@ -13,8 +13,10 @@ // limitations under the License. #![cfg_attr(not(feature = "std"), no_std)] +use ink_lang as ink; + /// Allows to increment and get the current value. -#[ink_lang::trait_definition] +#[ink::trait_definition] pub trait Increment { /// Increments the current value of the implementer by one (1). #[ink(message)] @@ -26,7 +28,7 @@ pub trait Increment { } /// Allows to reset the current value. -#[ink_lang::trait_definition] +#[ink::trait_definition] pub trait Reset { /// Resets the current value to zero. #[ink(message)]