Skip to content

Commit

Permalink
Add set_code_hash to EnvAccess (#1698)
Browse files Browse the repository at this point in the history
* Add `set_code_hash` to `EnvAccess`

* Update example

* Update examples to use Hash type

* Make it a non breaking change

* revert

* Remove `AsRef<u8; 32>`, 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 <[email protected]>
  • Loading branch information
ascjones and HCastano authored Mar 6, 2023
1 parent 4e56617 commit a505183
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 11 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
44 changes: 35 additions & 9 deletions crates/env/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
///
Expand Down Expand Up @@ -701,6 +710,23 @@ pub fn set_code_hash(code_hash: &[u8; 32]) -> Result<()> {
<EnvInstance as OnInstance>::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<E>(code_hash: &E::Hash) -> Result<()>
where
E: Environment,
{
<EnvInstance as OnInstance>::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.
Expand Down
31 changes: 31 additions & 0 deletions crates/ink/src/env_access.rs
Original file line number Diff line number Diff line change
Expand Up @@ -970,6 +970,37 @@ where
ink_env::own_code_hash::<E>()
}

/// 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::<E>(code_hash)
}

#[cfg(feature = "call-runtime")]
pub fn call_runtime<Call: scale::Encode>(self, call: &Call) -> Result<()> {
ink_env::call_runtime::<E, _>(call)
Expand Down
4 changes: 2 additions & 2 deletions integration-tests/set_code_hash/updated_incrementer/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down

0 comments on commit a505183

Please sign in to comment.