1use 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
37macro_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#[cfg_attr(all(feature = "std", not(test)), allow(dead_code))]
76#[cfg(feature = "unstable-hostfn")] pub(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")] fn 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 => {
115 if <R as ConstructorReturnType<ContractRef>>::IS_RESULT {
116 let result_variant = out_return_value.read_byte()?;
117 match result_variant {
118 0 => panic!("The callee reverted, but did not encode an error in the output buffer."),
120 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 => {
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 type ContractResult<T, E> = Result<T, E>;
158
159 #[derive(scale::Encode, scale::Decode)]
160 struct ContractError(String);
161
162 #[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}