Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add set_code_hash to EnvAccess #1698

Merged
merged 15 commits into from
Mar 6, 2023
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