#[cfg(feature = "std")]
use serde::{Serialize, Deserialize};
use codec::{Encode, Decode};
use sp_runtime::{
RuntimeDebug,
traits::SignedExtension,
generic::{CheckedExtrinsic, UncheckedExtrinsic},
};
use crate::dispatch::{DispatchErrorWithPostInfo, DispatchResultWithPostInfo, DispatchError};
use sp_runtime::traits::SaturatedConversion;
use sp_arithmetic::{Perbill, traits::{BaseArithmetic, Saturating, Unsigned}};
use smallvec::{smallvec, SmallVec};
pub use sp_runtime::transaction_validity::TransactionPriority;
pub type Weight = u64;
pub mod constants {
use super::{RuntimeDbWeight, Weight};
use crate::parameter_types;
pub const WEIGHT_PER_SECOND: Weight = 1_000_000_000_000;
pub const WEIGHT_PER_MILLIS: Weight = WEIGHT_PER_SECOND / 1000; pub const WEIGHT_PER_MICROS: Weight = WEIGHT_PER_MILLIS / 1000; pub const WEIGHT_PER_NANOS: Weight = WEIGHT_PER_MICROS / 1000;
parameter_types! {
pub const BlockExecutionWeight: Weight = 5 * WEIGHT_PER_MILLIS;
pub const ExtrinsicBaseWeight: Weight = 125 * WEIGHT_PER_MICROS;
pub const RocksDbWeight: RuntimeDbWeight = RuntimeDbWeight {
read: 25 * WEIGHT_PER_MICROS, write: 100 * WEIGHT_PER_MICROS, };
pub const ParityDbWeight: RuntimeDbWeight = RuntimeDbWeight {
read: 8 * WEIGHT_PER_MICROS, write: 50 * WEIGHT_PER_MICROS, };
}
}
pub trait WeighData<T> {
fn weigh_data(&self, target: T) -> Weight;
}
pub trait ClassifyDispatch<T> {
fn classify_dispatch(&self, target: T) -> DispatchClass;
}
pub trait PaysFee<T> {
fn pays_fee(&self, _target: T) -> Pays;
}
#[derive(Clone, Copy, Eq, PartialEq, RuntimeDebug, Encode, Decode)]
pub enum Pays {
Yes,
No,
}
impl Default for Pays {
fn default() -> Self {
Self::Yes
}
}
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))]
#[derive(PartialEq, Eq, Clone, Copy, Encode, Decode, RuntimeDebug)]
pub enum DispatchClass {
Normal,
Operational,
Mandatory,
}
impl Default for DispatchClass {
fn default() -> Self {
Self::Normal
}
}
pub mod priority {
pub const LIMIT: u64 = 13_835_058_055_282_163_711_u64;
pub enum FrameTransactionPriority {
Normal(u64),
Operational(u64),
}
impl From<FrameTransactionPriority> for u64 {
fn from(priority: FrameTransactionPriority) -> Self {
match priority {
FrameTransactionPriority::Normal(inner) => inner,
FrameTransactionPriority::Operational(inner) => inner.saturating_add(LIMIT),
}
}
}
}
#[derive(Clone, Copy, Eq, PartialEq, Default, RuntimeDebug, Encode, Decode)]
pub struct DispatchInfo {
pub weight: Weight,
pub class: DispatchClass,
pub pays_fee: Pays,
}
pub trait GetDispatchInfo {
fn get_dispatch_info(&self) -> DispatchInfo;
}
#[derive(Clone, Copy, Eq, PartialEq, Default, RuntimeDebug, Encode, Decode)]
pub struct PostDispatchInfo {
pub actual_weight: Option<Weight>,
pub pays_fee: Pays,
}
impl PostDispatchInfo {
pub fn calc_unspent(&self, info: &DispatchInfo) -> Weight {
info.weight - self.calc_actual_weight(info)
}
pub fn calc_actual_weight(&self, info: &DispatchInfo) -> Weight {
if let Some(actual_weight) = self.actual_weight {
actual_weight.min(info.weight)
} else {
info.weight
}
}
pub fn pays_fee(&self, info: &DispatchInfo) -> Pays {
if info.pays_fee == Pays::No || self.pays_fee == Pays::No {
Pays::No
} else {
Pays::Yes
}
}
}
pub fn extract_actual_weight(result: &DispatchResultWithPostInfo, info: &DispatchInfo) -> Weight {
match result {
Ok(post_info) => &post_info,
Err(err) => &err.post_info,
}.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(),
}
}
}
impl From<()> for PostDispatchInfo {
fn from(_: ()) -> Self {
Self {
actual_weight: None,
pays_fee: Default::default(),
}
}
}
impl sp_runtime::traits::Printable for PostDispatchInfo {
fn print(&self) {
"actual_weight=".print();
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(),
}
}
}
pub trait WithPostDispatchInfo {
fn with_weight(self, actual_weight: Weight) -> DispatchErrorWithPostInfo;
}
impl<T> WithPostDispatchInfo for T where
T: Into<DispatchError>
{
fn with_weight(self, actual_weight: Weight) -> DispatchErrorWithPostInfo {
DispatchErrorWithPostInfo {
post_info: PostDispatchInfo {
actual_weight: Some(actual_weight),
pays_fee: Default::default(),
},
error: self.into(),
}
}
}
impl<T> WeighData<T> for Weight {
fn weigh_data(&self, _: T) -> Weight {
return *self
}
}
impl<T> ClassifyDispatch<T> for Weight {
fn classify_dispatch(&self, _: T) -> DispatchClass {
DispatchClass::Normal
}
}
impl<T> PaysFee<T> for Weight {
fn pays_fee(&self, _: T) -> Pays {
Pays::Yes
}
}
impl<T> WeighData<T> for (Weight, DispatchClass, Pays) {
fn weigh_data(&self, _: T) -> Weight {
return self.0
}
}
impl<T> ClassifyDispatch<T> for (Weight, DispatchClass, Pays) {
fn classify_dispatch(&self, _: T) -> DispatchClass {
self.1
}
}
impl<T> PaysFee<T> for (Weight, DispatchClass, Pays) {
fn pays_fee(&self, _: T) -> Pays {
self.2
}
}
impl<T> WeighData<T> for (Weight, DispatchClass) {
fn weigh_data(&self, _: T) -> Weight {
return self.0
}
}
impl<T> ClassifyDispatch<T> for (Weight, DispatchClass) {
fn classify_dispatch(&self, _: T) -> DispatchClass {
self.1
}
}
impl<T> PaysFee<T> for (Weight, DispatchClass) {
fn pays_fee(&self, _: T) -> Pays {
Pays::Yes
}
}
impl<T> WeighData<T> for (Weight, Pays) {
fn weigh_data(&self, _: T) -> Weight {
return self.0
}
}
impl<T> ClassifyDispatch<T> for (Weight, Pays) {
fn classify_dispatch(&self, _: T) -> DispatchClass {
DispatchClass::Normal
}
}
impl<T> PaysFee<T> for (Weight, Pays) {
fn pays_fee(&self, _: T) -> Pays {
self.1
}
}
#[deprecated = "Function arguments are available directly inside the annotation now."]
pub struct FunctionOf<WD, CD, PF>(pub WD, pub CD, pub PF);
#[allow(deprecated)]
impl<Args, CD, PF> WeighData<Args> for FunctionOf<Weight, CD, PF> {
fn weigh_data(&self, _: Args) -> Weight {
self.0
}
}
#[allow(deprecated)]
impl<Args, WD, CD, PF> WeighData<Args> for FunctionOf<WD, CD, PF> where
WD : Fn(Args) -> Weight
{
fn weigh_data(&self, args: Args) -> Weight {
(self.0)(args)
}
}
#[allow(deprecated)]
impl<Args, WD, PF> ClassifyDispatch<Args> for FunctionOf<WD, DispatchClass, PF> {
fn classify_dispatch(&self, _: Args) -> DispatchClass {
self.1
}
}
#[allow(deprecated)]
impl<Args, WD, CD, PF> ClassifyDispatch<Args> for FunctionOf<WD, CD, PF> where
CD : Fn(Args) -> DispatchClass
{
fn classify_dispatch(&self, args: Args) -> DispatchClass {
(self.1)(args)
}
}
#[allow(deprecated)]
impl<Args, WD, CD> PaysFee<Args> for FunctionOf<WD, CD, Pays> {
fn pays_fee(&self, _: Args) -> Pays {
self.2
}
}
#[allow(deprecated)]
impl<Args, WD, CD, PF> PaysFee<Args> for FunctionOf<WD, CD, PF> where
PF : Fn(Args) -> Pays
{
fn pays_fee(&self, args: Args) -> Pays {
(self.2)(args)
}
}
impl<Address, Call, Signature, Extra> GetDispatchInfo
for UncheckedExtrinsic<Address, Call, Signature, Extra>
where
Call: GetDispatchInfo,
Extra: SignedExtension,
{
fn get_dispatch_info(&self) -> DispatchInfo {
self.function.get_dispatch_info()
}
}
impl<AccountId, Call, Extra> GetDispatchInfo
for CheckedExtrinsic<AccountId, Call, Extra>
where
Call: GetDispatchInfo,
{
fn get_dispatch_info(&self) -> DispatchInfo {
self.function.get_dispatch_info()
}
}
#[cfg(feature = "std")]
impl<Call: Encode, Extra: Encode> GetDispatchInfo for sp_runtime::testing::TestXt<Call, Extra> {
fn get_dispatch_info(&self) -> DispatchInfo {
DispatchInfo {
weight: self.encode().len() as _,
pays_fee: Pays::Yes,
..Default::default()
}
}
}
#[derive(Clone, Copy, Eq, PartialEq, Default, RuntimeDebug, Encode, Decode)]
pub struct RuntimeDbWeight {
pub read: Weight,
pub write: Weight,
}
impl RuntimeDbWeight {
pub fn reads(self, r: Weight) -> Weight {
self.read.saturating_mul(r)
}
pub fn writes(self, w: Weight) -> Weight {
self.write.saturating_mul(w)
}
pub fn reads_writes(self, r: Weight, w: Weight) -> Weight {
let read_weight = self.read.saturating_mul(r);
let write_weight = self.write.saturating_mul(w);
read_weight.saturating_add(write_weight)
}
}
#[derive(Clone, Encode, Decode)]
pub struct WeightToFeeCoefficient<Balance> {
pub coeff_integer: Balance,
pub coeff_frac: Perbill,
pub negative: bool,
pub degree: u8,
}
pub type WeightToFeeCoefficients<T> = SmallVec<[WeightToFeeCoefficient<T>; 4]>;
pub trait WeightToFeePolynomial {
type Balance: BaseArithmetic + From<u32> + Copy + Unsigned;
fn polynomial() -> WeightToFeeCoefficients<Self::Balance>;
fn calc(weight: &Weight) -> Self::Balance {
Self::polynomial().iter().fold(Self::Balance::saturated_from(0u32), |mut acc, args| {
let w = Self::Balance::saturated_from(*weight).saturating_pow(args.degree.into());
let frac = args.coeff_frac * w;
let integer = args.coeff_integer.saturating_mul(w);
if args.negative {
acc = acc.saturating_sub(frac);
acc = acc.saturating_sub(integer);
} else {
acc = acc.saturating_add(frac);
acc = acc.saturating_add(integer);
}
acc
})
}
}
pub struct IdentityFee<T>(sp_std::marker::PhantomData<T>);
impl<T> WeightToFeePolynomial for IdentityFee<T> where
T: BaseArithmetic + From<u32> + Copy + Unsigned
{
type Balance = T;
fn polynomial() -> WeightToFeeCoefficients<Self::Balance> {
smallvec!(WeightToFeeCoefficient {
coeff_integer: 1u32.into(),
coeff_frac: Perbill::zero(),
negative: false,
degree: 1,
})
}
}
#[cfg(test)]
#[allow(dead_code)]
mod tests {
use crate::{decl_module, parameter_types, traits::Get};
use super::*;
pub trait Trait {
type Origin;
type Balance;
type BlockNumber;
type DbWeight: Get<RuntimeDbWeight>;
}
pub struct TraitImpl {}
parameter_types! {
pub const DbWeight: RuntimeDbWeight = RuntimeDbWeight {
read: 100,
write: 1000,
};
}
impl Trait for TraitImpl {
type Origin = u32;
type BlockNumber = u32;
type Balance = u32;
type DbWeight = DbWeight;
}
decl_module! {
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
#[weight = 1000]
fn f00(_origin) { unimplemented!(); }
#[weight = (1000, DispatchClass::Mandatory)]
fn f01(_origin) { unimplemented!(); }
#[weight = (1000, Pays::No)]
fn f02(_origin) { unimplemented!(); }
#[weight = (1000, DispatchClass::Operational, Pays::No)]
fn f03(_origin) { unimplemented!(); }
#[weight = ((_a * 10 + _eb * 1) as Weight, DispatchClass::Normal, Pays::Yes)]
fn f11(_origin, _a: u32, _eb: u32) { unimplemented!(); }
#[weight = (0, DispatchClass::Operational, Pays::Yes)]
fn f12(_origin, _a: u32, _eb: u32) { unimplemented!(); }
#[weight = T::DbWeight::get().reads(3) + T::DbWeight::get().writes(2) + 10_000]
fn f2(_origin) { unimplemented!(); }
#[weight = T::DbWeight::get().reads_writes(6, 5) + 40_000]
fn f21(_origin) { unimplemented!(); }
}
}
#[test]
fn weights_are_correct() {
let info = Call::<TraitImpl>::f00().get_dispatch_info();
assert_eq!(info.weight, 1000);
assert_eq!(info.class, DispatchClass::Normal);
assert_eq!(info.pays_fee, Pays::Yes);
let info = Call::<TraitImpl>::f01().get_dispatch_info();
assert_eq!(info.weight, 1000);
assert_eq!(info.class, DispatchClass::Mandatory);
assert_eq!(info.pays_fee, Pays::Yes);
let info = Call::<TraitImpl>::f02().get_dispatch_info();
assert_eq!(info.weight, 1000);
assert_eq!(info.class, DispatchClass::Normal);
assert_eq!(info.pays_fee, Pays::No);
let info = Call::<TraitImpl>::f03().get_dispatch_info();
assert_eq!(info.weight, 1000);
assert_eq!(info.class, DispatchClass::Operational);
assert_eq!(info.pays_fee, Pays::No);
assert_eq!(Call::<TraitImpl>::f11(10, 20).get_dispatch_info().weight, 120);
assert_eq!(Call::<TraitImpl>::f11(10, 20).get_dispatch_info().class, DispatchClass::Normal);
assert_eq!(Call::<TraitImpl>::f12(10, 20).get_dispatch_info().weight, 0);
assert_eq!(Call::<TraitImpl>::f12(10, 20).get_dispatch_info().class, DispatchClass::Operational);
assert_eq!(Call::<TraitImpl>::f2().get_dispatch_info().weight, 12300);
assert_eq!(Call::<TraitImpl>::f21().get_dispatch_info().weight, 45600);
assert_eq!(Call::<TraitImpl>::f2().get_dispatch_info().class, DispatchClass::Normal);
}
#[test]
fn extract_actual_weight_works() {
let pre = DispatchInfo {
weight: 1000,
.. Default::default()
};
assert_eq!(extract_actual_weight(&Ok(Some(7).into()), &pre), 7);
assert_eq!(extract_actual_weight(&Ok(Some(1000).into()), &pre), 1000);
assert_eq!(
extract_actual_weight(&Err(DispatchError::BadOrigin.with_weight(9)), &pre),
9
);
}
#[test]
fn extract_actual_weight_caps_at_pre_weight() {
let pre = DispatchInfo {
weight: 1000,
.. Default::default()
};
assert_eq!(extract_actual_weight(&Ok(Some(1250).into()), &pre), 1000);
assert_eq!(
extract_actual_weight(&Err(DispatchError::BadOrigin.with_weight(1300)), &pre),
1000
);
}
type Balance = u64;
struct Poly;
impl WeightToFeePolynomial for Poly {
type Balance = Balance;
fn polynomial() -> WeightToFeeCoefficients<Self::Balance> {
smallvec![
WeightToFeeCoefficient {
coeff_integer: 0,
coeff_frac: Perbill::from_fraction(0.5),
negative: false,
degree: 3
},
WeightToFeeCoefficient {
coeff_integer: 2,
coeff_frac: Perbill::from_rational_approximation(1u32, 3u32),
negative: false,
degree: 2
},
WeightToFeeCoefficient {
coeff_integer: 7,
coeff_frac: Perbill::zero(),
negative: false,
degree: 1
},
WeightToFeeCoefficient {
coeff_integer: 10_000,
coeff_frac: Perbill::zero(),
negative: true,
degree: 0
},
]
}
}
#[test]
fn polynomial_works() {
assert_eq!(Poly::calc(&100), 514033);
assert_eq!(Poly::calc(&10_123), 518917034928);
}
#[test]
fn polynomial_does_not_underflow() {
assert_eq!(Poly::calc(&0), 0);
}
#[test]
fn polynomial_does_not_overflow() {
assert_eq!(Poly::calc(&Weight::max_value()), Balance::max_value() - 10_000);
}
#[test]
fn identity_fee_works() {
assert_eq!(IdentityFee::<Balance>::calc(&0), 0);
assert_eq!(IdentityFee::<Balance>::calc(&50), 50);
assert_eq!(IdentityFee::<Balance>::calc(&Weight::max_value()), Balance::max_value());
}
}