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