diff --git a/CHANGELOG.md b/CHANGELOG.md index 59088cd6b57..2e7f3462b94 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Fix decoding of `HostFn::minimum_balance` return value - [#2656](https://github.com/use-ink/ink/pull/2656) +- Fix handling of `HostFn::code_hash` and `HostFn::weight_to_fee` - [#2672](https://github.com/use-ink/ink/pull/2672) ### Fixed - `name` override fixes for message id computation and trait definitions - [#2649](https://github.com/use-ink/ink/pull/2649) diff --git a/crates/e2e/src/contract_results.rs b/crates/e2e/src/contract_results.rs index 5bb49c0fa27..8a070b1a74b 100644 --- a/crates/e2e/src/contract_results.rs +++ b/crates/e2e/src/contract_results.rs @@ -299,6 +299,7 @@ where fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { f.debug_struct("CallDryRunResult") .field("exec_result", &self.exec_result) + .field("trace", &self.trace) .finish() } } diff --git a/crates/e2e/src/subxt_client.rs b/crates/e2e/src/subxt_client.rs index 5bc722049d7..8e48a4ef66d 100644 --- a/crates/e2e/src/subxt_client.rs +++ b/crates/e2e/src/subxt_client.rs @@ -799,6 +799,10 @@ where let dispatch_error = DispatchError::decode_from(evt.field_bytes(), metadata) .map_err(|e| Error::Decoding(e.to_string()))?; + log_error(&format!( + "Attempt to stringify returned data: {:?}", + String::from_utf8_lossy(&trace.clone().unwrap().output[..]) + )); log_error(&format!( "extrinsic for `raw_call` failed: {dispatch_error} {trace:?}" )); @@ -841,6 +845,14 @@ where .await; log_info(&format!("call dry run result: {:?}", &exec_result.result)); + if exec_result.result.is_ok() && exec_result.clone().result.unwrap().did_revert() + { + log_error(&format!( + "Attempt to stringify returned data: {:?}", + String::from_utf8_lossy(&exec_result.clone().result.unwrap().data[..]) + )); + } + let exec_result = self .contract_result_to_result(exec_result) .map_err(Error::CallDryRun)?; diff --git a/crates/env/src/api.rs b/crates/env/src/api.rs index 61cf72e5517..5184cb463c1 100644 --- a/crates/env/src/api.rs +++ b/crates/env/src/api.rs @@ -16,6 +16,7 @@ use ink_primitives::{ Address, + CodeHashErr, H256, U256, abi::{ @@ -86,12 +87,9 @@ pub fn transferred_value() -> U256 { /// # Errors /// /// If the returned value cannot be properly decoded. -pub fn weight_to_fee(gas: Gas) -> E::Balance -where - E: Environment, -{ +pub fn weight_to_fee(gas: Gas) -> U256 { ::on_instance(|instance| { - TypedEnvBackend::weight_to_fee::(instance, gas) + TypedEnvBackend::weight_to_fee(instance, gas) }) } @@ -705,7 +703,7 @@ pub fn is_contract(account: &Address) -> bool { /// /// - If no code hash was found for the specified account id. /// - If the returned value cannot be properly decoded. -pub fn code_hash(addr: &Address) -> Result { +pub fn code_hash(addr: &Address) -> core::result::Result { ::on_instance(|instance| { TypedEnvBackend::code_hash(instance, addr) }) @@ -716,7 +714,7 @@ pub fn code_hash(addr: &Address) -> Result { /// # Errors /// /// If the returned value cannot be properly decoded. -pub fn own_code_hash() -> Result { +pub fn own_code_hash() -> H256 { ::on_instance(|instance| { TypedEnvBackend::own_code_hash(instance) }) @@ -735,12 +733,9 @@ pub fn own_code_hash() -> Result { /// # Errors /// /// If the returned value cannot be properly decoded. -pub fn caller_is_origin() -> bool -where - E: Environment, -{ +pub fn caller_is_origin() -> bool { ::on_instance(|instance| { - TypedEnvBackend::caller_is_origin::(instance) + TypedEnvBackend::caller_is_origin(instance) }) } @@ -755,12 +750,9 @@ where /// # Errors /// /// If the returned value cannot be properly decoded. -pub fn caller_is_root() -> bool -where - E: Environment, -{ +pub fn caller_is_root() -> bool { ::on_instance(|instance| { - TypedEnvBackend::caller_is_root::(instance) + TypedEnvBackend::caller_is_root(instance) }) } diff --git a/crates/env/src/backend.rs b/crates/env/src/backend.rs index 53aebb4dbf4..7b03a0d5924 100644 --- a/crates/env/src/backend.rs +++ b/crates/env/src/backend.rs @@ -14,6 +14,7 @@ use ink_primitives::{ Address, + CodeHashErr, H256, U256, abi::AbiEncodeWith, @@ -237,7 +238,7 @@ pub trait TypedEnvBackend: EnvBackend { /// # Note /// /// For more details visit: [`weight_to_fee`][`crate::weight_to_fee`] - fn weight_to_fee(&mut self, gas: u64) -> E::Balance; + fn weight_to_fee(&mut self, gas: u64) -> U256; /// Returns the timestamp of the current block. /// @@ -381,32 +382,29 @@ pub trait TypedEnvBackend: EnvBackend { /// # Note /// /// For more details visit: [`caller_is_origin`][`crate::caller_is_origin`] - fn caller_is_origin(&mut self) -> bool - where - E: Environment; + fn caller_is_origin(&mut self) -> bool; /// Checks whether the caller of the current contract is root. /// /// # Note /// /// For more details visit: [`caller_is_root`][`crate::caller_is_root`] - fn caller_is_root(&mut self) -> bool - where - E: Environment; + fn caller_is_root(&mut self) -> bool; /// Retrieves the code hash of the contract at the given `account` id. /// /// # Note /// /// For more details visit: [`code_hash`][`crate::code_hash`] - fn code_hash(&mut self, account: &Address) -> Result; + fn code_hash(&mut self, account: &Address) + -> core::result::Result; /// Retrieves the code hash of the currently executing contract. /// /// # Note /// /// For more details visit: [`own_code_hash`][`crate::own_code_hash`] - fn own_code_hash(&mut self) -> Result; + fn own_code_hash(&mut self) -> H256; /// Execute an XCM message locally, using the contract's address as the origin. /// diff --git a/crates/env/src/engine/off_chain/impls.rs b/crates/env/src/engine/off_chain/impls.rs index 5f9f06dbe1f..2509d87dc72 100644 --- a/crates/env/src/engine/off_chain/impls.rs +++ b/crates/env/src/engine/off_chain/impls.rs @@ -15,6 +15,7 @@ use ink_engine::ext::Engine; use ink_primitives::{ Address, + CodeHashErr, H256, U256, abi::{ @@ -752,7 +753,7 @@ impl TypedEnvBackend for EnvInstance { .map_err(Into::into) } - fn weight_to_fee(&mut self, gas: u64) -> E::Balance { + fn weight_to_fee(&mut self, gas: u64) -> U256 { let mut output: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE]; self.engine.weight_to_fee(gas, &mut &mut output[..]); scale::Decode::decode(&mut &output[..]).unwrap_or_else(|error| { @@ -764,39 +765,31 @@ impl TypedEnvBackend for EnvInstance { self.engine.is_contract(account) } - fn caller_is_origin(&mut self) -> bool - where - E: Environment, - { + fn caller_is_origin(&mut self) -> bool { unimplemented!("off-chain environment does not support cross-contract calls") } - fn caller_is_root(&mut self) -> bool - where - E: Environment, - { + fn caller_is_root(&mut self) -> bool { unimplemented!("off-chain environment does not support `caller_is_root`") } - fn code_hash(&mut self, addr: &Address) -> Result { + fn code_hash(&mut self, addr: &Address) -> core::result::Result { let code_hash = self.engine.database.get_code_hash(addr); if let Some(code_hash) = code_hash { // todo let code_hash = H256::decode(&mut &code_hash[..]).unwrap(); Ok(code_hash) } else { - Err(ReturnErrorCode::KeyNotFound.into()) + Err(CodeHashErr::AddressNotFound) } } - fn own_code_hash(&mut self) -> Result { + fn own_code_hash(&mut self) -> H256 { let callee = &self.engine.get_callee(); - let code_hash = self.engine.database.get_code_hash(callee); - if let Some(code_hash) = code_hash { - Ok(code_hash) - } else { - Err(ReturnErrorCode::KeyNotFound.into()) - } + self.engine + .database + .get_code_hash(callee) + .expect("own code hash not found") } #[cfg(all(feature = "xcm", feature = "unstable-hostfn"))] diff --git a/crates/env/src/engine/on_chain/pallet_revive.rs b/crates/env/src/engine/on_chain/pallet_revive.rs index 5f5cf37c2fb..f8ba30c07fa 100644 --- a/crates/env/src/engine/on_chain/pallet_revive.rs +++ b/crates/env/src/engine/on_chain/pallet_revive.rs @@ -14,6 +14,7 @@ use ink_primitives::{ Address, + CodeHashErr, H256, U256, abi::{ @@ -76,6 +77,19 @@ use crate::{ types::FromLittleEndian, }; +/// The code hash of an existing account without code. +/// This is the `keccak256` hash of empty data. +/// ```no_compile +/// const EMPTY_CODE_HASH: H256 = +/// H256(sp_core::hex2array!("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470")); +/// ```` +const EMPTY_CODE_HASH: H256 = H256([ + 197, 210, 70, 1, 134, 247, 35, 60, 146, 126, 125, 178, 220, 199, 3, 192, 229, 0, 182, + 83, 202, 130, 39, 59, 123, 250, 216, 4, 93, 133, 164, 112, +]); + +const H256_ZERO: H256 = H256::zero(); + impl CryptoHash for Blake2x128 { fn hash(input: &[u8], output: &mut ::Type) { type OutputType = [u8; 16]; @@ -927,12 +941,9 @@ impl TypedEnvBackend for EnvInstance { fn address(&mut self) -> Address { let mut scope = self.scoped_buffer(); - let h160: &mut [u8; 20] = scope.take(20).try_into().unwrap(); ext::address(h160); - - scale::Decode::decode(&mut &h160[..]) - .expect("A contract being executed must have a valid address.") + Address::from_slice(h160) } fn balance(&mut self) -> U256 { @@ -1193,54 +1204,49 @@ impl TypedEnvBackend for EnvInstance { } } - fn weight_to_fee(&mut self, gas: u64) -> E::Balance { + fn weight_to_fee(&mut self, gas: u64) -> U256 { let mut scope = self.scoped_buffer(); let u256: &mut [u8; 32] = scope.take(32).try_into().unwrap(); - // TODO: needs ref and proof ext::weight_to_fee(gas, gas, u256); - let mut result = ::Bytes::default(); - let len = result.as_ref().len(); - result.as_mut().copy_from_slice(&u256[..len]); - ::from_le_bytes(result) + U256::from_le_bytes(*u256) } fn is_contract(&mut self, addr: &Address) -> bool { ext::code_size(&addr.0) > 0 } - fn caller_is_origin(&mut self) -> bool - where - E: Environment, - { + fn caller_is_origin(&mut self) -> bool { let sel = const { solidity_selector("callerIsOrigin()") }; let output: &mut [u8; 32] = &mut self.scoped_buffer().take(32).try_into().unwrap(); call_bool_precompile(sel, output) } - fn caller_is_root(&mut self) -> bool - where - E: Environment, - { + fn caller_is_root(&mut self) -> bool { let sel = const { solidity_selector("callerIsRoot()") }; let output: &mut [u8; 32] = &mut self.scoped_buffer().take(32).try_into().unwrap(); call_bool_precompile(sel, output) } - fn code_hash(&mut self, addr: &Address) -> Result { + fn code_hash(&mut self, addr: &Address) -> core::result::Result { let mut scope = self.scoped_buffer(); - // todo can be simplified - let enc_addr: &mut [u8; 20] = - scope.take_encoded(addr)[..20].as_mut().try_into().unwrap(); let output: &mut [u8; 32] = scope.take_max_encoded_len::().try_into().unwrap(); - ext::code_hash(enc_addr, output); - let hash = scale::Decode::decode(&mut &output[..])?; - Ok(hash) + ext::code_hash(&addr.0, output); + let hash = H256::from_slice(output); + // If not a contract, but account exists, then `keccak_256([])` is returned. + // Otherwise `zero`. + if hash == H256_ZERO { + Err(CodeHashErr::AddressNotFound) + } else if hash == EMPTY_CODE_HASH { + Err(CodeHashErr::NotContractButAccount) + } else { + Ok(hash) + } } - fn own_code_hash(&mut self) -> Result { + fn own_code_hash(&mut self) -> H256 { let sel = const { solidity_selector("ownCodeHash()") }; let output: &mut [u8; 32] = &mut self .scoped_buffer() @@ -1260,8 +1266,7 @@ impl TypedEnvBackend for EnvInstance { Some(&mut &mut output[..]), ); call_result.expect("call host function failed"); - let hash = scale::Decode::decode(&mut &output[..])?; - Ok(hash) + H256::from_slice(output) } #[cfg(all(feature = "xcm", feature = "unstable-hostfn"))] diff --git a/crates/ink/src/env_access.rs b/crates/ink/src/env_access.rs index 4c79940d7b4..5dcb6e78949 100644 --- a/crates/ink/src/env_access.rs +++ b/crates/ink/src/env_access.rs @@ -34,6 +34,7 @@ use ink_env::{ }; use ink_primitives::{ Address, + CodeHashErr, H256, U256, abi::{ @@ -180,9 +181,8 @@ where /// # Note /// /// For more details visit: [`ink_env::weight_to_fee`] - /// todo: there is now also `gas_price` - pub fn weight_to_fee(self, gas: u64) -> E::Balance { - ink_env::weight_to_fee::(gas) + pub fn weight_to_fee(self, gas: u64) -> U256 { + ink_env::weight_to_fee(gas) } /// Returns the timestamp of the current block. @@ -1084,7 +1084,7 @@ where /// /// For more details visit: [`ink_env::caller_is_origin`] pub fn caller_is_origin(self) -> bool { - ink_env::caller_is_origin::() + ink_env::caller_is_origin() } /// Checks whether the caller of the current contract is root. @@ -1115,7 +1115,7 @@ where /// /// For more details visit: [`ink_env::caller_is_root`] pub fn caller_is_root(self) -> bool { - ink_env::caller_is_root::() + ink_env::caller_is_root() } /// Returns the code hash of the contract at the given `account` id. @@ -1146,7 +1146,7 @@ where /// # Note /// /// For more details visit: [`ink_env::code_hash`] - pub fn code_hash(self, addr: &Address) -> Result { + pub fn code_hash(self, addr: &Address) -> core::result::Result { ink_env::code_hash(addr) } @@ -1168,9 +1168,7 @@ where /// # /// #[ink(message)] /// pub fn own_code_hash(&mut self) -> ink::H256 { - /// self.env() - /// .own_code_hash() - /// .unwrap_or_else(|err| panic!("contract should have a code hash: {:?}", err)) + /// self.env().own_code_hash() /// } /// # } /// # } @@ -1179,7 +1177,7 @@ where /// # Note /// /// For more details visit: [`ink_env::own_code_hash`] - pub fn own_code_hash(self) -> Result { + pub fn own_code_hash(self) -> H256 { ink_env::own_code_hash() } diff --git a/crates/primitives/src/lib.rs b/crates/primitives/src/lib.rs index cb8abf5cb43..ffedbe19db4 100644 --- a/crates/primitives/src/lib.rs +++ b/crates/primitives/src/lib.rs @@ -81,6 +81,13 @@ pub enum LangError { CouldNotReadInput = 1u32, } +/// Error encountered while trying to look up a contract's hash. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum CodeHashErr { + NotContractButAccount, + AddressNotFound, +} + /// The `Result` type for ink! messages. pub type MessageResult = ::core::result::Result; diff --git a/integration-tests/public/own-code-hash/.gitignore b/integration-tests/internal/own-code-hash/.gitignore similarity index 100% rename from integration-tests/public/own-code-hash/.gitignore rename to integration-tests/internal/own-code-hash/.gitignore diff --git a/integration-tests/public/own-code-hash/Cargo.toml b/integration-tests/internal/own-code-hash/Cargo.toml similarity index 100% rename from integration-tests/public/own-code-hash/Cargo.toml rename to integration-tests/internal/own-code-hash/Cargo.toml diff --git a/integration-tests/public/own-code-hash/lib.rs b/integration-tests/internal/own-code-hash/lib.rs similarity index 66% rename from integration-tests/public/own-code-hash/lib.rs rename to integration-tests/internal/own-code-hash/lib.rs index 4a6950f169a..e727004b53b 100644 --- a/integration-tests/public/own-code-hash/lib.rs +++ b/integration-tests/internal/own-code-hash/lib.rs @@ -16,21 +16,28 @@ mod own_code_hash { /// Returns the code hash of the contract #[ink(message)] pub fn own_code_hash(&self) -> H256 { - self.env().own_code_hash().unwrap() + self.env().own_code_hash() } - /// Returns the code hash of the contract by providing its `account_id` + /// Returns the address of the contract #[ink(message)] - pub fn get_code(&self) -> H256 { - self.env() - .code_hash(&self.env().address()) - .expect("Failed to get code hash") + pub fn own_address(&self) -> Address { + self.env().address() } - } - impl Default for OwnCodeHash { - fn default() -> Self { - Self::new() + /// Returns the code hash of this contract by providing its address. + #[ink(message)] + pub fn code_hash_of_own_address(&self) -> H256 { + let own_hash = self + .env() + .code_hash(&self.env().address()) + .unwrap_or_else(|err| panic!("Failed to get : {err:?}")); + assert_eq!( + own_hash, + self.env().own_code_hash(), + "mismatch in code hashes" + ); + own_hash } } @@ -82,7 +89,7 @@ mod own_code_hash { type E2EResult = std::result::Result>; #[ink_e2e::test] - async fn get_own_code_hash(mut client: ink_e2e::Client) -> E2EResult<()> { + async fn get_own_address(mut client: ink_e2e::Client) -> E2EResult<()> { let mut constructor = OwnCodeHashRef::new(); let contract = client .instantiate("own_code_hash", &ink_e2e::bob(), &mut constructor) @@ -90,6 +97,30 @@ mod own_code_hash { .await .expect("instantiate failed"); + let call_builder = contract.call_builder::(); + let own_code_hash_res = client + .call(&ink_e2e::bob(), &call_builder.own_address()) + .submit() + .await + .expect("own_code_hash failed"); + + let addr = own_code_hash_res.return_value(); + assert_eq!(addr, contract.addr); + + Ok(()) + } + + #[ink_e2e::test] + async fn get_own_code_hash_from_address( + mut client: ink_e2e::Client, + ) -> E2EResult<()> { + let mut constructor = OwnCodeHashRef::new(); + let contract = client + .instantiate("own_code_hash", &ink_e2e::alice(), &mut constructor) + .submit() + .await + .expect("instantiate failed"); + let call_builder = contract.call_builder::(); let own_code_hash_res = client .call(&ink_e2e::bob(), &call_builder.own_code_hash()) @@ -99,8 +130,8 @@ mod own_code_hash { // Compare codes obtained differently with own_code_hash and code_hash let get_code_res = client - .call(&ink_e2e::alice(), &call_builder.get_code()) - .submit() + .call(&ink_e2e::bob(), &call_builder.code_hash_of_own_address()) + .dry_run() .await .expect("get_code failed"); diff --git a/integration-tests/public/contract-invocation/contract1/lib.rs b/integration-tests/public/contract-invocation/contract1/lib.rs index 5c1093e97c5..69b59715b2d 100644 --- a/integration-tests/public/contract-invocation/contract1/lib.rs +++ b/integration-tests/public/contract-invocation/contract1/lib.rs @@ -32,15 +32,6 @@ mod contract1 { pub fn own_addr(&self) -> Address { self.env().address() } - - /* - // todo - /// Returns the hash code of the contract through the function 'own_code_hash'. - #[ink(message)] - pub fn own_code_hash(&self) -> ink::H256 { - self.env().own_code_hash().unwrap() - } - */ } impl Default for Contract1 {