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

Commit

Permalink
Allow PostDispatchInfo to disable fees (#6749)
Browse files Browse the repository at this point in the history
* initial mock

* add test

* remove unneeded clone

* Update frame/support/src/weights.rs

Co-authored-by: Alexander Theißen <[email protected]>

* fix compile

* Update frame/support/src/weights.rs

Co-authored-by: Alexander Popiak <[email protected]>

* Update frame/sudo/src/lib.rs

Co-authored-by: André Silva <[email protected]>

* Apply suggestions from code review

Co-authored-by: Bastian Köcher <[email protected]>

Co-authored-by: Alexander Theißen <[email protected]>
Co-authored-by: Alexander Popiak <[email protected]>
Co-authored-by: André Silva <[email protected]>
Co-authored-by: Bastian Köcher <[email protected]>
  • Loading branch information
5 people authored Jul 29, 2020
1 parent 7bb8d82 commit 2afc363
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 11 deletions.
1 change: 1 addition & 0 deletions frame/contracts/src/gas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ impl<T: Trait> GasMeter<T> {
{
let post_info = PostDispatchInfo {
actual_weight: Some(self.gas_spent()),
pays_fee: Default::default(),
};

result
Expand Down
1 change: 1 addition & 0 deletions frame/contracts/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,7 @@ fn returns_base_call_cost() {
Ok(
PostDispatchInfo {
actual_weight: Some(67500000),
pays_fee: Default::default(),
}
)
);
Expand Down
25 changes: 20 additions & 5 deletions frame/sudo/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -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.
Expand All @@ -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.
Expand All @@ -166,14 +174,16 @@ 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);
let new = T::Lookup::lookup(new)?;

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
Expand All @@ -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);
Expand All @@ -204,6 +217,8 @@ decl_module! {
};

Self::deposit_event(RawEvent::SudoAsDone(res));
// Sudo user does not pay a fee.
Ok(Pays::No.into())
}
}
}
Expand Down
48 changes: 47 additions & 1 deletion frame/support/src/weights.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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.
Expand All @@ -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(),
}
}
}
Expand All @@ -305,6 +342,7 @@ impl From<()> for PostDispatchInfo {
fn from(_: ()) -> Self {
Self {
actual_weight: None,
pays_fee: Default::default(),
}
}
}
Expand All @@ -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(),
}
}
}
Expand All @@ -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(),
}
}
Expand Down
10 changes: 8 additions & 2 deletions frame/system/src/extensions/check_weight.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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| {
Expand Down
53 changes: 50 additions & 3 deletions frame/transaction-payment/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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]
Expand Down Expand Up @@ -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);
});
}
}

0 comments on commit 2afc363

Please sign in to comment.