mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-28 05:07:55 +00:00
[contracts] Port host functions to Weight V2 and storage deposit limit (#13565)
* added [unstable][seal2] call() * updated test to cover new seal_call proof_limit * docs updated * add [seal2][unstable] instantiate() and test * add [seal2][unstable] weight_to_fee() + docs and test * add [seal2][unstable] gas_left() + docs and test * update benchmarks * add DefaultDepositLimit to pallet Config * specify deposit limit for nested call add test for nested call deposit limit save: separate deposit limit for nested calls * specify deposit limit for nested instantiate save: works with test cleaned up debugging outputs * update benchmarks * added missing fixtures * fix benches * pass explicit deposit limit to storage bench * explicit deposit limit for another set_storage bench * add more deposit limit for storage benches * moving to simplified benchmarks * moved to simplified benchmarks * fix seal_weight_to_fee bench * fix seal_instantiate benchmark * doc typo fix * default dl for benchmarking more dl for tests dl for tests to max deposit_limit fix in instantiate bench fix instantiate bench fix instantiate benchmark fix instantiate bench again remove dbg fix seal bench again fixing it still seal_instantiate zero deposit less runs to check if deposit enough try try 2 try 3 try 4 * max_runtime_mem to Schedule limits * add default deposit limit fallback check to test * weight params renaming * fmt * Update frame/contracts/src/benchmarking/mod.rs Co-authored-by: PG Herveou <pgherveou@gmail.com> * prettify inputs in tests * typestate param refactored --------- Co-authored-by: PG Herveou <pgherveou@gmail.com>
This commit is contained in:
@@ -23,7 +23,9 @@ use crate::{
|
||||
};
|
||||
use frame_support::{
|
||||
crypto::ecdsa::ECDSAExt,
|
||||
dispatch::{DispatchError, DispatchResult, DispatchResultWithPostInfo, Dispatchable},
|
||||
dispatch::{
|
||||
fmt::Debug, DispatchError, DispatchResult, DispatchResultWithPostInfo, Dispatchable,
|
||||
},
|
||||
storage::{with_transaction, TransactionOutcome},
|
||||
traits::{
|
||||
tokens::{Fortitude::Polite, Preservation::Expendable},
|
||||
@@ -40,7 +42,7 @@ use sp_core::{
|
||||
sr25519::{Public as SR25519Public, Signature as SR25519Signature},
|
||||
};
|
||||
use sp_io::{crypto::secp256k1_ecdsa_recover_compressed, hashing::blake2_256};
|
||||
use sp_runtime::traits::{Convert, Hash};
|
||||
use sp_runtime::traits::{Convert, Hash, Zero};
|
||||
use sp_std::{marker::PhantomData, mem, prelude::*, vec::Vec};
|
||||
|
||||
pub type AccountIdOf<T> = <T as frame_system::Config>::AccountId;
|
||||
@@ -137,6 +139,7 @@ pub trait Ext: sealing::Sealed {
|
||||
fn call(
|
||||
&mut self,
|
||||
gas_limit: Weight,
|
||||
deposit_limit: BalanceOf<Self::T>,
|
||||
to: AccountIdOf<Self::T>,
|
||||
value: BalanceOf<Self::T>,
|
||||
input_data: Vec<u8>,
|
||||
@@ -160,6 +163,7 @@ pub trait Ext: sealing::Sealed {
|
||||
fn instantiate(
|
||||
&mut self,
|
||||
gas_limit: Weight,
|
||||
deposit_limit: BalanceOf<Self::T>,
|
||||
code: CodeHash<Self::T>,
|
||||
value: BalanceOf<Self::T>,
|
||||
input_data: Vec<u8>,
|
||||
@@ -692,8 +696,9 @@ where
|
||||
args,
|
||||
value,
|
||||
gas_meter,
|
||||
storage_meter,
|
||||
Weight::zero(),
|
||||
storage_meter,
|
||||
BalanceOf::<T>::zero(),
|
||||
schedule,
|
||||
determinism,
|
||||
)?;
|
||||
@@ -719,12 +724,13 @@ where
|
||||
///
|
||||
/// This does not take `self` because when constructing the first frame `self` is
|
||||
/// not initialized, yet.
|
||||
fn new_frame<S: storage::meter::State>(
|
||||
fn new_frame<S: storage::meter::State + Default + Debug>(
|
||||
frame_args: FrameArgs<T, E>,
|
||||
value_transferred: BalanceOf<T>,
|
||||
gas_meter: &mut GasMeter<T>,
|
||||
storage_meter: &mut storage::meter::GenericMeter<T, S>,
|
||||
gas_limit: Weight,
|
||||
storage_meter: &mut storage::meter::GenericMeter<T, S>,
|
||||
deposit_limit: BalanceOf<T>,
|
||||
schedule: &Schedule<T>,
|
||||
determinism: Determinism,
|
||||
) -> Result<(Frame<T>, E, Option<u64>), ExecError> {
|
||||
@@ -781,7 +787,7 @@ where
|
||||
account_id,
|
||||
entry_point,
|
||||
nested_gas: gas_meter.nested(gas_limit)?,
|
||||
nested_storage: storage_meter.nested(),
|
||||
nested_storage: storage_meter.nested(deposit_limit),
|
||||
allows_reentry: true,
|
||||
};
|
||||
|
||||
@@ -794,6 +800,7 @@ where
|
||||
frame_args: FrameArgs<T, E>,
|
||||
value_transferred: BalanceOf<T>,
|
||||
gas_limit: Weight,
|
||||
deposit_limit: BalanceOf<T>,
|
||||
) -> Result<E, ExecError> {
|
||||
if self.frames.len() == T::CallStack::size() {
|
||||
return Err(Error::<T>::MaxCallDepthReached.into())
|
||||
@@ -817,8 +824,9 @@ where
|
||||
frame_args,
|
||||
value_transferred,
|
||||
nested_gas,
|
||||
nested_storage,
|
||||
gas_limit,
|
||||
nested_storage,
|
||||
deposit_limit,
|
||||
self.schedule,
|
||||
self.determinism,
|
||||
)?;
|
||||
@@ -859,8 +867,10 @@ where
|
||||
return Ok(output)
|
||||
}
|
||||
|
||||
// Storage limit is enforced as late as possible (when the last frame returns) so that
|
||||
// the ordering of storage accesses does not matter.
|
||||
// Storage limit is normally enforced as late as possible (when the last frame returns)
|
||||
// so that the ordering of storage accesses does not matter.
|
||||
// (However, if a special limit was set for a sub-call, it should be enforced right
|
||||
// after the sub-call returned. See below for this case of enforcement).
|
||||
if self.frames.is_empty() {
|
||||
let frame = &mut self.first_frame;
|
||||
frame.contract_info.load(&frame.account_id);
|
||||
@@ -869,7 +879,7 @@ where
|
||||
}
|
||||
|
||||
let frame = self.top_frame();
|
||||
let account_id = &frame.account_id;
|
||||
let account_id = &frame.account_id.clone();
|
||||
match (entry_point, delegated_code_hash) {
|
||||
(ExportedFunction::Constructor, _) => {
|
||||
// It is not allowed to terminate a contract inside its constructor.
|
||||
@@ -877,6 +887,13 @@ where
|
||||
return Err(Error::<T>::TerminatedInConstructor.into())
|
||||
}
|
||||
|
||||
// If a special limit was set for the sub-call, we enforce it here.
|
||||
// This is needed because contract constructor might write to storage.
|
||||
// The sub-call will be rolled back in case the limit is exhausted.
|
||||
let frame = self.top_frame_mut();
|
||||
let contract = frame.contract_info.as_contract();
|
||||
frame.nested_storage.enforce_subcall_limit(contract)?;
|
||||
|
||||
// Deposit an instantiation event.
|
||||
Contracts::<T>::deposit_event(
|
||||
vec![T::Hashing::hash_of(self.caller()), T::Hashing::hash_of(account_id)],
|
||||
@@ -893,9 +910,15 @@ where
|
||||
);
|
||||
},
|
||||
(ExportedFunction::Call, None) => {
|
||||
// If a special limit was set for the sub-call, we enforce it here.
|
||||
// The sub-call will be rolled back in case the limit is exhausted.
|
||||
let frame = self.top_frame_mut();
|
||||
let contract = frame.contract_info.as_contract();
|
||||
frame.nested_storage.enforce_subcall_limit(contract)?;
|
||||
|
||||
let caller = self.caller();
|
||||
Contracts::<T>::deposit_event(
|
||||
vec![T::Hashing::hash_of(caller), T::Hashing::hash_of(account_id)],
|
||||
vec![T::Hashing::hash_of(caller), T::Hashing::hash_of(&account_id)],
|
||||
Event::Called { caller: caller.clone(), contract: account_id.clone() },
|
||||
);
|
||||
},
|
||||
@@ -1107,6 +1130,7 @@ where
|
||||
fn call(
|
||||
&mut self,
|
||||
gas_limit: Weight,
|
||||
deposit_limit: BalanceOf<T>,
|
||||
to: T::AccountId,
|
||||
value: BalanceOf<T>,
|
||||
input_data: Vec<u8>,
|
||||
@@ -1135,6 +1159,7 @@ where
|
||||
FrameArgs::Call { dest: to, cached_info, delegated_call: None },
|
||||
value,
|
||||
gas_limit,
|
||||
deposit_limit,
|
||||
)?;
|
||||
self.run(executable, input_data)
|
||||
};
|
||||
@@ -1166,6 +1191,7 @@ where
|
||||
},
|
||||
value,
|
||||
Weight::zero(),
|
||||
BalanceOf::<T>::zero(),
|
||||
)?;
|
||||
self.run(executable, input_data)
|
||||
}
|
||||
@@ -1173,6 +1199,7 @@ where
|
||||
fn instantiate(
|
||||
&mut self,
|
||||
gas_limit: Weight,
|
||||
deposit_limit: BalanceOf<Self::T>,
|
||||
code_hash: CodeHash<T>,
|
||||
value: BalanceOf<T>,
|
||||
input_data: Vec<u8>,
|
||||
@@ -1190,6 +1217,7 @@ where
|
||||
},
|
||||
value,
|
||||
gas_limit,
|
||||
deposit_limit,
|
||||
)?;
|
||||
let account_id = self.top_frame().account_id.clone();
|
||||
self.run(executable, input_data).map(|ret| (account_id, ret))
|
||||
@@ -1917,7 +1945,7 @@ mod tests {
|
||||
let value = Default::default();
|
||||
let recurse_ch = MockLoader::insert(Call, |ctx, _| {
|
||||
// Try to call into yourself.
|
||||
let r = ctx.ext.call(Weight::zero(), BOB, 0, vec![], true);
|
||||
let r = ctx.ext.call(Weight::zero(), BalanceOf::<Test>::zero(), BOB, 0, vec![], true);
|
||||
|
||||
ReachedBottom::mutate(|reached_bottom| {
|
||||
if !*reached_bottom {
|
||||
@@ -1971,7 +1999,11 @@ mod tests {
|
||||
WitnessedCallerBob::mutate(|caller| *caller = Some(ctx.ext.caller().clone()));
|
||||
|
||||
// Call into CHARLIE contract.
|
||||
assert_matches!(ctx.ext.call(Weight::zero(), CHARLIE, 0, vec![], true), Ok(_));
|
||||
assert_matches!(
|
||||
ctx.ext
|
||||
.call(Weight::zero(), BalanceOf::<Test>::zero(), CHARLIE, 0, vec![], true),
|
||||
Ok(_)
|
||||
);
|
||||
exec_success()
|
||||
});
|
||||
let charlie_ch = MockLoader::insert(Call, |ctx, _| {
|
||||
@@ -2105,7 +2137,8 @@ mod tests {
|
||||
// ALICE is the origin of the call stack
|
||||
assert!(ctx.ext.caller_is_origin());
|
||||
// BOB calls CHARLIE
|
||||
ctx.ext.call(Weight::zero(), CHARLIE, 0, vec![], true)
|
||||
ctx.ext
|
||||
.call(Weight::zero(), BalanceOf::<Test>::zero(), CHARLIE, 0, vec![], true)
|
||||
});
|
||||
|
||||
ExtBuilder::default().build().execute_with(|| {
|
||||
@@ -2136,7 +2169,11 @@ mod tests {
|
||||
assert_eq!(*ctx.ext.address(), BOB);
|
||||
|
||||
// Call into charlie contract.
|
||||
assert_matches!(ctx.ext.call(Weight::zero(), CHARLIE, 0, vec![], true), Ok(_));
|
||||
assert_matches!(
|
||||
ctx.ext
|
||||
.call(Weight::zero(), BalanceOf::<Test>::zero(), CHARLIE, 0, vec![], true),
|
||||
Ok(_)
|
||||
);
|
||||
exec_success()
|
||||
});
|
||||
let charlie_ch = MockLoader::insert(Call, |ctx, _| {
|
||||
@@ -2287,6 +2324,7 @@ mod tests {
|
||||
.ext
|
||||
.instantiate(
|
||||
Weight::zero(),
|
||||
BalanceOf::<Test>::zero(),
|
||||
dummy_ch,
|
||||
<Test as Config>::Currency::minimum_balance(),
|
||||
vec![],
|
||||
@@ -2351,6 +2389,7 @@ mod tests {
|
||||
assert_matches!(
|
||||
ctx.ext.instantiate(
|
||||
Weight::zero(),
|
||||
BalanceOf::<Test>::zero(),
|
||||
dummy_ch,
|
||||
<Test as Config>::Currency::minimum_balance(),
|
||||
vec![],
|
||||
@@ -2443,13 +2482,26 @@ mod tests {
|
||||
let info = ctx.ext.contract_info();
|
||||
assert_eq!(info.storage_byte_deposit, 0);
|
||||
info.storage_byte_deposit = 42;
|
||||
assert_eq!(ctx.ext.call(Weight::zero(), CHARLIE, 0, vec![], true), exec_trapped());
|
||||
assert_eq!(
|
||||
ctx.ext.call(
|
||||
Weight::zero(),
|
||||
BalanceOf::<Test>::zero(),
|
||||
CHARLIE,
|
||||
0,
|
||||
vec![],
|
||||
true
|
||||
),
|
||||
exec_trapped()
|
||||
);
|
||||
assert_eq!(ctx.ext.contract_info().storage_byte_deposit, 42);
|
||||
}
|
||||
exec_success()
|
||||
});
|
||||
let code_charlie = MockLoader::insert(Call, |ctx, _| {
|
||||
assert!(ctx.ext.call(Weight::zero(), BOB, 0, vec![99], true).is_ok());
|
||||
assert!(ctx
|
||||
.ext
|
||||
.call(Weight::zero(), BalanceOf::<Test>::zero(), BOB, 0, vec![99], true)
|
||||
.is_ok());
|
||||
exec_trapped()
|
||||
});
|
||||
|
||||
@@ -2479,7 +2531,7 @@ mod tests {
|
||||
fn recursive_call_during_constructor_fails() {
|
||||
let code = MockLoader::insert(Constructor, |ctx, _| {
|
||||
assert_matches!(
|
||||
ctx.ext.call(Weight::zero(), ctx.ext.address().clone(), 0, vec![], true),
|
||||
ctx.ext.call(Weight::zero(), BalanceOf::<Test>::zero(), ctx.ext.address().clone(), 0, vec![], true),
|
||||
Err(ExecError{error, ..}) if error == <Error<Test>>::ContractNotFound.into()
|
||||
);
|
||||
exec_success()
|
||||
@@ -2618,7 +2670,7 @@ mod tests {
|
||||
// call the contract passed as input with disabled reentry
|
||||
let code_bob = MockLoader::insert(Call, |ctx, _| {
|
||||
let dest = Decode::decode(&mut ctx.input_data.as_ref()).unwrap();
|
||||
ctx.ext.call(Weight::zero(), dest, 0, vec![], false)
|
||||
ctx.ext.call(Weight::zero(), BalanceOf::<Test>::zero(), dest, 0, vec![], false)
|
||||
});
|
||||
|
||||
let code_charlie = MockLoader::insert(Call, |_, _| exec_success());
|
||||
@@ -2665,15 +2717,17 @@ mod tests {
|
||||
fn call_deny_reentry() {
|
||||
let code_bob = MockLoader::insert(Call, |ctx, _| {
|
||||
if ctx.input_data[0] == 0 {
|
||||
ctx.ext.call(Weight::zero(), CHARLIE, 0, vec![], false)
|
||||
ctx.ext
|
||||
.call(Weight::zero(), BalanceOf::<Test>::zero(), CHARLIE, 0, vec![], false)
|
||||
} else {
|
||||
exec_success()
|
||||
}
|
||||
});
|
||||
|
||||
// call BOB with input set to '1'
|
||||
let code_charlie =
|
||||
MockLoader::insert(Call, |ctx, _| ctx.ext.call(Weight::zero(), BOB, 0, vec![1], true));
|
||||
let code_charlie = MockLoader::insert(Call, |ctx, _| {
|
||||
ctx.ext.call(Weight::zero(), BalanceOf::<Test>::zero(), BOB, 0, vec![1], true)
|
||||
});
|
||||
|
||||
ExtBuilder::default().build().execute_with(|| {
|
||||
let schedule = <Test as Config>::Schedule::get();
|
||||
@@ -2862,6 +2916,7 @@ mod tests {
|
||||
ctx.ext
|
||||
.instantiate(
|
||||
Weight::zero(),
|
||||
BalanceOf::<Test>::zero(),
|
||||
fail_code,
|
||||
ctx.ext.minimum_balance() * 100,
|
||||
vec![],
|
||||
@@ -2875,6 +2930,7 @@ mod tests {
|
||||
.ext
|
||||
.instantiate(
|
||||
Weight::zero(),
|
||||
BalanceOf::<Test>::zero(),
|
||||
success_code,
|
||||
ctx.ext.minimum_balance() * 100,
|
||||
vec![],
|
||||
@@ -2883,7 +2939,9 @@ mod tests {
|
||||
.unwrap();
|
||||
|
||||
// a plain call should not influence the account counter
|
||||
ctx.ext.call(Weight::zero(), account_id, 0, vec![], false).unwrap();
|
||||
ctx.ext
|
||||
.call(Weight::zero(), BalanceOf::<Test>::zero(), account_id, 0, vec![], false)
|
||||
.unwrap();
|
||||
|
||||
exec_success()
|
||||
});
|
||||
@@ -3387,7 +3445,14 @@ mod tests {
|
||||
assert_eq!(ctx.ext.nonce(), 1);
|
||||
// Should not change with a failed instantiation
|
||||
assert_err!(
|
||||
ctx.ext.instantiate(Weight::zero(), fail_code, 0, vec![], &[],),
|
||||
ctx.ext.instantiate(
|
||||
Weight::zero(),
|
||||
BalanceOf::<Test>::zero(),
|
||||
fail_code,
|
||||
0,
|
||||
vec![],
|
||||
&[],
|
||||
),
|
||||
ExecError {
|
||||
error: <Error<Test>>::ContractTrapped.into(),
|
||||
origin: ErrorOrigin::Callee
|
||||
@@ -3395,7 +3460,16 @@ mod tests {
|
||||
);
|
||||
assert_eq!(ctx.ext.nonce(), 1);
|
||||
// Successful instantiation increments
|
||||
ctx.ext.instantiate(Weight::zero(), success_code, 0, vec![], &[]).unwrap();
|
||||
ctx.ext
|
||||
.instantiate(
|
||||
Weight::zero(),
|
||||
BalanceOf::<Test>::zero(),
|
||||
success_code,
|
||||
0,
|
||||
vec![],
|
||||
&[],
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(ctx.ext.nonce(), 2);
|
||||
exec_success()
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user