// Copyright 2018-2022 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::{ ext, EnvInstance, Error as ExtError, ScopedBuffer, }; use crate::{ call::{ Call, CallParams, ConstructorReturnType, CreateParams, DelegateCall, FromAccountId, }, hash::{ Blake2x128, Blake2x256, CryptoHash, HashOutput, Keccak256, Sha2x256, }, topics::{ Topics, TopicsBuilderBackend, }, Clear, EnvBackend, Environment, Error, FromLittleEndian, Result, ReturnFlags, TypedEnvBackend, }; use ink_storage_traits::Storable; 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); ext::hash_blake2_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); ext::hash_blake2_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); ext::hash_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); ext::hash_keccak_256(input, output); } } impl From for Error { fn from(ext_error: ext::Error) -> Self { match ext_error { ext::Error::Unknown => Self::Unknown, ext::Error::CalleeTrapped => Self::CalleeTrapped, ext::Error::CalleeReverted => Self::CalleeReverted, ext::Error::KeyNotFound => Self::KeyNotFound, ext::Error::_BelowSubsistenceThreshold => Self::_BelowSubsistenceThreshold, ext::Error::TransferFailed => Self::TransferFailed, ext::Error::_EndowmentTooLow => Self::_EndowmentTooLow, ext::Error::CodeNotFound => Self::CodeNotFound, ext::Error::NotCallable => Self::NotCallable, ext::Error::LoggingDisabled => Self::LoggingDisabled, ext::Error::CallRuntimeFailed => Self::CallRuntimeFailed, ext::Error::EcdsaRecoveryFailed => Self::EcdsaRecoveryFailed, } } } pub struct TopicsBuilder<'a, E> { scoped_buffer: ScopedBuffer<'a>, marker: core::marker::PhantomData E>, } impl<'a, E> From> for TopicsBuilder<'a, E> where E: Environment, { fn from(scoped_buffer: ScopedBuffer<'a>) -> Self { Self { scoped_buffer, marker: Default::default(), } } } impl<'a, E> TopicsBuilderBackend for TopicsBuilder<'a, E> where E: Environment, { type Output = (ScopedBuffer<'a>, &'a mut [u8]); fn expect(&mut self, expected_topics: usize) { self.scoped_buffer .append_encoded(&scale::Compact(expected_topics as u32)); } fn push_topic(&mut self, topic_value: &T) where T: scale::Encode, { fn inner(encoded: &mut [u8]) -> ::Hash { let len_encoded = encoded.len(); let mut result = ::Hash::CLEAR_HASH; let len_result = result.as_ref().len(); if len_encoded <= len_result { result.as_mut()[..len_encoded].copy_from_slice(encoded); } else { let mut hash_output = ::Type::default(); ::hash(encoded, &mut hash_output); let copy_len = core::cmp::min(hash_output.len(), len_result); result.as_mut()[0..copy_len].copy_from_slice(&hash_output[0..copy_len]); } result } let mut split = self.scoped_buffer.split(); let encoded = split.take_encoded(topic_value); let result = inner::(encoded); self.scoped_buffer.append_encoded(&result); } fn output(mut self) -> Self::Output { let encoded_topics = self.scoped_buffer.take_appended(); (self.scoped_buffer, encoded_topics) } } impl EnvInstance { #[inline(always)] /// Returns a new scoped buffer for the entire scope of the static 16 kB buffer. fn scoped_buffer(&mut self) -> ScopedBuffer { ScopedBuffer::from(&mut self.buffer[..]) } /// Returns the contract property value from its little-endian representation. /// /// # Note /// /// This skips the potentially costly decoding step that is often equivalent to a /// `memcpy`. #[inline(always)] fn get_property_little_endian(&mut self, ext_fn: fn(output: &mut &mut [u8])) -> T where T: FromLittleEndian, { let mut result = ::Bytes::default(); ext_fn(&mut result.as_mut()); ::from_le_bytes(result) } /// Returns the contract property value. #[inline(always)] fn get_property(&mut self, ext_fn: fn(output: &mut &mut [u8])) -> Result where T: scale::Decode, { let full_scope = &mut self.scoped_buffer().take_rest(); ext_fn(full_scope); scale::Decode::decode(&mut &full_scope[..]).map_err(Into::into) } } impl EnvBackend for EnvInstance { fn set_contract_storage(&mut self, key: &K, value: &V) -> Result> where K: scale::Encode, V: Storable, { let mut buffer = self.scoped_buffer(); let key = buffer.take_encoded(key); if value.size_hint() > buffer.len() { return Err(Error::Unknown) } let value = buffer.take_storable_encoded(value); Ok(ext::set_storage(key, value)) } fn get_contract_storage(&mut self, key: &K) -> Result> where K: scale::Encode, R: Storable, { let mut buffer = self.scoped_buffer(); let key = buffer.take_encoded(key); let output = &mut buffer.take_rest(); match ext::get_storage(key, output) { Ok(_) => (), Err(ExtError::KeyNotFound) => return Ok(None), Err(_) => panic!("encountered unexpected error"), } let decoded = Storable::decode(&mut &output[..])?; Ok(Some(decoded)) } fn take_contract_storage(&mut self, key: &K) -> Result> where K: scale::Encode, R: Storable, { let mut buffer = self.scoped_buffer(); let key = buffer.take_encoded(key); let output = &mut buffer.take_rest(); match ext::take_storage(key, output) { Ok(_) => (), Err(ExtError::KeyNotFound) => return Ok(None), Err(_) => panic!("encountered unexpected error"), } let decoded = Storable::decode(&mut &output[..])?; Ok(Some(decoded)) } fn contains_contract_storage(&mut self, key: &K) -> Option where K: scale::Encode, { let mut buffer = self.scoped_buffer(); let key = buffer.take_encoded(key); ext::storage_contains(key) } fn clear_contract_storage(&mut self, key: &K) -> Option where K: scale::Encode, { let mut buffer = self.scoped_buffer(); let key = buffer.take_encoded(key); ext::clear_storage(key) } fn decode_input(&mut self) -> Result where T: scale::Decode, { self.get_property::(ext::input) } fn return_value(&mut self, flags: ReturnFlags, return_value: &R) -> ! where R: scale::Encode, { let mut scope = super::EncodeScope::from(&mut self.buffer[..]); return_value.encode_to(&mut scope); let len = scope.len(); ext::return_value(flags, &self.buffer[..][..len]); } fn debug_message(&mut self, content: &str) { ext::debug_message(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 mut scope = self.scoped_buffer(); let enc_input = scope.take_encoded(input); ::hash(enc_input, output) } fn ecdsa_recover( &mut self, signature: &[u8; 65], message_hash: &[u8; 32], output: &mut [u8; 33], ) -> Result<()> { ext::ecdsa_recover(signature, message_hash, output).map_err(Into::into) } fn ecdsa_to_eth_address( &mut self, pubkey: &[u8; 33], output: &mut [u8; 20], ) -> Result<()> { ext::ecdsa_to_eth_address(pubkey, output).map_err(Into::into) } fn call_chain_extension( &mut self, func_id: u32, input: &I, status_to_result: F, decode_to_result: D, ) -> ::core::result::Result where I: scale::Encode, T: scale::Decode, E: From, F: FnOnce(u32) -> ::core::result::Result<(), ErrorCode>, D: FnOnce(&[u8]) -> ::core::result::Result, { let mut scope = self.scoped_buffer(); let enc_input = scope.take_encoded(input); let output = &mut scope.take_rest(); status_to_result(ext::call_chain_extension(func_id, enc_input, output))?; let decoded = decode_to_result(&output[..])?; Ok(decoded) } fn set_code_hash(&mut self, code_hash_ptr: &[u8]) -> Result<()> { ext::set_code_hash(code_hash_ptr).map_err(Into::into) } } impl TypedEnvBackend for EnvInstance { fn caller(&mut self) -> E::AccountId { self.get_property::(ext::caller) .expect("The executed contract must have a caller with a valid account id.") } fn transferred_value(&mut self) -> E::Balance { self.get_property_little_endian::(ext::value_transferred) } fn gas_left(&mut self) -> u64 { self.get_property_little_endian::(ext::gas_left) } fn block_timestamp(&mut self) -> E::Timestamp { self.get_property_little_endian::(ext::now) } fn account_id(&mut self) -> E::AccountId { self.get_property::(ext::address) .expect("A contract being executed must have a valid account id.") } fn balance(&mut self) -> E::Balance { self.get_property_little_endian::(ext::balance) } fn block_number(&mut self) -> E::BlockNumber { self.get_property_little_endian::(ext::block_number) } fn minimum_balance(&mut self) -> E::Balance { self.get_property_little_endian::(ext::minimum_balance) } fn emit_event(&mut self, event: Event) where E: Environment, Event: Topics + scale::Encode, { let (mut scope, enc_topics) = event.topics::(TopicsBuilder::from(self.scoped_buffer()).into()); let enc_data = scope.take_encoded(&event); ext::deposit_event(enc_topics, enc_data); } fn invoke_contract( &mut self, params: &CallParams, Args, R>, ) -> Result> where E: Environment, Args: scale::Encode, R: scale::Decode, { let mut scope = self.scoped_buffer(); let gas_limit = params.gas_limit(); let enc_callee = scope.take_encoded(params.callee()); let enc_transferred_value = scope.take_encoded(params.transferred_value()); let call_flags = params.call_flags(); let enc_input = if !call_flags.forward_input() && !call_flags.clone_input() { scope.take_encoded(params.exec_input()) } else { &mut [] }; let output = &mut scope.take_rest(); let flags = params.call_flags().into_u32(); let call_result = ext::call( flags, enc_callee, gas_limit, enc_transferred_value, enc_input, output, ); match call_result { Ok(()) | Err(ext::Error::CalleeReverted) => { let decoded = scale::Decode::decode(&mut &output[..])?; Ok(decoded) } Err(actual_error) => Err(actual_error.into()), } } fn invoke_contract_delegate( &mut self, params: &CallParams, Args, R>, ) -> Result where E: Environment, Args: scale::Encode, R: scale::Decode, { let mut scope = self.scoped_buffer(); let call_flags = params.call_flags(); let enc_code_hash = scope.take_encoded(params.code_hash()); let enc_input = if !call_flags.forward_input() && !call_flags.clone_input() { scope.take_encoded(params.exec_input()) } else { &mut [] }; let output = &mut scope.take_rest(); let flags = params.call_flags().into_u32(); let call_result = ext::delegate_call(flags, enc_code_hash, enc_input, output); match call_result { Ok(()) | Err(ext::Error::CalleeReverted) => { let decoded = scale::Decode::decode(&mut &output[..])?; Ok(decoded) } Err(actual_error) => Err(actual_error.into()), } } fn instantiate_contract( &mut self, params: &CreateParams, ) -> Result< ink_primitives::ConstructorResult< >::Output, >, > where E: Environment, ContractRef: FromAccountId, Args: scale::Encode, Salt: AsRef<[u8]>, RetType: ConstructorReturnType, { let mut scoped = self.scoped_buffer(); let gas_limit = params.gas_limit(); let enc_code_hash = scoped.take_encoded(params.code_hash()); let enc_endowment = scoped.take_encoded(params.endowment()); let enc_input = scoped.take_encoded(params.exec_input()); // We support `AccountId` types with an encoding that requires up to // 1024 bytes. Beyond that limit ink! contracts will trap for now. // In the default configuration encoded `AccountId` require 32 bytes. let out_address = &mut scoped.take(1024); let salt = params.salt_bytes().as_ref(); let out_return_value = &mut scoped.take_rest(); let instantiate_result = ext::instantiate( enc_code_hash, gas_limit, enc_endowment, enc_input, out_address, out_return_value, salt, ); crate::engine::decode_instantiate_result::<_, E, ContractRef, RetType>( instantiate_result.map_err(Into::into), &mut &out_address[..], &mut &out_return_value[..], ) } fn terminate_contract(&mut self, beneficiary: E::AccountId) -> ! where E: Environment, { let buffer = self.scoped_buffer().take_encoded(&beneficiary); ext::terminate(buffer); } fn transfer(&mut self, destination: E::AccountId, value: E::Balance) -> Result<()> where E: Environment, { let mut scope = self.scoped_buffer(); let enc_destination = scope.take_encoded(&destination); let enc_value = scope.take_encoded(&value); ext::transfer(enc_destination, enc_value).map_err(Into::into) } fn weight_to_fee(&mut self, gas: u64) -> E::Balance { let mut result = ::Bytes::default(); ext::weight_to_fee(gas, &mut result.as_mut()); ::from_le_bytes(result) } fn is_contract(&mut self, account_id: &E::AccountId) -> bool where E: Environment, { let mut scope = self.scoped_buffer(); let enc_account_id = scope.take_encoded(account_id); ext::is_contract(enc_account_id) } fn caller_is_origin(&mut self) -> bool where E: Environment, { ext::caller_is_origin() } fn code_hash(&mut self, account_id: &E::AccountId) -> Result where E: Environment, { let mut scope = self.scoped_buffer(); let output = scope.take(32); scope.append_encoded(account_id); let enc_account_id = scope.take_appended(); ext::code_hash(enc_account_id, output)?; let hash = scale::Decode::decode(&mut &output[..])?; Ok(hash) } fn own_code_hash(&mut self) -> Result where E: Environment, { let output = &mut self.scoped_buffer().take(32); ext::own_code_hash(output); let hash = scale::Decode::decode(&mut &output[..])?; Ok(hash) } #[cfg(feature = "call-runtime")] fn call_runtime(&mut self, call: &Call) -> Result<()> where E: Environment, Call: scale::Encode, { let mut scope = self.scoped_buffer(); let enc_call = scope.take_encoded(call); ext::call_runtime(enc_call).map_err(Into::into) } }