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