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

contracts: add seal_code_hash and seal_own_code_hash to API #10933

Merged
merged 28 commits into from
Mar 29, 2022
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
41a683b
`seal_origin` + tests added
agryaznov Feb 24, 2022
be988fe
`seal_origin` benchmark added
agryaznov Feb 24, 2022
6d95421
`seal_code_hash` + tests added
agryaznov Feb 25, 2022
3de5904
`seal_code_hash` benchmark added
agryaznov Feb 25, 2022
ee33b30
`seal_own_code_hash` + tests added
agryaznov Feb 27, 2022
807cc12
`seal_own_code_hash` benchmark added
agryaznov Feb 27, 2022
23d6891
Merge branch 'master' into code_hash
agryaznov Feb 27, 2022
e7af71f
fmt lil fix
agryaznov Feb 27, 2022
9c08447
akward accident bug fix
agryaznov Feb 28, 2022
e9bb2f3
Apply suggestions from code review
agryaznov Mar 15, 2022
ef032f4
Apply suggestions from code review
agryaznov Mar 16, 2022
2abebfa
benchmark fix
agryaznov Mar 16, 2022
13a2802
`WasmModule::getter()` to take `module_name` arg
agryaznov Mar 16, 2022
8a3e5bd
test enhanced
agryaznov Mar 16, 2022
3049fbd
fixes based on review feedback
agryaznov Mar 16, 2022
4f2c3b7
Apply suggestions from code review
agryaznov Mar 16, 2022
4bd83a5
Merge branch 'master' into code_hash
agryaznov Mar 16, 2022
3db6f53
Hash left as const to return a ref to it from mock
agryaznov Mar 16, 2022
2250336
Merge branch 'code_hash' of github.com:paritytech/substrate into code…
agryaznov Mar 16, 2022
746f8ef
HASH test val to local const in mock
agryaznov Mar 16, 2022
654f14a
Apply suggestions from code review
agryaznov Mar 17, 2022
113dce0
fixes to benchmarks according to review feedback
agryaznov Mar 17, 2022
ab27649
Merge branch 'master' into code_hash
agryaznov Mar 17, 2022
c45bde0
Merge branch 'master' of https://github.com/paritytech/substrate into…
Mar 21, 2022
f6d2b2b
cargo run --quiet --profile=production --features=runtime-benchmarks…
Mar 21, 2022
1b43cbc
Merge branch 'master' into code_hash
agryaznov Mar 24, 2022
6236bda
removed `seal_origin` from API
agryaznov Mar 24, 2022
5d2f0db
Merge branch 'master' into code_hash
agryaznov Mar 29, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
79 changes: 70 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 @@ -456,58 +509,66 @@ benchmarks! {
let origin = RawOrigin::Signed(instance.caller.clone());
}: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![])

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

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 +2402,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
116 changes: 116 additions & 0 deletions frame/contracts/src/exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,12 +162,23 @@ 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.
/// However, this function does not require any storage lookup and therefore uses less weight.
fn caller_is_origin(&self) -> bool;

/// Returns the address of the origin of the whole call stack.
fn origin(&self) -> &AccountIdOf<Self::T>;

/// Returns a reference to the account id of the current contract.
fn address(&self) -> &AccountIdOf<Self::T>;

Expand Down Expand Up @@ -1103,10 +1114,22 @@ 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
agryaznov marked this conversation as resolved.
Show resolved Hide resolved
}

fn caller_is_origin(&self) -> bool {
self.caller() == &self.origin
}

fn origin(&self) -> &AccountIdOf<Self::T> {
&self.origin
}

fn balance(&self) -> BalanceOf<T> {
T::Currency::free_balance(&self.top_frame().account_id)
}
Expand Down Expand Up @@ -1753,6 +1776,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 Expand Up @@ -1788,6 +1867,43 @@ mod tests {
});
}

#[test]
fn origin_returns_proper_values() {
let code_charlie = MockLoader::insert(Call, |ctx, _| {
// BOB is caller but not the origin of the stack call
assert_eq!(ctx.ext.caller(), &BOB);
// ALICE is the origin of the call stack
assert_eq!(ctx.ext.origin(), &ALICE);
exec_success()
});

let code_bob = MockLoader::insert(Call, |ctx, _| {
// ALICE is the origin of the call stack
assert_eq!(ctx.ext.origin(), &ALICE);
agryaznov marked this conversation as resolved.
Show resolved Hide resolved
assert_eq!(ctx.ext.caller(), &ALICE);
// BOB calls CHARLIE
ctx.ext.call(0, CHARLIE, 0, vec![], true)
});
ExtBuilder::default().build().execute_with(|| {
let schedule = <Test as Config>::Schedule::get();
place_contract(&BOB, code_bob);
place_contract(&CHARLIE, code_charlie);
let mut storage_meter = storage::meter::Meter::new(&ALICE, Some(0), 0).unwrap();
// ALICE -> BOB: (origin is ALICE) -> CHARLIE (origin is still ALICE)
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 address_returns_proper_values() {
let bob_ch = MockLoader::insert(Call, |ctx, _| {
Expand Down
12 changes: 12 additions & 0 deletions frame/contracts/src/schedule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -265,9 +265,18 @@ 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,

/// Weight of calling `seal_origin`.
pub origin: Weight,

/// Weight of calling `seal_address`.
pub address: Weight,

Expand Down Expand Up @@ -584,7 +593,10 @@ 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),
origin: cost_batched!(seal_origin),
address: cost_batched!(seal_address),
gas_left: cost_batched!(seal_gas_left),
balance: cost_batched!(seal_balance),
Expand Down
Loading