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