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

Estimate call fee #9395

Merged
10 commits merged into from
Jul 23, 2021
1 change: 1 addition & 0 deletions bin/node/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,7 @@ impl pallet_election_provider_multi_phase::BenchmarkingConfig for BenchmarkConfi
impl pallet_election_provider_multi_phase::Config for Runtime {
type Event = Event;
type Currency = Balances;
type EstimateCallFee = TransactionPayment;
type SignedPhase = SignedPhase;
type UnsignedPhase = UnsignedPhase;
type SolutionImprovementThreshold = SolutionImprovementThreshold;
Expand Down
14 changes: 12 additions & 2 deletions frame/election-provider-multi-phase/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ use frame_support::{
};
use frame_system::{ensure_none, offchain::SendTransactionTypes};
use sp_arithmetic::{
traits::{CheckedAdd, Zero},
traits::{CheckedAdd, Saturating, Zero},
UpperOf,
};
use sp_npos_elections::{
Expand Down Expand Up @@ -555,6 +555,7 @@ pub use pallet::*;
pub mod pallet {
use super::*;
use frame_support::pallet_prelude::*;
use frame_support::traits::EstimateCallFee;
use frame_system::pallet_prelude::*;

#[pallet::config]
Expand All @@ -566,6 +567,9 @@ pub mod pallet {
/// Currency type.
type Currency: ReservableCurrency<Self::AccountId> + Currency<Self::AccountId>;

/// Something that can predict the fee of a call. Used to sensibly distribute rewards.
type EstimateCallFee: EstimateCallFee<Call<Self>, BalanceOf<Self>>;

/// Duration of the unsigned phase.
#[pallet::constant]
type UnsignedPhase: Get<Self::BlockNumber>;
Expand Down Expand Up @@ -973,7 +977,13 @@ pub mod pallet {

// create the submission
let deposit = Self::deposit_for(&solution, size);
let submission = SignedSubmission { who: who.clone(), deposit, solution };
let reward = {
let call = Call::submit(solution.clone(), num_signed_submissions);
emostov marked this conversation as resolved.
Show resolved Hide resolved
let call_fee = T::EstimateCallFee::estimate_call_fee(&call, None.into());
T::SignedRewardBase::get().saturating_add(call_fee)
};

let submission = SignedSubmission { who: who.clone(), deposit, solution, reward };

// insert the submission if the queue has space or it's better than the weakest
// eject the weakest if the queue was full
Expand Down
2 changes: 1 addition & 1 deletion frame/election-provider-multi-phase/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,6 @@ parameter_types! {
pub static SignedDepositByte: Balance = 0;
pub static SignedDepositWeight: Balance = 0;
pub static SignedRewardBase: Balance = 7;
pub static SignedRewardMax: Balance = 10;
pub static SignedMaxWeight: Weight = BlockWeights::get().max_block;
pub static MinerMaxIterations: u32 = 5;
pub static MinerTxPriority: u64 = 100;
Expand Down Expand Up @@ -356,6 +355,7 @@ impl multi_phase::weights::WeightInfo for DualMockWeightInfo {
impl crate::Config for Runtime {
type Event = Event;
type Currency = Balances;
type EstimateCallFee = frame_support::traits::ConstU32<8>;
type SignedPhase = SignedPhase;
type UnsignedPhase = UnsignedPhase;
type SolutionImprovementThreshold = SolutionImprovementThreshold;
Expand Down
12 changes: 6 additions & 6 deletions frame/election-provider-multi-phase/src/signed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ pub struct SignedSubmission<AccountId, Balance: HasCompact, CompactSolution> {
pub deposit: Balance,
/// The raw solution itself.
pub solution: RawSolution<CompactSolution>,
/// The reward that should potentially be paid for this solution, if accepted.
pub reward: Balance,
}

impl<AccountId, Balance, CompactSolution> Ord
Expand Down Expand Up @@ -351,10 +353,8 @@ impl<T: Config> Pallet<T> {
let SolutionOrSnapshotSize { voters, targets } =
Self::snapshot_metadata().unwrap_or_default();

let reward = T::SignedRewardBase::get();

while let Some(best) = all_submissions.pop_last() {
let SignedSubmission { solution, who, deposit } = best;
let SignedSubmission { solution, who, deposit, reward } = best;
let active_voters = solution.compact.voter_count() as u32;
let feasibility_weight = {
// defensive only: at the end of signed phase, snapshot will exits.
Expand Down Expand Up @@ -567,7 +567,7 @@ mod tests {
assert_eq!(balances(&99), (95, 5));

assert!(MultiPhase::finalize_signed_phase().0);
assert_eq!(balances(&99), (100 + 7, 0));
assert_eq!(balances(&99), (100 + 7 + 8, 0));
})
}

Expand Down Expand Up @@ -616,7 +616,7 @@ mod tests {
assert!(MultiPhase::finalize_signed_phase().0);

// 99 is rewarded.
assert_eq!(balances(&99), (100 + 7, 0));
assert_eq!(balances(&99), (100 + 7 + 8, 0));
// 999 gets everything back.
assert_eq!(balances(&999), (100, 0));
})
Expand Down Expand Up @@ -807,7 +807,7 @@ mod tests {
assert!(MultiPhase::finalize_signed_phase().0);

// 99 is rewarded.
assert_eq!(balances(&99), (100 + 7, 0));
assert_eq!(balances(&99), (100 + 7 + 8, 0));
// 999 is slashed.
assert_eq!(balances(&999), (95, 0));
// 9999 gets everything back.
Expand Down
6 changes: 3 additions & 3 deletions frame/support/src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,9 @@ pub use filter::{

mod misc;
pub use misc::{
Backing, ConstU32, EnsureInherentsAreFirst, ExecuteBlock, ExtrinsicCall, Get, GetBacking,
GetDefault, HandleLifetime, IsSubType, IsType, Len, OffchainWorker, OnKilledAccount,
OnNewAccount, SameOrOther, Time, TryDrop, UnixTime,
Backing, ConstU32, EnsureInherentsAreFirst, EstimateCallFee, ExecuteBlock, ExtrinsicCall, Get,
GetBacking, GetDefault, HandleLifetime, IsSubType, IsType, Len, OffchainWorker,
OnKilledAccount, OnNewAccount, SameOrOther, Time, TryDrop, UnixTime,
};

mod stored_map;
Expand Down
23 changes: 20 additions & 3 deletions frame/support/src/traits/misc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
//! Smaller traits used in FRAME which don't need their own file.

use crate::dispatch::Parameter;
use sp_arithmetic::traits::AtLeast32Bit;
use sp_runtime::{traits::Block as BlockT, DispatchError};

/// Anything that can have a `::len()` method.
Expand Down Expand Up @@ -181,7 +180,7 @@ pub trait HandleLifetime<T> {
impl<T> HandleLifetime<T> for () {}

pub trait Time {
type Moment: AtLeast32Bit + Parameter + Default + Copy;
type Moment: sp_arithmetic::traits::AtLeast32Bit + Parameter + Default + Copy;

fn now() -> Self::Moment;
}
Expand Down Expand Up @@ -307,7 +306,7 @@ pub trait OffchainWorker<BlockNumber> {
fn offchain_worker(_n: BlockNumber) {}
}

/// Some amount of backing from a group. The precise defintion of what it means to "back" something
/// Some amount of backing from a group. The precise definition of what it means to "back" something
/// is left flexible.
pub struct Backing {
/// The number of members of the group that back some motion.
Expand Down Expand Up @@ -358,3 +357,21 @@ where
&self.function
}
}

/// Something that can estimate the fee of a (frame-based) call.
///
/// Typically, the same pallet that will charge transaction fees will implement this.
pub trait EstimateCallFee<Call, Balance> {
/// Estimate the fee of this call.
///
/// The dispatch info and the length is deduced from the call. The post info can optionally be
/// provided.
fn estimate_call_fee(call: &Call, post_info: crate::weights::PostDispatchInfo) -> Balance;
}

#[cfg(feature = "std")]
kianenigma marked this conversation as resolved.
Show resolved Hide resolved
impl<Call, Balance: From<u32>, const T: u32> EstimateCallFee<Call, Balance> for ConstU32<T> {
fn estimate_call_fee(_: &Call, _: crate::weights::PostDispatchInfo) -> Balance {
T.into()
}
}
17 changes: 15 additions & 2 deletions frame/transaction-payment/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ use codec::{Decode, Encode};
use sp_runtime::{
traits::{
Convert, DispatchInfoOf, Dispatchable, PostDispatchInfoOf, SaturatedConversion, Saturating,
SignedExtension,
SignedExtension, Zero,
},
transaction_validity::{
TransactionPriority, TransactionValidity, TransactionValidityError, ValidTransaction,
Expand All @@ -63,7 +63,7 @@ use sp_std::prelude::*;

use frame_support::{
dispatch::DispatchResult,
traits::Get,
traits::{EstimateCallFee, Get},
weights::{
DispatchClass, DispatchInfo, GetDispatchInfo, Pays, PostDispatchInfo, Weight,
WeightToFeeCoefficient, WeightToFeePolynomial,
Expand Down Expand Up @@ -656,6 +656,19 @@ where
}
}

impl<T: Config, AnyCall: GetDispatchInfo + Encode> EstimateCallFee<AnyCall, BalanceOf<T>>
Copy link
Member

@shawntabrizi shawntabrizi Jul 23, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be called MaxCallFee?

Estimate implies to me it could be more or less, and that it is actually estimating something, but really it can only be less than this fee. This is just the max.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm trying to take into account that other implementations might differ, and might be more or less. The implementation by transaction-payment is actually accurate to the point (excluding tip).

But the trait name should not be moulded by the implementation detail, IMO in any case.

for Pallet<T>
where
BalanceOf<T>: FixedPointOperand,
T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
{
fn estimate_call_fee(call: &AnyCall, post_info: PostDispatchInfo) -> BalanceOf<T> {
let len = call.encoded_size() as u32;
let info = call.get_dispatch_info();
Self::compute_actual_fee(len, &info, &post_info, Zero::zero())
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down