mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 18:11:10 +00:00
config: Add SkipCheckIfFeeless signed extension (#1264)
* config: Add `SkipCheckIfFeeless` signed extension Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * config: Add extra extension to the default params Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * examples: Adjust signed extension example Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * config: Extend SkipCheckIfFeeless with inner signed extension Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * config: Configure SkipCheck with inner signed extension params Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * config: Implement Deafult for SkipCheckIfFeelessParams with Option Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * examples: Fix example with proper extension Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * config: Extend <T as Config>::AssetId with EncodeAsType and Clone Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * config: Add SkipCheck with AssetTx Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * config: Encode as type from metadata the inner signed extensions Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Adjust examples Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * blocks: Use `SkipCheckIfFeeless` for decoding the tip of extensions Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * config: Decode `SkipCheckIfFeeless` with `Self` Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * tests: Adjust testing Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * config: Descriptive errors for building `SkipCheckIfFeeless` Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * config: Add docs for extra error types Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * subxt: Add extra derives to signed extensions Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * config: Use `Default::default` to simplify type init Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> --------- Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>
This commit is contained in:
@@ -3,7 +3,19 @@ use subxt::client::OfflineClientT;
|
||||
use subxt::config::{Config, ExtrinsicParams, ExtrinsicParamsEncoder};
|
||||
use subxt_signer::sr25519::dev;
|
||||
|
||||
#[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata_full.scale")]
|
||||
#[subxt::subxt(
|
||||
runtime_metadata_path = "../artifacts/polkadot_metadata_full.scale",
|
||||
derive_for_type(path = "xcm::v2::multilocation::MultiLocation", derive = "Clone"),
|
||||
derive_for_type(path = "xcm::v2::multilocation::Junctions", derive = "Clone"),
|
||||
derive_for_type(path = "xcm::v2::junction::Junction", derive = "Clone"),
|
||||
derive_for_type(path = "xcm::v2::NetworkId", derive = "Clone"),
|
||||
derive_for_type(path = "xcm::v2::BodyId", derive = "Clone"),
|
||||
derive_for_type(path = "xcm::v2::BodyPart", derive = "Clone"),
|
||||
derive_for_type(
|
||||
path = "bounded_collections::weak_bounded_vec::WeakBoundedVec",
|
||||
derive = "Clone"
|
||||
)
|
||||
)]
|
||||
pub mod runtime {}
|
||||
use runtime::runtime_types::xcm::v2::multilocation::MultiLocation;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use codec::Encode;
|
||||
use scale_encode::EncodeAsType;
|
||||
use subxt::client::OfflineClientT;
|
||||
use subxt::config::signed_extensions;
|
||||
use subxt::config::{
|
||||
@@ -11,6 +12,7 @@ pub mod runtime {}
|
||||
|
||||
// We don't need to construct this at runtime,
|
||||
// so an empty enum is appropriate:
|
||||
#[derive(EncodeAsType)]
|
||||
pub enum CustomConfig {}
|
||||
|
||||
impl Config for CustomConfig {
|
||||
@@ -32,6 +34,10 @@ impl Config for CustomConfig {
|
||||
signed_extensions::CheckMortality<Self>,
|
||||
signed_extensions::ChargeAssetTxPayment<Self>,
|
||||
signed_extensions::ChargeTransactionPayment,
|
||||
signed_extensions::SkipCheckIfFeeless<
|
||||
Self,
|
||||
signed_extensions::ChargeAssetTxPayment<Self>,
|
||||
>,
|
||||
// And add a new one of our own:
|
||||
CustomSignedExtension,
|
||||
),
|
||||
@@ -81,8 +87,8 @@ impl ExtrinsicParamsEncoder for CustomSignedExtension {
|
||||
pub fn custom(
|
||||
params: DefaultExtrinsicParamsBuilder<CustomConfig>,
|
||||
) -> <<CustomConfig as Config>::ExtrinsicParams as ExtrinsicParams<CustomConfig>>::OtherParams {
|
||||
let (a, b, c, d, e, f, g) = params.build();
|
||||
(a, b, c, d, e, f, g, ())
|
||||
let (a, b, c, d, e, f, g, h) = params.build();
|
||||
(a, b, c, d, e, f, g, h, ())
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
|
||||
@@ -13,7 +13,7 @@ use crate::{
|
||||
};
|
||||
|
||||
use crate::config::signed_extensions::{
|
||||
ChargeAssetTxPayment, ChargeTransactionPayment, CheckNonce,
|
||||
ChargeAssetTxPayment, ChargeTransactionPayment, CheckNonce, SkipCheckIfFeeless,
|
||||
};
|
||||
use crate::config::SignedExtension;
|
||||
use crate::dynamic::DecodedValue;
|
||||
@@ -685,7 +685,7 @@ impl<'a, T: Config> ExtrinsicSignedExtensions<'a, T> {
|
||||
///
|
||||
/// Returns `None` if `tip` was not found or decoding failed.
|
||||
pub fn tip(&self) -> Option<u128> {
|
||||
// Note: the overhead of iterating twice should be negligible.
|
||||
// Note: the overhead of iterating multiple time should be negligible.
|
||||
self.find::<ChargeTransactionPayment>()
|
||||
.ok()
|
||||
.flatten()
|
||||
@@ -696,6 +696,12 @@ impl<'a, T: Config> ExtrinsicSignedExtensions<'a, T> {
|
||||
.flatten()
|
||||
.map(|e| e.tip())
|
||||
})
|
||||
.or_else(|| {
|
||||
self.find::<SkipCheckIfFeeless<T, ChargeAssetTxPayment<T>>>()
|
||||
.ok()
|
||||
.flatten()
|
||||
.map(|skip_check| skip_check.inner_signed_extension().tip())
|
||||
})
|
||||
}
|
||||
|
||||
/// The nonce of the account that submitted the extrinsic, extracted from the CheckNonce signed extension.
|
||||
|
||||
@@ -17,6 +17,7 @@ pub type DefaultExtrinsicParams<T> = signed_extensions::AnyOf<
|
||||
signed_extensions::CheckMortality<T>,
|
||||
signed_extensions::ChargeAssetTxPayment<T>,
|
||||
signed_extensions::ChargeTransactionPayment,
|
||||
signed_extensions::SkipCheckIfFeeless<T, signed_extensions::ChargeAssetTxPayment<T>>,
|
||||
),
|
||||
>;
|
||||
|
||||
@@ -131,6 +132,9 @@ impl<T: Config> DefaultExtrinsicParamsBuilder<T> {
|
||||
let charge_transaction_params =
|
||||
signed_extensions::ChargeTransactionPaymentParams::tip(self.tip);
|
||||
|
||||
let skip_check_params =
|
||||
signed_extensions::SkipCheckIfFeelessParams::from(charge_asset_tx_params.clone());
|
||||
|
||||
(
|
||||
(),
|
||||
(),
|
||||
@@ -139,6 +143,7 @@ impl<T: Config> DefaultExtrinsicParamsBuilder<T> {
|
||||
check_mortality_params,
|
||||
charge_asset_tx_params,
|
||||
charge_transaction_params,
|
||||
skip_check_params,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,19 @@ pub enum ExtrinsicParamsError {
|
||||
/// A signed extension was encountered that we don't know about.
|
||||
#[error("Error constructing extrinsic parameters: Unknown signed extension '{0}'")]
|
||||
UnknownSignedExtension(String),
|
||||
/// Some custom error.
|
||||
/// Cannot find the type id of a signed extension in the metadata.
|
||||
#[error("Cannot find extension's '{0}' type id '{1} in the metadata")]
|
||||
MissingTypeId(String, u32),
|
||||
/// User provided a different signed extension than the one expected.
|
||||
#[error("Provided a different signed extension for '{0}', the metadata expect '{1}'")]
|
||||
ExpectedAnotherExtension(String, String),
|
||||
/// The inner type of a signed extension is not present in the metadata.
|
||||
#[error("The inner type of the signed extension '{0}' is not present in the metadata")]
|
||||
MissingInnerSignedExtension(String),
|
||||
/// The inner type of the signed extension is not named.
|
||||
#[error("The signed extension's '{0}' type id '{1}' does not have a name in the metadata")]
|
||||
ExpectedNamedTypeId(String, u32),
|
||||
/// Some custom error.s
|
||||
#[error("Error constructing extrinsic parameters: {0}")]
|
||||
Custom(CustomError),
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ pub mod substrate;
|
||||
use codec::{Decode, Encode};
|
||||
use core::fmt::Debug;
|
||||
use scale_decode::DecodeAsType;
|
||||
use scale_encode::EncodeAsType;
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
|
||||
pub use default_extrinsic_params::{DefaultExtrinsicParams, DefaultExtrinsicParamsBuilder};
|
||||
@@ -54,7 +55,7 @@ pub trait Config: Sized + Send + Sync + 'static {
|
||||
type ExtrinsicParams: ExtrinsicParams<Self>;
|
||||
|
||||
/// This is used to identify an asset in the `ChargeAssetTxPayment` signed extension.
|
||||
type AssetId: Debug + Encode + DecodeAsType;
|
||||
type AssetId: Debug + Clone + Encode + DecodeAsType + EncodeAsType;
|
||||
}
|
||||
|
||||
/// given some [`Config`], this return the other params needed for its `ExtrinsicParams`.
|
||||
|
||||
@@ -9,11 +9,12 @@
|
||||
|
||||
use super::extrinsic_params::{ExtrinsicParams, ExtrinsicParamsEncoder, ExtrinsicParamsError};
|
||||
use crate::utils::Era;
|
||||
use crate::{client::OfflineClientT, Config};
|
||||
use crate::{client::OfflineClientT, Config, Metadata};
|
||||
use codec::{Compact, Encode};
|
||||
use core::fmt::Debug;
|
||||
|
||||
use scale_decode::DecodeAsType;
|
||||
use scale_encode::EncodeAsType;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
@@ -32,7 +33,7 @@ pub trait SignedExtension<T: Config>: ExtrinsicParams<T> {
|
||||
}
|
||||
|
||||
/// The [`CheckSpecVersion`] signed extension.
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone, Debug, EncodeAsType, DecodeAsType)]
|
||||
pub struct CheckSpecVersion(u32);
|
||||
|
||||
impl<T: Config> ExtrinsicParams<T> for CheckSpecVersion {
|
||||
@@ -60,7 +61,7 @@ impl<T: Config> SignedExtension<T> for CheckSpecVersion {
|
||||
}
|
||||
|
||||
/// The [`CheckNonce`] signed extension.
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone, Debug, EncodeAsType, DecodeAsType)]
|
||||
pub struct CheckNonce(Compact<u64>);
|
||||
|
||||
impl<T: Config> ExtrinsicParams<T> for CheckNonce {
|
||||
@@ -88,7 +89,7 @@ impl<T: Config> SignedExtension<T> for CheckNonce {
|
||||
}
|
||||
|
||||
/// The [`CheckTxVersion`] signed extension.
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone, Debug, EncodeAsType, DecodeAsType)]
|
||||
pub struct CheckTxVersion(u32);
|
||||
|
||||
impl<T: Config> ExtrinsicParams<T> for CheckTxVersion {
|
||||
@@ -116,6 +117,9 @@ impl<T: Config> SignedExtension<T> for CheckTxVersion {
|
||||
}
|
||||
|
||||
/// The [`CheckGenesis`] signed extension.
|
||||
#[derive(Clone, EncodeAsType, DecodeAsType)]
|
||||
#[decode_as_type(trait_bounds = "T::Hash: DecodeAsType")]
|
||||
#[encode_as_type(trait_bounds = "T::Hash: EncodeAsType")]
|
||||
pub struct CheckGenesis<T: Config>(T::Hash);
|
||||
|
||||
impl<T: Config> std::fmt::Debug for CheckGenesis<T> {
|
||||
@@ -149,12 +153,16 @@ impl<T: Config> SignedExtension<T> for CheckGenesis<T> {
|
||||
}
|
||||
|
||||
/// The [`CheckMortality`] signed extension.
|
||||
#[derive(Clone, EncodeAsType, DecodeAsType)]
|
||||
#[decode_as_type(trait_bounds = "T::Hash: DecodeAsType")]
|
||||
#[encode_as_type(trait_bounds = "T::Hash: EncodeAsType")]
|
||||
pub struct CheckMortality<T: Config> {
|
||||
era: Era,
|
||||
checkpoint: T::Hash,
|
||||
}
|
||||
|
||||
/// Parameters to configure the [`CheckMortality`] signed extension.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct CheckMortalityParams<T: Config> {
|
||||
era: Era,
|
||||
checkpoint: Option<T::Hash>,
|
||||
@@ -229,8 +237,9 @@ impl<T: Config> SignedExtension<T> for CheckMortality<T> {
|
||||
}
|
||||
|
||||
/// The [`ChargeAssetTxPayment`] signed extension.
|
||||
#[derive(Debug, DecodeAsType)]
|
||||
#[derive(Clone, Debug, DecodeAsType, EncodeAsType)]
|
||||
#[decode_as_type(trait_bounds = "T::AssetId: DecodeAsType")]
|
||||
#[encode_as_type(trait_bounds = "T::AssetId: EncodeAsType")]
|
||||
pub struct ChargeAssetTxPayment<T: Config> {
|
||||
tip: Compact<u128>,
|
||||
asset_id: Option<T::AssetId>,
|
||||
@@ -249,11 +258,22 @@ impl<T: Config> ChargeAssetTxPayment<T> {
|
||||
}
|
||||
|
||||
/// Parameters to configure the [`ChargeAssetTxPayment`] signed extension.
|
||||
#[derive(Debug)]
|
||||
pub struct ChargeAssetTxPaymentParams<T: Config> {
|
||||
tip: u128,
|
||||
asset_id: Option<T::AssetId>,
|
||||
}
|
||||
|
||||
// Dev note: `#[derive(Clone)]` implies `T: Clone` instead of `T::AssetId: Clone`.
|
||||
impl<T: Config> Clone for ChargeAssetTxPaymentParams<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
tip: self.tip,
|
||||
asset_id: self.asset_id.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> Default for ChargeAssetTxPaymentParams<T> {
|
||||
fn default() -> Self {
|
||||
ChargeAssetTxPaymentParams {
|
||||
@@ -315,7 +335,7 @@ impl<T: Config> SignedExtension<T> for ChargeAssetTxPayment<T> {
|
||||
}
|
||||
|
||||
/// The [`ChargeTransactionPayment`] signed extension.
|
||||
#[derive(Debug, DecodeAsType)]
|
||||
#[derive(Clone, Debug, DecodeAsType, EncodeAsType)]
|
||||
pub struct ChargeTransactionPayment {
|
||||
tip: Compact<u128>,
|
||||
}
|
||||
@@ -370,6 +390,211 @@ impl<T: Config> SignedExtension<T> for ChargeTransactionPayment {
|
||||
type Decoded = Self;
|
||||
}
|
||||
|
||||
/// Information needed to encode the [`SkipCheckIfFeeless`] signed extension.
|
||||
#[derive(Debug)]
|
||||
struct SkipCheckIfFeelessEncodingData {
|
||||
metadata: Metadata,
|
||||
type_id: u32,
|
||||
}
|
||||
|
||||
impl SkipCheckIfFeelessEncodingData {
|
||||
/// Construct [`SkipCheckIfFeelessEncodingData`].
|
||||
fn new(
|
||||
metadata: Metadata,
|
||||
extension: &str,
|
||||
inner_extension: &str,
|
||||
) -> Result<Self, ExtrinsicParamsError> {
|
||||
let skip_check_type_id = metadata
|
||||
.extrinsic()
|
||||
.signed_extensions()
|
||||
.iter()
|
||||
.find_map(|ext| {
|
||||
if ext.identifier() == extension {
|
||||
Some(ext.extra_ty())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
let Some(skip_check_type_id) = skip_check_type_id else {
|
||||
return Err(ExtrinsicParamsError::UnknownSignedExtension(
|
||||
inner_extension.to_owned(),
|
||||
));
|
||||
};
|
||||
|
||||
// Ensure that the `SkipCheckIfFeeless` type has the same inner signed extension as provided.
|
||||
let Some(skip_check_ty) = metadata.types().resolve(skip_check_type_id) else {
|
||||
return Err(ExtrinsicParamsError::MissingTypeId(
|
||||
inner_extension.to_owned(),
|
||||
skip_check_type_id,
|
||||
));
|
||||
};
|
||||
|
||||
// The substrate's `SkipCheckIfFeeless` contains 2 types: the inner signed extension and a phantom data.
|
||||
// Phantom data does not have a type associated, so we need to find the inner signed extension.
|
||||
let Some(inner_type_id) = skip_check_ty
|
||||
.type_params
|
||||
.iter()
|
||||
.find_map(|param| param.ty.map(|ty| ty.id))
|
||||
else {
|
||||
return Err(ExtrinsicParamsError::MissingInnerSignedExtension(
|
||||
inner_extension.to_owned(),
|
||||
));
|
||||
};
|
||||
|
||||
// Get the inner type of the `SkipCheckIfFeeless` extension to check if the naming matches the provided parameters.
|
||||
let Some(inner_extension_ty) = metadata.types().resolve(inner_type_id) else {
|
||||
return Err(ExtrinsicParamsError::MissingTypeId(
|
||||
inner_extension.to_owned(),
|
||||
inner_type_id,
|
||||
));
|
||||
};
|
||||
|
||||
let Some(inner_extension_name) = inner_extension_ty.path.segments.last() else {
|
||||
return Err(ExtrinsicParamsError::ExpectedNamedTypeId(
|
||||
inner_extension.to_owned(),
|
||||
inner_type_id,
|
||||
));
|
||||
};
|
||||
|
||||
if inner_extension_name != inner_extension {
|
||||
return Err(ExtrinsicParamsError::ExpectedAnotherExtension(
|
||||
inner_extension.to_owned(),
|
||||
inner_extension_name.to_owned(),
|
||||
));
|
||||
}
|
||||
|
||||
Ok(SkipCheckIfFeelessEncodingData {
|
||||
metadata,
|
||||
type_id: inner_type_id,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// The [`SkipCheckIfFeeless`] signed extension.
|
||||
#[derive(Debug, DecodeAsType, EncodeAsType)]
|
||||
#[decode_as_type(trait_bounds = "S: DecodeAsType")]
|
||||
#[encode_as_type(trait_bounds = "S: EncodeAsType")]
|
||||
pub struct SkipCheckIfFeeless<T, S>
|
||||
where
|
||||
T: Config,
|
||||
S: SignedExtension<T> + DecodeAsType + EncodeAsType,
|
||||
{
|
||||
inner: S,
|
||||
// Dev note: This is `Option` because `#[derive(DecodeAsType)]` requires the
|
||||
// `Default` bound on skipped parameters.
|
||||
// This field is populated when the [`SkipCheckIfFeeless`] is constructed from
|
||||
// [`ExtrinsicParams`] (ie, when subxt submits extrinsics). However, it is not
|
||||
// populated when decoding signed extensions from the node.
|
||||
#[decode_as_type(skip)]
|
||||
#[encode_as_type(skip)]
|
||||
encoding_data: Option<SkipCheckIfFeelessEncodingData>,
|
||||
#[decode_as_type(skip)]
|
||||
#[encode_as_type(skip)]
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T, S> SkipCheckIfFeeless<T, S>
|
||||
where
|
||||
T: Config,
|
||||
S: SignedExtension<T> + DecodeAsType + EncodeAsType,
|
||||
{
|
||||
/// The inner signed extension.
|
||||
pub fn inner_signed_extension(&self) -> &S {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, S> ExtrinsicParams<T> for SkipCheckIfFeeless<T, S>
|
||||
where
|
||||
T: Config,
|
||||
S: SignedExtension<T> + DecodeAsType + EncodeAsType,
|
||||
<S as ExtrinsicParams<T>>::OtherParams: Default,
|
||||
{
|
||||
type OtherParams = SkipCheckIfFeelessParams<T, S>;
|
||||
type Error = ExtrinsicParamsError;
|
||||
|
||||
fn new<Client: OfflineClientT<T>>(
|
||||
nonce: u64,
|
||||
client: Client,
|
||||
other_params: Self::OtherParams,
|
||||
) -> Result<Self, Self::Error> {
|
||||
let other_params = other_params.0.unwrap_or_default();
|
||||
|
||||
let metadata = client.metadata();
|
||||
let encoding_data = SkipCheckIfFeelessEncodingData::new(metadata, Self::NAME, S::NAME)?;
|
||||
let inner_extension = S::new(nonce, client, other_params).map_err(Into::into)?;
|
||||
|
||||
Ok(SkipCheckIfFeeless {
|
||||
inner: inner_extension,
|
||||
encoding_data: Some(encoding_data),
|
||||
_phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, S> ExtrinsicParamsEncoder for SkipCheckIfFeeless<T, S>
|
||||
where
|
||||
T: Config,
|
||||
S: SignedExtension<T> + DecodeAsType + EncodeAsType,
|
||||
{
|
||||
fn encode_extra_to(&self, v: &mut Vec<u8>) {
|
||||
if let Some(encoding_data) = &self.encoding_data {
|
||||
let _ = self.inner.encode_as_type_to(
|
||||
encoding_data.type_id,
|
||||
encoding_data.metadata.types(),
|
||||
v,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, S> SignedExtension<T> for SkipCheckIfFeeless<T, S>
|
||||
where
|
||||
T: Config,
|
||||
S: SignedExtension<T> + DecodeAsType + EncodeAsType,
|
||||
<S as ExtrinsicParams<T>>::OtherParams: Default,
|
||||
{
|
||||
const NAME: &'static str = "SkipCheckIfFeeless";
|
||||
type Decoded = Self;
|
||||
}
|
||||
|
||||
/// Parameters to configure the [`SkipCheckIfFeeless`] signed extension.
|
||||
pub struct SkipCheckIfFeelessParams<T, S>(Option<<S as ExtrinsicParams<T>>::OtherParams>)
|
||||
where
|
||||
T: Config,
|
||||
S: SignedExtension<T>;
|
||||
|
||||
impl<T, S> std::fmt::Debug for SkipCheckIfFeelessParams<T, S>
|
||||
where
|
||||
T: Config,
|
||||
S: SignedExtension<T>,
|
||||
{
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_tuple("SkipCheckIfFeelessParams").finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config, S: SignedExtension<T>> Default for SkipCheckIfFeelessParams<T, S>
|
||||
where
|
||||
T: Config,
|
||||
S: SignedExtension<T>,
|
||||
{
|
||||
fn default() -> Self {
|
||||
SkipCheckIfFeelessParams(None)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, S> SkipCheckIfFeelessParams<T, S>
|
||||
where
|
||||
T: Config,
|
||||
S: SignedExtension<T>,
|
||||
{
|
||||
/// Skip the check if the transaction is feeless.
|
||||
pub fn from(extrinsic_params: <S as ExtrinsicParams<T>>::OtherParams) -> Self {
|
||||
SkipCheckIfFeelessParams(Some(extrinsic_params))
|
||||
}
|
||||
}
|
||||
|
||||
/// This accepts a tuple of [`SignedExtension`]s, and will dynamically make use of whichever
|
||||
/// ones are actually required for the chain in the correct order, ignoring the rest. This
|
||||
/// is a sensible default, and allows for a single configuration to work across multiple chains.
|
||||
|
||||
+11
-1
@@ -3,11 +3,21 @@
|
||||
// see LICENSE for license details.
|
||||
|
||||
use scale_decode::DecodeAsType;
|
||||
use scale_encode::EncodeAsType;
|
||||
|
||||
// Dev note: This and related bits taken from `sp_runtime::generic::Era`
|
||||
/// An era to describe the longevity of a transaction.
|
||||
#[derive(
|
||||
PartialEq, Default, Eq, Clone, Copy, Debug, serde::Serialize, serde::Deserialize, DecodeAsType,
|
||||
PartialEq,
|
||||
Default,
|
||||
Eq,
|
||||
Clone,
|
||||
Copy,
|
||||
Debug,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
DecodeAsType,
|
||||
EncodeAsType,
|
||||
)]
|
||||
pub enum Era {
|
||||
/// The transaction is valid forever. The genesis hash must be present in the signed content.
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
use crate::{test_context, utils::node_runtime};
|
||||
use codec::{Compact, Encode};
|
||||
use futures::StreamExt;
|
||||
use subxt::config::signed_extensions::{ChargeAssetTxPayment, CheckMortality, CheckNonce};
|
||||
use subxt::config::signed_extensions::{
|
||||
ChargeAssetTxPayment, CheckMortality, CheckNonce, SkipCheckIfFeeless,
|
||||
};
|
||||
use subxt::config::DefaultExtrinsicParamsBuilder;
|
||||
use subxt::config::SubstrateConfig;
|
||||
use subxt::utils::Era;
|
||||
@@ -276,13 +278,15 @@ async fn decode_signed_extensions_from_blocks() {
|
||||
|
||||
let transaction1 = submit_transfer_extrinsic_and_get_it_back!(1234);
|
||||
let extensions1 = transaction1.signed_extensions().unwrap();
|
||||
|
||||
let nonce1 = extensions1.nonce().unwrap();
|
||||
let nonce1_static = extensions1.find::<CheckNonce>().unwrap().unwrap().0;
|
||||
let tip1 = extensions1.tip().unwrap();
|
||||
let tip1_static: u128 = extensions1
|
||||
.find::<ChargeAssetTxPayment<SubstrateConfig>>()
|
||||
.find::<SkipCheckIfFeeless<SubstrateConfig, ChargeAssetTxPayment<SubstrateConfig>>>()
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.inner_signed_extension()
|
||||
.tip();
|
||||
|
||||
let transaction2 = submit_transfer_extrinsic_and_get_it_back!(5678);
|
||||
@@ -291,9 +295,10 @@ async fn decode_signed_extensions_from_blocks() {
|
||||
let nonce2_static = extensions2.find::<CheckNonce>().unwrap().unwrap().0;
|
||||
let tip2 = extensions2.tip().unwrap();
|
||||
let tip2_static: u128 = extensions2
|
||||
.find::<ChargeAssetTxPayment<SubstrateConfig>>()
|
||||
.find::<SkipCheckIfFeeless<SubstrateConfig, ChargeAssetTxPayment<SubstrateConfig>>>()
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.inner_signed_extension()
|
||||
.tip();
|
||||
|
||||
assert_eq!(nonce1, 0);
|
||||
@@ -313,7 +318,7 @@ async fn decode_signed_extensions_from_blocks() {
|
||||
"CheckMortality",
|
||||
"CheckNonce",
|
||||
"CheckWeight",
|
||||
"ChargeAssetTxPayment",
|
||||
"SkipCheckIfFeeless",
|
||||
];
|
||||
|
||||
assert_eq!(extensions1.iter().count(), expected_signed_extensions.len());
|
||||
|
||||
Reference in New Issue
Block a user