ink_ir/ir/selector.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
15use super::blake2::blake2b_256;
16use crate::literal::HexLiteral;
17use proc_macro2::TokenStream as TokenStream2;
18use std::marker::PhantomData;
19use syn::spanned::Spanned as _;
20
21/// The selector of an ink! dispatchable.
22///
23/// # Note
24///
25/// This is equal to the first four bytes of the BLAKE-2 256 hash of a function's name.
26#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
27pub struct Selector {
28 bytes: [u8; 4],
29}
30
31/// The trait prefix to compute a composed selector for trait implementation blocks.
32#[derive(Debug, Copy, Clone)]
33pub struct TraitPrefix<'a> {
34 /// The namespace of the ink! trait definition.
35 ///
36 /// By default this is equal to the `module_path!` at the ink! trait definition site.
37 /// It can be customized by the ink! trait definition author using `#[ink(namespace =
38 /// N)]` ink! attribute.
39 namespace: Option<&'a syn::LitStr>,
40 /// The Rust identifier of the ink! trait definition.
41 trait_ident: &'a syn::Ident,
42}
43
44impl<'a> TraitPrefix<'a> {
45 /// Creates a new trait prefix.
46 pub fn new(trait_ident: &'a syn::Ident, namespace: Option<&'a syn::LitStr>) -> Self {
47 Self {
48 namespace,
49 trait_ident,
50 }
51 }
52
53 /// Returns a vector over the bytes of the namespace.
54 pub fn namespace_bytes(&self) -> Vec<u8> {
55 self.namespace
56 .map(|namespace| namespace.value().into_bytes())
57 .unwrap_or_default()
58 }
59
60 /// Returns a shared reference to the Rust identifier of the trait.
61 pub fn trait_ident(&self) -> &'a syn::Ident {
62 self.trait_ident
63 }
64}
65
66impl Selector {
67 /// Computes the BLAKE-2 256-bit based selector from the given input bytes.
68 pub fn compute(input: &[u8]) -> Self {
69 let mut output = [0; 32];
70 blake2b_256(input, &mut output);
71 Self::from([output[0], output[1], output[2], output[3]])
72 }
73
74 /// # Note
75 ///
76 /// - `trait_prefix` is `None` when computing the selector of ink! constructors and
77 /// messages in inherent implementation blocks.
78 /// - `trait_prefix` is `Some` when computing the selector of ink! constructors and
79 /// messages in trait implementation blocks. In this case the `namespace` is either
80 /// the full path of the trait definition gained by Rust's `module_path!` macro by
81 /// default or it is customized by manual application of the `#[ink(namespace =
82 /// "my_namespace")]` ink! attribute. In the example `my_namespace` concatenated
83 /// with `::` and the identifier of the trait definition would then be part of the
84 /// provided `trait_prefix` parameter.
85 /// - `fn_ident` refers to the ink! constructor or message identifier.
86 ///
87 /// # Inherent Implementation Blocks
88 ///
89 /// For inherent implementation blocks, when `trait_prefix` is `None` the composed
90 /// selector is computed as follows:
91 ///
92 /// 1. Apply `BLAKE2` 256-bit hash `H` on the bytes of the ASCII representation of the
93 /// `fn_ident` identifier.
94 /// 1. The first 4 bytes of `H` make up the selector.
95 ///
96 /// # Trait Implementation Blocks
97 ///
98 /// For trait implementation blocks, when `trait_prefix` is
99 /// `Some((namespace, trait_ident))` the composed selector is computed as follows:
100 ///
101 /// 1. Compute the ASCII byte representation of `fn_ident` and call it `F`.
102 /// 1. Compute the ASCII byte representation of `namespace` and call it `N`.
103 /// 1. Compute the ASCII byte representation of `trait_ident` and call it `T`.
104 /// 1. Concatenate `N`, `T` and `F` using `::` as separator and call it `C`.
105 /// 1. Apply the `BLAKE2` 256-bit hash `H` of `C`.
106 /// 1. The first 4 bytes of `H` make up the selector.
107 pub fn compose<'a, T>(trait_prefix: T, fn_ident: &syn::Ident) -> Self
108 where
109 T: Into<Option<TraitPrefix<'a>>>,
110 {
111 let fn_ident = fn_ident.to_string().into_bytes();
112 let input_bytes: Vec<u8> = match trait_prefix.into() {
113 Some(trait_prefix) => {
114 let namespace = trait_prefix.namespace_bytes();
115 let trait_ident = trait_prefix.trait_ident().to_string().into_bytes();
116 let separator = &b"::"[..];
117 if namespace.is_empty() {
118 [&trait_ident[..], &fn_ident[..]].join(separator)
119 } else {
120 [&namespace[..], &trait_ident[..], &fn_ident[..]].join(separator)
121 }
122 }
123 None => fn_ident.to_vec(),
124 };
125 Self::compute(&input_bytes)
126 }
127
128 /// Returns the underlying four bytes.
129 pub fn to_bytes(&self) -> [u8; 4] {
130 self.bytes
131 }
132
133 /// Returns the big-endian `u32` representation of the selector bytes.
134 pub fn into_be_u32(self) -> u32 {
135 u32::from_be_bytes(self.bytes)
136 }
137
138 /// Returns the 4 bytes that make up the selector as hex encoded bytes.
139 pub fn hex_lits(self) -> [syn::LitInt; 4] {
140 self.bytes.map(<u8 as HexLiteral>::hex_padded_suffixed)
141 }
142}
143
144impl From<[u8; 4]> for Selector {
145 fn from(bytes: [u8; 4]) -> Self {
146 Self { bytes }
147 }
148}
149
150/// Used as generic parameter for the `selector_id!` macro.
151pub enum SelectorId {}
152
153/// Used as generic parameter for the `selector_bytes!` macro.
154pub enum SelectorBytes {}
155
156/// The selector ID of an ink! dispatchable.
157///
158/// # Note
159///
160/// This is mainly used for analysis and codegen of the `selector_id!` macro.
161#[derive(Debug)]
162pub struct SelectorMacro<T> {
163 selector: Selector,
164 input: syn::Lit,
165 _marker: PhantomData<fn() -> T>,
166}
167
168impl<T> SelectorMacro<T> {
169 /// Returns the underlying selector.
170 pub fn selector(&self) -> Selector {
171 self.selector
172 }
173
174 /// Returns the literal input of the selector ID.
175 pub fn input(&self) -> &syn::Lit {
176 &self.input
177 }
178}
179
180impl<T> TryFrom<TokenStream2> for SelectorMacro<T> {
181 type Error = syn::Error;
182
183 fn try_from(input: TokenStream2) -> Result<Self, Self::Error> {
184 let input_span = input.span();
185 let lit = syn::parse2::<syn::Lit>(input).map_err(|error| {
186 format_err!(
187 input_span,
188 "expected string or byte string literal as input: {}",
189 error
190 )
191 })?;
192 let input_bytes = match lit {
193 syn::Lit::Str(ref lit_str) => lit_str.value().into_bytes(),
194 syn::Lit::ByteStr(ref byte_str) => byte_str.value(),
195 invalid => {
196 return Err(format_err!(
197 invalid.span(),
198 "expected string or byte string literal as input. found {:?}",
199 invalid,
200 ))
201 }
202 };
203 let selector = Selector::compute(&input_bytes);
204 Ok(Self {
205 selector,
206 input: lit,
207 _marker: PhantomData,
208 })
209 }
210}
211
212#[cfg(test)]
213mod tests {
214 use super::*;
215
216 #[test]
217 fn hex_lits_works() {
218 let hex_lits = Selector::from([0xC0, 0xDE, 0xCA, 0xFE]).hex_lits();
219 assert_eq!(
220 hex_lits,
221 [
222 syn::parse_quote! { 0xC0_u8 },
223 syn::parse_quote! { 0xDE_u8 },
224 syn::parse_quote! { 0xCA_u8 },
225 syn::parse_quote! { 0xFE_u8 },
226 ]
227 )
228 }
229}