ink_ir/ir/item_impl/
callable.rs

1// Copyright (C) Use Ink (UK) Ltd.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Utilities and helper routines that are useful for both ink! messages
16//! and ink! constructors.
17
18use crate::ir;
19use core::fmt;
20use proc_macro2::{
21    Ident,
22    Span,
23};
24use quote::ToTokens as _;
25use syn::spanned::Spanned as _;
26
27/// The kind of externally callable smart contract entity.
28#[derive(Debug, Copy, Clone)]
29pub enum CallableKind {
30    /// An ink! message externally callable.
31    Message,
32    /// An ink! constructor externally callable.
33    Constructor,
34}
35
36impl fmt::Display for CallableKind {
37    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
38        match self {
39            Self::Message => write!(f, "message"),
40            Self::Constructor => write!(f, "constructor"),
41        }
42    }
43}
44
45/// Wrapper for a callable that adds its composed selector.
46#[derive(Debug)]
47pub struct CallableWithSelector<'a, C> {
48    /// The composed selector computed by the associated implementation block
49    /// and the given callable.
50    composed_selector: ir::Selector,
51    /// The parent implementation block.
52    item_impl: &'a ir::ItemImpl,
53    /// The actual callable.
54    callable: &'a C,
55}
56
57impl<C> Copy for CallableWithSelector<'_, C> {}
58impl<C> Clone for CallableWithSelector<'_, C> {
59    fn clone(&self) -> Self {
60        *self
61    }
62}
63
64impl<'a, C> CallableWithSelector<'a, C>
65where
66    C: Callable,
67{
68    /// Creates a new wrapper around the given callable and parent `impl` block.
69    pub(super) fn new(item_impl: &'a ir::ItemImpl, callable: &'a C) -> Self {
70        Self {
71            composed_selector: compose_selector(item_impl, callable),
72            item_impl,
73            callable,
74        }
75    }
76}
77
78impl<'a, C> CallableWithSelector<'a, C> {
79    /// Returns the composed selector of the ink! callable the `impl` block.
80    pub fn composed_selector(&self) -> ir::Selector {
81        self.composed_selector
82    }
83
84    /// Returns a shared reference to the underlying callable.
85    pub fn callable(&self) -> &'a C {
86        self.callable
87    }
88
89    /// Returns the parent implementation block of the ink! callable.
90    pub fn item_impl(&self) -> &'a ir::ItemImpl {
91        self.item_impl
92    }
93}
94
95impl<C> Callable for CallableWithSelector<'_, C>
96where
97    C: Callable,
98{
99    fn kind(&self) -> CallableKind {
100        <C as Callable>::kind(self.callable)
101    }
102
103    fn ident(&self) -> &Ident {
104        <C as Callable>::ident(self.callable)
105    }
106
107    fn user_provided_selector(&self) -> Option<&ir::Selector> {
108        <C as Callable>::user_provided_selector(self.callable)
109    }
110
111    fn is_payable(&self) -> bool {
112        <C as Callable>::is_payable(self.callable)
113    }
114
115    fn is_default(&self) -> bool {
116        <C as Callable>::is_default(self.callable)
117    }
118
119    fn has_wildcard_selector(&self) -> bool {
120        <C as Callable>::has_wildcard_selector(self.callable)
121    }
122
123    fn has_wildcard_complement_selector(&self) -> bool {
124        <C as Callable>::has_wildcard_complement_selector(self.callable)
125    }
126
127    fn visibility(&self) -> Visibility {
128        <C as Callable>::visibility(self.callable)
129    }
130
131    fn inputs(&self) -> InputsIter<'_> {
132        <C as Callable>::inputs(self.callable)
133    }
134
135    fn inputs_span(&self) -> Span {
136        <C as Callable>::inputs_span(self.callable)
137    }
138
139    fn statements(&self) -> &[syn::Stmt] {
140        <C as Callable>::statements(self.callable)
141    }
142
143    fn name(&self) -> Option<&str> {
144        <C as Callable>::name(self.callable)
145    }
146}
147
148impl<C> ::core::ops::Deref for CallableWithSelector<'_, C> {
149    type Target = C;
150
151    fn deref(&self) -> &Self::Target {
152        self.callable
153    }
154}
155
156/// An ink! callable.
157///
158/// This is either an ink! message or an ink! constructor.
159/// Used to share common behavior between different callable types.
160pub trait Callable {
161    /// Returns the kind of the ink! callable.
162    fn kind(&self) -> CallableKind;
163
164    /// Returns the identifier of the ink! callable.
165    fn ident(&self) -> &Ident;
166
167    /// Returns the selector of the ink! callable if any has been manually set.
168    fn user_provided_selector(&self) -> Option<&ir::Selector>;
169
170    /// Returns `true` if the ink! callable is flagged as payable.
171    ///
172    /// # Note
173    ///
174    /// Flagging as payable is done using the `#[ink(payable)]` attribute.
175    fn is_payable(&self) -> bool;
176
177    /// Returns `true` if the ink! callable is flagged as default.
178    ///
179    /// # Note
180    ///
181    /// Flagging as default is done using the `#[ink(default)]` attribute.
182    fn is_default(&self) -> bool;
183
184    /// Returns `true` if the ink! callable is flagged as a wildcard selector.
185    fn has_wildcard_selector(&self) -> bool;
186
187    /// Returns `true` if the ink! callable is flagged as a wildcard complement selector.
188    fn has_wildcard_complement_selector(&self) -> bool;
189
190    /// Returns the visibility of the ink! callable.
191    fn visibility(&self) -> Visibility;
192
193    /// Returns an iterator yielding all input parameters of the ink! callable.
194    fn inputs(&self) -> InputsIter<'_>;
195
196    /// Returns the span of the inputs of the ink! callable.
197    fn inputs_span(&self) -> Span;
198
199    /// Returns a slice over shared references to the statements of the callable.
200    fn statements(&self) -> &[syn::Stmt];
201
202    /// Returns the function name override (if any).
203    fn name(&self) -> Option<&str>;
204}
205
206/// Returns the composed selector of the ink! callable.
207///
208/// Composition takes into account the given [`ir::ItemImpl`].
209///
210/// # Details
211///
212/// Given
213///
214/// - the identifier `i` of the callable
215/// - the optionally set selector `s` of the callable
216/// - the `impl` blocks trait path in case it implements a trait, `P`
217/// - 16 kB blocks optional user provided namespace `S`
218///
219/// Then the selector is composed in the following way:
220///
221/// - If `s` is given we simply return `s`.
222/// - Otherwise if `T` is not `None` (trait `impl` block) we concatenate `S`, `T` and `i`
223///   with `::` as separator if `T` refers to a full-path. If `T` refers to a relative
224///   path or is just an identifier we only take its last segment `p` (e.g. the trait's
225///   identifier) into consideration and use it instead of `P` in the above concatenation.
226///   In the following we refer to the resulting concatenation as `C`.
227/// - Now we take the BLAKE-2 hash of `C` which results in 32 bytes of output and take the
228///   first 4 bytes that are returned in order as the composed selector.
229///
230/// # Examples
231///
232/// ## Overriding the composed selector
233///
234/// Given
235///
236/// ```no_compile
237/// impl MyStorage {
238///     #[ink(message, selector = 0xDEADBEEF)]
239///     fn my_message(&self) {}
240/// }
241/// ```
242///
243/// …then the selector of `my_message` is simply `0xDEADBEEF` since it overrides
244/// the composed selector.
245///
246/// ## Inherent implementation block
247///
248/// Given
249///
250/// ```no_compile
251/// impl MyStorage {
252///     #[ink(message)]
253///     fn my_message(&self) {}
254/// }
255/// ```
256///
257/// …then the selector of `my_message` is composed such as:
258/// ```no_compile
259/// BLAKE2("my_message".to_string().as_bytes())[0..4]
260/// ```
261///
262/// ## Trait implementation block
263///
264/// Given
265///
266/// ```no_compile
267/// impl MyTrait for MyStorage {
268///     #[ink(message)]
269///     fn my_message(&self) {}
270/// }
271/// ```
272///
273/// …then the selector of `my_message` is composed such as:
274/// ```no_compile
275/// BLAKE2("MyTrait::my_message".to_string().as_bytes())[0..4]
276/// ```
277///
278/// ## Using full path for trait
279///
280/// Given
281///
282/// ```no_compile
283/// impl ::my_full::long_path::MyTrait for MyStorage {
284///     #[ink(message)]
285///     fn my_message(&self) {}
286/// }
287/// ```
288///
289/// …then the selector of `my_message` is composed such as:
290/// ```no_compile
291/// BLAKE2("::my_full::long_path::MyTrait::my_message".to_string().as_bytes())[0..4]
292/// ```
293///
294/// ## Using a namespace
295///
296/// Given
297///
298/// ```no_compile
299/// #[ink(namespace = "my_namespace")]
300/// impl MyTrait for MyStorage {
301///     #[ink(message)]
302///     fn my_message(&self) {}
303/// }
304/// ```
305///
306/// …then the selector of `my_message` is composed such as:
307/// ```no_compile
308/// BLAKE2("my_namespace::MyTrait::my_message".to_string().as_bytes())[0..4]
309/// ```
310///
311/// ## Note
312///
313/// All above examples work similarly for ink! constructors interchangeably.
314///
315/// ## Usage Recommendations
316///
317/// These recommendation mainly apply to trait implementation blocks:
318///
319/// - The recommendation by the ink! team is to use the full-path approach wherever
320///   possible; OR import the trait and use only its identifier with an additional
321///   namespace if required to disambiguate selectors.
322/// - Try not to intermix the above recommendations.
323/// - Avoid directly setting the selector of an ink! message or constructor. Only do this
324///   if nothing else helps and you need a very specific selector, e.g. in case of
325///   backwards compatibility.
326/// - Do not use the namespace unless required to disambiguate.
327pub fn compose_selector<C>(item_impl: &ir::ItemImpl, callable: &C) -> ir::Selector
328where
329    C: Callable,
330{
331    if let Some(selector) = callable.user_provided_selector() {
332        return *selector
333    }
334    let preimage = compose_selector_preimage(item_impl, callable);
335    ir::Selector::compute(&preimage)
336}
337
338fn compose_selector_preimage<C>(item_impl: &ir::ItemImpl, callable: &C) -> Vec<u8>
339where
340    C: Callable,
341{
342    let callable_name = callable
343        .name()
344        .map(|name| name.as_bytes().to_vec())
345        .unwrap_or_else(|| callable.ident().to_string().into_bytes());
346    let namespace_bytes = item_impl
347        .namespace()
348        .map(|namespace| namespace.as_bytes().to_vec())
349        .unwrap_or_default();
350    let separator = &b"::"[..];
351    match item_impl.trait_path() {
352        None => {
353            // Inherent implementation block:
354            if namespace_bytes.is_empty() {
355                callable_name
356            } else {
357                [namespace_bytes, callable_name].join(separator)
358            }
359        }
360        Some(path) => {
361            // Trait implementation block:
362            //
363            // We need to separate between full-path, e.g. `::my::full::Path`
364            // starting with `::` and relative paths for the composition.
365            let path_bytes = if path.leading_colon.is_some() {
366                let mut str_repr = path.to_token_stream().to_string();
367                str_repr.retain(|c| !c.is_whitespace());
368                str_repr.into_bytes()
369            } else {
370                path.segments
371                    .last()
372                    .expect("encountered empty trait path")
373                    .ident
374                    .to_string()
375                    .into_bytes()
376            };
377            if namespace_bytes.is_empty() {
378                [path_bytes, callable_name].join(separator)
379            } else {
380                [namespace_bytes, path_bytes, callable_name].join(separator)
381            }
382        }
383    }
384}
385
386/// Ensures that common invariants of externally callable ink! entities are met.
387///
388/// # Errors
389///
390/// In case any of the common externally callable invariants are not met:
391/// - This is `true` if the externally callable is:
392///  - generic
393///  - `const` (compile-time evaluable)
394///  - `async` (asynchronous WebAssembly smart contract calling is not allowed)
395///  - `unsafe` (caller provided assertions not yet stable)
396/// - Furthermore this is `true` if the externally callable is defined for a non default
397///   ABI (e.g. `extern "C"`) or does not have valid visibility.
398pub(super) fn ensure_callable_invariants(
399    method_item: &syn::ImplItemFn,
400    kind: CallableKind,
401) -> Result<(), syn::Error> {
402    let bad_visibility = match &method_item.vis {
403        syn::Visibility::Inherited => None,
404        syn::Visibility::Restricted(vis_restricted) => Some(vis_restricted.span()),
405        syn::Visibility::Public(_) => None,
406    };
407    if let Some(bad_visibility) = bad_visibility {
408        return Err(format_err!(
409            bad_visibility,
410            "ink! {}s must have public or inherited visibility",
411            kind
412        ))
413    }
414    if !method_item.sig.generics.params.is_empty() {
415        return Err(format_err_spanned!(
416            method_item.sig.generics.params,
417            "ink! {}s must not be generic",
418            kind,
419        ))
420    }
421    if method_item.sig.constness.is_some() {
422        return Err(format_err_spanned!(
423            method_item.sig.constness,
424            "ink! {}s must not be const",
425            kind,
426        ))
427    }
428    if method_item.sig.asyncness.is_some() {
429        return Err(format_err_spanned!(
430            method_item.sig.asyncness,
431            "ink! {}s must not be async",
432            kind,
433        ))
434    }
435    if method_item.sig.unsafety.is_some() {
436        return Err(format_err_spanned!(
437            method_item.sig.unsafety,
438            "ink! {}s must not be unsafe",
439            kind,
440        ))
441    }
442    if method_item.sig.abi.is_some() {
443        return Err(format_err_spanned!(
444            method_item.sig.abi,
445            "ink! {}s must not have explicit ABI",
446            kind,
447        ))
448    }
449    if method_item.sig.variadic.is_some() {
450        return Err(format_err_spanned!(
451            method_item.sig.variadic,
452            "ink! {}s must not be variadic",
453            kind,
454        ))
455    }
456
457    if let Some(arg) = method_item.sig.inputs.iter().find(|input| {
458        match input {
459            syn::FnArg::Typed(pat) => !matches!(*pat.pat, syn::Pat::Ident(_)),
460            _ => false,
461        }
462    }) {
463        return Err(format_err_spanned!(
464            arg,
465            "ink! {} arguments must have an identifier",
466            kind
467        ))
468    }
469    Ok(())
470}
471
472/// The visibility of an ink! message or constructor.
473#[derive(Debug, Clone)]
474pub enum Visibility {
475    Public(syn::Token![pub]),
476    Inherited,
477}
478
479impl quote::ToTokens for Visibility {
480    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
481        match self {
482            Self::Public(vis_public) => vis_public.to_tokens(tokens),
483            Self::Inherited => (),
484        }
485    }
486}
487
488impl Visibility {
489    /// Returns `true` if the visibility of the ink! message of constructor is public
490    /// (`pub`).
491    ///
492    /// # Note
493    ///
494    /// Messages in normal implementation blocks must have public visibility.
495    pub fn is_pub(&self) -> bool {
496        matches!(self, Self::Public(_))
497    }
498
499    /// Returns `true` if the visibility of the ink! message of constructor is inherited.
500    ///
501    /// # Note
502    ///
503    /// Messages in trait implementation blocks must have inherited visibility.
504    pub fn is_inherited(&self) -> bool {
505        matches!(self, Self::Inherited)
506    }
507
508    /// Returns the associated span if any.
509    pub fn span(&self) -> Option<Span> {
510        match self {
511            Self::Public(vis_public) => Some(vis_public.span()),
512            Self::Inherited => None,
513        }
514    }
515}
516
517/// Iterator over the input parameters of an ink! message or constructor.
518///
519/// Does not yield the self receiver of ink! messages.
520pub struct InputsIter<'a> {
521    iter: syn::punctuated::Iter<'a, syn::FnArg>,
522}
523
524impl<'a> InputsIter<'a> {
525    /// Creates a new inputs iterator over the given `syn` punctuation.
526    pub(crate) fn new<P>(inputs: &'a syn::punctuated::Punctuated<syn::FnArg, P>) -> Self {
527        Self {
528            iter: inputs.iter(),
529        }
530    }
531}
532
533impl<'a> From<&'a ir::Message> for InputsIter<'a> {
534    fn from(message: &'a ir::Message) -> Self {
535        Self::new(&message.item.sig.inputs)
536    }
537}
538
539impl<'a> From<&'a ir::Constructor> for InputsIter<'a> {
540    fn from(constructor: &'a ir::Constructor) -> Self {
541        Self::new(&constructor.item.sig.inputs)
542    }
543}
544
545impl<'a> Iterator for InputsIter<'a> {
546    type Item = &'a syn::PatType;
547
548    fn next(&mut self) -> Option<Self::Item> {
549        'repeat: loop {
550            match self.iter.next() {
551                None => return None,
552                Some(syn::FnArg::Typed(pat_typed)) => return Some(pat_typed),
553                Some(syn::FnArg::Receiver(_)) => continue 'repeat,
554            }
555        }
556    }
557}
558
559impl ExactSizeIterator for InputsIter<'_> {
560    fn len(&self) -> usize {
561        self.iter.len()
562    }
563}
564
565#[cfg(test)]
566mod tests {
567    use super::*;
568    use core::fmt::Debug;
569
570    pub enum ExpectedSelector {
571        Raw([u8; 4]),
572        Blake2(Vec<u8>),
573    }
574
575    impl From<[u8; 4]> for ExpectedSelector {
576        fn from(raw_selector: [u8; 4]) -> Self {
577            ExpectedSelector::Raw(raw_selector)
578        }
579    }
580
581    impl From<Vec<u8>> for ExpectedSelector {
582        fn from(blake2_input: Vec<u8>) -> Self {
583            ExpectedSelector::Blake2(blake2_input)
584        }
585    }
586
587    impl ExpectedSelector {
588        pub fn expected_selector(self) -> ir::Selector {
589            match self {
590                Self::Raw(raw_selector) => ir::Selector::from(raw_selector),
591                Self::Blake2(blake2_input) => ir::Selector::compute(&blake2_input),
592            }
593        }
594    }
595
596    /// Asserts that the given ink! implementation block and the given ink!
597    /// message result in the same composed selector as the expected bytes.
598    fn assert_compose_selector<C, S>(
599        item_impl: syn::ItemImpl,
600        item_method: syn::ImplItemFn,
601        expected_selector: S,
602    ) where
603        C: Callable + TryFrom<syn::ImplItemFn>,
604        <C as TryFrom<syn::ImplItemFn>>::Error: Debug,
605        S: Into<ExpectedSelector>,
606    {
607        assert_eq!(
608            compose_selector(
609                &<ir::ItemImpl as TryFrom<syn::ItemImpl>>::try_from(item_impl).unwrap(),
610                &<C as TryFrom<syn::ImplItemFn>>::try_from(item_method).unwrap(),
611            ),
612            expected_selector.into().expected_selector(),
613        )
614    }
615
616    #[test]
617    fn inherent_selector_works() {
618        assert_compose_selector::<ir::Message, _>(
619            syn::parse_quote! {
620                #[ink(impl)]
621                impl MyStorage {}
622            },
623            syn::parse_quote! {
624                #[ink(message)]
625                fn my_message(&self) {}
626            },
627            b"my_message".to_vec(),
628        );
629    }
630
631    #[test]
632    fn trait_selector_works() {
633        assert_compose_selector::<ir::Message, _>(
634            syn::parse_quote! {
635                #[ink(impl)]
636                impl MyTrait for MyStorage {}
637            },
638            syn::parse_quote! {
639                #[ink(message)]
640                fn my_message(&self) {}
641            },
642            b"MyTrait::my_message".to_vec(),
643        );
644        assert_compose_selector::<ir::Message, _>(
645            syn::parse_quote! {
646                #[ink(impl)]
647                impl ::my::full::path::MyTrait for MyStorage {}
648            },
649            syn::parse_quote! {
650                #[ink(message)]
651                fn my_message(&self) {}
652            },
653            b"::my::full::path::MyTrait::my_message".to_vec(),
654        );
655        assert_compose_selector::<ir::Message, _>(
656            syn::parse_quote! {
657                #[ink(impl)]
658                impl relative::path_to::MyTrait for MyStorage {}
659            },
660            syn::parse_quote! {
661                #[ink(message)]
662                fn my_message(&self) {}
663            },
664            b"MyTrait::my_message".to_vec(),
665        );
666    }
667
668    #[test]
669    fn namespaced_selector_works() {
670        assert_compose_selector::<ir::Message, _>(
671            syn::parse_quote! {
672                #[ink(impl, namespace = "my_namespace")]
673                impl MyStorage {}
674            },
675            syn::parse_quote! {
676                #[ink(message)]
677                fn my_message(&self) {}
678            },
679            b"my_namespace::my_message".to_vec(),
680        );
681    }
682
683    #[test]
684    fn custom_selector_works() {
685        assert_compose_selector::<ir::Message, _>(
686            syn::parse_quote! {
687                #[ink(impl)]
688                impl MyStorage {}
689            },
690            syn::parse_quote! {
691                #[ink(message, selector = 0xDEADBEEF)]
692                fn my_message(&self) {}
693            },
694            [0xDE, 0xAD, 0xBE, 0xEF],
695        );
696        assert_compose_selector::<ir::Message, _>(
697            syn::parse_quote! {
698                #[ink(impl)]
699                impl MyTrait for MyStorage {}
700            },
701            syn::parse_quote! {
702                #[ink(message, selector = 0xDEADBEEF)]
703                fn my_message(&self) {}
704            },
705            [0xDE, 0xAD, 0xBE, 0xEF],
706        );
707    }
708
709    #[test]
710    fn name_override_selector_works() {
711        assert_compose_selector::<ir::Message, _>(
712            syn::parse_quote! {
713                #[ink(impl)]
714                impl MyStorage {}
715            },
716            syn::parse_quote! {
717                #[ink(message, name = "myMessage")]
718                fn my_message(&self) {}
719            },
720            b"myMessage".to_vec(),
721        );
722    }
723
724    #[test]
725    fn custom_selector_precedence_works() {
726        // Custom selector has higher precedence than name override.
727        assert_compose_selector::<ir::Message, _>(
728            syn::parse_quote! {
729                #[ink(impl)]
730                impl MyStorage {}
731            },
732            syn::parse_quote! {
733                #[ink(message, name = "myMessage", selector = 0xDEADBEEF)]
734                fn my_message(&self) {}
735            },
736            [0xDE, 0xAD, 0xBE, 0xEF],
737        );
738    }
739}