ink_env/call/
execution.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 core::marker::PhantomData;
16use ink_prelude::vec::Vec;
17use ink_primitives::{
18    abi::{
19        AbiEncodeWith,
20        Ink,
21        Sol,
22    },
23    SolEncode,
24};
25
26use super::{
27    selector::Selector,
28    utils::{
29        DecodeMessageResult,
30        ReturnType,
31    },
32};
33use crate::Environment;
34
35/// The input data and the expected return type of a contract execution.
36pub struct Execution<Args, Output, Abi> {
37    /// The input data for initiating a contract execution.
38    pub input: ExecutionInput<Args, Abi>,
39    /// The type of the expected return value of the contract execution.
40    pub output: ReturnType<Output>,
41}
42
43impl<Args, Output, Abi> Execution<Args, Output, Abi>
44where
45    Args: AbiEncodeWith<Abi>,
46    Output: DecodeMessageResult<Abi>,
47{
48    /// Construct a new contract execution with the given input data.
49    pub fn new(input: ExecutionInput<Args, Abi>) -> Self {
50        Self {
51            input,
52            output: ReturnType::default(),
53        }
54    }
55
56    /// Perform the execution of the contract with the given executor.
57    pub fn exec<I, E>(
58        self,
59        executor: &I,
60    ) -> Result<ink_primitives::MessageResult<Output>, I::Error>
61    where
62        E: Environment,
63        I: Executor<E>,
64    {
65        executor.exec(&self.input)
66    }
67}
68
69/// Implemented in different environments to perform contract execution.
70pub trait Executor<E: Environment> {
71    /// The type of the error that can be returned during execution.
72    type Error;
73    /// Perform the contract execution with the given input data, and return the result.
74    fn exec<Args, Output, Abi>(
75        &self,
76        input: &ExecutionInput<Args, Abi>,
77    ) -> Result<ink_primitives::MessageResult<Output>, Self::Error>
78    where
79        Args: AbiEncodeWith<Abi>,
80        Output: DecodeMessageResult<Abi>;
81}
82
83/// The input data for a smart contract execution.
84#[derive(Clone, Default, Debug)]
85pub struct ExecutionInput<Args, Abi> {
86    /// The selector (if any) for the smart contract execution.
87    selector: Option<Selector>,
88    /// The arguments of the smart contract execution.
89    args: Args,
90    _marker: PhantomData<Abi>,
91}
92
93impl<Abi> ExecutionInput<EmptyArgumentList<Abi>, Abi> {
94    /// Creates a new execution input with the given selector.
95    #[inline]
96    pub fn new(selector: Selector) -> Self {
97        Self {
98            selector: Some(selector),
99            args: ArgumentList::empty(),
100            _marker: Default::default(),
101        }
102    }
103}
104
105impl ExecutionInput<EmptyArgumentList<Sol>, Sol> {
106    /// Creates a new execution input with no selector.
107    ///
108    /// # Note
109    ///
110    /// Should only be used for Solidity ABI encoded constructors/instantiation.
111    #[inline]
112    pub fn no_selector() -> Self {
113        Self {
114            selector: None,
115            args: ArgumentList::empty(),
116            _marker: Default::default(),
117        }
118    }
119}
120
121impl<Abi> ExecutionInput<EmptyArgumentList<Abi>, Abi> {
122    /// Pushes an argument to the execution input.
123    #[inline]
124    pub fn push_arg<T>(
125        self,
126        arg: T,
127    ) -> ExecutionInput<ArgumentList<Argument<T>, EmptyArgumentList<Abi>, Abi>, Abi>
128    where
129        T: AbiEncodeWith<Abi>,
130    {
131        ExecutionInput {
132            selector: self.selector,
133            args: self.args.push_arg(arg),
134            _marker: Default::default(),
135        }
136    }
137}
138
139impl<Head, Rest, Abi> ExecutionInput<ArgumentList<Argument<Head>, Rest, Abi>, Abi> {
140    /// Pushes an argument to the execution input.
141    #[allow(clippy::type_complexity)]
142    #[inline]
143    pub fn push_arg<T>(
144        self,
145        arg: T,
146    ) -> ExecutionInput<ArgsList<T, ArgsList<Head, Rest, Abi>, Abi>, Abi>
147    where
148        T: AbiEncodeWith<Abi>,
149    {
150        ExecutionInput {
151            selector: self.selector,
152            args: self.args.push_arg(arg),
153            _marker: Default::default(),
154        }
155    }
156}
157
158impl<Args, Abi> ExecutionInput<Args, Abi> {
159    /// Modify the selector.
160    ///
161    /// Useful when using the [`ExecutionInput`] generated as part of the
162    /// `ContractRef`, but using a custom selector.
163    pub fn update_selector(&mut self, selector: Selector) {
164        self.selector = Some(selector);
165    }
166}
167
168impl<Args, Abi> ExecutionInput<Args, Abi>
169where
170    Args: AbiEncodeWith<Abi>,
171{
172    /// Encodes the execution input into a dynamic vector by combining the selector and
173    /// encoded arguments.
174    pub fn encode(&self) -> Vec<u8> {
175        let mut encoded = Vec::new();
176        if let Some(selector) = &self.selector {
177            encoded.extend(selector.to_bytes());
178        }
179        self.args.encode_to_vec(&mut encoded);
180        encoded
181    }
182
183    /// Encodes the execution input into a static buffer by combining the selector and
184    /// encoded arguments.
185    pub fn encode_to_slice(&self, buffer: &mut [u8]) -> usize {
186        let selector_len = if let Some(selector) = &self.selector {
187            let selector_bytes = selector.to_bytes();
188            let selector_len = selector_bytes.len();
189            buffer[..selector_len].copy_from_slice(&selector_bytes);
190            selector_len
191        } else {
192            0
193        };
194        let args_len = self.args.encode_to_slice(&mut buffer[selector_len..]);
195        selector_len + args_len
196    }
197}
198
199/// An argument list.
200///
201/// This type is constructed mainly at compile type via type constructions
202/// to avoid having to allocate heap memory while constructing the encoded
203/// arguments. The potentially heap allocating encoding is done right at the end
204/// where we can leverage the static environmental buffer instead of allocating
205/// heap memory.
206#[derive(Clone, Default, Debug)]
207pub struct ArgumentList<Head, Rest, Abi> {
208    /// The first argument of the argument list.
209    head: Head,
210    /// All the rest arguments.
211    rest: Rest,
212    _marker: PhantomData<Abi>,
213}
214
215/// Minor simplification of an argument list with a head and rest.
216pub type ArgsList<Head, Rest, Abi> = ArgumentList<Argument<Head>, Rest, Abi>;
217
218/// A single argument and its reference to a known value.
219#[derive(Clone, Debug)]
220pub struct Argument<T> {
221    /// The reference to the known value.
222    ///
223    /// Used for the encoding at the end of the construction.
224    arg: T,
225}
226
227impl<T> Argument<T> {
228    /// Creates a new argument.
229    #[inline]
230    fn new(arg: T) -> Self {
231        Self { arg }
232    }
233}
234
235/// The end of an argument list.
236#[derive(Clone, Default, Debug)]
237pub struct ArgumentListEnd;
238
239/// An empty argument list.
240pub type EmptyArgumentList<Abi> = ArgumentList<ArgumentListEnd, ArgumentListEnd, Abi>;
241
242impl<Abi> EmptyArgumentList<Abi> {
243    /// Creates a new empty argument list.
244    #[inline]
245    pub fn empty() -> EmptyArgumentList<Abi> {
246        ArgumentList {
247            head: ArgumentListEnd,
248            rest: ArgumentListEnd,
249            _marker: Default::default(),
250        }
251    }
252
253    /// Pushes the first argument to the empty argument list.
254    #[inline]
255    pub fn push_arg<T>(self, arg: T) -> ArgumentList<Argument<T>, Self, Abi>
256    where
257        T: AbiEncodeWith<Abi>,
258    {
259        ArgumentList {
260            head: Argument::new(arg),
261            rest: self,
262            _marker: Default::default(),
263        }
264    }
265}
266
267impl<Head, Rest, Abi> ArgumentList<Argument<Head>, Rest, Abi> {
268    /// Pushes another argument to the argument list.
269    #[inline]
270    pub fn push_arg<T>(self, arg: T) -> ArgumentList<Argument<T>, Self, Abi>
271    where
272        T: AbiEncodeWith<Abi>,
273    {
274        ArgumentList {
275            head: Argument::new(arg),
276            rest: self,
277            _marker: Default::default(),
278        }
279    }
280}
281
282impl<T> scale::Encode for Argument<T>
283where
284    T: scale::Encode,
285{
286    #[inline]
287    fn size_hint(&self) -> usize {
288        <T as scale::Encode>::size_hint(&self.arg)
289    }
290
291    #[inline]
292    fn encode_to<O: scale::Output + ?Sized>(&self, output: &mut O) {
293        <T as scale::Encode>::encode_to(&self.arg, output)
294    }
295}
296
297impl scale::Encode for EmptyArgumentList<Ink> {
298    #[inline]
299    fn size_hint(&self) -> usize {
300        0
301    }
302
303    #[inline]
304    fn encode_to<O: scale::Output + ?Sized>(&self, _output: &mut O) {}
305}
306
307impl<Head, Rest> scale::Encode for ArgumentList<Argument<Head>, Rest, Ink>
308where
309    Head: scale::Encode,
310    Rest: scale::Encode,
311{
312    #[inline]
313    fn size_hint(&self) -> usize {
314        scale::Encode::size_hint(&self.head)
315            .checked_add(scale::Encode::size_hint(&self.rest))
316            .expect("unable to checked_add")
317    }
318
319    #[inline]
320    fn encode_to<O: scale::Output + ?Sized>(&self, output: &mut O) {
321        // We reverse the order of encoding because we build up the list of
322        // arguments in reverse order, too. This way we encode the arguments
323        // in the same order in which they have been pushed to the argument list
324        // while the argument list itself organizes them in reverse order.
325        scale::Encode::encode_to(&self.rest, output);
326        scale::Encode::encode_to(&self.head, output);
327    }
328}
329
330impl<Args> scale::Encode for ExecutionInput<Args, Ink>
331where
332    Args: scale::Encode,
333{
334    #[inline]
335    fn size_hint(&self) -> usize {
336        let selector_size = match &self.selector {
337            None => 0,
338            Some(_) => scale::Encode::size_hint(&self.selector),
339        };
340        selector_size
341            .checked_add(scale::Encode::size_hint(&self.args))
342            .expect("unable to checked_add")
343    }
344
345    #[inline]
346    fn encode_to<O: scale::Output + ?Sized>(&self, output: &mut O) {
347        if let Some(selector) = &self.selector {
348            scale::Encode::encode_to(selector, output);
349        }
350        scale::Encode::encode_to(&self.args, output);
351    }
352}
353
354impl<'a, T> SolEncode<'a> for Argument<T>
355where
356    T: SolEncode<'a>,
357{
358    type SolType = <T as SolEncode<'a>>::SolType;
359
360    fn to_sol_type(&'a self) -> Self::SolType {
361        self.arg.to_sol_type()
362    }
363}
364
365impl SolEncode<'_> for EmptyArgumentList<Sol> {
366    type SolType = ();
367
368    fn encode(&self) -> Vec<u8> {
369        Vec::new()
370    }
371
372    // NOTE: Not actually used for encoding because of `encode` override above.
373    fn to_sol_type(&self) {}
374}
375
376impl<Head, Rest> SolEncode<'_> for ArgumentList<Argument<Head>, Rest, Sol>
377where
378    Self: SolEncodeArgsList,
379{
380    // NOTE: Not actually used for encoding because of `encode` override below.
381    type SolType = ();
382
383    fn encode(&self) -> Vec<u8> {
384        // Collects encoding info for each arg in `Argument list`.
385        let mut head = Vec::new();
386        let mut tail = Vec::new();
387        let mut offset = self.encode_args(&mut head, &mut tail);
388
389        // Composes the head and tail of the argument list encoding.
390        // Ref: <https://docs.soliditylang.org/en/latest/abi-spec.html#function-selector-and-argument-encoding>
391        head.into_iter()
392            .flat_map(|info| {
393                match info {
394                    SolEncodeHeadInfo::Bytes(encoded) => encoded,
395                    SolEncodeHeadInfo::Size(size) => {
396                        let encoded_offset = SolEncode::encode(&(offset as u128));
397                        offset += size;
398                        encoded_offset
399                    }
400                }
401            })
402            .chain(tail)
403            .collect()
404    }
405
406    // NOTE: Not actually used for encoding because of `encode` override above.
407    fn to_sol_type(&self) {}
408}
409
410/// Helper trait for Solidity ABI encoding `ArgumentList`.
411trait SolEncodeArgsList {
412    /// Solidity ABI encodes each arg in `ArgumentList`,
413    /// and returns the current `offset` for dynamic data for the entire list.
414    ///
415    /// Ref: <https://docs.soliditylang.org/en/latest/abi-spec.html#function-selector-and-argument-encoding>
416    fn encode_args(&self, head: &mut Vec<SolEncodeHeadInfo>, tail: &mut Vec<u8>)
417        -> usize;
418}
419
420/// Head info for Solidity ABI encoding `ArgumentList`.
421enum SolEncodeHeadInfo {
422    /// Encoded bytes for a static type.
423    Bytes(Vec<u8>),
424    /// Encoded size for a dynamic type.
425    Size(usize),
426}
427
428impl SolEncodeArgsList for EmptyArgumentList<Sol> {
429    fn encode_args(&self, _: &mut Vec<SolEncodeHeadInfo>, _: &mut Vec<u8>) -> usize {
430        0
431    }
432}
433
434impl<Head, Rest> SolEncodeArgsList for ArgumentList<Argument<Head>, Rest, Sol>
435where
436    Head: for<'a> SolEncode<'a>,
437    Rest: SolEncodeArgsList,
438{
439    fn encode_args(
440        &self,
441        head: &mut Vec<SolEncodeHeadInfo>,
442        tail: &mut Vec<u8>,
443    ) -> usize {
444        let mut offset = self.rest.encode_args(head, tail);
445        let encoded = self.head.arg.encode();
446        if <Head as SolEncode>::DYNAMIC {
447            // Dynamic types are represented (in the head) by their offset,
448            // which is always 32 bytes long.
449            offset += 32;
450            // Standalone encoding for dynamic types includes a 32 bytes offset.
451            let data = &encoded[32..];
452            head.push(SolEncodeHeadInfo::Size(data.len()));
453            tail.extend(data);
454        } else {
455            offset += encoded.len();
456            head.push(SolEncodeHeadInfo::Bytes(encoded));
457        }
458
459        offset
460    }
461}
462
463#[cfg(test)]
464mod tests {
465    use super::*;
466
467    #[test]
468    fn empty_exec_input_works() {
469        let selector = Selector::new([0x01, 0x02, 0x03, 0x04]);
470        let exec_input = ExecutionInput::new(selector);
471        let encoded = scale::Encode::encode(&exec_input);
472        assert!(!encoded.is_empty());
473        let decoded = <Selector as scale::Decode>::decode(&mut &encoded[..]).unwrap();
474        assert_eq!(decoded, selector);
475    }
476
477    #[test]
478    fn empty_args_works() {
479        let empty_list = ArgumentList::empty();
480        let encoded = scale::Encode::encode(&empty_list);
481        assert_eq!(encoded, <Vec<u8>>::new());
482    }
483
484    #[test]
485    fn single_argument_works() {
486        let empty_list = ArgumentList::empty().push_arg(&1i32);
487        let encoded = scale::Encode::encode(&empty_list);
488        assert!(!encoded.is_empty());
489        let decoded = <i32 as scale::Decode>::decode(&mut &encoded[..]).unwrap();
490        assert_eq!(decoded, 1i32);
491    }
492
493    #[test]
494    fn multiple_arguments_works() {
495        let empty_list = ArgumentList::empty()
496            .push_arg(&42i32)
497            .push_arg(&true)
498            .push_arg(&[0x66u8; 4]);
499        let encoded = scale::Encode::encode(&empty_list);
500        assert!(!encoded.is_empty());
501        let decoded =
502            <(i32, bool, [u8; 4]) as scale::Decode>::decode(&mut &encoded[..]).unwrap();
503        assert_eq!(decoded, (42i32, true, [0x66; 4]));
504    }
505
506    #[test]
507    fn sol_encoding_arguments_works() {
508        let args_list = EmptyArgumentList::<Sol>::empty()
509            .push_arg(100u8)
510            .push_arg(vec![1, 2, 3, 4])
511            .push_arg(String::from("Hello, world!"))
512            .push_arg(true);
513        let encoded_args_list = SolEncode::encode(&args_list);
514
515        let args_tuple = (100u8, vec![1, 2, 3, 4], String::from("Hello, world!"), true);
516        let encoded_args_tuple =
517            ink_primitives::sol::SolParamsEncode::encode(&args_tuple);
518
519        assert_eq!(encoded_args_list, encoded_args_tuple);
520    }
521}