Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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