Skip to content

Commit

Permalink
set_code_hash impl, tests, benchmark
Browse files Browse the repository at this point in the history
  • Loading branch information
yarikbratashchuk committed Jan 11, 2022
1 parent b1dd3d7 commit 68e5a61
Show file tree
Hide file tree
Showing 8 changed files with 842 additions and 626 deletions.
13 changes: 13 additions & 0 deletions frame/contracts/fixtures/new_set_code_hash_contract.wat
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
(module
(import "seal0" "seal_return" (func $seal_return (param i32 i32 i32)))
(import "env" "memory" (memory 1 1))

;; [0, 32) return value
(data (i32.const 0) "\11")

(func (export "deploy"))

(func (export "call")
(call $seal_return (i32.const 0) (i32.const 0) (i32.const 4))
)
)
43 changes: 43 additions & 0 deletions frame/contracts/fixtures/set_code_hash.wat
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
(module
(import "seal0" "seal_input" (func $seal_input (param i32 i32)))
(import "seal0" "seal_return" (func $seal_return (param i32 i32 i32)))
(import "__unstable__" "seal_set_code_hash" (func $seal_set_code_hash (param i32) (result i32)))

(import "env" "memory" (memory 1 1))

;; [0, 32) here we store input

;; [32, 36) input size
(data (i32.const 32) "\20")

;; [36, 40) return value
(data (i32.const 36) "\01")

(func $assert (param i32)
(block $ok
(br_if $ok
(get_local 0)
)
(unreachable)
)
)

(func (export "call")
(local $exit_code i32)

(call $seal_input (i32.const 0) (i32.const 32))

(set_local $exit_code
(call $seal_set_code_hash (i32.const 0)) ;; Pointer to the input data.
)
(call $assert
(i32.eq (get_local $exit_code) (i32.const 0)) ;; ReturnCode::Success
)

;; we return 1 after setting new code_hash
;; next call will NOT return this value, because contract code has been changed
(call $seal_return (i32.const 0) (i32.const 36) (i32.const 4))
)

(func (export "deploy"))
)
40 changes: 40 additions & 0 deletions frame/contracts/src/benchmarking/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -808,6 +808,46 @@ benchmarks! {
let origin = RawOrigin::Signed(instance.caller.clone());
}: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![])

seal_set_code_hash {
let r in 0 .. API_BENCHMARK_BATCHES;
let code_hashes = (0..r * API_BENCHMARK_BATCH_SIZE)
.map(|i| {
let new_code = WasmModule::<T>::dummy_with_bytes(i);
Contracts::<T>::store_code_raw(new_code.code, whitelisted_caller())?;
Ok(new_code.hash)
})
.collect::<Result<Vec<_>, &'static str>>()?;
let code_hash_len = code_hashes.get(0).map(|x| x.encode().len()).unwrap_or(0);
let code_hashes_bytes = code_hashes.iter().flat_map(|x| x.encode()).collect::<Vec<_>>();
let code_hashes_len = code_hashes_bytes.len();

let code = WasmModule::<T>::from(ModuleDefinition {
memory: Some(ImportedMemory::max::<T>()),
imported_functions: vec![ImportedFunction {
module: "__unstable__",
name: "seal_set_code_hash",
params: vec![
ValueType::I32,
],
return_type: Some(ValueType::I32),
}],
data_segments: vec![
DataSegment {
offset: 0 as u32,
value: code_hashes_bytes,
},
],
call_body: Some(body::repeated_dyn(r * API_BENCHMARK_BATCH_SIZE, vec![
Counter(0 as u32, code_hash_len as u32), // code_hash_ptr
Regular(Instruction::Call(0)),
Regular(Instruction::Drop),
])),
.. Default::default()
});
let instance = Contract::<T>::new(code, vec![])?;
let origin = RawOrigin::Signed(instance.caller.clone());
}: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![])

seal_set_storage_per_kb {
let n in 0 .. T::Schedule::get().limits.payload_len / 1024;
let key = T::Hashing::hash_of(&1u32).as_ref().to_vec();
Expand Down
4 changes: 4 additions & 0 deletions frame/contracts/src/schedule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,9 @@ pub struct HostFnWeights<T: Config> {
/// Weight per byte of an item stored with `seal_set_storage`.
pub set_storage_per_byte: Weight,

/// Weight of calling `seal_set_code_hash`.
pub set_code_hash: Weight,

/// Weight of calling `seal_clear_storage`.
pub clear_storage: Weight,

Expand Down Expand Up @@ -587,6 +590,7 @@ impl<T: Config> Default for HostFnWeights<T> {
debug_message: cost_batched!(seal_debug_message),
set_storage: cost_batched!(seal_set_storage),
set_storage_per_byte: cost_byte_batched!(seal_set_storage_per_kb),
set_code_hash: cost_batched!(seal_set_code_hash),
clear_storage: cost_batched!(seal_clear_storage),
contains_storage: cost_batched!(seal_contains_storage),
get_storage: cost_batched!(seal_get_storage),
Expand Down
47 changes: 47 additions & 0 deletions frame/contracts/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2812,6 +2812,53 @@ fn call_after_killed_account_needs_funding() {
});
}

#[test]
#[cfg(feature = "unstable-interface")]
fn set_code_hash() {
let (wasm, code_hash) = compile_module::<Test>("set_code_hash").unwrap();
let (new_wasm, new_code_hash) = compile_module::<Test>("new_set_code_hash_contract").unwrap();

let contract_addr = Contracts::contract_address(&ALICE, &code_hash, &[]);

ExtBuilder::default().existential_deposit(100).build().execute_with(|| {
let _ = Balances::deposit_creating(&ALICE, 1_000_000);

// Instantiate the 'caller'
assert_ok!(Contracts::instantiate_with_code(
Origin::signed(ALICE),
300_000,
GAS_LIMIT,
None,
wasm,
vec![],
vec![],
));
// upload new code
assert_ok!(Contracts::upload_code(Origin::signed(ALICE), new_wasm.clone(), None));

// First call sets new code_hash and returns 1
let result = Contracts::bare_call(
ALICE,
contract_addr.clone(),
0,
GAS_LIMIT,
None,
new_code_hash.as_ref().to_vec(),
true,
)
.result
.unwrap();
assert_return_code!(result, 1);

// Second calls new contract code that returns 17
let result =
Contracts::bare_call(ALICE, contract_addr.clone(), 0, GAS_LIMIT, None, vec![], true)
.result
.unwrap();
assert_return_code!(result, 17);
});
}

#[test]
fn contract_reverted() {
let (wasm, code_hash) = compile_module::<Test>("return_with_data").unwrap();
Expand Down
46 changes: 45 additions & 1 deletion frame/contracts/src/wasm/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// This file is part of Substrate.
// This file is part of substrate.

// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
Expand Down Expand Up @@ -406,6 +406,9 @@ mod tests {
}
Ok(result)
}
fn set_code_hash(&mut self, hash: CodeHash<Self::T>) {
self.storage.insert(StorageKey::from(hash), vec![1, 2, 3]);
}
fn caller(&self) -> &AccountIdOf<Self::T> {
&ALICE
}
Expand Down Expand Up @@ -2180,6 +2183,47 @@ mod tests {
assert_eq!(&result.data.0[4..], &[0u8; 0]);
}

#[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.storage.get(&[17u8; 32]), Some(&vec![1, 2, 3]));
}

#[test]
#[cfg(feature = "unstable-interface")]
fn contains_storage_works() {
Expand Down
6 changes: 6 additions & 0 deletions frame/contracts/src/wasm/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,9 @@ pub enum RuntimeCosts {
DebugMessage,
/// Weight of calling `seal_set_storage` for the given storage item size.
SetStorage(u32),
/// Weight of calling `seal_set_code_hash`
#[cfg(feature = "unstable-interface")]
SetCodeHash,
/// Weight of calling `seal_clear_storage`.
ClearStorage,
/// Weight of calling `seal_contains_storage`.
Expand Down Expand Up @@ -251,6 +254,8 @@ impl RuntimeCosts {
DebugMessage => s.debug_message,
SetStorage(len) =>
s.set_storage.saturating_add(s.set_storage_per_byte.saturating_mul(len.into())),
#[cfg(feature = "unstable-interface")]
SetCodeHash => s.set_code_hash,
ClearStorage => s.clear_storage,
#[cfg(feature = "unstable-interface")]
ContainsStorage => s.contains_storage,
Expand Down Expand Up @@ -1859,6 +1864,7 @@ define_env!(Env, <E: Ext>,
// - requested buffer is not within the bounds of the sandbox memory.
// - the buffer contents cannot be decoded as the required type.
[__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)?;
ctx.ext.set_code_hash(code_hash);
Ok(ReturnCode::Success)
Expand Down
Loading

0 comments on commit 68e5a61

Please sign in to comment.