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 = "xcm")]
17use ink_primitives::Weight;
18use ink_primitives::{
19    Address,
20    CodeHashErr,
21    H256,
22    U256,
23    abi::{
24        AbiEncodeWith,
25        Ink,
26        Sol,
27    },
28    sol::SolResultEncode,
29    types::{
30        AccountIdMapper,
31        Environment,
32    },
33};
34use ink_storage_traits::{
35    Storable,
36    decode_all,
37};
38use pallet_revive_uapi::{
39    ReturnErrorCode,
40    ReturnFlags,
41};
42#[cfg(feature = "unstable-hostfn")]
43use schnorrkel::{
44    PublicKey,
45    Signature,
46};
47
48use super::EnvInstance;
49use crate::{
50    DecodeDispatch,
51    DispatchError,
52    EnvBackend,
53    Result,
54    TypedEnvBackend,
55    call::{
56        Call,
57        CallParams,
58        ConstructorReturnType,
59        CreateParams,
60        DelegateCall,
61        FromAddr,
62        LimitParamsV2,
63        utils::{
64            DecodeMessageResult,
65            EncodeArgsWith,
66        },
67    },
68    event::{
69        Event,
70        TopicEncoder,
71        TopicsBuilderBackend,
72    },
73    hash::{
74        Blake2x128,
75        Blake2x256,
76        CryptoHash,
77        HashOutput,
78        Keccak256,
79        Sha2x256,
80    },
81    test::callee,
82};
83
84/// The capacity of the static buffer.
85/// This is the same size as the ink! on-chain environment. We chose to use the same size
86/// to be as close to the on-chain behavior as possible.
87const BUFFER_SIZE: usize = crate::BUFFER_SIZE;
88
89/// Proxy function used to simulate code hash and to invoke contract methods.
90fn execute_contract_call<ContractRef>(input: Vec<u8>) -> Vec<u8>
91where
92    ContractRef: crate::ContractReverseReference,
93    <ContractRef as crate::ContractReverseReference>::Type:
94        crate::reflect::ContractMessageDecoder,
95{
96    let dispatch = <
97        <
98            <
99                ContractRef
100                as crate::ContractReverseReference
101            >::Type
102            as crate::reflect::ContractMessageDecoder
103        >::Type
104        as DecodeDispatch
105    >::decode_dispatch(&mut &input[..])
106        .unwrap_or_else(|e| panic!("Failed to decode constructor call: {e:?}"));
107
108    crate::reflect::ExecuteDispatchable::execute_dispatchable(dispatch)
109        .unwrap_or_else(|e| panic!("Message call failed: {e:?}"));
110
111    crate::test::get_return_value()
112}
113
114fn invoke_contract_impl<R, Abi>(
115    env: &mut EnvInstance,
116    _gas_limit: Option<u64>,
117    _call_flags: u32,
118    _transferred_value: Option<&U256>,
119    callee_account: Address,
120    input: Vec<u8>,
121) -> Result<ink_primitives::MessageResult<R>>
122where
123    R: DecodeMessageResult<Abi>,
124{
125    let callee_code_hash = env.code_hash(&callee_account).unwrap_or_else(|err| {
126        panic!("failed getting code hash for {callee_account:?}: {err:?}")
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    // TODO: (@davidsemakula) Track return flag and set `did_revert` as appropriate.
141    R::decode_output(&result, false)
142}
143
144fn invoke_contract_impl_delegate<R, Abi>(
145    env: &mut EnvInstance,
146    _gas_limit: Option<u64>,
147    _call_flags: u32,
148    _transferred_value: Option<&U256>,
149    callee_account: Address,
150    input: Vec<u8>,
151) -> Result<ink_primitives::MessageResult<R>>
152where
153    R: DecodeMessageResult<Abi>,
154{
155    let callee_code_hash = env.code_hash(&callee_account).unwrap_or_else(|err| {
156        panic!("failed getting code hash for {callee_account:?}: {err:?}")
157    });
158
159    let handler = env
160        .engine
161        .database
162        .get_contract_message_handler(&callee_code_hash);
163    let result = handler(input);
164
165    // TODO: (@davidsemakula) Track return flag and set `did_revert` as appropriate.
166    R::decode_output(&result, false)
167}
168
169impl CryptoHash for Blake2x128 {
170    fn hash(input: &[u8], output: &mut <Self as HashOutput>::Type) {
171        type OutputType = [u8; 16];
172        static_assertions::assert_type_eq_all!(
173            <Blake2x128 as HashOutput>::Type,
174            OutputType
175        );
176        let output: &mut OutputType = array_mut_ref!(output, 0, 16);
177        Engine::hash_blake2_128(input, output);
178    }
179
180    fn hash_with_buffer(
181        _input: &[u8],
182        _buffer: &mut [u8],
183        _output: &mut <Self as HashOutput>::Type,
184    ) {
185        unreachable!("not required, `hash` has been implemented.");
186    }
187}
188
189impl CryptoHash for Blake2x256 {
190    fn hash(input: &[u8], output: &mut <Self as HashOutput>::Type) {
191        // For the engine (simulation of `pallet-revive` in `std`) we skip on the buffer
192        // in the implementation for simplicity.
193        type OutputType = [u8; 32];
194        static_assertions::assert_type_eq_all!(
195            <Blake2x256 as HashOutput>::Type,
196            OutputType
197        );
198        let output: &mut OutputType = array_mut_ref!(output, 0, 32);
199        Engine::hash_blake2_256(input, output);
200    }
201
202    fn hash_with_buffer(
203        _input: &[u8],
204        _buffer: &mut [u8],
205        _output: &mut <Self as HashOutput>::Type,
206    ) {
207        unreachable!("not required, `hash` has been implemented.");
208    }
209}
210
211impl CryptoHash for Sha2x256 {
212    fn hash(input: &[u8], output: &mut <Self as HashOutput>::Type) {
213        // For the engine (simulation of `pallet-revive` in `std`) we skip on the buffer
214        // in the implementation for simplicity.
215        type OutputType = [u8; 32];
216        static_assertions::assert_type_eq_all!(
217            <Sha2x256 as HashOutput>::Type,
218            OutputType
219        );
220        let output: &mut OutputType = array_mut_ref!(output, 0, 32);
221        Engine::hash_sha2_256(input, output);
222    }
223
224    fn hash_with_buffer(
225        _input: &[u8],
226        _buffer: &mut [u8],
227        _output: &mut <Self as HashOutput>::Type,
228    ) {
229        unreachable!("not required, `hash` has been implemented.");
230    }
231}
232
233impl CryptoHash for Keccak256 {
234    fn hash(input: &[u8], output: &mut <Self as HashOutput>::Type) {
235        type OutputType = [u8; 32];
236        static_assertions::assert_type_eq_all!(
237            <Keccak256 as HashOutput>::Type,
238            OutputType
239        );
240        let output: &mut OutputType = array_mut_ref!(output, 0, 32);
241        Engine::hash_keccak_256(input, output);
242    }
243
244    fn hash_with_buffer(
245        _input: &[u8],
246        _buffer: &mut [u8],
247        _output: &mut <Self as HashOutput>::Type,
248    ) {
249        unreachable!("not required, `hash` has been implemented.");
250    }
251}
252
253#[derive(Default)]
254pub struct TopicsBuilder {
255    pub topics: Vec<[u8; 32]>,
256}
257
258impl<Abi> TopicsBuilderBackend<Abi> for TopicsBuilder
259where
260    Abi: TopicEncoder,
261{
262    type Output = Vec<[u8; 32]>;
263
264    fn push_topic<T>(&mut self, topic_value: &T)
265    where
266        T: AbiEncodeWith<Abi>,
267    {
268        let output = <Abi as TopicEncoder>::encode_topic(topic_value);
269        self.topics.push(output);
270    }
271
272    fn output(self) -> Self::Output {
273        self.topics
274    }
275}
276
277impl TopicEncoder for Ink {
278    const REQUIRES_BUFFER: bool = false;
279
280    fn encode_topic<T>(value: &T) -> [u8; 32]
281    where
282        T: AbiEncodeWith<Self>,
283    {
284        value.encode_topic(<Blake2x256 as CryptoHash>::hash)
285    }
286
287    fn encode_topic_with_hash_buffer<T>(
288        _value: &T,
289        _output: &mut [u8; 32],
290        _buffer: &mut [u8],
291    ) where
292        T: AbiEncodeWith<Self>,
293    {
294        unreachable!("not required, `encode_topic` has been implemented.");
295    }
296}
297
298impl TopicEncoder for Sol {
299    const REQUIRES_BUFFER: bool = false;
300
301    fn encode_topic<T>(value: &T) -> [u8; 32]
302    where
303        T: AbiEncodeWith<Self>,
304    {
305        value.encode_topic(<Keccak256 as CryptoHash>::hash)
306    }
307
308    fn encode_topic_with_hash_buffer<T>(
309        _value: &T,
310        _output: &mut [u8; 32],
311        _buffer: &mut [u8],
312    ) where
313        T: AbiEncodeWith<Self>,
314    {
315        unreachable!("not required, `encode_topic` has been implemented.");
316    }
317}
318
319impl EnvInstance {
320    /// Returns the contract property value.
321    fn get_property<T>(
322        &mut self,
323        ext_fn: fn(engine: &Engine, output: &mut &mut [u8]),
324    ) -> Result<T>
325    where
326        T: scale::Decode,
327    {
328        let mut full_scope: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE];
329        let full_scope = &mut &mut full_scope[..];
330        ext_fn(&self.engine, full_scope);
331        scale::Decode::decode(&mut &full_scope[..]).map_err(Into::into)
332    }
333
334    pub fn get_return_value(&mut self) -> Vec<u8> {
335        self.engine.get_storage(&[255_u8; 32]).unwrap().to_vec()
336    }
337
338    pub fn upload_code<ContractRef>(&mut self) -> H256
339    where
340        ContractRef: crate::ContractReverseReference,
341        <ContractRef as crate::ContractReverseReference>::Type:
342            crate::reflect::ContractMessageDecoder,
343    {
344        H256::from(
345            self.engine
346                .database
347                .set_contract_message_handler(execute_contract_call::<ContractRef>),
348        )
349    }
350}
351
352impl EnvBackend for EnvInstance {
353    fn set_contract_storage<K, V>(&mut self, key: &K, value: &V) -> Option<u32>
354    where
355        K: scale::Encode,
356        V: Storable,
357    {
358        let mut v = vec![];
359        Storable::encode(value, &mut v);
360        self.engine.set_storage(&key.encode(), &v[..])
361    }
362
363    fn get_contract_storage<K, R>(&mut self, key: &K) -> Result<Option<R>>
364    where
365        K: scale::Encode,
366        R: Storable,
367    {
368        match self.engine.get_storage(&key.encode()) {
369            Ok(res) => {
370                let decoded = decode_all(&mut &res[..])?;
371                Ok(Some(decoded))
372            }
373            Err(ReturnErrorCode::KeyNotFound) => Ok(None),
374            Err(_) => panic!("encountered unexpected error"),
375        }
376    }
377
378    fn take_contract_storage<K, R>(&mut self, key: &K) -> Result<Option<R>>
379    where
380        K: scale::Encode,
381        R: Storable,
382    {
383        match self.engine.take_storage(&key.encode()) {
384            Ok(output) => {
385                let decoded = decode_all(&mut &output[..])?;
386                Ok(Some(decoded))
387            }
388            Err(ReturnErrorCode::KeyNotFound) => Ok(None),
389            Err(_) => panic!("encountered unexpected error"),
390        }
391    }
392
393    fn remaining_buffer(&mut self) -> usize {
394        BUFFER_SIZE
395    }
396
397    fn contains_contract_storage<K>(&mut self, key: &K) -> Option<u32>
398    where
399        K: scale::Encode,
400    {
401        self.engine.contains_storage(&key.encode())
402    }
403
404    fn clear_contract_storage<K>(&mut self, key: &K) -> Option<u32>
405    where
406        K: scale::Encode,
407    {
408        self.engine.clear_storage(&key.encode())
409    }
410
411    fn decode_input<T>(&mut self) -> core::result::Result<T, DispatchError>
412    where
413        T: DecodeDispatch,
414    {
415        unimplemented!("the off-chain env does not implement `input`")
416    }
417
418    #[cfg(not(feature = "std"))]
419    fn return_value<R>(&mut self, _flags: ReturnFlags, _return_value: &R) -> !
420    where
421        R: scale::Encode,
422    {
423        panic!("enable feature `std` to use `return_value()`")
424    }
425
426    #[cfg(feature = "std")]
427    fn return_value<R>(&mut self, _flags: ReturnFlags, return_value: &R)
428    where
429        R: scale::Encode,
430    {
431        let mut v = vec![];
432        return_value.encode_to(&mut v);
433        self.engine.set_storage(&[255_u8; 32], &v[..]);
434    }
435
436    fn return_value_solidity<R>(&mut self, _flags: ReturnFlags, _return_value: &R) -> !
437    where
438        R: for<'a> SolResultEncode<'a>,
439    {
440        unimplemented!("the off-chain env does not implement `return_value_solidity`")
441    }
442
443    fn hash_bytes<H>(&mut self, input: &[u8], output: &mut <H as HashOutput>::Type)
444    where
445        H: CryptoHash,
446    {
447        <H as CryptoHash>::hash(input, output)
448    }
449
450    fn hash_encoded<H, T>(&mut self, input: &T, output: &mut <H as HashOutput>::Type)
451    where
452        H: CryptoHash,
453        T: scale::Encode,
454    {
455        let enc_input = &scale::Encode::encode(input)[..];
456        <H as CryptoHash>::hash(enc_input, output)
457    }
458
459    fn bn128_add(&mut self, _x1: U256, _y1: U256, _x2: U256, _y2: U256) -> (U256, U256) {
460        unimplemented!(
461            "`bn128_add` is not implemented in the off-chain testing environment"
462        );
463    }
464
465    fn bn128_mul(&mut self, _x1: U256, _y1: U256, _scalar: U256) -> (U256, U256) {
466        unimplemented!(
467            "`bn128_mul` is not implemented in the off-chain testing environment"
468        );
469    }
470
471    fn bn128_pairing(&mut self, _input: &[u8]) -> bool {
472        unimplemented!(
473            "`bn128_pairing` is not implemented in the off-chain testing environment"
474        );
475    }
476
477    #[allow(clippy::arithmetic_side_effects)] // todo
478    fn ecdsa_recover(
479        &mut self,
480        signature: &[u8; 65],
481        message_hash: &[u8; 32],
482        output: &mut [u8; 33],
483    ) -> Result<()> {
484        use secp256k1::{
485            Message,
486            SECP256K1,
487            ecdsa::{
488                RecoverableSignature,
489                RecoveryId,
490            },
491        };
492
493        // In most implementations, the v is just 0 or 1 internally, but 27 was added
494        // as an arbitrary number for signing Bitcoin messages and Ethereum adopted that
495        // as well.
496        let recovery_byte = if signature[64] > 26 {
497            signature[64] - 27
498        } else {
499            signature[64]
500        };
501        let recovery_id = RecoveryId::try_from(recovery_byte as i32)
502            .unwrap_or_else(|error| panic!("Unable to parse the recovery id: {error}"));
503        let message = Message::from_digest_slice(message_hash).unwrap_or_else(|error| {
504            panic!("Unable to create the message from hash: {error}")
505        });
506        let signature =
507            RecoverableSignature::from_compact(&signature[0..64], recovery_id)
508                .unwrap_or_else(|error| panic!("Unable to parse the signature: {error}"));
509
510        let pub_key = SECP256K1.recover_ecdsa(&message, &signature);
511        match pub_key {
512            Ok(pub_key) => {
513                *output = pub_key.serialize();
514                Ok(())
515            }
516            Err(_) => Err(ReturnErrorCode::EcdsaRecoveryFailed.into()),
517        }
518    }
519
520    #[cfg(feature = "unstable-hostfn")]
521    fn ecdsa_to_eth_address(
522        &mut self,
523        pubkey: &[u8; 33],
524        output: &mut [u8; 20],
525    ) -> Result<()> {
526        let pk = secp256k1::PublicKey::from_slice(pubkey)
527            .map_err(|_| ReturnErrorCode::EcdsaRecoveryFailed)?;
528        let uncompressed = pk.serialize_uncompressed();
529        let mut hash = <Keccak256 as HashOutput>::Type::default();
530        <Keccak256>::hash(&uncompressed[1..], &mut hash);
531        output.as_mut().copy_from_slice(&hash[12..]);
532        Ok(())
533    }
534
535    #[cfg(feature = "unstable-hostfn")]
536    fn sr25519_verify(
537        &mut self,
538        signature: &[u8; 64],
539        message: &[u8],
540        pub_key: &[u8; 32],
541    ) -> Result<()> {
542        // the context associated with the signing (specific to the sr25519 algorithm)
543        // defaults to "substrate" in substrate, but could be different elsewhere
544        // https://github.com/paritytech/substrate/blob/c32f5ed2ae6746d6f791f08cecbfc22fa188f5f9/primitives/core/src/sr25519.rs#L60
545        let context = b"substrate";
546        // attempt to parse a signature from bytes
547        let signature: Signature = Signature::from_bytes(signature)
548            .map_err(|_| ReturnErrorCode::Sr25519VerifyFailed)?;
549        // attempt to parse a public key from bytes
550        let public_key: PublicKey = PublicKey::from_bytes(pub_key)
551            .map_err(|_| ReturnErrorCode::Sr25519VerifyFailed)?;
552        // verify the signature
553        public_key
554            .verify_simple(context, message, &signature)
555            .map_err(|_| ReturnErrorCode::Sr25519VerifyFailed.into())
556    }
557
558    #[cfg(feature = "unstable-hostfn")]
559    fn set_code_hash(&mut self, code_hash: &H256) -> Result<()> {
560        self.engine
561            .database
562            .set_code_hash(&self.engine.get_callee(), code_hash);
563        Ok(())
564    }
565}
566
567impl TypedEnvBackend for EnvInstance {
568    fn caller(&mut self) -> Address {
569        self.get_property::<Address>(Engine::caller)
570            .unwrap_or_else(|error| panic!("could not read `caller` property: {error:?}"))
571    }
572
573    fn gas_limit(&mut self) -> u64 {
574        unimplemented!("not implemented, the off-chain environment will be removed");
575    }
576
577    fn gas_price(&mut self) -> u64 {
578        unimplemented!("not implemented, the off-chain environment will be removed");
579    }
580
581    fn gas_left(&mut self) -> u64 {
582        unimplemented!("not implemented, the off-chain environment will be removed");
583    }
584
585    fn chain_id(&mut self) -> U256 {
586        unimplemented!("not implemented, the off-chain environment will be removed");
587    }
588
589    fn balance_of(&mut self, _addr: Address) -> U256 {
590        unimplemented!("not implemented, the off-chain environment will be removed");
591    }
592
593    fn base_fee(&mut self) -> U256 {
594        unimplemented!("not implemented, the off-chain environment will be removed");
595    }
596
597    fn origin(&mut self) -> Address {
598        unimplemented!("not implemented, the off-chain environment will be removed");
599    }
600
601    fn block_hash<E: Environment>(&mut self, _block_number: E::BlockNumber) -> H256 {
602        unimplemented!("not implemented, the off-chain environment will be removed");
603    }
604
605    fn block_author(&mut self) -> Address {
606        unimplemented!("not implemented, the off-chain environment will be removed");
607    }
608
609    fn code_size(&mut self, _addr: Address) -> u64 {
610        unimplemented!("not implemented, the off-chain environment will be removed");
611    }
612
613    fn call_data_size(&mut self) -> u64 {
614        unimplemented!("not implemented, the off-chain environment will be removed");
615    }
616
617    fn return_data_size(&mut self) -> u64 {
618        unimplemented!("not implemented, the off-chain environment will be removed");
619    }
620
621    fn transferred_value(&mut self) -> U256 {
622        self.get_property(Engine::value_transferred)
623            .unwrap_or_else(|error| {
624                panic!("could not read `transferred_value` property: {error:?}")
625            })
626    }
627
628    fn block_timestamp<E: Environment>(&mut self) -> E::Timestamp {
629        self.get_property::<E::Timestamp>(Engine::block_timestamp)
630            .unwrap_or_else(|error| {
631                panic!("could not read `block_timestamp` property: {error:?}")
632            })
633    }
634
635    fn to_account_id<E: Environment>(&mut self, addr: Address) -> E::AccountId {
636        let mut full_scope: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE];
637        let full_scope = &mut &mut full_scope[..];
638        Engine::to_account_id(&self.engine, addr.as_bytes(), full_scope);
639        scale::Decode::decode(&mut &full_scope[..]).unwrap()
640    }
641
642    fn account_id<E: Environment>(&mut self) -> E::AccountId {
643        self.get_property::<E::AccountId>(Engine::account_id)
644            .unwrap_or_else(|error| {
645                panic!("could not read `account_id` property: {error:?}")
646            })
647    }
648
649    fn address(&mut self) -> Address {
650        self.get_property::<Address>(Engine::address)
651            .unwrap_or_else(|error| {
652                panic!("could not read `account_id` property: {error:?}")
653            })
654    }
655
656    fn balance(&mut self) -> U256 {
657        self.get_property::<U256>(Engine::balance)
658            .unwrap_or_else(|error| {
659                panic!("could not read `balance` property: {error:?}")
660            })
661    }
662
663    fn block_number<E: Environment>(&mut self) -> E::BlockNumber {
664        self.get_property::<E::BlockNumber>(Engine::block_number)
665            .unwrap_or_else(|error| {
666                panic!("could not read `block_number` property: {error:?}")
667            })
668    }
669
670    fn minimum_balance(&mut self) -> U256 {
671        self.get_property::<U256>(Engine::minimum_balance)
672            .unwrap_or_else(|error| {
673                panic!("could not read `minimum_balance` property: {error:?}")
674            })
675    }
676
677    fn emit_event<Evt, Abi>(&mut self, event: &Evt)
678    where
679        Evt: Event<Abi>,
680        Abi: TopicEncoder,
681    {
682        let builder = TopicsBuilder::default();
683        let enc_topics = event.topics(builder.into());
684        let enc_data = event.encode_data();
685        self.engine
686            .deposit_event(&enc_topics[..], enc_data.as_slice());
687    }
688
689    fn invoke_contract<E, Args, R, Abi>(
690        &mut self,
691        params: &CallParams<E, Call, Args, R, Abi>,
692    ) -> Result<ink_primitives::MessageResult<R>>
693    where
694        E: Environment,
695        Args: EncodeArgsWith<Abi>,
696        R: DecodeMessageResult<Abi>,
697    {
698        let call_flags = params.call_flags().bits();
699        let transferred_value = params.transferred_value();
700        let input = params.exec_input().encode();
701        let callee_account = params.callee();
702
703        invoke_contract_impl::<R, Abi>(
704            self,
705            None,
706            call_flags,
707            Some(transferred_value),
708            *callee_account, // todo possibly return no reference from callee()
709            input,
710        )
711    }
712
713    fn invoke_contract_delegate<E, Args, R, Abi>(
714        &mut self,
715        params: &CallParams<E, DelegateCall, Args, R, Abi>,
716    ) -> Result<ink_primitives::MessageResult<R>>
717    where
718        E: Environment,
719        Args: EncodeArgsWith<Abi>,
720        R: DecodeMessageResult<Abi>,
721    {
722        let _addr = params.address(); // todo remove
723        let call_flags = params.call_flags().bits();
724        let input = params.exec_input().encode();
725
726        invoke_contract_impl_delegate::<R, Abi>(
727            self,
728            None,
729            call_flags,
730            None,
731            *params.address(),
732            input,
733        )
734    }
735
736    fn instantiate_contract<E, ContractRef, Args, R, Abi>(
737        &mut self,
738        params: &CreateParams<E, ContractRef, LimitParamsV2, Args, R, Abi>,
739    ) -> Result<
740        ink_primitives::ConstructorResult<
741            <R as ConstructorReturnType<ContractRef, Abi>>::Output,
742        >,
743    >
744    where
745        E: Environment,
746        ContractRef: FromAddr + crate::ContractReverseReference,
747        <ContractRef as crate::ContractReverseReference>::Type:
748            crate::reflect::ContractConstructorDecoder,
749        Args: EncodeArgsWith<Abi>,
750        R: ConstructorReturnType<ContractRef, Abi>,
751    {
752        let endowment = params.endowment();
753        let salt_bytes = params.salt_bytes();
754        let code_hash = params.code_hash();
755
756        let input = params.exec_input().encode();
757
758        // Compute address for instantiated contract.
759        let account_id_vec = {
760            let mut account_input = Vec::<u8>::new();
761            account_input.extend(&b"contract_addr".to_vec());
762            scale::Encode::encode_to(
763                &self.engine.exec_context.caller.as_bytes(),
764                &mut account_input,
765            );
766            account_input.extend(&code_hash.0);
767            account_input.extend(&input);
768            if let Some(salt) = salt_bytes {
769                account_input.extend(salt);
770            }
771            let mut account_id = [0_u8; 32];
772            ink_engine::hashing::blake2b_256(&account_input[..], &mut account_id);
773            account_id.to_vec()
774        };
775        // todo don't convert to vec and back, simplify type
776        let contract_addr = AccountIdMapper::to_address(&account_id_vec[..]);
777
778        let old_callee = self.engine.get_callee();
779        self.engine.set_callee(contract_addr);
780
781        let dispatch = <
782            <
783                <
784                    ContractRef
785                    as crate::ContractReverseReference
786                >::Type
787                as crate::reflect::ContractConstructorDecoder
788            >::Type
789            as DecodeDispatch
790        >::decode_dispatch(&mut &input[..])
791            .unwrap_or_else(|e| panic!("Failed to decode constructor call: {e:?}"));
792        crate::reflect::ExecuteDispatchable::execute_dispatchable(dispatch)
793            .unwrap_or_else(|e| panic!("Constructor call failed: {e:?}"));
794
795        self.engine
796            .database
797            .set_code_hash(&self.engine.get_callee(), code_hash);
798        self.engine.set_contract(callee());
799        self.engine
800            .database
801            // todo passing the types instead of refs would be better
802            .set_balance(&callee(), *endowment);
803
804        // todo why?
805        self.engine.set_callee(old_callee);
806
807        Ok(Ok(R::ok(<ContractRef as FromAddr>::from_addr(
808            contract_addr,
809        ))))
810    }
811
812    #[cfg(feature = "unstable-hostfn")]
813    fn terminate_contract(&mut self, beneficiary: Address) -> ! {
814        self.engine.terminate(beneficiary)
815    }
816
817    fn transfer<E>(&mut self, destination: Address, value: U256) -> Result<()>
818    where
819        E: Environment,
820    {
821        let enc_value = &scale::Encode::encode(&value)[..];
822        self.engine
823            .transfer(destination, enc_value)
824            .map_err(Into::into)
825    }
826
827    fn weight_to_fee(&mut self, gas: u64) -> U256 {
828        let mut output: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE];
829        self.engine.weight_to_fee(gas, &mut &mut output[..]);
830        scale::Decode::decode(&mut &output[..]).unwrap_or_else(|error| {
831            panic!("could not read `weight_to_fee` property: {error:?}")
832        })
833    }
834
835    fn is_contract(&mut self, account: &Address) -> bool {
836        self.engine.is_contract(account)
837    }
838
839    fn caller_is_origin(&mut self) -> bool {
840        unimplemented!("off-chain environment does not support cross-contract calls")
841    }
842
843    fn caller_is_root(&mut self) -> bool {
844        unimplemented!("off-chain environment does not support `caller_is_root`")
845    }
846
847    fn code_hash(&mut self, addr: &Address) -> core::result::Result<H256, CodeHashErr> {
848        let code_hash = self.engine.database.get_code_hash(addr);
849        if let Some(code_hash) = code_hash {
850            // todo
851            let code_hash = H256::decode(&mut &code_hash[..]).unwrap();
852            Ok(code_hash)
853        } else {
854            Err(CodeHashErr::AddressNotFound)
855        }
856    }
857
858    fn own_code_hash(&mut self) -> H256 {
859        let callee = &self.engine.get_callee();
860        self.engine
861            .database
862            .get_code_hash(callee)
863            .expect("own code hash not found")
864    }
865
866    #[cfg(feature = "xcm")]
867    fn xcm_weigh<Call>(&mut self, _msg: &xcm::VersionedXcm<Call>) -> Result<Weight> {
868        unimplemented!("off-chain environment does not support `xcm_execute`")
869    }
870
871    #[cfg(feature = "xcm")]
872    fn xcm_execute<Call>(
873        &mut self,
874        _msg: &xcm::VersionedXcm<Call>,
875        _weight: Weight,
876    ) -> Result<()> {
877        unimplemented!("off-chain environment does not support `xcm_execute`")
878    }
879
880    #[cfg(feature = "xcm")]
881    fn xcm_send<Call>(
882        &mut self,
883        _dest: &xcm::VersionedLocation,
884        _msg: &xcm::VersionedXcm<Call>,
885    ) -> Result<()> {
886        unimplemented!("off-chain environment does not support `xcm_send`")
887    }
888}