ink_env/engine/
mod.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 cfg_if::cfg_if;
16use ink_primitives::ConstructorResult;
17use pallet_revive_uapi::ReturnErrorCode;
18
19use crate::{
20    Error,
21    Result as EnvResult,
22    backend::{
23        EnvBackend,
24        TypedEnvBackend,
25    },
26    call::{
27        ConstructorReturnType,
28        FromAddr,
29        utils::{
30            ConstructorError,
31            DecodeConstructorError,
32        },
33    },
34};
35
36/// Convert a slice into an array reference.
37///
38/// Creates an array reference of size `$len` pointing to `$offset` within `$arr`.
39///
40/// # Panics
41///
42/// - The selected range is out of bounds given the supplied slice
43/// - Integer overflow on `$offset + $len`
44macro_rules! array_mut_ref {
45    ($arr:expr, $offset:expr, $len:expr) => {{
46        {
47            fn as_array<T>(slice: &mut [T]) -> &mut [T; $len] {
48                slice.try_into().unwrap()
49            }
50            let offset: usize = $offset;
51            let slice = &mut $arr[offset..offset.checked_add($len).unwrap()];
52            as_array(slice)
53        }
54    }};
55}
56
57pub trait OnInstance: EnvBackend + TypedEnvBackend {
58    fn on_instance<F, R>(f: F) -> R
59    where
60        F: FnOnce(&mut Self) -> R;
61}
62
63cfg_if! {
64    if #[cfg(not(feature = "std"))] {
65        mod on_chain;
66        pub use self::on_chain::EnvInstance;
67    } else {
68        pub mod off_chain;
69        pub use self::off_chain::EnvInstance;
70    }
71}
72
73// We only use this function when 1) compiling for PolkaVM 2) compiling for tests.
74#[cfg_attr(all(feature = "std", not(test)), allow(dead_code))]
75pub(crate) fn decode_instantiate_result<I, ContractRef, R, Abi>(
76    instantiate_result: EnvResult<()>,
77    out_address: &mut I,
78    out_return_value: &[u8],
79) -> EnvResult<ConstructorResult<<R as ConstructorReturnType<ContractRef, Abi>>::Output>>
80where
81    I: scale::Input,
82    ContractRef: FromAddr,
83    R: ConstructorReturnType<ContractRef, Abi>,
84{
85    match instantiate_result {
86        Ok(()) => {
87            let addr = scale::Decode::decode(out_address)?;
88            let contract_ref = <ContractRef as FromAddr>::from_addr(addr);
89            let output = <R as ConstructorReturnType<ContractRef, Abi>>::ok(contract_ref);
90            Ok(Ok(output))
91        }
92        Err(Error::ReturnError(ReturnErrorCode::CalleeReverted)) => {
93            decode_instantiate_err::<ContractRef, R, Abi>(out_return_value)
94        }
95        Err(actual_error) => Err(actual_error),
96    }
97}
98
99#[cfg_attr(all(feature = "std", not(test)), allow(dead_code))]
100fn decode_instantiate_err<ContractRef, R, Abi>(
101    out_return_value: &[u8],
102) -> EnvResult<ConstructorResult<<R as ConstructorReturnType<ContractRef, Abi>>::Output>>
103where
104    ContractRef: FromAddr,
105    R: ConstructorReturnType<ContractRef, Abi>,
106{
107    let decoded_error =
108        <<R as ConstructorReturnType<ContractRef, Abi>>::Error as DecodeConstructorError<
109            Abi,
110        >>::decode_error_output(out_return_value);
111    match decoded_error {
112        ConstructorError::Contract(contract_err) => {
113            let error_output =
114                <R as ConstructorReturnType<ContractRef, Abi>>::err(contract_err);
115            match error_output {
116                Some(output) => Ok(Ok(output)),
117                None => {
118                    // No user defined error variant, and successful error decoding
119                    // (i.e. to unit), so we maintain the `CalleeReverted`
120                    // environmental error.
121                    Err(Error::ReturnError(ReturnErrorCode::CalleeReverted))
122                }
123            }
124        }
125        ConstructorError::Lang(lang_err) => Ok(Err(lang_err)),
126        ConstructorError::Env(env_err) => Err(env_err),
127    }
128}
129
130#[cfg(test)]
131mod decode_instantiate_result_tests {
132    use super::*;
133    use crate::DefaultEnvironment;
134    use ink_primitives::Address;
135    use scale::Encode;
136
137    // The `Result` type used to represent the programmer defined contract output.
138    type ContractResult<T, E> = Result<T, E>;
139
140    #[derive(Debug, PartialEq, scale::Encode, scale::Decode)]
141    struct ContractError(String);
142
143    // The `allow(dead_code)` is for the `AccountId` in the struct.
144    #[allow(dead_code)]
145    #[derive(Debug, PartialEq)]
146    struct TestContractRef(Address);
147
148    impl crate::ContractEnv for TestContractRef {
149        type Env = DefaultEnvironment;
150    }
151
152    impl FromAddr for TestContractRef {
153        fn from_addr(addr: Address) -> Self {
154            Self(addr)
155        }
156    }
157
158    fn encode_and_decode_return_value(
159        return_value: ConstructorResult<Result<(), ContractError>>,
160    ) -> EnvResult<ConstructorResult<Result<TestContractRef, ContractError>>> {
161        let out_address = Vec::new();
162        let encoded_return_value = return_value.encode();
163        decode_return_value_fallible(&mut &out_address[..], &encoded_return_value[..])
164    }
165
166    fn decode_return_value_fallible<I: scale::Input>(
167        out_address: &mut I,
168        out_return_value: &[u8],
169    ) -> EnvResult<ConstructorResult<Result<TestContractRef, ContractError>>> {
170        decode_instantiate_result::<
171            I,
172            TestContractRef,
173            Result<TestContractRef, ContractError>,
174            ink_primitives::abi::Ink,
175        >(
176            Err(ReturnErrorCode::CalleeReverted.into()),
177            out_address,
178            out_return_value,
179        )
180    }
181
182    #[test]
183    fn revert_branch_rejects_valid_output_buffer_with_success_case() {
184        let return_value = ConstructorResult::Ok(ContractResult::Ok(()));
185
186        let decoded_result = encode_and_decode_return_value(return_value);
187
188        let expected_error: EnvResult<
189            ConstructorResult<Result<TestContractRef, ContractError>>,
190        > = EnvResult::Err(crate::Error::Decode(
191            "The callee reverted, but did not encode an error in the output buffer."
192                .into(),
193        ));
194        assert_eq!(decoded_result, expected_error);
195    }
196
197    #[test]
198    fn successful_dispatch_with_error_from_contract_constructor() {
199        let return_value = ConstructorResult::Ok(ContractResult::Err(ContractError(
200            "Contract's constructor failed.".to_owned(),
201        )));
202
203        let decoded_result = encode_and_decode_return_value(return_value);
204
205        assert!(matches!(
206            decoded_result,
207            EnvResult::Ok(ConstructorResult::Ok(ContractResult::Err(ContractError(_))))
208        ))
209    }
210
211    #[test]
212    fn dispatch_error_gets_decoded_correctly() {
213        let return_value =
214            ConstructorResult::Err(ink_primitives::LangError::CouldNotReadInput);
215
216        let decoded_result = encode_and_decode_return_value(return_value);
217
218        assert!(matches!(
219            decoded_result,
220            EnvResult::Ok(ConstructorResult::Err(
221                ink_primitives::LangError::CouldNotReadInput
222            ))
223        ))
224    }
225
226    #[test]
227    fn invalid_bytes_in_output_buffer_fail_decoding() {
228        let out_address = Vec::new();
229        let invalid_encoded_return_value = [69];
230
231        let decoded_result = decode_return_value_fallible(
232            &mut &out_address[..],
233            &invalid_encoded_return_value[..],
234        );
235
236        assert!(matches!(decoded_result, Err(crate::Error::Decode(_))))
237    }
238}