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 7 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
106 changes: 106 additions & 0 deletions frame/contracts/src/benchmarking/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,78 @@ 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 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: accounts_bytes,
}],
call_body: Some(body::repeated_dyn(r * API_BENCHMARK_BATCH_SIZE, vec![
Counter(0, account_len as u32), // address_ptr
Regular(Instruction::I32Const(4)), // ptr where to store output
Regular(Instruction::I32Const(0)), // ptr to 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 repeat = r * API_BENCHMARK_BATCH_SIZE;
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_own_code_hash",
params: vec![ValueType::I32, ValueType::I32],
return_type: None,
}],
// Write the output buffer size. The output size will be overwritten by the
// supervisor with the real size when calling this getter. Since this size does not
// change between calls it suffices to start with an initial value and then just
// leave as whatever value was written there.
data_segments: vec![DataSegment {
offset: 0,
value: (pages * 64 * 1024 - 4).to_le_bytes().to_vec(),
}],
call_body: Some(body::repeated(
repeat,
&[
Instruction::I32Const(4), // ptr where to store output
Instruction::I32Const(0), // ptr to length
Instruction::Call(0), // call the imported function
],
)),
.. Default::default()
});
let instance = Contract::<T>::new(code, vec![])?;
let info = instance.info()?;
let origin = RawOrigin::Signed(instance.caller.clone());
}: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![])
athei marked this conversation as resolved.
Show resolved Hide resolved

seal_caller_is_origin {
let r in 0 .. API_BENCHMARK_BATCHES;
let code = WasmModule::<T>::from(ModuleDefinition {
Expand All @@ -456,6 +528,40 @@ 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 repeat = r * API_BENCHMARK_BATCH_SIZE;
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_origin",
params: vec![ValueType::I32, ValueType::I32],
return_type: None,
}],
// Write the output buffer size. The output size will be overwritten by the
// supervisor with the real size when calling this getter. Since this size does not
// change between calls it suffices to start with an initial value and then just
// leave as whatever value was written there.
data_segments: vec![DataSegment {
offset: 0,
value: (pages * 64 * 1024 - 4).to_le_bytes().to_vec(),
}],
call_body: Some(body::repeated(
repeat,
&[
Instruction::I32Const(4), // ptr where to store output
Instruction::I32Const(0), // ptr to length
Instruction::Call(0), // call the imported function
],
)),
..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![])
athei marked this conversation as resolved.
Show resolved Hide resolved

seal_address {
let r in 0 .. API_BENCHMARK_BATCHES;
let instance = Contract::<T>::new(WasmModule::getter(
Expand Down
108 changes: 108 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.
agryaznov marked this conversation as resolved.
Show resolved Hide resolved
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;

/// Return address of the origin of current call stack
agryaznov marked this conversation as resolved.
Show resolved Hide resolved
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 @@ -1102,10 +1113,26 @@ where
ContractInfoOf::<T>::contains_key(&address)
}

fn code_hash(&self, address: &T::AccountId) -> Option<CodeHash<Self::T>> {
if let Some(contract) = <ContractInfoOf<T>>::get(&address) {
Some(contract.code_hash)
} else {
None
}
agryaznov marked this conversation as resolved.
Show resolved Hide resolved
}

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 @@ -1752,6 +1779,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 @@ -1787,6 +1870,31 @@ mod tests {
});
}

#[test]
fn origin_returns_proper_values() {
let code_bob = MockLoader::insert(Call, |ctx, _| {
assert_eq!(ctx.ext.origin(), &ALICE);
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 -> BOB: (origin is 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(_));
});
}
athei marked this conversation as resolved.
Show resolved Hide resolved

#[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
2 changes: 2 additions & 0 deletions frame/contracts/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,8 @@ pub const BOB: AccountId32 = AccountId32::new([2u8; 32]);
pub const CHARLIE: AccountId32 = AccountId32::new([3u8; 32]);
pub const DJANGO: AccountId32 = AccountId32::new([4u8; 32]);

pub const HASH: H256 = H256::repeat_byte(0x10);

athei marked this conversation as resolved.
Show resolved Hide resolved
pub const GAS_LIMIT: Weight = 100_000_000_000;

pub struct ExtBuilder {
Expand Down
Loading