Upgradable contracts using set_code function (#10690)

* poc logic

* set_code_hash impl, tests, benchmark

* Address @xgreenx's comments

* Move func defs closer to set_storage

* Check if code exists

- increment/decrement codehash refcount

* Document error for non-existing code hash

* Revert unrelated change

* Changes due to @athei's review

* Fix error handling

- comment errors: ReturnCodes
- update mock ext implementation
- return Error::CodeNotFound when no code for such hash

* Emit ContractCodeUpdated when setting new code_hash

* Address @athei's comments

* Move related defs to the bottom

* Minor comment update

Co-authored-by: Alexander Theißen <alex.theissen@me.com>

* Improve docs

* Improve docs

* Update frame/contracts/src/wasm/runtime.rs

Co-authored-by: Alexander Theißen <alex.theissen@me.com>

* Refactor set_code_hash test

* Minor change to benchmark

Co-authored-by: Alexander Theißen <alex.theissen@me.com>

* Minor change to benchmark

Co-authored-by: Alexander Theißen <alex.theissen@me.com>

* Minor comment refactor

Co-authored-by: Alexander Theißen <alex.theissen@me.com>

* Address @HCastano's comments

* Update seal_set_code_hash comment

Co-authored-by: Hernando Castano <HCastano@users.noreply.github.com>

* Move set_code_hash after delegate_call

* Move function to the bottom

* Moved and changed banchmark, added verify block

* Bring back previous benchmark

* Remove skip_meta for seal_set_code_hash

* Bring back skip_meta for seal_set_storage_per_new_kb

* Apply weights

Co-authored-by: Alexander Theißen <alex.theissen@me.com>
Co-authored-by: Hernando Castano <HCastano@users.noreply.github.com>
This commit is contained in:
Yarik Bratashchuk
2022-02-11 09:46:51 +02:00
committed by GitHub
parent 83eed8018b
commit e70ffbf44d
11 changed files with 1111 additions and 785 deletions
@@ -222,6 +222,9 @@ pub enum RuntimeCosts {
/// Weight charged for calling into the runtime.
#[cfg(feature = "unstable-interface")]
CallRuntime(Weight),
/// Weight of calling `seal_set_code_hash`
#[cfg(feature = "unstable-interface")]
SetCodeHash,
}
impl RuntimeCosts {
@@ -305,6 +308,8 @@ impl RuntimeCosts {
CopyIn(len) => s.return_per_byte.saturating_mul(len.into()),
#[cfg(feature = "unstable-interface")]
CallRuntime(weight) => weight,
#[cfg(feature = "unstable-interface")]
SetCodeHash => s.set_code_hash,
};
RuntimeToken {
#[cfg(test)]
@@ -1960,4 +1965,41 @@ define_env!(Env, <E: Ext>,
Err(_) => Ok(ReturnCode::EcdsaRecoverFailed),
}
},
// Replace the contract code at the specified address with new code.
//
// # Note
//
// There are a couple of important considerations which must be taken into account when
// using this API:
//
// 1. The storage at the code address 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.
//
// 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.
//
// 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 `seal_set_code_hash` and the next caller would use
// the old code.
//
// # Parameters
//
// - code_hash_ptr: A pointer to the buffer that contains the new code hash.
//
// # Errors
//
// `ReturnCode::CodeNotFound`
[__unstable__] seal_set_code_hash(ctx, code_hash_ptr: u32) -> ReturnCode => {
ctx.charge_gas(RuntimeCosts::SetCodeHash)?;
let code_hash: CodeHash<<E as Ext>::T> = ctx.read_sandbox_memory_as(code_hash_ptr)?;
match ctx.ext.set_code_hash(code_hash) {
Err(err) => {
let code = Runtime::<E>::err_into_return_code(err)?;
Ok(code)
},
Ok(()) => Ok(ReturnCode::Success)
}
},
);