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

Add payment_queryFeeDetails RPC #7692

Merged
41 commits merged into from
Jan 14, 2021
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
a72fb9a
Return FeeDetails in compute_fee_raw()
liuchengxu Dec 8, 2020
1c807af
Add payment_queryDetails rpc
liuchengxu Dec 8, 2020
7685575
Simplify serde attribute a bit
liuchengxu Dec 8, 2020
5994f88
Merge branch 'master' of https://github.com/paritytech/substrate into…
liuchengxu Dec 8, 2020
7b965bf
Merge branch 'master' of https://github.com/paritytech/substrate into…
liuchengxu Dec 9, 2020
ca58b81
Fix line width check
liuchengxu Dec 9, 2020
f319b32
Use saturating_add()
liuchengxu Dec 9, 2020
71f675d
Merge branch 'master' of https://github.com/paritytech/substrate into…
liuchengxu Dec 16, 2020
df5131b
Move transaction payment rpc types to types.rs
liuchengxu Dec 16, 2020
43ea398
Add file header
liuchengxu Dec 16, 2020
57a3c8c
Fix test
liuchengxu Dec 16, 2020
e2f76b7
Update Cargo.lock
liuchengxu Dec 16, 2020
517d38a
Nit
liuchengxu Dec 16, 2020
9d1b780
Merge branch 'master' of https://github.com/paritytech/substrate into…
liuchengxu Dec 17, 2020
a0693da
Apply the review suggestions
liuchengxu Dec 19, 2020
8c2cae8
.
liuchengxu Dec 19, 2020
00d80a2
.
liuchengxu Dec 19, 2020
a331455
Fix serde
liuchengxu Dec 19, 2020
8aa6313
Fix rust doc
liuchengxu Dec 19, 2020
422e6d1
.
liuchengxu Dec 19, 2020
1632dbf
Merge branch 'master' of https://github.com/paritytech/substrate into…
liuchengxu Dec 29, 2020
3cfc821
Update frame/transaction-payment/src/types.rs
liuchengxu Jan 1, 2021
e4556f9
Merge branch 'master' of https://github.com/paritytech/substrate into…
liuchengxu Jan 1, 2021
7bf0be6
Merge branch 'add-fee-details-rpc' of https://github.com/liuchengxu/s…
liuchengxu Jan 1, 2021
ff6bf96
Use NumberOrHex in fee details RPC
liuchengxu Jan 1, 2021
1676d18
Address review feedback
liuchengxu Jan 1, 2021
0a54c50
Nits
liuchengxu Jan 1, 2021
98869e7
Update some docs
liuchengxu Jan 1, 2021
ae77260
Merge branch 'master' of https://github.com/paritytech/substrate into…
liuchengxu Jan 1, 2021
ea053a3
Address review
liuchengxu Jan 4, 2021
f9365eb
Merge branch 'master' of https://github.com/paritytech/substrate into…
liuchengxu Jan 4, 2021
b02d85d
Update frame/transaction-payment/src/types.rs
liuchengxu Jan 4, 2021
adb7a58
Happy 2021
liuchengxu Jan 4, 2021
e75131e
Merge branch 'master' of https://github.com/paritytech/substrate into…
liuchengxu Jan 7, 2021
14c1319
Nit
liuchengxu Jan 7, 2021
05ea3b7
Merge branch 'master' of https://github.com/paritytech/substrate into…
liuchengxu Jan 8, 2021
d6ed580
Merge branch 'master' of https://github.com/paritytech/substrate into…
liuchengxu Jan 13, 2021
e6e945b
Address code review
liuchengxu Jan 13, 2021
db686bd
Merge branch 'master' of https://github.com/paritytech/substrate into…
liuchengxu Jan 13, 2021
76312b4
Remove needless bound
liuchengxu Jan 14, 2021
faacef8
Merge branch 'master' of https://github.com/paritytech/substrate into…
liuchengxu Jan 14, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions bin/node-template/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,12 @@ impl_runtime_apis! {
) -> pallet_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo<Balance> {
TransactionPayment::query_info(uxt, len)
}
fn query_fee_details(
uxt: <Block as BlockT>::Extrinsic,
len: u32,
) -> pallet_transaction_payment_rpc_runtime_api::FeeDetails<Balance> {
TransactionPayment::query_fee_details(uxt, len)
}
}

#[cfg(feature = "runtime-benchmarks")]
Expand Down
5 changes: 4 additions & 1 deletion bin/node/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ use pallet_grandpa::{AuthorityId as GrandpaId, AuthorityList as GrandpaAuthority
use pallet_grandpa::fg_primitives;
use pallet_im_online::sr25519::AuthorityId as ImOnlineId;
use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId;
use pallet_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo;
use pallet_transaction_payment_rpc_runtime_api::{FeeDetails, RuntimeDispatchInfo};
pub use pallet_transaction_payment::{Multiplier, TargetedFeeAdjustment, CurrencyAdapter};
use pallet_session::{historical as pallet_session_historical};
use sp_inherents::{InherentData, CheckInherentsResult};
Expand Down Expand Up @@ -1186,6 +1186,9 @@ impl_runtime_apis! {
fn query_info(uxt: <Block as BlockT>::Extrinsic, len: u32) -> RuntimeDispatchInfo<Balance> {
TransactionPayment::query_info(uxt, len)
}
fn query_fee_details(uxt: <Block as BlockT>::Extrinsic, len: u32) -> FeeDetails<Balance> {
TransactionPayment::query_fee_details(uxt, len)
}
}

impl sp_session::SessionKeys<Block> for Runtime {
Expand Down
93 changes: 80 additions & 13 deletions frame/transaction-payment/rpc/runtime-api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,43 +23,110 @@ use sp_std::prelude::*;
use frame_support::weights::{Weight, DispatchClass};
use codec::{Encode, Codec, Decode};
#[cfg(feature = "std")]
use serde::{Serialize, Deserialize, Serializer, Deserializer};
use sp_runtime::traits::{MaybeDisplay, MaybeFromStr};
use serde::{Serialize, Deserialize};
use sp_runtime::traits::{AtLeast32BitUnsigned, MaybeDisplay, MaybeFromStr};

/// The base fee and adjusted weight and length fees constitute the _inclusion fee,_ which is
/// the minimum fee for a transaction to be included in a block.
#[derive(Encode, Decode, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))]
#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))]
#[cfg_attr(feature = "std", serde(bound(serialize = "Balance: std::fmt::Display")))]
#[cfg_attr(feature = "std", serde(bound(deserialize = "Balance: std::str::FromStr")))]
pub struct InclusionFee<Balance> {
/// This is the minimum amount a user pays for a transaction. It is declared
/// as a base _weight_ in the runtime and converted to a fee using `WeightToFee`.
#[cfg_attr(feature = "std", serde(with = "serde_balance"))]
pub base_fee: Balance,
liuchengxu marked this conversation as resolved.
Show resolved Hide resolved
/// The length fee, the amount paid for the encoded length (in bytes) of the transaction.
#[cfg_attr(feature = "std", serde(with = "serde_balance"))]
pub len_fee: Balance,
/// - `targeted_fee_adjustment`: This is a multiplier that can tune the final fee based on
/// the congestion of the network.
/// - `weight_fee`: This amount is computed based on the weight of the transaction. Weight
/// accounts for the execution time of a transaction.
///
/// adjusted_weight_fee = targeted_fee_adjustment * weight_fee
#[cfg_attr(feature = "std", serde(with = "serde_balance"))]
pub adjusted_weight_fee: Balance,
}

impl<Balance: AtLeast32BitUnsigned + Copy> InclusionFee<Balance> {
/// Returns the total of inclusion fee.
///
/// ```ignore
/// inclusion_fee = base_fee + len_fee + adjusted_weight_fee
/// ```
pub fn total(&self) -> Balance {
self.base_fee
.saturating_add(self.len_fee)
.saturating_add(self.adjusted_weight_fee)
}
}

/// The `final_fee` is composed of:
/// - (Optional) `inclusion_fee`: Only the `Pays::Yes` transaction can have the inclusion fee.
/// - (Optional) `tip`: If included in the transaction, the tip will be added on top. Only
/// signed transactions can have a tip.
#[derive(Encode, Decode, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))]
#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))]
#[cfg_attr(feature = "std", serde(bound(serialize = "Balance: std::fmt::Display")))]
#[cfg_attr(feature = "std", serde(bound(deserialize = "Balance: std::str::FromStr")))]
pub struct FeeDetails<Balance> {
pub inclusion_fee: Option<InclusionFee<Balance>>,
#[cfg_attr(feature = "std", serde(with = "serde_balance"))]
pub tip: Balance,
}

impl<Balance: AtLeast32BitUnsigned + Default + Copy> FeeDetails<Balance> {
/// Returns the final fee.
///
/// ```ignore
/// final_fee = inclusion_fee + tip;
/// ```
pub fn final_fee(&self) -> Balance {
self.inclusion_fee.as_ref().map(|i|i.total()).unwrap_or_default().saturating_add(self.tip)
}
}

/// Information related to a dispatchable's class, weight, and fee that can be queried from the runtime.
#[derive(Eq, PartialEq, Encode, Decode, Default)]
#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))]
#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))]
#[cfg_attr(feature = "std", serde(bound(serialize = "Balance: std::fmt::Display")))]
#[cfg_attr(feature = "std", serde(bound(deserialize = "Balance: std::str::FromStr")))]
pub struct RuntimeDispatchInfo<Balance> {
/// Weight of this dispatch.
pub weight: Weight,
/// Class of this dispatch.
pub class: DispatchClass,
/// The inclusion fee of this dispatch. This does not include a tip or anything else that
/// depends on the signature (i.e. depends on a `SignedExtension`).
#[cfg_attr(feature = "std", serde(bound(serialize = "Balance: std::fmt::Display")))]
#[cfg_attr(feature = "std", serde(serialize_with = "serialize_as_string"))]
#[cfg_attr(feature = "std", serde(bound(deserialize = "Balance: std::str::FromStr")))]
#[cfg_attr(feature = "std", serde(deserialize_with = "deserialize_from_string"))]
#[cfg_attr(feature = "std", serde(with = "serde_balance"))]
pub partial_fee: Balance,
}

#[cfg(feature = "std")]
fn serialize_as_string<S: Serializer, T: std::fmt::Display>(t: &T, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(&t.to_string())
}
mod serde_balance {
use serde::{Deserialize, Serializer, Deserializer};

#[cfg(feature = "std")]
fn deserialize_from_string<'de, D: Deserializer<'de>, T: std::str::FromStr>(deserializer: D) -> Result<T, D::Error> {
let s = String::deserialize(deserializer)?;
s.parse::<T>().map_err(|_| serde::de::Error::custom("Parse from string failed"))
pub fn serialize<S: Serializer, T: std::fmt::Display>(t: &T, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(&t.to_string())
}

pub fn deserialize<'de, D: Deserializer<'de>, T: std::str::FromStr>(deserializer: D) -> Result<T, D::Error> {
let s = String::deserialize(deserializer)?;
s.parse::<T>().map_err(|_| serde::de::Error::custom("Parse from string failed"))
}
}

sp_api::decl_runtime_apis! {
pub trait TransactionPaymentApi<Balance> where
Balance: Codec + MaybeDisplay + MaybeFromStr,
{
fn query_info(uxt: Block::Extrinsic, len: u32) -> RuntimeDispatchInfo<Balance>;
fn query_fee_details(uxt: Block::Extrinsic, len: u32) -> FeeDetails<Balance>;
}
}

Expand Down
42 changes: 38 additions & 4 deletions frame/transaction-payment/rpc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,24 @@ use jsonrpc_derive::rpc;
use sp_runtime::{generic::BlockId, traits::{Block as BlockT, MaybeDisplay, MaybeFromStr}};
use sp_api::ProvideRuntimeApi;
use sp_core::Bytes;
use pallet_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo;
use pallet_transaction_payment_rpc_runtime_api::{FeeDetails, RuntimeDispatchInfo};
pub use pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi as TransactionPaymentRuntimeApi;
pub use self::gen_client::Client as TransactionPaymentClient;

#[rpc]
pub trait TransactionPaymentApi<BlockHash, ResponseType> {
pub trait TransactionPaymentApi<BlockHash, ResponseType, FeeDetails> {
#[rpc(name = "payment_queryInfo")]
fn query_info(
&self,
encoded_xt: Bytes,
at: Option<BlockHash>
) -> Result<ResponseType>;
#[rpc(name = "payment_queryFeeDetails")]
fn query_fee_details(
&self,
encoded_xt: Bytes,
at: Option<BlockHash>
) -> Result<FeeDetails>;
}

/// A struct that implements the [`TransactionPaymentApi`].
Expand Down Expand Up @@ -69,8 +75,11 @@ impl From<Error> for i64 {
}
}

impl<C, Block, Balance> TransactionPaymentApi<<Block as BlockT>::Hash, RuntimeDispatchInfo<Balance>>
for TransactionPayment<C, Block>
impl<C, Block, Balance> TransactionPaymentApi<
<Block as BlockT>::Hash,
RuntimeDispatchInfo<Balance>,
FeeDetails<Balance>,
> for TransactionPayment<C, Block>
where
Block: BlockT,
C: Send + Sync + 'static + ProvideRuntimeApi<Block> + HeaderBackend<Block>,
Expand Down Expand Up @@ -101,4 +110,29 @@ where
data: Some(format!("{:?}", e).into()),
})
}

fn query_fee_details(
&self,
encoded_xt: Bytes,
at: Option<<Block as BlockT>::Hash>
liuchengxu marked this conversation as resolved.
Show resolved Hide resolved
) -> Result<FeeDetails<Balance>> {
let api = self.client.runtime_api();
let at = BlockId::hash(at.unwrap_or_else(||
// If the block hash is not supplied assume the best block.
self.client.info().best_hash
));

let encoded_len = encoded_xt.len() as u32;

let uxt: Block::Extrinsic = Decode::decode(&mut &*encoded_xt).map_err(|e| RpcError {
code: ErrorCode::ServerError(Error::DecodeError.into()),
message: "Unable to query fee details.".into(),
data: Some(format!("{:?}", e).into()),
})?;
api.query_fee_details(&at, uxt, encoded_len).map_err(|e| RpcError {
code: ErrorCode::ServerError(Error::RuntimeError.into()),
message: "Unable to query fee details.".into(),
data: Some(format!("{:?}", e).into()),
})
}
}
88 changes: 53 additions & 35 deletions frame/transaction-payment/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,10 @@ use sp_runtime::{
},
traits::{
Saturating, SignedExtension, SaturatedConversion, Convert, Dispatchable,
DispatchInfoOf, PostDispatchInfoOf,
DispatchInfoOf, PostDispatchInfoOf
},
};
use pallet_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo;
use pallet_transaction_payment_rpc_runtime_api::{FeeDetails, InclusionFee, RuntimeDispatchInfo};
liuchengxu marked this conversation as resolved.
Show resolved Hide resolved

mod payment;
pub use payment::*;
Expand Down Expand Up @@ -331,41 +331,40 @@ impl<T: Config> Module<T> where
RuntimeDispatchInfo { weight, class, partial_fee }
}

/// Query the detailed fee of a given `call`.
pub fn query_fee_details<Extrinsic: GetDispatchInfo>(
unchecked_extrinsic: Extrinsic,
len: u32,
) -> FeeDetails<BalanceOf<T>>
where
T: Send + Sync,
BalanceOf<T>: Send + Sync,
liuchengxu marked this conversation as resolved.
Show resolved Hide resolved
T::Call: Dispatchable<Info=DispatchInfo>,
{
let dispatch_info = <Extrinsic as GetDispatchInfo>::get_dispatch_info(&unchecked_extrinsic);
Self::compute_fee_details(len, &dispatch_info, 0u32.into())
}

/// Compute the final fee value for a particular transaction.
///
/// The final fee is composed of:
liuchengxu marked this conversation as resolved.
Show resolved Hide resolved
/// - `base_fee`: This is the minimum amount a user pays for a transaction. It is declared
/// as a base _weight_ in the runtime and converted to a fee using `WeightToFee`.
/// - `len_fee`: The length fee, the amount paid for the encoded length (in bytes) of the
/// transaction.
/// - `weight_fee`: This amount is computed based on the weight of the transaction. Weight
/// accounts for the execution time of a transaction.
/// - `targeted_fee_adjustment`: This is a multiplier that can tune the final fee based on
/// the congestion of the network.
/// - (Optional) `tip`: If included in the transaction, the tip will be added on top. Only
/// signed transactions can have a tip.
///
/// The base fee and adjusted weight and length fees constitute the _inclusion fee,_ which is
/// the minimum fee for a transaction to be included in a block.
///
/// ```ignore
/// inclusion_fee = base_fee + len_fee + [targeted_fee_adjustment * weight_fee];
/// final_fee = inclusion_fee + tip;
/// ```
pub fn compute_fee(
len: u32,
info: &DispatchInfoOf<T::Call>,
tip: BalanceOf<T>,
) -> BalanceOf<T> where
T::Call: Dispatchable<Info=DispatchInfo>,
{
Self::compute_fee_raw(
len,
info.weight,
tip,
info.pays_fee,
info.class,
)
Self::compute_fee_details(len, info, tip).final_fee()
}

/// Compute the fee details for a particular transaction.
pub fn compute_fee_details(
len: u32,
info: &DispatchInfoOf<T::Call>,
tip: BalanceOf<T>,
) -> FeeDetails<BalanceOf<T>> where
T::Call: Dispatchable<Info=DispatchInfo>,
{
Self::compute_fee_raw(len, info.weight, tip, info.pays_fee, info.class)
}

/// Compute the actual post dispatch fee for a particular transaction.
Expand All @@ -379,6 +378,18 @@ impl<T: Config> Module<T> where
tip: BalanceOf<T>,
) -> BalanceOf<T> where
T::Call: Dispatchable<Info=DispatchInfo,PostInfo=PostDispatchInfo>,
{
Self::compute_actual_fee_details(len, info, post_info, tip).final_fee()
}

/// Compute the actual post dispatch fee details for a particular transaction.
pub fn compute_actual_fee_details(
len: u32,
info: &DispatchInfoOf<T::Call>,
post_info: &PostDispatchInfoOf<T::Call>,
tip: BalanceOf<T>,
) -> FeeDetails<BalanceOf<T>> where
T::Call: Dispatchable<Info=DispatchInfo,PostInfo=PostDispatchInfo>,
{
Self::compute_fee_raw(
len,
Expand All @@ -395,7 +406,7 @@ impl<T: Config> Module<T> where
tip: BalanceOf<T>,
pays_fee: Pays,
class: DispatchClass,
) -> BalanceOf<T> {
) -> FeeDetails<BalanceOf<T>> {
if pays_fee == Pays::Yes {
let len = <BalanceOf<T>>::from(len);
let per_byte = T::TransactionByteFee::get();
Expand All @@ -410,12 +421,19 @@ impl<T: Config> Module<T> where
let adjusted_weight_fee = multiplier.saturating_mul_int(unadjusted_weight_fee);

let base_fee = Self::weight_to_fee(T::BlockWeights::get().get(class).base_extrinsic);
base_fee
.saturating_add(fixed_len_fee)
.saturating_add(adjusted_weight_fee)
.saturating_add(tip)
FeeDetails {
inclusion_fee: Some(InclusionFee {
base_fee,
len_fee: fixed_len_fee,
adjusted_weight_fee
}),
tip
}
} else {
tip
FeeDetails {
inclusion_fee: None,
tip
}
}
}

Expand Down