mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-11 08:21:05 +00:00
Allow PostDispatchInfo to disable fees (#6749)
* initial mock * add test * remove unneeded clone * Update frame/support/src/weights.rs Co-authored-by: Alexander Theißen <alex.theissen@me.com> * fix compile * Update frame/support/src/weights.rs Co-authored-by: Alexander Popiak <alexander.popiak@parity.io> * Update frame/sudo/src/lib.rs Co-authored-by: André Silva <123550+andresilva@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com> Co-authored-by: Alexander Theißen <alex.theissen@me.com> Co-authored-by: Alexander Popiak <alexander.popiak@parity.io> Co-authored-by: André Silva <123550+andresilva@users.noreply.github.com> Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com>
This commit is contained in:
@@ -194,6 +194,7 @@ impl<T: Trait> GasMeter<T> {
|
||||
{
|
||||
let post_info = PostDispatchInfo {
|
||||
actual_weight: Some(self.gas_spent()),
|
||||
pays_fee: Default::default(),
|
||||
};
|
||||
|
||||
result
|
||||
|
||||
@@ -291,6 +291,7 @@ fn returns_base_call_cost() {
|
||||
Ok(
|
||||
PostDispatchInfo {
|
||||
actual_weight: Some(67500000),
|
||||
pays_fee: Default::default(),
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
@@ -93,7 +93,11 @@ use sp_runtime::{DispatchResult, traits::StaticLookup};
|
||||
use frame_support::{
|
||||
Parameter, decl_module, decl_event, decl_storage, decl_error, ensure,
|
||||
};
|
||||
use frame_support::{weights::{Weight, GetDispatchInfo}, traits::UnfilteredDispatchable};
|
||||
use frame_support::{
|
||||
weights::{Weight, GetDispatchInfo, Pays},
|
||||
traits::UnfilteredDispatchable,
|
||||
dispatch::DispatchResultWithPostInfo,
|
||||
};
|
||||
use frame_system::ensure_signed;
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -127,13 +131,15 @@ decl_module! {
|
||||
/// - Weight of derivative `call` execution + 10,000.
|
||||
/// # </weight>
|
||||
#[weight = (call.get_dispatch_info().weight + 10_000, call.get_dispatch_info().class)]
|
||||
fn sudo(origin, call: Box<<T as Trait>::Call>) {
|
||||
fn sudo(origin, call: Box<<T as Trait>::Call>) -> DispatchResultWithPostInfo {
|
||||
// This is a public call, so we ensure that the origin is some signed account.
|
||||
let sender = ensure_signed(origin)?;
|
||||
ensure!(sender == Self::key(), Error::<T>::RequireSudo);
|
||||
|
||||
let res = call.dispatch_bypass_filter(frame_system::RawOrigin::Root.into());
|
||||
Self::deposit_event(RawEvent::Sudid(res.map(|_| ()).map_err(|e| e.error)));
|
||||
// Sudo user does not pay a fee.
|
||||
Ok(Pays::No.into())
|
||||
}
|
||||
|
||||
/// Authenticates the sudo key and dispatches a function call with `Root` origin.
|
||||
@@ -147,13 +153,15 @@ decl_module! {
|
||||
/// - The weight of this call is defined by the caller.
|
||||
/// # </weight>
|
||||
#[weight = (*_weight, call.get_dispatch_info().class)]
|
||||
fn sudo_unchecked_weight(origin, call: Box<<T as Trait>::Call>, _weight: Weight) {
|
||||
fn sudo_unchecked_weight(origin, call: Box<<T as Trait>::Call>, _weight: Weight) -> DispatchResultWithPostInfo {
|
||||
// This is a public call, so we ensure that the origin is some signed account.
|
||||
let sender = ensure_signed(origin)?;
|
||||
ensure!(sender == Self::key(), Error::<T>::RequireSudo);
|
||||
|
||||
let res = call.dispatch_bypass_filter(frame_system::RawOrigin::Root.into());
|
||||
Self::deposit_event(RawEvent::Sudid(res.map(|_| ()).map_err(|e| e.error)));
|
||||
// Sudo user does not pay a fee.
|
||||
Ok(Pays::No.into())
|
||||
}
|
||||
|
||||
/// Authenticates the current sudo key and sets the given AccountId (`new`) as the new sudo key.
|
||||
@@ -166,7 +174,7 @@ decl_module! {
|
||||
/// - One DB change.
|
||||
/// # </weight>
|
||||
#[weight = 0]
|
||||
fn set_key(origin, new: <T::Lookup as StaticLookup>::Source) {
|
||||
fn set_key(origin, new: <T::Lookup as StaticLookup>::Source) -> DispatchResultWithPostInfo {
|
||||
// This is a public call, so we ensure that the origin is some signed account.
|
||||
let sender = ensure_signed(origin)?;
|
||||
ensure!(sender == Self::key(), Error::<T>::RequireSudo);
|
||||
@@ -174,6 +182,8 @@ decl_module! {
|
||||
|
||||
Self::deposit_event(RawEvent::KeyChanged(Self::key()));
|
||||
<Key<T>>::put(new);
|
||||
// Sudo user does not pay a fee.
|
||||
Ok(Pays::No.into())
|
||||
}
|
||||
|
||||
/// Authenticates the sudo key and dispatches a function call with `Signed` origin from
|
||||
@@ -188,7 +198,10 @@ decl_module! {
|
||||
/// - Weight of derivative `call` execution + 10,000.
|
||||
/// # </weight>
|
||||
#[weight = (call.get_dispatch_info().weight + 10_000, call.get_dispatch_info().class)]
|
||||
fn sudo_as(origin, who: <T::Lookup as StaticLookup>::Source, call: Box<<T as Trait>::Call>) {
|
||||
fn sudo_as(origin,
|
||||
who: <T::Lookup as StaticLookup>::Source,
|
||||
call: Box<<T as Trait>::Call>
|
||||
) -> DispatchResultWithPostInfo {
|
||||
// This is a public call, so we ensure that the origin is some signed account.
|
||||
let sender = ensure_signed(origin)?;
|
||||
ensure!(sender == Self::key(), Error::<T>::RequireSudo);
|
||||
@@ -204,6 +217,8 @@ decl_module! {
|
||||
};
|
||||
|
||||
Self::deposit_event(RawEvent::SudoAsDone(res));
|
||||
// Sudo user does not pay a fee.
|
||||
Ok(Pays::No.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -263,10 +263,13 @@ pub trait GetDispatchInfo {
|
||||
}
|
||||
|
||||
/// Weight information that is only available post dispatch.
|
||||
/// NOTE: This can only be used to reduce the weight or fee, not increase it.
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Default, RuntimeDebug, Encode, Decode)]
|
||||
pub struct PostDispatchInfo {
|
||||
/// Actual weight consumed by a call or `None` which stands for the worst case static weight.
|
||||
pub actual_weight: Option<Weight>,
|
||||
/// Whether this transaction should pay fees when all is said and done.
|
||||
pub pays_fee: Pays,
|
||||
}
|
||||
|
||||
impl PostDispatchInfo {
|
||||
@@ -283,6 +286,20 @@ impl PostDispatchInfo {
|
||||
info.weight
|
||||
}
|
||||
}
|
||||
|
||||
/// Determine if user should actually pay fees at the end of the dispatch.
|
||||
pub fn pays_fee(&self, info: &DispatchInfo) -> Pays {
|
||||
// If they originally were not paying fees, or the post dispatch info
|
||||
// says they should not pay fees, then they don't pay fees.
|
||||
// This is because the pre dispatch information must contain the
|
||||
// worst case for weight and fees paid.
|
||||
if info.pays_fee == Pays::No || self.pays_fee == Pays::No {
|
||||
Pays::No
|
||||
} else {
|
||||
// Otherwise they pay.
|
||||
Pays::Yes
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Extract the actual weight from a dispatch result if any or fall back to the default weight.
|
||||
@@ -293,10 +310,30 @@ pub fn extract_actual_weight(result: &DispatchResultWithPostInfo, info: &Dispatc
|
||||
}.calc_actual_weight(info)
|
||||
}
|
||||
|
||||
impl From<(Option<Weight>, Pays)> for PostDispatchInfo {
|
||||
fn from(post_weight_info: (Option<Weight>, Pays)) -> Self {
|
||||
let (actual_weight, pays_fee) = post_weight_info;
|
||||
Self {
|
||||
actual_weight,
|
||||
pays_fee,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Pays> for PostDispatchInfo {
|
||||
fn from(pays_fee: Pays) -> Self {
|
||||
Self {
|
||||
actual_weight: None,
|
||||
pays_fee,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Option<Weight>> for PostDispatchInfo {
|
||||
fn from(actual_weight: Option<Weight>) -> Self {
|
||||
Self {
|
||||
actual_weight,
|
||||
pays_fee: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -305,6 +342,7 @@ impl From<()> for PostDispatchInfo {
|
||||
fn from(_: ()) -> Self {
|
||||
Self {
|
||||
actual_weight: None,
|
||||
pays_fee: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -315,6 +353,11 @@ impl sp_runtime::traits::Printable for PostDispatchInfo {
|
||||
match self.actual_weight {
|
||||
Some(weight) => weight.print(),
|
||||
None => "max-weight".print(),
|
||||
};
|
||||
"pays_fee=".print();
|
||||
match self.pays_fee {
|
||||
Pays::Yes => "Yes".print(),
|
||||
Pays::No => "No".print(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -338,7 +381,10 @@ impl<T> WithPostDispatchInfo for T where
|
||||
{
|
||||
fn with_weight(self, actual_weight: Weight) -> DispatchErrorWithPostInfo {
|
||||
DispatchErrorWithPostInfo {
|
||||
post_info: PostDispatchInfo { actual_weight: Some(actual_weight) },
|
||||
post_info: PostDispatchInfo {
|
||||
actual_weight: Some(actual_weight),
|
||||
pays_fee: Default::default(),
|
||||
},
|
||||
error: self.into(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -575,7 +575,10 @@ mod tests {
|
||||
new_test_ext().execute_with(|| {
|
||||
// This is half of the max block weight
|
||||
let info = DispatchInfo { weight: 512, ..Default::default() };
|
||||
let post_info = PostDispatchInfo { actual_weight: Some(128), };
|
||||
let post_info = PostDispatchInfo {
|
||||
actual_weight: Some(128),
|
||||
pays_fee: Default::default(),
|
||||
};
|
||||
let len = 0_usize;
|
||||
|
||||
// We allow 75% for normal transaction, so we put 25% - extrinsic base weight
|
||||
@@ -601,7 +604,10 @@ mod tests {
|
||||
fn signed_ext_check_weight_actual_weight_higher_than_max_is_capped() {
|
||||
new_test_ext().execute_with(|| {
|
||||
let info = DispatchInfo { weight: 512, ..Default::default() };
|
||||
let post_info = PostDispatchInfo { actual_weight: Some(700), };
|
||||
let post_info = PostDispatchInfo {
|
||||
actual_weight: Some(700),
|
||||
pays_fee: Default::default(),
|
||||
};
|
||||
let len = 0_usize;
|
||||
|
||||
BlockWeight::mutate(|current_weight| {
|
||||
|
||||
@@ -371,7 +371,7 @@ impl<T: Trait> Module<T> where
|
||||
) -> BalanceOf<T> where
|
||||
T::Call: Dispatchable<Info=DispatchInfo,PostInfo=PostDispatchInfo>,
|
||||
{
|
||||
Self::compute_fee_raw(len, post_info.calc_actual_weight(info), tip, info.pays_fee)
|
||||
Self::compute_fee_raw(len, post_info.calc_actual_weight(info), tip, post_info.pays_fee(info))
|
||||
}
|
||||
|
||||
fn compute_fee_raw(
|
||||
@@ -762,11 +762,24 @@ mod tests {
|
||||
}
|
||||
|
||||
fn post_info_from_weight(w: Weight) -> PostDispatchInfo {
|
||||
PostDispatchInfo { actual_weight: Some(w), }
|
||||
PostDispatchInfo {
|
||||
actual_weight: Some(w),
|
||||
pays_fee: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn post_info_from_pays(p: Pays) -> PostDispatchInfo {
|
||||
PostDispatchInfo {
|
||||
actual_weight: None,
|
||||
pays_fee: p,
|
||||
}
|
||||
}
|
||||
|
||||
fn default_post_info() -> PostDispatchInfo {
|
||||
PostDispatchInfo { actual_weight: None, }
|
||||
PostDispatchInfo {
|
||||
actual_weight: None,
|
||||
pays_fee: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1211,4 +1224,38 @@ mod tests {
|
||||
assert_eq!(refund_based_fee, actual_fee);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn post_info_can_change_pays_fee() {
|
||||
ExtBuilder::default()
|
||||
.balance_factor(10)
|
||||
.base_weight(7)
|
||||
.build()
|
||||
.execute_with(||
|
||||
{
|
||||
let info = info_from_weight(100);
|
||||
let post_info = post_info_from_pays(Pays::No);
|
||||
let prev_balance = Balances::free_balance(2);
|
||||
let len = 10;
|
||||
let tip = 5;
|
||||
|
||||
NextFeeMultiplier::put(Multiplier::saturating_from_rational(5, 4));
|
||||
|
||||
let pre = ChargeTransactionPayment::<Runtime>::from(tip)
|
||||
.pre_dispatch(&2, CALL, &info, len)
|
||||
.unwrap();
|
||||
|
||||
ChargeTransactionPayment::<Runtime>
|
||||
::post_dispatch(pre, &info, &post_info, len, &Ok(()))
|
||||
.unwrap();
|
||||
|
||||
let refund_based_fee = prev_balance - Balances::free_balance(2);
|
||||
let actual_fee = Module::<Runtime>
|
||||
::compute_actual_fee(len as u32, &info, &post_info, tip);
|
||||
|
||||
// Only 5 tip is paid
|
||||
assert_eq!(actual_fee, 5);
|
||||
assert_eq!(refund_based_fee, actual_fee);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user