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 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
80/// The capacity of the static buffer.
81/// This is the same size as the ink! on-chain environment. We chose to use the same size
82/// to be as close to the on-chain behavior as possible.
83const BUFFER_SIZE: usize = crate::BUFFER_SIZE;
84
85/// Proxy function used to simulate code hash and to invoke contract methods.
86fn 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    // TODO: (@davidsemakula) Track return flag and set `did_revert` as appropriate.
137    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    // TODO: (@davidsemakula) Track return flag and set `did_revert` as appropriate.
162    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        // todo
232        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    /// Returns the contract property value.
263    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)] // todo
401    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        // In most implementations, the v is just 0 or 1 internally, but 27 was added
417        // as an arbitrary number for signing Bitcoin messages and Ethereum adopted that
418        // as well.
419        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        // the context associated with the signing (specific to the sr25519 algorithm)
466        // defaults to "substrate" in substrate, but could be different elsewhere
467        // https://github.com/paritytech/substrate/blob/c32f5ed2ae6746d6f791f08cecbfc22fa188f5f9/primitives/core/src/sr25519.rs#L60
468        let context = b"substrate";
469        // attempt to parse a signature from bytes
470        let signature: Signature = Signature::from_bytes(signature)
471            .map_err(|_| ReturnErrorCode::Sr25519VerifyFailed)?;
472        // attempt to parse a public key from bytes
473        let public_key: PublicKey = PublicKey::from_bytes(pub_key)
474            .map_err(|_| ReturnErrorCode::Sr25519VerifyFailed)?;
475        // verify the signature
476        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        // todo should not use `Engine::account_id`
543        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, // todo possibly return no reference from callee()
609            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(); // todo remove
623        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        // Compute address for instantiated contract.
660        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        // todo don't convert to vec and back, simplify type
677        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            // todo passing the types instead of refs would be better
701            .set_balance(&callee(), *endowment);
702
703        // todo why?
704        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            // todo
759            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}