From a505183937ce618aa3022dd497ba3334f4d881d7 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Mon, 6 Mar 2023 09:12:00 +0000 Subject: [PATCH] Add `set_code_hash` to `EnvAccess` (#1698) * Add `set_code_hash` to `EnvAccess` * Update example * Update examples to use Hash type * Make it a non breaking change * revert * Remove `AsRef`, add set_code_hash2 * CHANGELOG.md * Deduplicate comment * Remove deleted file * Attempt update at comment * Improve some of the doc comments * Fix typo --------- Co-authored-by: Hernando Castano --- CHANGELOG.md | 1 + crates/env/src/api.rs | 44 +++++++++++++++---- crates/ink/src/env_access.rs | 31 +++++++++++++ .../set_code_hash/updated_incrementer/lib.rs | 4 +- 4 files changed, 69 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f1cfc5409d..336cbfe4793 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Basic support for `dyn Trait` to allow cross-contract calls only with trait - [#1673](https://github.com/paritytech/ink/pull/1673) - E2E: auto detect contracts to be built - [#1691](https://github.com/paritytech/ink/pull/1691) +- Add `set_code_hash` to `EnvAccess` - [#1698](https://github.com/paritytech/ink/pull/1698) ## Version 4.0.1 diff --git a/crates/env/src/api.rs b/crates/env/src/api.rs index 1449e707388..bfe98eee4a6 100644 --- a/crates/env/src/api.rs +++ b/crates/env/src/api.rs @@ -612,19 +612,28 @@ where /// /// # Note /// -/// There are a couple of important considerations which must be taken into account when +/// There are a few important considerations which must be taken into account when /// using this API: /// -/// 1. The storage at the code hash will remain untouched. This means that contract developers -/// must ensure that the storage layout of the new code is compatible with that of the old code. +/// 1. The storage at the code hash will remain untouched. /// -/// 2. Contracts using this API can't be assumed as having deterministic addresses. Said another way, -/// when using this API you lose the guarantee that an address always identifies a specific code hash. +/// Contract developers **must ensure** that the storage layout of the new code is compatible with +/// that of the old code. /// -/// 3. If a contract calls into itself after changing its code the new call would use -/// the new code. However, if the original caller panics after returning from the sub call it -/// would revert the changes made by `set_code_hash` and the next caller would use -/// the old code. +/// 2. The contract address (`AccountId`) remains the same, while the `code_hash` changes. +/// +/// Contract addresses are initially derived from `hash(deploying_address ++ code_hash ++ salt)`. +/// This makes it possible to determine a contracts address (`AccountId`) using the `code_hash` of +/// the *initial* code used to instantiate the contract. +/// +/// However, because `set_code_hash` can modify the underlying `code_hash` of a contract, it should +/// not be relied upon that a contracts address can always be derived from its stored `code_hash`. +/// +/// 3. Re-entrant calls use new `code_hash`. +/// +/// If a contract calls into itself after changing its code the new call would use the new code. +/// However, if the original caller panics after returning from the sub call it would revert the +/// changes made by `set_code_hash` and the next caller would use the old code. /// /// # Errors /// @@ -701,6 +710,23 @@ pub fn set_code_hash(code_hash: &[u8; 32]) -> Result<()> { ::on_instance(|instance| instance.set_code_hash(code_hash)) } +/// Replace the contract code at the specified address with new code. +/// +/// # Compatibility +/// +/// This is new version of the existing [`set_code_hash`] function. We plan to place the old +/// function with this in the next `MAJOR` release. +/// +/// See the original [`set_code_hash`] function for full details. +pub fn set_code_hash2(code_hash: &E::Hash) -> Result<()> +where + E: Environment, +{ + ::on_instance(|instance| { + instance.set_code_hash(code_hash.as_ref()) + }) +} + /// Tries to trigger a runtime dispatchable, i.e. an extrinsic from a pallet. /// /// `call` (after SCALE encoding) should be decodable to a valid instance of `RuntimeCall` enum. diff --git a/crates/ink/src/env_access.rs b/crates/ink/src/env_access.rs index 46c94dc2497..8c97bd94e2a 100644 --- a/crates/ink/src/env_access.rs +++ b/crates/ink/src/env_access.rs @@ -970,6 +970,37 @@ where ink_env::own_code_hash::() } + /// Replace the contract code at the specified address with new code. + /// + /// # Example + /// + /// ``` + /// # #[ink::contract] + /// # pub mod my_contract { + /// # #[ink(storage)] + /// # pub struct MyContract { } + /// # + /// # impl MyContract { + /// # #[ink(constructor)] + /// # pub fn new() -> Self { + /// # Self {} + /// # } + /// # + /// #[ink(message)] + /// pub fn set_code_hash(&mut self, code_hash: Hash) { + /// self.env().set_code_hash(&code_hash).unwrap_or_else(|err| panic!("failed to set code hash: {:?}", err)) + /// } + /// # } + /// # } + /// ``` + /// + /// # Note + /// + /// For more details visit: [`ink_env::set_code_hash`] + pub fn set_code_hash(self, code_hash: &E::Hash) -> Result<()> { + ink_env::set_code_hash2::(code_hash) + } + #[cfg(feature = "call-runtime")] pub fn call_runtime(self, call: &Call) -> Result<()> { ink_env::call_runtime::(call) diff --git a/integration-tests/set_code_hash/updated_incrementer/lib.rs b/integration-tests/set_code_hash/updated_incrementer/lib.rs index 14d4758393c..1ef5be7ebcb 100644 --- a/integration-tests/set_code_hash/updated_incrementer/lib.rs +++ b/integration-tests/set_code_hash/updated_incrementer/lib.rs @@ -57,8 +57,8 @@ pub mod incrementer { /// /// In a production contract you would do some authorization here! #[ink(message)] - pub fn set_code(&mut self, code_hash: [u8; 32]) { - ink::env::set_code_hash(&code_hash).unwrap_or_else(|err| { + pub fn set_code(&mut self, code_hash: Hash) { + self.env().set_code_hash(&code_hash).unwrap_or_else(|err| { panic!("Failed to `set_code_hash` to {code_hash:?} due to {err:?}") }); ink::env::debug_println!("Switched code hash to {:?}.", code_hash);