// Copyright 2018-2020 Parity Technologies (UK) Ltd. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. use super::{ hashing, Account, EnvInstance, }; use crate::{ call::{ utils::ReturnType, CallParams, CreateParams, }, hash::{ Blake2x128, Blake2x256, CryptoHash, HashOutput, Keccak256, Sha2x256, }, topics::Topics, Env, EnvError, EnvTypes, Result, ReturnFlags, TypedEnv, }; use core::convert::TryInto; use ink_primitives::Key; use num_traits::Bounded; impl EnvInstance { /// Returns the callee account. fn callee_account(&self) -> &Account { let callee = self .exec_context() .expect("uninitialized execution context") .callee .clone(); self.accounts .get_account_off(&callee) .expect("callee account does not exist") } /// Returns the callee account as mutable reference. fn callee_account_mut(&mut self) -> &mut Account { let callee = self .exec_context() .expect("uninitialized execution context") .callee .clone(); self.accounts .get_account_off_mut(&callee) .expect("callee account does not exist") } } impl CryptoHash for Blake2x128 { fn hash(input: &[u8], output: &mut ::Type) { type OutputType = [u8; 16]; static_assertions::assert_type_eq_all!( ::Type, OutputType ); let output: &mut OutputType = arrayref::array_mut_ref!(output, 0, 16); hashing::blake2b_128(input, output); } } impl CryptoHash for Blake2x256 { fn hash(input: &[u8], output: &mut ::Type) { type OutputType = [u8; 32]; static_assertions::assert_type_eq_all!( ::Type, OutputType ); let output: &mut OutputType = arrayref::array_mut_ref!(output, 0, 32); hashing::blake2b_256(input, output); } } impl CryptoHash for Sha2x256 { fn hash(input: &[u8], output: &mut ::Type) { type OutputType = [u8; 32]; static_assertions::assert_type_eq_all!( ::Type, OutputType ); let output: &mut OutputType = arrayref::array_mut_ref!(output, 0, 32); hashing::sha2_256(input, output); } } impl CryptoHash for Keccak256 { fn hash(input: &[u8], output: &mut ::Type) { type OutputType = [u8; 32]; static_assertions::assert_type_eq_all!( ::Type, OutputType ); let output: &mut OutputType = arrayref::array_mut_ref!(output, 0, 32); hashing::keccak_256(input, output); } } impl Env for EnvInstance { fn set_contract_storage(&mut self, key: &Key, value: &V) where V: scale::Encode, { self.callee_account_mut() .set_storage(*key, value) .expect("callee account is not a smart contract"); } fn get_contract_storage(&mut self, key: &Key) -> Result> where R: scale::Decode, { self.callee_account() .get_storage::(*key) .map_err(Into::into) } fn clear_contract_storage(&mut self, key: &Key) { self.callee_account_mut() .clear_storage(*key) .expect("callee account is not a smart contract"); } fn decode_input(&mut self) -> Result where T: scale::Decode, { self.exec_context() .map(|exec_ctx| &exec_ctx.call_data) .map(|call_data| scale::Encode::encode(call_data)) .map_err(Into::into) .and_then(|encoded| { ::decode(&mut &encoded[..]) .map_err(|_| scale::Error::from("could not decode input call data")) .map_err(Into::into) }) } fn return_value(&mut self, flags: ReturnFlags, return_value: &R) -> ! where R: scale::Encode, { let ctx = self .exec_context_mut() .expect("uninitialized execution context"); ctx.output = Some(return_value.encode()); std::process::exit(flags.into_u32() as i32) } fn println(&mut self, content: &str) { self.console.println(content) } fn hash_bytes(&mut self, input: &[u8], output: &mut ::Type) where H: CryptoHash, { ::hash(input, output) } fn hash_encoded(&mut self, input: &T, output: &mut ::Type) where H: CryptoHash, T: scale::Encode, { let encoded = input.encode(); self.hash_bytes::(&encoded[..], output) } #[cfg(feature = "ink-unstable-chain-extensions")] fn call_chain_extension(&mut self, func_id: u32, input: &I) -> Result where I: scale::Codec + 'static, O: scale::Codec + 'static, { self.chain_extension_handler.eval(func_id, input) } } impl EnvInstance { fn transfer_impl( &mut self, destination: T::AccountId, value: T::Balance, ) -> Result<()> where T: EnvTypes, { let src_id = self.account_id::()?; let src_value = self .accounts .get_account::(&src_id) .expect("account of executed contract must exist") .balance::()?; if src_value < value { return Err(EnvError::TransferFailed) } let dst_value = self .accounts .get_or_create_account::(&destination) .balance::()?; self.accounts .get_account_mut::(&src_id) .expect("account of executed contract must exist") .set_balance::(src_value - value)?; self.accounts .get_account_mut::(&destination) .expect("the account must exist already or has just been created") .set_balance::(dst_value + value)?; Ok(()) } } impl TypedEnv for EnvInstance { fn caller(&mut self) -> Result { self.exec_context() .expect("uninitialized execution context") .caller::() .map_err(|_| scale::Error::from("could not decode caller")) .map_err(Into::into) } fn transferred_balance(&mut self) -> Result { self.exec_context() .expect("uninitialized execution context") .transferred_value::() .map_err(|_| scale::Error::from("could not decode transferred balance")) .map_err(Into::into) } /// Emulates gas price calculation fn weight_to_fee(&mut self, gas: u64) -> Result { use crate::arithmetic::Saturating as _; let gas_price = self .chain_spec .gas_price::() .map_err(|_| scale::Error::from("could not decode gas price"))?; Ok(gas_price .saturating_mul(gas.try_into().unwrap_or_else(|_| Bounded::max_value()))) } fn gas_left(&mut self) -> Result { self.exec_context() .expect("uninitialized execution context") .gas::() .map_err(|_| scale::Error::from("could not decode gas left")) .map_err(Into::into) } fn block_timestamp(&mut self) -> Result { self.current_block() .expect("uninitialized execution context") .timestamp::() .map_err(|_| scale::Error::from("could not decode block time")) .map_err(Into::into) } fn account_id(&mut self) -> Result { self.exec_context() .expect("uninitialized execution context") .callee::() .map_err(|_| scale::Error::from("could not decode callee")) .map_err(Into::into) } fn balance(&mut self) -> Result { self.callee_account() .balance::() .map_err(|_| scale::Error::from("could not decode callee balance")) .map_err(Into::into) } fn rent_allowance(&mut self) -> Result { self.callee_account() .rent_allowance::() .map_err(|_| scale::Error::from("could not decode callee rent allowance")) .map_err(Into::into) } fn block_number(&mut self) -> Result { self.current_block() .expect("uninitialized execution context") .number::() .map_err(|_| scale::Error::from("could not decode block number")) .map_err(Into::into) } fn minimum_balance(&mut self) -> Result { self.chain_spec .minimum_balance::() .map_err(|_| scale::Error::from("could not decode minimum balance")) .map_err(Into::into) } fn tombstone_deposit(&mut self) -> Result { self.chain_spec .tombstone_deposit::() .map_err(|_| scale::Error::from("could not decode tombstone deposit")) .map_err(Into::into) } fn emit_event(&mut self, new_event: Event) where T: EnvTypes, Event: Topics + scale::Encode, { self.emitted_events.record::(new_event) } fn set_rent_allowance(&mut self, new_rent_allowance: T::Balance) where T: EnvTypes, { self.callee_account_mut() .set_rent_allowance::(new_rent_allowance) .expect("could not encode rent allowance") } fn invoke_contract( &mut self, _call_params: &CallParams, ) -> Result<()> where T: EnvTypes, Args: scale::Encode, { unimplemented!("off-chain environment does not support contract invokation") } fn eval_contract( &mut self, _call_params: &CallParams>, ) -> Result where T: EnvTypes, Args: scale::Encode, R: scale::Decode, { unimplemented!("off-chain environment does not support contract evaluation") } fn instantiate_contract( &mut self, _params: &CreateParams, ) -> Result where T: EnvTypes, Args: scale::Encode, { unimplemented!("off-chain environment does not support contract instantiation") } fn terminate_contract(&mut self, _beneficiary: T::AccountId) -> ! where T: EnvTypes, { unimplemented!("off-chain environment does not support contract termination") } fn restore_contract( &mut self, _account_id: T::AccountId, _code_hash: T::Hash, _rent_allowance: T::Balance, _filtered_keys: &[Key], ) where T: EnvTypes, { unimplemented!("off-chain environment does not support contract restoration") } fn transfer(&mut self, destination: T::AccountId, value: T::Balance) -> Result<()> where T: EnvTypes, { self.transfer_impl::(destination, value) } fn random(&mut self, subject: &[u8]) -> Result where T: EnvTypes, { self.current_block() .expect("uninitialized execution context") .random::(subject) .map_err(Into::into) } }