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::{
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
85/// The capacity of the static buffer.
86/// This is the same size as the ink! on-chain environment. We chose to use the same size
87/// to be as close to the on-chain behavior as possible.
88const BUFFER_SIZE: usize = crate::BUFFER_SIZE;
89
90/// Proxy function used to simulate code hash and to invoke contract methods.
91fn 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        // todo
241        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    /// Returns the contract property value.
272    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)] // todo
410    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        // In most implementations, the v is just 0 or 1 internally, but 27 was added
426        // as an arbitrary number for signing Bitcoin messages and Ethereum adopted that
427        // as well.
428        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        // the context associated with the signing (specific to the sr25519 algorithm)
475        // defaults to "substrate" in substrate, but could be different elsewhere
476        // https://github.com/paritytech/substrate/blob/c32f5ed2ae6746d6f791f08cecbfc22fa188f5f9/primitives/core/src/sr25519.rs#L60
477        let context = b"substrate";
478        // attempt to parse a signature from bytes
479        let signature: Signature = Signature::from_bytes(signature)
480            .map_err(|_| ReturnErrorCode::Sr25519VerifyFailed)?;
481        // attempt to parse a public key from bytes
482        let public_key: PublicKey = PublicKey::from_bytes(pub_key)
483            .map_err(|_| ReturnErrorCode::Sr25519VerifyFailed)?;
484        // verify the signature
485        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        // todo should not use `Engine::account_id`
552        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, // todo possibly return no reference from callee()
618            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(); // todo remove
632        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        // Compute address for instantiated contract.
669        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        // todo don't convert to vec and back, simplify type
686        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            // todo passing the types instead of refs would be better
710            .set_balance(&callee(), *endowment);
711
712        // todo why?
713        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            // todo
768            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}