1use super::EnvInstance;
16use crate::{
17 call::{
18 utils::DecodeMessageResult,
19 Call,
20 CallParams,
21 DelegateCall,
22 },
23 event::{
24 Event,
25 TopicsBuilderBackend,
26 },
27 hash::{
28 CryptoHash,
29 HashOutput,
30 Keccak256,
31 Sha2x256,
32 },
33 DecodeDispatch,
34 DispatchError,
35 EnvBackend,
36 Result,
37 TypedEnvBackend,
38};
39#[cfg(feature = "unstable-hostfn")]
40use crate::{
41 call::{
42 ConstructorReturnType,
43 CreateParams,
44 FromAddr,
45 LimitParamsV2,
46 },
47 hash::{
48 Blake2x128,
49 Blake2x256,
50 },
51 test::callee,
52 Clear,
53};
54use ink_engine::ext::Engine;
55#[cfg(feature = "unstable-hostfn")]
56use ink_primitives::{
57 reflect::ScaleEncoding,
58 types::AccountIdMapper,
59};
60use ink_primitives::{
61 reflect::{
62 AbiDecodeWith,
63 AbiEncodeWith,
64 },
65 types::Environment,
66 Address,
67 SolEncode,
68 H256,
69 U256,
70};
71use ink_storage_traits::{
72 decode_all,
73 Storable,
74};
75use pallet_revive_uapi::{
76 ReturnErrorCode,
77 ReturnFlags,
78};
79#[cfg(feature = "unstable-hostfn")]
80use schnorrkel::{
81 PublicKey,
82 Signature,
83};
84
85const BUFFER_SIZE: usize = crate::BUFFER_SIZE;
89
90fn execute_contract_call<ContractRef>(input: Vec<u8>) -> Vec<u8>
92where
93 ContractRef: crate::ContractReverseReference,
94 <ContractRef as crate::ContractReverseReference>::Type:
95 crate::reflect::ContractMessageDecoder,
96{
97 let dispatch = <
98 <
99 <
100 ContractRef
101 as crate::ContractReverseReference
102 >::Type
103 as crate::reflect::ContractMessageDecoder
104 >::Type
105 as DecodeDispatch
106 >::decode_dispatch(&mut &input[..])
107 .unwrap_or_else(|e| panic!("Failed to decode constructor call: {:?}", e));
108
109 crate::reflect::ExecuteDispatchable::execute_dispatchable(dispatch)
110 .unwrap_or_else(|e| panic!("Message call failed: {:?}", e));
111
112 crate::test::get_return_value()
113}
114
115fn invoke_contract_impl<R, Abi>(
116 env: &mut EnvInstance,
117 _gas_limit: Option<u64>,
118 _call_flags: u32,
119 _transferred_value: Option<&U256>,
120 callee_account: Address,
121 input: Vec<u8>,
122) -> Result<ink_primitives::MessageResult<R>>
123where
124 R: AbiDecodeWith<Abi> + DecodeMessageResult<Abi>,
125{
126 let callee_code_hash = env.code_hash(&callee_account).unwrap_or_else(|err| {
127 panic!(
128 "failed getting code hash for {:?}: {:?}",
129 callee_account, err
130 )
131 });
132
133 let handler = env
134 .engine
135 .database
136 .get_contract_message_handler(&callee_code_hash);
137 let old_callee = env.engine.get_callee();
138 env.engine.set_callee(callee_account);
139
140 let result = handler(input);
141
142 env.engine.set_callee(old_callee);
143
144 R::decode_output(&result)
145}
146
147fn invoke_contract_impl_delegate<R, Abi>(
148 env: &mut EnvInstance,
149 _gas_limit: Option<u64>,
150 _call_flags: u32,
151 _transferred_value: Option<&U256>,
152 callee_account: Address,
153 input: Vec<u8>,
154) -> Result<ink_primitives::MessageResult<R>>
155where
156 R: AbiDecodeWith<Abi> + DecodeMessageResult<Abi>,
157{
158 let callee_code_hash = env.code_hash(&callee_account).unwrap_or_else(|err| {
159 panic!(
160 "failed getting code hash for {:?}: {:?}",
161 callee_account, err
162 )
163 });
164
165 let handler = env
166 .engine
167 .database
168 .get_contract_message_handler(&callee_code_hash);
169 let result = handler(input);
170
171 R::decode_output(&result)
172}
173
174#[cfg(feature = "unstable-hostfn")]
175impl CryptoHash for Blake2x128 {
176 fn hash(input: &[u8], output: &mut <Self as HashOutput>::Type) {
177 type OutputType = [u8; 16];
178 static_assertions::assert_type_eq_all!(
179 <Blake2x128 as HashOutput>::Type,
180 OutputType
181 );
182 let output: &mut OutputType = array_mut_ref!(output, 0, 16);
183 Engine::hash_blake2_128(input, output);
184 }
185}
186
187#[cfg(feature = "unstable-hostfn")]
188impl CryptoHash for Blake2x256 {
189 fn hash(input: &[u8], output: &mut <Self as HashOutput>::Type) {
190 type OutputType = [u8; 32];
191 static_assertions::assert_type_eq_all!(
192 <Blake2x256 as HashOutput>::Type,
193 OutputType
194 );
195 let output: &mut OutputType = array_mut_ref!(output, 0, 32);
196 Engine::hash_blake2_256(input, output);
197 }
198}
199
200impl CryptoHash for Sha2x256 {
201 fn hash(input: &[u8], output: &mut <Self as HashOutput>::Type) {
202 type OutputType = [u8; 32];
203 static_assertions::assert_type_eq_all!(
204 <Sha2x256 as HashOutput>::Type,
205 OutputType
206 );
207 let output: &mut OutputType = array_mut_ref!(output, 0, 32);
208 Engine::hash_sha2_256(input, output);
209 }
210}
211
212impl CryptoHash for Keccak256 {
213 fn hash(input: &[u8], output: &mut <Self as HashOutput>::Type) {
214 type OutputType = [u8; 32];
215 static_assertions::assert_type_eq_all!(
216 <Keccak256 as HashOutput>::Type,
217 OutputType
218 );
219 let output: &mut OutputType = array_mut_ref!(output, 0, 32);
220 Engine::hash_keccak_256(input, output);
221 }
222}
223
224#[derive(Default)]
225pub struct TopicsBuilder {
226 pub topics: Vec<Vec<u8>>,
227}
228
229impl<E> TopicsBuilderBackend<E> for TopicsBuilder
230where
231 E: Environment,
232{
233 type Output = Vec<u8>;
234
235 #[cfg(feature = "unstable-hostfn")]
236 fn push_topic<T>(&mut self, topic_value: &T)
237 where
238 T: scale::Encode,
239 {
240 let encoded = topic_value.encode();
242 let len_encoded = encoded.len();
243 let mut result = <E as Environment>::Hash::CLEAR_HASH;
244 let len_result = result.as_ref().len();
245 if len_encoded <= len_result {
246 result.as_mut()[..len_encoded].copy_from_slice(&encoded[..]);
247 } else {
248 let mut hash_output = <Blake2x256 as HashOutput>::Type::default();
249 <Blake2x256 as CryptoHash>::hash(&encoded[..], &mut hash_output);
250 let copy_len = core::cmp::min(hash_output.len(), len_result);
251 result.as_mut()[0..copy_len].copy_from_slice(&hash_output[0..copy_len]);
252 }
253 let off_hash = result.as_ref();
254 let off_hash = off_hash.to_vec();
255 self.topics.push(off_hash);
256 }
257
258 fn output(self) -> Self::Output {
259 let mut all: Vec<u8> = Vec::new();
260
261 let topics_len_compact = &scale::Compact(self.topics.len() as u32);
262 let topics_encoded = &scale::Encode::encode(&topics_len_compact)[..];
263 all.append(&mut topics_encoded.to_vec());
264
265 self.topics.into_iter().for_each(|mut v| all.append(&mut v));
266 all
267 }
268}
269
270impl EnvInstance {
271 fn get_property<T>(
273 &mut self,
274 ext_fn: fn(engine: &Engine, output: &mut &mut [u8]),
275 ) -> Result<T>
276 where
277 T: scale::Decode,
278 {
279 let mut full_scope: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE];
280 let full_scope = &mut &mut full_scope[..];
281 ext_fn(&self.engine, full_scope);
282 scale::Decode::decode(&mut &full_scope[..]).map_err(Into::into)
283 }
284
285 pub fn get_return_value(&mut self) -> Vec<u8> {
286 self.engine.get_storage(&[255_u8; 32]).unwrap().to_vec()
287 }
288
289 pub fn upload_code<ContractRef>(&mut self) -> H256
290 where
291 ContractRef: crate::ContractReverseReference,
292 <ContractRef as crate::ContractReverseReference>::Type:
293 crate::reflect::ContractMessageDecoder,
294 {
295 H256::from(
296 self.engine
297 .database
298 .set_contract_message_handler(execute_contract_call::<ContractRef>),
299 )
300 }
301}
302
303impl EnvBackend for EnvInstance {
304 fn set_contract_storage<K, V>(&mut self, key: &K, value: &V) -> Option<u32>
305 where
306 K: scale::Encode,
307 V: Storable,
308 {
309 let mut v = vec![];
310 Storable::encode(value, &mut v);
311 self.engine.set_storage(&key.encode(), &v[..])
312 }
313
314 fn get_contract_storage<K, R>(&mut self, key: &K) -> Result<Option<R>>
315 where
316 K: scale::Encode,
317 R: Storable,
318 {
319 match self.engine.get_storage(&key.encode()) {
320 Ok(res) => {
321 let decoded = decode_all(&mut &res[..])?;
322 Ok(Some(decoded))
323 }
324 Err(ReturnErrorCode::KeyNotFound) => Ok(None),
325 Err(_) => panic!("encountered unexpected error"),
326 }
327 }
328
329 #[cfg(feature = "unstable-hostfn")]
330 fn take_contract_storage<K, R>(&mut self, key: &K) -> Result<Option<R>>
331 where
332 K: scale::Encode,
333 R: Storable,
334 {
335 match self.engine.take_storage(&key.encode()) {
336 Ok(output) => {
337 let decoded = decode_all(&mut &output[..])?;
338 Ok(Some(decoded))
339 }
340 Err(ReturnErrorCode::KeyNotFound) => Ok(None),
341 Err(_) => panic!("encountered unexpected error"),
342 }
343 }
344
345 #[cfg(feature = "unstable-hostfn")]
346 fn contains_contract_storage<K>(&mut self, key: &K) -> Option<u32>
347 where
348 K: scale::Encode,
349 {
350 self.engine.contains_storage(&key.encode())
351 }
352
353 #[cfg(feature = "unstable-hostfn")]
354 fn clear_contract_storage<K>(&mut self, key: &K) -> Option<u32>
355 where
356 K: scale::Encode,
357 {
358 self.engine.clear_storage(&key.encode())
359 }
360
361 fn decode_input<T>(&mut self) -> core::result::Result<T, DispatchError>
362 where
363 T: DecodeDispatch,
364 {
365 unimplemented!("the off-chain env does not implement `input`")
366 }
367
368 #[cfg(not(feature = "std"))]
369 fn return_value<R>(&mut self, _flags: ReturnFlags, _return_value: &R) -> !
370 where
371 R: scale::Encode,
372 {
373 panic!("enable feature `std` to use `return_value()`")
374 }
375
376 #[cfg(feature = "std")]
377 fn return_value<R>(&mut self, _flags: ReturnFlags, return_value: &R)
378 where
379 R: scale::Encode,
380 {
381 let mut v = vec![];
382 return_value.encode_to(&mut v);
383 self.engine.set_storage(&[255_u8; 32], &v[..]);
384 }
385
386 fn return_value_solidity<R>(&mut self, _flags: ReturnFlags, _return_value: &R) -> !
387 where
388 R: for<'a> SolEncode<'a>,
389 {
390 unimplemented!("the off-chain env does not implement `return_value_solidity`")
391 }
392
393 fn hash_bytes<H>(&mut self, input: &[u8], output: &mut <H as HashOutput>::Type)
394 where
395 H: CryptoHash,
396 {
397 <H as CryptoHash>::hash(input, output)
398 }
399
400 fn hash_encoded<H, T>(&mut self, input: &T, output: &mut <H as HashOutput>::Type)
401 where
402 H: CryptoHash,
403 T: scale::Encode,
404 {
405 let enc_input = &scale::Encode::encode(input)[..];
406 <H as CryptoHash>::hash(enc_input, output)
407 }
408
409 #[allow(clippy::arithmetic_side_effects)] fn ecdsa_recover(
411 &mut self,
412 signature: &[u8; 65],
413 message_hash: &[u8; 32],
414 output: &mut [u8; 33],
415 ) -> Result<()> {
416 use secp256k1::{
417 ecdsa::{
418 RecoverableSignature,
419 RecoveryId,
420 },
421 Message,
422 SECP256K1,
423 };
424
425 let recovery_byte = if signature[64] > 26 {
429 signature[64] - 27
430 } else {
431 signature[64]
432 };
433 let recovery_id = RecoveryId::try_from(recovery_byte as i32)
434 .unwrap_or_else(|error| panic!("Unable to parse the recovery id: {error}"));
435 let message = Message::from_digest_slice(message_hash).unwrap_or_else(|error| {
436 panic!("Unable to create the message from hash: {error}")
437 });
438 let signature =
439 RecoverableSignature::from_compact(&signature[0..64], recovery_id)
440 .unwrap_or_else(|error| panic!("Unable to parse the signature: {error}"));
441
442 let pub_key = SECP256K1.recover_ecdsa(&message, &signature);
443 match pub_key {
444 Ok(pub_key) => {
445 *output = pub_key.serialize();
446 Ok(())
447 }
448 Err(_) => Err(ReturnErrorCode::EcdsaRecoveryFailed.into()),
449 }
450 }
451
452 #[cfg(feature = "unstable-hostfn")]
453 fn ecdsa_to_eth_address(
454 &mut self,
455 pubkey: &[u8; 33],
456 output: &mut [u8; 20],
457 ) -> Result<()> {
458 let pk = secp256k1::PublicKey::from_slice(pubkey)
459 .map_err(|_| ReturnErrorCode::EcdsaRecoveryFailed)?;
460 let uncompressed = pk.serialize_uncompressed();
461 let mut hash = <Keccak256 as HashOutput>::Type::default();
462 <Keccak256>::hash(&uncompressed[1..], &mut hash);
463 output.as_mut().copy_from_slice(&hash[12..]);
464 Ok(())
465 }
466
467 #[cfg(feature = "unstable-hostfn")]
468 fn sr25519_verify(
469 &mut self,
470 signature: &[u8; 64],
471 message: &[u8],
472 pub_key: &[u8; 32],
473 ) -> Result<()> {
474 let context = b"substrate";
478 let signature: Signature = Signature::from_bytes(signature)
480 .map_err(|_| ReturnErrorCode::Sr25519VerifyFailed)?;
481 let public_key: PublicKey = PublicKey::from_bytes(pub_key)
483 .map_err(|_| ReturnErrorCode::Sr25519VerifyFailed)?;
484 public_key
486 .verify_simple(context, message, &signature)
487 .map_err(|_| ReturnErrorCode::Sr25519VerifyFailed.into())
488 }
489
490 #[cfg(feature = "unstable-hostfn")]
491 fn call_chain_extension<I, T, E, ErrorCode, F, D>(
492 &mut self,
493 id: u32,
494 input: &I,
495 status_to_result: F,
496 decode_to_result: D,
497 ) -> ::core::result::Result<T, E>
498 where
499 I: scale::Encode,
500 T: scale::Decode,
501 E: From<ErrorCode>,
502 F: FnOnce(u32) -> ::core::result::Result<(), ErrorCode>,
503 D: FnOnce(&[u8]) -> ::core::result::Result<T, E>,
504 {
505 let enc_input = &scale::Encode::encode(input)[..];
506 let mut output: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE];
507
508 self.engine
509 .call_chain_extension(id, enc_input, &mut &mut output[..]);
510 let (status, out): (u32, Vec<u8>) = scale::Decode::decode(&mut &output[..])
511 .unwrap_or_else(|error| {
512 panic!("could not decode `call_chain_extension` output: {error:?}")
513 });
514
515 status_to_result(status)?;
516 let decoded = decode_to_result(&out[..])?;
517 Ok(decoded)
518 }
519
520 #[cfg(feature = "unstable-hostfn")]
521 fn set_code_hash(&mut self, code_hash: &H256) -> Result<()> {
522 self.engine
523 .database
524 .set_code_hash(&self.engine.get_callee(), code_hash);
525 Ok(())
526 }
527}
528
529impl TypedEnvBackend for EnvInstance {
530 fn caller(&mut self) -> Address {
531 self.get_property::<Address>(Engine::caller)
532 .unwrap_or_else(|error| panic!("could not read `caller` property: {error:?}"))
533 }
534
535 fn transferred_value(&mut self) -> U256 {
536 self.get_property(Engine::value_transferred)
537 .unwrap_or_else(|error| {
538 panic!("could not read `transferred_value` property: {error:?}")
539 })
540 }
541
542 fn block_timestamp<E: Environment>(&mut self) -> E::Timestamp {
543 self.get_property::<E::Timestamp>(Engine::block_timestamp)
544 .unwrap_or_else(|error| {
545 panic!("could not read `block_timestamp` property: {error:?}")
546 })
547 }
548
549 #[cfg(feature = "unstable-hostfn")]
550 fn account_id<E: Environment>(&mut self) -> E::AccountId {
551 self.get_property::<E::AccountId>(Engine::address)
553 .unwrap_or_else(|error| {
554 panic!("could not read `account_id` property: {error:?}")
555 })
556 }
557
558 fn address(&mut self) -> Address {
559 self.get_property::<Address>(Engine::address)
560 .unwrap_or_else(|error| {
561 panic!("could not read `account_id` property: {error:?}")
562 })
563 }
564
565 fn balance(&mut self) -> U256 {
566 self.get_property::<U256>(Engine::balance)
567 .unwrap_or_else(|error| {
568 panic!("could not read `balance` property: {error:?}")
569 })
570 }
571
572 fn block_number<E: Environment>(&mut self) -> E::BlockNumber {
573 self.get_property::<E::BlockNumber>(Engine::block_number)
574 .unwrap_or_else(|error| {
575 panic!("could not read `block_number` property: {error:?}")
576 })
577 }
578
579 #[cfg(feature = "unstable-hostfn")]
580 fn minimum_balance<E: Environment>(&mut self) -> E::Balance {
581 self.get_property::<E::Balance>(Engine::minimum_balance)
582 .unwrap_or_else(|error| {
583 panic!("could not read `minimum_balance` property: {error:?}")
584 })
585 }
586
587 fn emit_event<E, Evt>(&mut self, event: Evt)
588 where
589 E: Environment,
590 Evt: Event,
591 {
592 let builder = TopicsBuilder::default();
593 let enc_topics = event.topics::<E, _>(builder.into());
594 let enc_data = &scale::Encode::encode(&event)[..];
595 self.engine.deposit_event(&enc_topics[..], enc_data);
596 }
597
598 fn invoke_contract<E, Args, R, Abi>(
599 &mut self,
600 params: &CallParams<E, Call, Args, R, Abi>,
601 ) -> Result<ink_primitives::MessageResult<R>>
602 where
603 E: Environment,
604 Args: AbiEncodeWith<Abi>,
605 R: AbiDecodeWith<Abi> + DecodeMessageResult<Abi>,
606 {
607 let call_flags = params.call_flags().bits();
608 let transferred_value = params.transferred_value();
609 let input = params.exec_input().encode();
610 let callee_account = params.callee();
611
612 invoke_contract_impl::<R, Abi>(
613 self,
614 None,
615 call_flags,
616 Some(transferred_value),
617 *callee_account, input,
619 )
620 }
621
622 fn invoke_contract_delegate<E, Args, R, Abi>(
623 &mut self,
624 params: &CallParams<E, DelegateCall, Args, R, Abi>,
625 ) -> Result<ink_primitives::MessageResult<R>>
626 where
627 E: Environment,
628 Args: AbiEncodeWith<Abi>,
629 R: AbiDecodeWith<Abi> + DecodeMessageResult<Abi>,
630 {
631 let _addr = params.address(); let call_flags = params.call_flags().bits();
633 let input = params.exec_input().encode();
634
635 invoke_contract_impl_delegate::<R, Abi>(
636 self,
637 None,
638 call_flags,
639 None,
640 *params.address(),
641 input,
642 )
643 }
644
645 #[cfg(feature = "unstable-hostfn")]
646 fn instantiate_contract<E, ContractRef, Args, R>(
647 &mut self,
648 params: &CreateParams<E, ContractRef, LimitParamsV2, Args, R>,
649 ) -> Result<
650 ink_primitives::ConstructorResult<
651 <R as ConstructorReturnType<ContractRef>>::Output,
652 >,
653 >
654 where
655 E: Environment,
656 ContractRef: FromAddr + crate::ContractReverseReference,
657 <ContractRef as crate::ContractReverseReference>::Type:
658 crate::reflect::ContractConstructorDecoder,
659 Args: AbiEncodeWith<ScaleEncoding>,
660 R: ConstructorReturnType<ContractRef>,
661 {
662 let endowment = params.endowment();
663 let salt_bytes = params.salt_bytes();
664 let code_hash = params.code_hash();
665
666 let input = params.exec_input().encode();
667
668 let account_id_vec = {
670 let mut account_input = Vec::<u8>::new();
671 account_input.extend(&b"contract_addr".to_vec());
672 scale::Encode::encode_to(
673 &self.engine.exec_context.caller.as_bytes(),
674 &mut account_input,
675 );
676 account_input.extend(&code_hash.0);
677 account_input.extend(&input);
678 if let Some(salt) = salt_bytes {
679 account_input.extend(salt);
680 }
681 let mut account_id = [0_u8; 32];
682 ink_engine::hashing::blake2b_256(&account_input[..], &mut account_id);
683 account_id.to_vec()
684 };
685 let contract_addr = AccountIdMapper::to_address(&account_id_vec[..]);
687
688 let old_callee = self.engine.get_callee();
689 self.engine.set_callee(contract_addr);
690
691 let dispatch = <
692 <
693 <
694 ContractRef
695 as crate::ContractReverseReference
696 >::Type
697 as crate::reflect::ContractConstructorDecoder
698 >::Type
699 as DecodeDispatch
700 >::decode_dispatch(&mut &input[..])
701 .unwrap_or_else(|e| panic!("Failed to decode constructor call: {:?}", e));
702 crate::reflect::ExecuteDispatchable::execute_dispatchable(dispatch)
703 .unwrap_or_else(|e| panic!("Constructor call failed: {:?}", e));
704
705 self.set_code_hash(code_hash)?;
706 self.engine.set_contract(callee());
707 self.engine
708 .database
709 .set_balance(&callee(), *endowment);
711
712 self.engine.set_callee(old_callee);
714
715 Ok(Ok(R::ok(<ContractRef as FromAddr>::from_addr(
716 contract_addr,
717 ))))
718 }
719
720 #[cfg(feature = "unstable-hostfn")]
721 fn terminate_contract(&mut self, beneficiary: Address) -> ! {
722 self.engine.terminate(beneficiary)
723 }
724
725 fn transfer<E>(&mut self, destination: Address, value: U256) -> Result<()>
726 where
727 E: Environment,
728 {
729 let enc_value = &scale::Encode::encode(&value)[..];
730 self.engine
731 .transfer(destination, enc_value)
732 .map_err(Into::into)
733 }
734
735 fn weight_to_fee<E: Environment>(&mut self, gas: u64) -> E::Balance {
736 let mut output: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE];
737 self.engine.weight_to_fee(gas, &mut &mut output[..]);
738 scale::Decode::decode(&mut &output[..]).unwrap_or_else(|error| {
739 panic!("could not read `weight_to_fee` property: {error:?}")
740 })
741 }
742
743 #[cfg(feature = "unstable-hostfn")]
744 fn is_contract(&mut self, account: &Address) -> bool {
745 self.engine.is_contract(account)
746 }
747
748 #[cfg(feature = "unstable-hostfn")]
749 fn caller_is_origin<E>(&mut self) -> bool
750 where
751 E: Environment,
752 {
753 unimplemented!("off-chain environment does not support cross-contract calls")
754 }
755
756 #[cfg(feature = "unstable-hostfn")]
757 fn caller_is_root<E>(&mut self) -> bool
758 where
759 E: Environment,
760 {
761 unimplemented!("off-chain environment does not support `caller_is_root`")
762 }
763
764 fn code_hash(&mut self, addr: &Address) -> Result<H256> {
765 let code_hash = self.engine.database.get_code_hash(addr);
766 if let Some(code_hash) = code_hash {
767 let code_hash = H256::decode(&mut &code_hash[..]).unwrap();
769 Ok(code_hash)
770 } else {
771 Err(ReturnErrorCode::KeyNotFound.into())
772 }
773 }
774
775 #[cfg(feature = "unstable-hostfn")]
776 fn own_code_hash(&mut self) -> Result<H256> {
777 let callee = &self.engine.get_callee();
778 let code_hash = self.engine.database.get_code_hash(callee);
779 if let Some(code_hash) = code_hash {
780 Ok(code_hash)
781 } else {
782 Err(ReturnErrorCode::KeyNotFound.into())
783 }
784 }
785
786 #[cfg(feature = "unstable-hostfn")]
787 fn call_runtime<E, Call>(&mut self, _call: &Call) -> Result<()>
788 where
789 E: Environment,
790 {
791 unimplemented!("off-chain environment does not support `call_runtime`")
792 }
793
794 #[cfg(feature = "unstable-hostfn")]
795 fn xcm_execute<E, Call>(&mut self, _msg: &xcm::VersionedXcm<Call>) -> Result<()>
796 where
797 E: Environment,
798 {
799 unimplemented!("off-chain environment does not support `xcm_execute`")
800 }
801
802 #[cfg(feature = "unstable-hostfn")]
803 fn xcm_send<E, Call>(
804 &mut self,
805 _dest: &xcm::VersionedLocation,
806 _msg: &xcm::VersionedXcm<Call>,
807 ) -> Result<xcm::v4::XcmHash>
808 where
809 E: Environment,
810 {
811 unimplemented!("off-chain environment does not support `xcm_send`")
812 }
813}