Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.

Commit 62fa7d2

Browse files
agryaznovatheiParity Bot
authored
contracts: add seal_code_hash and seal_own_code_hash to API (#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 <alex.theissen@me.com> * Apply suggestions from code review Co-authored-by: Alexander Theißen <alex.theissen@me.com> * 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 <alex.theissen@me.com> * 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 <alex.theissen@me.com> * 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 <alex.theissen@me.com> Co-authored-by: Parity Bot <admin@parity.io>
1 parent 71bb50c commit 62fa7d2

File tree

7 files changed

+951
-614
lines changed

7 files changed

+951
-614
lines changed

frame/contracts/src/benchmarking/code.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -339,12 +339,12 @@ where
339339
/// Creates a wasm module that calls the imported function named `getter_name` `repeat`
340340
/// times. The imported function is expected to have the "getter signature" of
341341
/// (out_ptr: u32, len_ptr: u32) -> ().
342-
pub fn getter(getter_name: &'static str, repeat: u32) -> Self {
342+
pub fn getter(module_name: &'static str, getter_name: &'static str, repeat: u32) -> Self {
343343
let pages = max_pages::<T>();
344344
ModuleDefinition {
345345
memory: Some(ImportedMemory::max::<T>()),
346346
imported_functions: vec![ImportedFunction {
347-
module: "seal0",
347+
module: module_name,
348348
name: getter_name,
349349
params: vec![ValueType::I32, ValueType::I32],
350350
return_type: None,

frame/contracts/src/benchmarking/mod.rs

Lines changed: 62 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -394,7 +394,7 @@ benchmarks! {
394394
seal_caller {
395395
let r in 0 .. API_BENCHMARK_BATCHES;
396396
let instance = Contract::<T>::new(WasmModule::getter(
397-
"seal_caller", r * API_BENCHMARK_BATCH_SIZE
397+
"seal0", "seal_caller", r * API_BENCHMARK_BATCH_SIZE
398398
), vec![])?;
399399
let origin = RawOrigin::Signed(instance.caller.clone());
400400
}: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![])
@@ -436,6 +436,59 @@ benchmarks! {
436436
let origin = RawOrigin::Signed(instance.caller.clone());
437437
}: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![])
438438

439+
seal_code_hash {
440+
let r in 0 .. API_BENCHMARK_BATCHES;
441+
let accounts = (0 .. r * API_BENCHMARK_BATCH_SIZE)
442+
.map(|n| account::<T::AccountId>("account", n, 0))
443+
.collect::<Vec<_>>();
444+
let account_len = accounts.get(0).map(|i| i.encode().len()).unwrap_or(0);
445+
let accounts_bytes = accounts.iter().map(|a| a.encode()).flatten().collect::<Vec<_>>();
446+
let accounts_len = accounts_bytes.len();
447+
let pages = code::max_pages::<T>();
448+
let code = WasmModule::<T>::from(ModuleDefinition {
449+
memory: Some(ImportedMemory::max::<T>()),
450+
imported_functions: vec![ImportedFunction {
451+
module: "__unstable__",
452+
name: "seal_code_hash",
453+
params: vec![ValueType::I32, ValueType::I32, ValueType::I32],
454+
return_type: Some(ValueType::I32),
455+
}],
456+
data_segments: vec![
457+
DataSegment {
458+
offset: 0,
459+
value: 32u32.to_le_bytes().to_vec(), // output length
460+
},
461+
DataSegment {
462+
offset: 36,
463+
value: accounts_bytes,
464+
},
465+
],
466+
call_body: Some(body::repeated_dyn(r * API_BENCHMARK_BATCH_SIZE, vec![
467+
Counter(36, account_len as u32), // address_ptr
468+
Regular(Instruction::I32Const(4)), // ptr to output data
469+
Regular(Instruction::I32Const(0)), // ptr to output length
470+
Regular(Instruction::Call(0)),
471+
Regular(Instruction::Drop),
472+
])),
473+
.. Default::default()
474+
});
475+
let instance = Contract::<T>::new(code, vec![])?;
476+
let info = instance.info()?;
477+
// every account would be a contract (worst case)
478+
for acc in accounts.iter() {
479+
<ContractInfoOf<T>>::insert(acc, info.clone());
480+
}
481+
let origin = RawOrigin::Signed(instance.caller.clone());
482+
}: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![])
483+
484+
seal_own_code_hash {
485+
let r in 0 .. API_BENCHMARK_BATCHES;
486+
let instance = Contract::<T>::new(WasmModule::getter(
487+
"__unstable__", "seal_own_code_hash", r * API_BENCHMARK_BATCH_SIZE
488+
), vec![])?;
489+
let origin = RawOrigin::Signed(instance.caller.clone());
490+
}: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![])
491+
439492
seal_caller_is_origin {
440493
let r in 0 .. API_BENCHMARK_BATCHES;
441494
let code = WasmModule::<T>::from(ModuleDefinition {
@@ -459,55 +512,55 @@ benchmarks! {
459512
seal_address {
460513
let r in 0 .. API_BENCHMARK_BATCHES;
461514
let instance = Contract::<T>::new(WasmModule::getter(
462-
"seal_address", r * API_BENCHMARK_BATCH_SIZE
515+
"seal0", "seal_address", r * API_BENCHMARK_BATCH_SIZE
463516
), vec![])?;
464517
let origin = RawOrigin::Signed(instance.caller.clone());
465518
}: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![])
466519

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

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

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

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

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

507560
seal_now {
508561
let r in 0 .. API_BENCHMARK_BATCHES;
509562
let instance = Contract::<T>::new(WasmModule::getter(
510-
"seal_now", r * API_BENCHMARK_BATCH_SIZE
563+
"seal0", "seal_now", r * API_BENCHMARK_BATCH_SIZE
511564
), vec![])?;
512565
let origin = RawOrigin::Signed(instance.caller.clone());
513566
}: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![])
@@ -2341,7 +2394,7 @@ benchmarks! {
23412394
}
23422395

23432396
// w_memory_grow = w_bench - 2 * w_param
2344-
// We can only allow allocate as much memory as it is allowed in a a contract.
2397+
// We can only allow allocate as much memory as it is allowed in a contract.
23452398
// Therefore the repeat count is limited by the maximum memory any contract can have.
23462399
// Using a contract with more memory will skew the benchmark because the runtime of grow
23472400
// depends on how much memory is already allocated.

frame/contracts/src/exec.rs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,14 @@ pub trait Ext: sealing::Sealed {
162162
/// Check if a contract lives at the specified `address`.
163163
fn is_contract(&self, address: &AccountIdOf<Self::T>) -> bool;
164164

165+
/// Returns the code hash of the contract for the given `address`.
166+
///
167+
/// Returns `None` if the `address` does not belong to a contract.
168+
fn code_hash(&self, address: &AccountIdOf<Self::T>) -> Option<CodeHash<Self::T>>;
169+
170+
/// Returns the code hash of the contract being executed.
171+
fn own_code_hash(&mut self) -> &CodeHash<Self::T>;
172+
165173
/// Check if the caller of the current contract is the origin of the whole call stack.
166174
///
167175
/// This can be checked with `is_contract(self.caller())` as well.
@@ -1103,6 +1111,14 @@ where
11031111
ContractInfoOf::<T>::contains_key(&address)
11041112
}
11051113

1114+
fn code_hash(&self, address: &T::AccountId) -> Option<CodeHash<Self::T>> {
1115+
<ContractInfoOf<T>>::get(&address).map(|contract| contract.code_hash)
1116+
}
1117+
1118+
fn own_code_hash(&mut self) -> &CodeHash<Self::T> {
1119+
&self.top_frame_mut().contract_info().code_hash
1120+
}
1121+
11061122
fn caller_is_origin(&self) -> bool {
11071123
self.caller() == &self.origin
11081124
}
@@ -1753,6 +1769,62 @@ mod tests {
17531769
});
17541770
}
17551771

1772+
#[test]
1773+
fn code_hash_returns_proper_values() {
1774+
let code_bob = MockLoader::insert(Call, |ctx, _| {
1775+
// ALICE is not a contract and hence she does not have a code_hash
1776+
assert!(ctx.ext.code_hash(&ALICE).is_none());
1777+
// BOB is a contract and hence he has a code_hash
1778+
assert!(ctx.ext.code_hash(&BOB).is_some());
1779+
exec_success()
1780+
});
1781+
1782+
ExtBuilder::default().build().execute_with(|| {
1783+
let schedule = <Test as Config>::Schedule::get();
1784+
place_contract(&BOB, code_bob);
1785+
let mut storage_meter = storage::meter::Meter::new(&ALICE, Some(0), 0).unwrap();
1786+
// ALICE (not contract) -> BOB (contract)
1787+
let result = MockStack::run_call(
1788+
ALICE,
1789+
BOB,
1790+
&mut GasMeter::<Test>::new(GAS_LIMIT),
1791+
&mut storage_meter,
1792+
&schedule,
1793+
0,
1794+
vec![0],
1795+
None,
1796+
);
1797+
assert_matches!(result, Ok(_));
1798+
});
1799+
}
1800+
1801+
#[test]
1802+
fn own_code_hash_returns_proper_values() {
1803+
let bob_ch = MockLoader::insert(Call, |ctx, _| {
1804+
let code_hash = ctx.ext.code_hash(&BOB).unwrap();
1805+
assert_eq!(*ctx.ext.own_code_hash(), code_hash);
1806+
exec_success()
1807+
});
1808+
1809+
ExtBuilder::default().build().execute_with(|| {
1810+
let schedule = <Test as Config>::Schedule::get();
1811+
place_contract(&BOB, bob_ch);
1812+
let mut storage_meter = storage::meter::Meter::new(&ALICE, Some(0), 0).unwrap();
1813+
// ALICE (not contract) -> BOB (contract)
1814+
let result = MockStack::run_call(
1815+
ALICE,
1816+
BOB,
1817+
&mut GasMeter::<Test>::new(GAS_LIMIT),
1818+
&mut storage_meter,
1819+
&schedule,
1820+
0,
1821+
vec![0],
1822+
None,
1823+
);
1824+
assert_matches!(result, Ok(_));
1825+
});
1826+
}
1827+
17561828
#[test]
17571829
fn caller_is_origin_returns_proper_values() {
17581830
let code_charlie = MockLoader::insert(Call, |ctx, _| {

frame/contracts/src/schedule.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,12 @@ pub struct HostFnWeights<T: Config> {
265265
/// Weight of calling `seal_is_contract`.
266266
pub is_contract: Weight,
267267

268+
/// Weight of calling `seal_code_hash`.
269+
pub code_hash: Weight,
270+
271+
/// Weight of calling `seal_own_code_hash`.
272+
pub own_code_hash: Weight,
273+
268274
/// Weight of calling `seal_caller_is_origin`.
269275
pub caller_is_origin: Weight,
270276

@@ -584,6 +590,8 @@ impl<T: Config> Default for HostFnWeights<T> {
584590
Self {
585591
caller: cost_batched!(seal_caller),
586592
is_contract: cost_batched!(seal_is_contract),
593+
code_hash: cost_batched!(seal_code_hash),
594+
own_code_hash: cost_batched!(seal_own_code_hash),
587595
caller_is_origin: cost_batched!(seal_caller_is_origin),
588596
address: cost_batched!(seal_address),
589597
gas_left: cost_batched!(seal_gas_left),

0 commit comments

Comments
 (0)