ink_env/engine/off_chain/
impls.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 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
81/// The capacity of the static buffer.
82/// This is the same size as the ink! on-chain environment. We chose to use the same size
83/// to be as close to the on-chain behavior as possible.
84const BUFFER_SIZE: usize = crate::BUFFER_SIZE;
85
86/// Proxy function used to simulate code hash and to invoke contract methods.
87fn 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        // todo
237        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    /// Returns the contract property value.
268    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)] // todo
406    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        // In most implementations, the v is just 0 or 1 internally, but 27 was added
422        // as an arbitrary number for signing Bitcoin messages and Ethereum adopted that
423        // as well.
424        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        // the context associated with the signing (specific to the sr25519 algorithm)
471        // defaults to "substrate" in substrate, but could be different elsewhere
472        // https://github.com/paritytech/substrate/blob/c32f5ed2ae6746d6f791f08cecbfc22fa188f5f9/primitives/core/src/sr25519.rs#L60
473        let context = b"substrate";
474        // attempt to parse a signature from bytes
475        let signature: Signature = Signature::from_bytes(signature)
476            .map_err(|_| ReturnErrorCode::Sr25519VerifyFailed)?;
477        // attempt to parse a public key from bytes
478        let public_key: PublicKey = PublicKey::from_bytes(pub_key)
479            .map_err(|_| ReturnErrorCode::Sr25519VerifyFailed)?;
480        // verify the signature
481        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        // todo should not use `Engine::account_id`
548        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, // todo possibly return no reference from callee()
614            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(); // todo remove
628        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        // Compute address for instantiated contract.
665        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        // todo don't convert to vec and back, simplify type
682        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            // todo passing the types instead of refs would be better
706            .set_balance(&callee(), *endowment);
707
708        // todo why?
709        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            // todo
764            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}