Skip to content
This repository has been archived by the owner on Jan 2, 2023. It is now read-only.

Commit

Permalink
contracts: add seal_code_hash and seal_own_code_hash to API (pari…
Browse files Browse the repository at this point in the history
…tytech#10933)

* `seal_origin` + tests added

* `seal_origin` benchmark added

* `seal_code_hash` + tests added

* `seal_code_hash` benchmark added

* `seal_own_code_hash` + tests added

* `seal_own_code_hash` benchmark added

* fmt lil fix

* akward accident bug fix

* Apply suggestions from code review

Co-authored-by: Alexander Theißen <[email protected]>

* Apply suggestions from code review

Co-authored-by: Alexander Theißen <[email protected]>

* benchmark fix

* `WasmModule::getter()` to take `module_name` arg

* test enhanced

* fixes based on review feedback

* Apply suggestions from code review

Co-authored-by: Alexander Theißen <[email protected]>

* Hash left as const to return a ref to it from mock

* HASH test val to local const in mock

* Apply suggestions from code review

Co-authored-by: Alexander Theißen <[email protected]>

* fixes to benchmarks according to review feedback

* cargo run --quiet --profile=production  --features=runtime-benchmarks --manifest-path=bin/node/cli/Cargo.toml -- benchmark --chain=dev --steps=50 --repeat=20 --pallet=pallet_contracts --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --output=./frame/contracts/src/weights.rs --template=./.maintain/frame-weight-template.hbs

* removed `seal_origin` from API

Co-authored-by: Alexander Theißen <[email protected]>
Co-authored-by: Parity Bot <[email protected]>
  • Loading branch information
3 people committed Mar 29, 2022
1 parent 71bb50c commit 62fa7d2
Show file tree
Hide file tree
Showing 7 changed files with 951 additions and 614 deletions.
4 changes: 2 additions & 2 deletions frame/contracts/src/benchmarking/code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -339,12 +339,12 @@ where
/// Creates a wasm module that calls the imported function named `getter_name` `repeat`
/// times. The imported function is expected to have the "getter signature" of
/// (out_ptr: u32, len_ptr: u32) -> ().
pub fn getter(getter_name: &'static str, repeat: u32) -> Self {
pub fn getter(module_name: &'static str, getter_name: &'static str, repeat: u32) -> Self {
let pages = max_pages::<T>();
ModuleDefinition {
memory: Some(ImportedMemory::max::<T>()),
imported_functions: vec![ImportedFunction {
module: "seal0",
module: module_name,
name: getter_name,
params: vec![ValueType::I32, ValueType::I32],
return_type: None,
Expand Down
71 changes: 62 additions & 9 deletions frame/contracts/src/benchmarking/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,7 @@ benchmarks! {
seal_caller {
let r in 0 .. API_BENCHMARK_BATCHES;
let instance = Contract::<T>::new(WasmModule::getter(
"seal_caller", r * API_BENCHMARK_BATCH_SIZE
"seal0", "seal_caller", r * API_BENCHMARK_BATCH_SIZE
), vec![])?;
let origin = RawOrigin::Signed(instance.caller.clone());
}: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![])
Expand Down Expand Up @@ -436,6 +436,59 @@ benchmarks! {
let origin = RawOrigin::Signed(instance.caller.clone());
}: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![])

seal_code_hash {
let r in 0 .. API_BENCHMARK_BATCHES;
let accounts = (0 .. r * API_BENCHMARK_BATCH_SIZE)
.map(|n| account::<T::AccountId>("account", n, 0))
.collect::<Vec<_>>();
let account_len = accounts.get(0).map(|i| i.encode().len()).unwrap_or(0);
let accounts_bytes = accounts.iter().map(|a| a.encode()).flatten().collect::<Vec<_>>();
let accounts_len = accounts_bytes.len();
let pages = code::max_pages::<T>();
let code = WasmModule::<T>::from(ModuleDefinition {
memory: Some(ImportedMemory::max::<T>()),
imported_functions: vec![ImportedFunction {
module: "__unstable__",
name: "seal_code_hash",
params: vec![ValueType::I32, ValueType::I32, ValueType::I32],
return_type: Some(ValueType::I32),
}],
data_segments: vec![
DataSegment {
offset: 0,
value: 32u32.to_le_bytes().to_vec(), // output length
},
DataSegment {
offset: 36,
value: accounts_bytes,
},
],
call_body: Some(body::repeated_dyn(r * API_BENCHMARK_BATCH_SIZE, vec![
Counter(36, account_len as u32), // address_ptr
Regular(Instruction::I32Const(4)), // ptr to output data
Regular(Instruction::I32Const(0)), // ptr to output length
Regular(Instruction::Call(0)),
Regular(Instruction::Drop),
])),
.. Default::default()
});
let instance = Contract::<T>::new(code, vec![])?;
let info = instance.info()?;
// every account would be a contract (worst case)
for acc in accounts.iter() {
<ContractInfoOf<T>>::insert(acc, info.clone());
}
let origin = RawOrigin::Signed(instance.caller.clone());
}: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![])

seal_own_code_hash {
let r in 0 .. API_BENCHMARK_BATCHES;
let instance = Contract::<T>::new(WasmModule::getter(
"__unstable__", "seal_own_code_hash", r * API_BENCHMARK_BATCH_SIZE
), vec![])?;
let origin = RawOrigin::Signed(instance.caller.clone());
}: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![])

seal_caller_is_origin {
let r in 0 .. API_BENCHMARK_BATCHES;
let code = WasmModule::<T>::from(ModuleDefinition {
Expand All @@ -459,55 +512,55 @@ benchmarks! {
seal_address {
let r in 0 .. API_BENCHMARK_BATCHES;
let instance = Contract::<T>::new(WasmModule::getter(
"seal_address", r * API_BENCHMARK_BATCH_SIZE
"seal0", "seal_address", r * API_BENCHMARK_BATCH_SIZE
), vec![])?;
let origin = RawOrigin::Signed(instance.caller.clone());
}: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![])

seal_gas_left {
let r in 0 .. API_BENCHMARK_BATCHES;
let instance = Contract::<T>::new(WasmModule::getter(
"seal_gas_left", r * API_BENCHMARK_BATCH_SIZE
"seal0", "seal_gas_left", r * API_BENCHMARK_BATCH_SIZE
), vec![])?;
let origin = RawOrigin::Signed(instance.caller.clone());
}: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![])

seal_balance {
let r in 0 .. API_BENCHMARK_BATCHES;
let instance = Contract::<T>::new(WasmModule::getter(
"seal_balance", r * API_BENCHMARK_BATCH_SIZE
"seal0", "seal_balance", r * API_BENCHMARK_BATCH_SIZE
), vec![])?;
let origin = RawOrigin::Signed(instance.caller.clone());
}: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![])

seal_value_transferred {
let r in 0 .. API_BENCHMARK_BATCHES;
let instance = Contract::<T>::new(WasmModule::getter(
"seal_value_transferred", r * API_BENCHMARK_BATCH_SIZE
"seal0", "seal_value_transferred", r * API_BENCHMARK_BATCH_SIZE
), vec![])?;
let origin = RawOrigin::Signed(instance.caller.clone());
}: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![])

seal_minimum_balance {
let r in 0 .. API_BENCHMARK_BATCHES;
let instance = Contract::<T>::new(WasmModule::getter(
"seal_minimum_balance", r * API_BENCHMARK_BATCH_SIZE
"seal0", "seal_minimum_balance", r * API_BENCHMARK_BATCH_SIZE
), vec![])?;
let origin = RawOrigin::Signed(instance.caller.clone());
}: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![])

seal_block_number {
let r in 0 .. API_BENCHMARK_BATCHES;
let instance = Contract::<T>::new(WasmModule::getter(
"seal_block_number", r * API_BENCHMARK_BATCH_SIZE
"seal0", "seal_block_number", r * API_BENCHMARK_BATCH_SIZE
), vec![])?;
let origin = RawOrigin::Signed(instance.caller.clone());
}: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![])

seal_now {
let r in 0 .. API_BENCHMARK_BATCHES;
let instance = Contract::<T>::new(WasmModule::getter(
"seal_now", r * API_BENCHMARK_BATCH_SIZE
"seal0", "seal_now", r * API_BENCHMARK_BATCH_SIZE
), vec![])?;
let origin = RawOrigin::Signed(instance.caller.clone());
}: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![])
Expand Down Expand Up @@ -2341,7 +2394,7 @@ benchmarks! {
}

// w_memory_grow = w_bench - 2 * w_param
// We can only allow allocate as much memory as it is allowed in a a contract.
// We can only allow allocate as much memory as it is allowed in a contract.
// Therefore the repeat count is limited by the maximum memory any contract can have.
// Using a contract with more memory will skew the benchmark because the runtime of grow
// depends on how much memory is already allocated.
Expand Down
72 changes: 72 additions & 0 deletions frame/contracts/src/exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,14 @@ pub trait Ext: sealing::Sealed {
/// Check if a contract lives at the specified `address`.
fn is_contract(&self, address: &AccountIdOf<Self::T>) -> bool;

/// Returns the code hash of the contract for the given `address`.
///
/// Returns `None` if the `address` does not belong to a contract.
fn code_hash(&self, address: &AccountIdOf<Self::T>) -> Option<CodeHash<Self::T>>;

/// Returns the code hash of the contract being executed.
fn own_code_hash(&mut self) -> &CodeHash<Self::T>;

/// Check if the caller of the current contract is the origin of the whole call stack.
///
/// This can be checked with `is_contract(self.caller())` as well.
Expand Down Expand Up @@ -1103,6 +1111,14 @@ where
ContractInfoOf::<T>::contains_key(&address)
}

fn code_hash(&self, address: &T::AccountId) -> Option<CodeHash<Self::T>> {
<ContractInfoOf<T>>::get(&address).map(|contract| contract.code_hash)
}

fn own_code_hash(&mut self) -> &CodeHash<Self::T> {
&self.top_frame_mut().contract_info().code_hash
}

fn caller_is_origin(&self) -> bool {
self.caller() == &self.origin
}
Expand Down Expand Up @@ -1753,6 +1769,62 @@ mod tests {
});
}

#[test]
fn code_hash_returns_proper_values() {
let code_bob = MockLoader::insert(Call, |ctx, _| {
// ALICE is not a contract and hence she does not have a code_hash
assert!(ctx.ext.code_hash(&ALICE).is_none());
// BOB is a contract and hence he has a code_hash
assert!(ctx.ext.code_hash(&BOB).is_some());
exec_success()
});

ExtBuilder::default().build().execute_with(|| {
let schedule = <Test as Config>::Schedule::get();
place_contract(&BOB, code_bob);
let mut storage_meter = storage::meter::Meter::new(&ALICE, Some(0), 0).unwrap();
// ALICE (not contract) -> BOB (contract)
let result = MockStack::run_call(
ALICE,
BOB,
&mut GasMeter::<Test>::new(GAS_LIMIT),
&mut storage_meter,
&schedule,
0,
vec![0],
None,
);
assert_matches!(result, Ok(_));
});
}

#[test]
fn own_code_hash_returns_proper_values() {
let bob_ch = MockLoader::insert(Call, |ctx, _| {
let code_hash = ctx.ext.code_hash(&BOB).unwrap();
assert_eq!(*ctx.ext.own_code_hash(), code_hash);
exec_success()
});

ExtBuilder::default().build().execute_with(|| {
let schedule = <Test as Config>::Schedule::get();
place_contract(&BOB, bob_ch);
let mut storage_meter = storage::meter::Meter::new(&ALICE, Some(0), 0).unwrap();
// ALICE (not contract) -> BOB (contract)
let result = MockStack::run_call(
ALICE,
BOB,
&mut GasMeter::<Test>::new(GAS_LIMIT),
&mut storage_meter,
&schedule,
0,
vec![0],
None,
);
assert_matches!(result, Ok(_));
});
}

#[test]
fn caller_is_origin_returns_proper_values() {
let code_charlie = MockLoader::insert(Call, |ctx, _| {
Expand Down
8 changes: 8 additions & 0 deletions frame/contracts/src/schedule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,12 @@ pub struct HostFnWeights<T: Config> {
/// Weight of calling `seal_is_contract`.
pub is_contract: Weight,

/// Weight of calling `seal_code_hash`.
pub code_hash: Weight,

/// Weight of calling `seal_own_code_hash`.
pub own_code_hash: Weight,

/// Weight of calling `seal_caller_is_origin`.
pub caller_is_origin: Weight,

Expand Down Expand Up @@ -584,6 +590,8 @@ impl<T: Config> Default for HostFnWeights<T> {
Self {
caller: cost_batched!(seal_caller),
is_contract: cost_batched!(seal_is_contract),
code_hash: cost_batched!(seal_code_hash),
own_code_hash: cost_batched!(seal_own_code_hash),
caller_is_origin: cost_batched!(seal_caller_is_origin),
address: cost_batched!(seal_address),
gas_left: cost_batched!(seal_gas_left),
Expand Down
Loading

0 comments on commit 62fa7d2

Please sign in to comment.