mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-16 19:01:02 +00:00
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:
committed by
GitHub
parent
83eed8018b
commit
e70ffbf44d
@@ -26,7 +26,10 @@ mod runtime;
|
||||
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
pub use self::code_cache::reinstrument;
|
||||
pub use self::runtime::{ReturnCode, Runtime, RuntimeCosts};
|
||||
pub use self::{
|
||||
code_cache::{decrement_refcount, increment_refcount},
|
||||
runtime::{ReturnCode, Runtime, RuntimeCosts},
|
||||
};
|
||||
use crate::{
|
||||
exec::{ExecResult, Executable, ExportedFunction, Ext},
|
||||
gas::GasMeter,
|
||||
@@ -322,6 +325,7 @@ mod tests {
|
||||
gas_meter: GasMeter<Test>,
|
||||
debug_buffer: Vec<u8>,
|
||||
ecdsa_recover: RefCell<Vec<([u8; 65], [u8; 32])>>,
|
||||
code_hashes: Vec<CodeHash<Test>>,
|
||||
}
|
||||
|
||||
/// The call is mocked and just returns this hardcoded value.
|
||||
@@ -332,6 +336,7 @@ mod tests {
|
||||
impl Default for MockExt {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
code_hashes: Default::default(),
|
||||
storage: Default::default(),
|
||||
instantiates: Default::default(),
|
||||
terminations: Default::default(),
|
||||
@@ -390,6 +395,10 @@ mod tests {
|
||||
ExecReturnValue { flags: ReturnFlags::empty(), data: Bytes(Vec::new()) },
|
||||
))
|
||||
}
|
||||
fn set_code_hash(&mut self, hash: CodeHash<Self::T>) -> Result<(), DispatchError> {
|
||||
self.code_hashes.push(hash);
|
||||
Ok(())
|
||||
}
|
||||
fn transfer(&mut self, to: &AccountIdOf<Self::T>, value: u64) -> Result<(), DispatchError> {
|
||||
self.transfers.push(TransferEntry { to: to.clone(), value });
|
||||
Ok(())
|
||||
@@ -798,6 +807,67 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "unstable-interface")]
|
||||
fn contains_storage_works() {
|
||||
const CODE: &str = r#"
|
||||
(module
|
||||
(import "seal0" "seal_return" (func $seal_return (param i32 i32 i32)))
|
||||
(import "seal0" "seal_input" (func $seal_input (param i32 i32)))
|
||||
(import "__unstable__" "seal_contains_storage" (func $seal_contains_storage (param i32) (result i32)))
|
||||
(import "env" "memory" (memory 1 1))
|
||||
|
||||
;; [0, 4) size of input buffer (32 byte as we copy the key here)
|
||||
(data (i32.const 0) "\20")
|
||||
|
||||
;; [4, 36) input buffer
|
||||
|
||||
;; [36, inf) output buffer
|
||||
|
||||
(func (export "call")
|
||||
;; Receive key
|
||||
(call $seal_input
|
||||
(i32.const 4) ;; Pointer to the input buffer
|
||||
(i32.const 0) ;; Size of the length buffer
|
||||
)
|
||||
|
||||
;; Load the return value into the output buffer
|
||||
(i32.store (i32.const 36)
|
||||
(call $seal_contains_storage
|
||||
(i32.const 4) ;; The pointer to the storage key to fetch
|
||||
)
|
||||
)
|
||||
|
||||
;; Return the contents of the buffer
|
||||
(call $seal_return
|
||||
(i32.const 0) ;; flags
|
||||
(i32.const 36) ;; output buffer ptr
|
||||
(i32.const 4) ;; result is integer (4 bytes)
|
||||
)
|
||||
)
|
||||
|
||||
(func (export "deploy"))
|
||||
)
|
||||
"#;
|
||||
|
||||
let mut ext = MockExt::default();
|
||||
|
||||
ext.storage.insert([1u8; 32], vec![42u8]);
|
||||
ext.storage.insert([2u8; 32], vec![]);
|
||||
|
||||
// value does not exist -> sentinel value returned
|
||||
let result = execute(CODE, [3u8; 32].encode(), &mut ext).unwrap();
|
||||
assert_eq!(u32::from_le_bytes(result.data.0.try_into().unwrap()), crate::SENTINEL);
|
||||
|
||||
// value did exist -> success
|
||||
let result = execute(CODE, [1u8; 32].encode(), &mut ext).unwrap();
|
||||
assert_eq!(u32::from_le_bytes(result.data.0.try_into().unwrap()), 1,);
|
||||
|
||||
// value did exist -> success (zero sized type)
|
||||
let result = execute(CODE, [2u8; 32].encode(), &mut ext).unwrap();
|
||||
assert_eq!(u32::from_le_bytes(result.data.0.try_into().unwrap()), 0,);
|
||||
}
|
||||
|
||||
const CODE_INSTANTIATE: &str = r#"
|
||||
(module
|
||||
;; seal_instantiate(
|
||||
@@ -2249,67 +2319,6 @@ mod tests {
|
||||
assert_eq!(&result.data.0[4..], &[0u8; 0]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "unstable-interface")]
|
||||
fn contains_storage_works() {
|
||||
const CODE: &str = r#"
|
||||
(module
|
||||
(import "seal0" "seal_return" (func $seal_return (param i32 i32 i32)))
|
||||
(import "seal0" "seal_input" (func $seal_input (param i32 i32)))
|
||||
(import "__unstable__" "seal_contains_storage" (func $seal_contains_storage (param i32) (result i32)))
|
||||
(import "env" "memory" (memory 1 1))
|
||||
|
||||
;; [0, 4) size of input buffer (32 byte as we copy the key here)
|
||||
(data (i32.const 0) "\20")
|
||||
|
||||
;; [4, 36) input buffer
|
||||
|
||||
;; [36, inf) output buffer
|
||||
|
||||
(func (export "call")
|
||||
;; Receive key
|
||||
(call $seal_input
|
||||
(i32.const 4) ;; Pointer to the input buffer
|
||||
(i32.const 0) ;; Size of the length buffer
|
||||
)
|
||||
|
||||
;; Load the return value into the output buffer
|
||||
(i32.store (i32.const 36)
|
||||
(call $seal_contains_storage
|
||||
(i32.const 4) ;; The pointer to the storage key to fetch
|
||||
)
|
||||
)
|
||||
|
||||
;; Return the contents of the buffer
|
||||
(call $seal_return
|
||||
(i32.const 0) ;; flags
|
||||
(i32.const 36) ;; output buffer ptr
|
||||
(i32.const 4) ;; result is integer (4 bytes)
|
||||
)
|
||||
)
|
||||
|
||||
(func (export "deploy"))
|
||||
)
|
||||
"#;
|
||||
|
||||
let mut ext = MockExt::default();
|
||||
|
||||
ext.storage.insert([1u8; 32], vec![42u8]);
|
||||
ext.storage.insert([2u8; 32], vec![]);
|
||||
|
||||
// value does not exist -> sentinel value returned
|
||||
let result = execute(CODE, [3u8; 32].encode(), &mut ext).unwrap();
|
||||
assert_eq!(u32::from_le_bytes(result.data.0.try_into().unwrap()), crate::SENTINEL);
|
||||
|
||||
// value did exist -> success
|
||||
let result = execute(CODE, [1u8; 32].encode(), &mut ext).unwrap();
|
||||
assert_eq!(u32::from_le_bytes(result.data.0.try_into().unwrap()), 1,);
|
||||
|
||||
// value did exist -> success (zero sized type)
|
||||
let result = execute(CODE, [2u8; 32].encode(), &mut ext).unwrap();
|
||||
assert_eq!(u32::from_le_bytes(result.data.0.try_into().unwrap()), 0,);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "unstable-interface")]
|
||||
fn is_contract_works() {
|
||||
@@ -2385,4 +2394,45 @@ mod tests {
|
||||
ExecReturnValue { flags: ReturnFlags::empty(), data: Bytes(0u32.encode()) },
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "unstable-interface")]
|
||||
fn set_code_hash() {
|
||||
const CODE: &str = r#"
|
||||
(module
|
||||
(import "__unstable__" "seal_set_code_hash" (func $seal_set_code_hash (param i32) (result i32)))
|
||||
(import "env" "memory" (memory 1 1))
|
||||
(func $assert (param i32)
|
||||
(block $ok
|
||||
(br_if $ok
|
||||
(get_local 0)
|
||||
)
|
||||
(unreachable)
|
||||
)
|
||||
)
|
||||
(func (export "call")
|
||||
(local $exit_code i32)
|
||||
(set_local $exit_code
|
||||
(call $seal_set_code_hash (i32.const 0))
|
||||
)
|
||||
(call $assert
|
||||
(i32.eq (get_local $exit_code) (i32.const 0)) ;; ReturnCode::Success
|
||||
)
|
||||
)
|
||||
|
||||
(func (export "deploy"))
|
||||
|
||||
;; Hash of code.
|
||||
(data (i32.const 0)
|
||||
"\11\11\11\11\11\11\11\11\11\11\11\11\11\11\11\11"
|
||||
"\11\11\11\11\11\11\11\11\11\11\11\11\11\11\11\11"
|
||||
)
|
||||
)
|
||||
"#;
|
||||
|
||||
let mut mock_ext = MockExt::default();
|
||||
execute(CODE, [0u8; 32].encode(), &mut mock_ext).unwrap();
|
||||
|
||||
assert_eq!(mock_ext.code_hashes.pop().unwrap(), H256::from_slice(&[17u8; 32]));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user