diff --git a/crates/env/src/api.rs b/crates/env/src/api.rs index fb96c44e87..889d763d24 100644 --- a/crates/env/src/api.rs +++ b/crates/env/src/api.rs @@ -514,6 +514,35 @@ where }) } +/// Retrieves the code hash of the contract at the specified account id. +/// +/// # Errors +/// +/// - If no code hash was found for the specified account id. +/// - If the returned value cannot be properly decoded. +pub fn code_hash(account: &E::AccountId) -> Result +where + E: Environment, +{ + ::on_instance(|instance| { + TypedEnvBackend::code_hash::(instance, account) + }) +} + +/// Retrieves the code hash of the currently executing contract. +/// +/// # Errors +/// +/// If the returned value cannot be properly decoded. +pub fn own_code_hash() -> Result +where + E: Environment, +{ + ::on_instance(|instance| { + TypedEnvBackend::own_code_hash::(instance) + }) +} + /// Checks whether the caller of the current contract is the origin of the whole call stack. /// /// Prefer this over [`is_contract`] when checking whether your contract is being called by diff --git a/crates/env/src/backend.rs b/crates/env/src/backend.rs index cb7b7723a9..1bbaa4c7e1 100644 --- a/crates/env/src/backend.rs +++ b/crates/env/src/backend.rs @@ -447,4 +447,22 @@ pub trait TypedEnvBackend: EnvBackend { fn caller_is_origin(&mut self) -> bool where E: Environment; + + /// 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: &E::AccountId) -> Result + where + E: Environment; + + /// 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 + where + E: Environment; } diff --git a/crates/env/src/engine/off_chain/impls.rs b/crates/env/src/engine/off_chain/impls.rs index 7c302b16d2..969d761746 100644 --- a/crates/env/src/engine/off_chain/impls.rs +++ b/crates/env/src/engine/off_chain/impls.rs @@ -486,4 +486,18 @@ impl TypedEnvBackend for EnvInstance { { unimplemented!("off-chain environment does not support cross-contract calls") } + + fn code_hash(&mut self, _account: &E::AccountId) -> Result + where + E: Environment, + { + unimplemented!("off-chain environment does not support `code_hash`") + } + + fn own_code_hash(&mut self) -> Result + where + E: Environment, + { + unimplemented!("off-chain environment does not support `own_code_hash`") + } } diff --git a/crates/env/src/engine/on_chain/ext.rs b/crates/env/src/engine/on_chain/ext.rs index f35cae20da..13d63677da 100644 --- a/crates/env/src/engine/on_chain/ext.rs +++ b/crates/env/src/engine/on_chain/ext.rs @@ -347,6 +347,17 @@ mod sys { message_hash_ptr: Ptr32<[u8]>, output_ptr: Ptr32Mut<[u8]>, ) -> ReturnCode; + + pub fn seal_code_hash( + account_id_ptr: Ptr32<[u8]>, + output_ptr: Ptr32Mut<[u8]>, + output_len_ptr: Ptr32Mut, + ) -> ReturnCode; + + pub fn seal_own_code_hash( + output_ptr: Ptr32Mut<[u8]>, + output_len_ptr: Ptr32Mut, + ); } } @@ -676,3 +687,25 @@ pub fn caller_is_origin() -> bool { let ret_val = unsafe { sys::seal_caller_is_origin() }; ret_val.into_bool() } + +pub fn code_hash(account_id: &[u8], output: &mut [u8]) -> Result { + let mut output_len = output.len() as u32; + let ret_val = unsafe { + sys::seal_code_hash( + Ptr32::from_slice(account_id), + Ptr32Mut::from_slice(output), + Ptr32Mut::from_ref(&mut output_len), + ) + }; + ret_val.into() +} + +pub fn own_code_hash(output: &mut [u8]) { + let mut output_len = output.len() as u32; + unsafe { + sys::seal_own_code_hash( + Ptr32Mut::from_slice(output), + Ptr32Mut::from_ref(&mut output_len), + ) + } +} diff --git a/crates/env/src/engine/on_chain/impls.rs b/crates/env/src/engine/on_chain/impls.rs index da6a54bba8..e6611b2367 100644 --- a/crates/env/src/engine/on_chain/impls.rs +++ b/crates/env/src/engine/on_chain/impls.rs @@ -512,4 +512,28 @@ impl TypedEnvBackend for EnvInstance { { 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) + } } diff --git a/crates/lang/src/env_access.rs b/crates/lang/src/env_access.rs index ede514f098..fa843c6826 100644 --- a/crates/lang/src/env_access.rs +++ b/crates/lang/src/env_access.rs @@ -901,4 +901,68 @@ where pub fn caller_is_origin(self) -> bool { ink_env::caller_is_origin::() } + + /// Returns the code hash of the contract at the given `account` id. + /// + /// # Example + /// + /// ``` + /// # use ink_lang as ink; + /// # #[ink::contract] + /// # pub mod my_contract { + /// # #[ink(storage)] + /// # pub struct MyContract { } + /// # + /// # impl MyContract { + /// # #[ink(constructor)] + /// # pub fn new() -> Self { + /// # Self {} + /// # } + /// # + /// #[ink(message)] + /// pub fn code_hash(&mut self, account_id: AccountId) -> Option { + /// self.env().code_hash(&account_id).ok() + /// } + /// # } + /// # } + /// ``` + /// + /// # Note + /// + /// For more details visit: [`ink_env::code_hash`] + pub fn code_hash(self, account_id: &E::AccountId) -> Result { + ink_env::code_hash::(account_id) + } + + /// Returns the code hash of the contract at the given `account` id. + /// + /// # Example + /// + /// ``` + /// # use ink_lang as ink; + /// # #[ink::contract] + /// # pub mod my_contract { + /// # #[ink(storage)] + /// # pub struct MyContract { } + /// # + /// # impl MyContract { + /// # #[ink(constructor)] + /// # pub fn new() -> Self { + /// # Self {} + /// # } + /// # + /// #[ink(message)] + /// pub fn own_code_hash(&mut self) -> Hash { + /// self.env().own_code_hash().expect("contract should have a code hash") + /// } + /// # } + /// # } + /// ``` + /// + /// # Note + /// + /// For more details visit: [`ink_env::own_code_hash`] + pub fn own_code_hash(self) -> Result { + ink_env::own_code_hash::() + } }