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