mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-21 04:01:01 +00:00
Static Decoding of Signed Extensions: Simple Approach (#1235)
* skeleton commit * signed extension decoding * fix some minor things * make api more similar to Extrinsics * defer decoding of signed extensions * fix byte slices * add test for nonce signed extension * adjust test and extend for tip * clippy * support both ChargeTransactionPayment and ChargeAssetTxPayment * address PR comments * Extend lifetimes, expose pub structs, remove as_type * add signed extensions to block subscribing example * add Decoded type * fix merging bug and tests * add decoded type in CustomSignedExtension * fix minor issues, extend test * cargo fmt differences * remove the `decoded` function * new as_signed_extra fn, do not expose as_type anymore * fix Result-Option order, simplify obtaining Nonce * tx: Remove `wait_for_in_block` helper method (#1237) Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Update smoldot to 0.12 (#1212) * Update lightclient Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * testing: Fix typo Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * testing: Update cargo.toml Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * lightclient: Add tracing logs to improve debugging Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * lightclient: Add socket buffers module for `PlatformRef` Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * lightclient: Update `SubxtPlatform` Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * cargo: Add lightclient dependencies Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Update cargo.lock of wasm tests Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * lightclient: Add constant for with-buffer module Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * lightclient: Replace rand crate with getrandom Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * example: Update cargo lock file Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * examples: Update deps Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> --------- Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> Co-authored-by: Tadeo Hepperle <62739623+tadeohepperle@users.noreply.github.com> * ChargeAssetTxPayment: support providing u32 or MultiLocation in default impl (#1227) * Asset Id in Config trait * add example configuring the config * fmt * fix Default trait bound * merge examples, fix default again * adjust config in examples * Update subxt/src/config/mod.rs Co-authored-by: James Wilson <james@jsdw.me> --------- Co-authored-by: James Wilson <james@jsdw.me> * generic AssetId * fix generics * fmt --------- Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> Co-authored-by: James Wilson <james@jsdw.me> Co-authored-by: Alexandru Vasile <60601340+lexnv@users.noreply.github.com>
This commit is contained in:
@@ -40,7 +40,6 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
|
|
||||||
for evt in events.iter() {
|
for evt in events.iter() {
|
||||||
let evt = evt?;
|
let evt = evt?;
|
||||||
|
|
||||||
let pallet_name = evt.pallet_name();
|
let pallet_name = evt.pallet_name();
|
||||||
let event_name = evt.variant_name();
|
let event_name = evt.variant_name();
|
||||||
let event_values = evt.field_values()?;
|
let event_values = evt.field_values()?;
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ pub struct CustomSignedExtension;
|
|||||||
// up in the chain metadata in order to know when and if to use it.
|
// up in the chain metadata in order to know when and if to use it.
|
||||||
impl<T: Config> signed_extensions::SignedExtension<T> for CustomSignedExtension {
|
impl<T: Config> signed_extensions::SignedExtension<T> for CustomSignedExtension {
|
||||||
const NAME: &'static str = "CustomSignedExtension";
|
const NAME: &'static str = "CustomSignedExtension";
|
||||||
|
type Decoded = ();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gather together any params we need for our signed extension, here none.
|
// Gather together any params we need for our signed extension, here none.
|
||||||
|
|||||||
@@ -12,9 +12,13 @@ use crate::{
|
|||||||
Metadata,
|
Metadata,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::config::signed_extensions::{
|
||||||
|
ChargeAssetTxPayment, ChargeTransactionPayment, CheckNonce,
|
||||||
|
};
|
||||||
|
use crate::config::SignedExtension;
|
||||||
use crate::dynamic::DecodedValue;
|
use crate::dynamic::DecodedValue;
|
||||||
use crate::utils::strip_compact_prefix;
|
use crate::utils::strip_compact_prefix;
|
||||||
use codec::{Compact, Decode};
|
use codec::Decode;
|
||||||
use derivative::Derivative;
|
use derivative::Derivative;
|
||||||
use scale_decode::{DecodeAsFields, DecodeAsType};
|
use scale_decode::{DecodeAsFields, DecodeAsType};
|
||||||
|
|
||||||
@@ -366,12 +370,13 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `None` if the extrinsic is not signed.
|
/// Returns `None` if the extrinsic is not signed.
|
||||||
pub fn signed_extensions(&self) -> Option<ExtrinsicSignedExtensions<'_>> {
|
pub fn signed_extensions(&self) -> Option<ExtrinsicSignedExtensions<'_, T>> {
|
||||||
let signed = self.signed_details.as_ref()?;
|
let signed = self.signed_details.as_ref()?;
|
||||||
let extra_bytes = &self.bytes[signed.signature_end_idx..signed.extra_end_idx];
|
let extra_bytes = &self.bytes[signed.signature_end_idx..signed.extra_end_idx];
|
||||||
Some(ExtrinsicSignedExtensions {
|
Some(ExtrinsicSignedExtensions {
|
||||||
bytes: extra_bytes,
|
bytes: extra_bytes,
|
||||||
metadata: &self.metadata,
|
metadata: &self.metadata,
|
||||||
|
_marker: std::marker::PhantomData,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -605,21 +610,22 @@ impl<T: Config> ExtrinsicEvents<T> {
|
|||||||
|
|
||||||
/// The signed extensions of an extrinsic.
|
/// The signed extensions of an extrinsic.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ExtrinsicSignedExtensions<'a> {
|
pub struct ExtrinsicSignedExtensions<'a, T: Config> {
|
||||||
bytes: &'a [u8],
|
bytes: &'a [u8],
|
||||||
metadata: &'a Metadata,
|
metadata: &'a Metadata,
|
||||||
|
_marker: std::marker::PhantomData<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ExtrinsicSignedExtensions<'a> {
|
impl<'a, T: Config> ExtrinsicSignedExtensions<'a, T> {
|
||||||
/// Returns an iterator over each of the signed extension details of the extrinsic.
|
/// Returns an iterator over each of the signed extension details of the extrinsic.
|
||||||
/// If the decoding of any signed extension fails, an error item is yielded and the iterator stops.
|
/// If the decoding of any signed extension fails, an error item is yielded and the iterator stops.
|
||||||
pub fn iter(&self) -> impl Iterator<Item = Result<ExtrinsicSignedExtension<'a>, Error>> {
|
pub fn iter(&self) -> impl Iterator<Item = Result<ExtrinsicSignedExtension<T>, Error>> {
|
||||||
let signed_extension_types = self.metadata.extrinsic().signed_extensions();
|
let signed_extension_types = self.metadata.extrinsic().signed_extensions();
|
||||||
let num_signed_extensions = signed_extension_types.len();
|
let num_signed_extensions = signed_extension_types.len();
|
||||||
let bytes = self.bytes;
|
let bytes = self.bytes;
|
||||||
let metadata = self.metadata;
|
|
||||||
let mut index = 0;
|
let mut index = 0;
|
||||||
let mut byte_start_idx = 0;
|
let mut byte_start_idx = 0;
|
||||||
|
let metadata = &self.metadata;
|
||||||
|
|
||||||
std::iter::from_fn(move || {
|
std::iter::from_fn(move || {
|
||||||
if index == num_signed_extensions {
|
if index == num_signed_extensions {
|
||||||
@@ -649,49 +655,69 @@ impl<'a> ExtrinsicSignedExtensions<'a> {
|
|||||||
ty_id,
|
ty_id,
|
||||||
identifier: extension.identifier(),
|
identifier: extension.identifier(),
|
||||||
metadata,
|
metadata,
|
||||||
|
_marker: std::marker::PhantomData,
|
||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn find_by_name(&self, name: &str) -> Option<ExtrinsicSignedExtension<'_, T>> {
|
||||||
|
let signed_extension = self
|
||||||
|
.iter()
|
||||||
|
.find_map(|e| e.ok().filter(|e| e.name() == name))?;
|
||||||
|
Some(signed_extension)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Searches through all signed extensions to find a specific one.
|
||||||
|
/// If the Signed Extension is not found `Ok(None)` is returned.
|
||||||
|
/// If the Signed Extension is found but decoding failed `Err(_)` is returned.
|
||||||
|
pub fn find<S: SignedExtension<T>>(&self) -> Result<Option<S::Decoded>, Error> {
|
||||||
|
self.find_by_name(S::NAME)
|
||||||
|
.map(|s| {
|
||||||
|
s.as_signed_extra::<S>().map(|e| {
|
||||||
|
e.expect("signed extra name is correct, because it was found before; qed.")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.transpose()
|
||||||
|
}
|
||||||
|
|
||||||
/// The tip of an extrinsic, extracted from the ChargeTransactionPayment or ChargeAssetTxPayment
|
/// The tip of an extrinsic, extracted from the ChargeTransactionPayment or ChargeAssetTxPayment
|
||||||
/// signed extension, depending on which is present.
|
/// signed extension, depending on which is present.
|
||||||
///
|
///
|
||||||
/// Returns `None` if `tip` was not found or decoding failed.
|
/// Returns `None` if `tip` was not found or decoding failed.
|
||||||
pub fn tip(&self) -> Option<u128> {
|
pub fn tip(&self) -> Option<u128> {
|
||||||
let tip = self.iter().find_map(|e| {
|
// Note: the overhead of iterating twice should be negligible.
|
||||||
e.ok().filter(|e| {
|
self.find::<ChargeTransactionPayment>()
|
||||||
e.name() == "ChargeTransactionPayment" || e.name() == "ChargeAssetTxPayment"
|
.ok()
|
||||||
|
.flatten()
|
||||||
|
.map(|e| e.tip())
|
||||||
|
.or_else(|| {
|
||||||
|
self.find::<ChargeAssetTxPayment<T>>()
|
||||||
|
.ok()
|
||||||
|
.flatten()
|
||||||
|
.map(|e| e.tip())
|
||||||
})
|
})
|
||||||
})?;
|
|
||||||
|
|
||||||
// Note: ChargeAssetTxPayment might have addition information in it (asset_id).
|
|
||||||
// But both should start with a compact encoded u128, so this decoding is fine.
|
|
||||||
let tip = Compact::<u128>::decode(&mut tip.bytes()).ok()?.0;
|
|
||||||
Some(tip)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The nonce of the account that submitted the extrinsic, extracted from the CheckNonce signed extension.
|
/// The nonce of the account that submitted the extrinsic, extracted from the CheckNonce signed extension.
|
||||||
///
|
///
|
||||||
/// Returns `None` if `nonce` was not found or decoding failed.
|
/// Returns `None` if `nonce` was not found or decoding failed.
|
||||||
pub fn nonce(&self) -> Option<u64> {
|
pub fn nonce(&self) -> Option<u64> {
|
||||||
let nonce = self
|
let nonce = self.find::<CheckNonce>().ok()??.0;
|
||||||
.iter()
|
|
||||||
.find_map(|e| e.ok().filter(|e| e.name() == "CheckNonce"))?;
|
|
||||||
let nonce = Compact::<u64>::decode(&mut nonce.bytes()).ok()?.0;
|
|
||||||
Some(nonce)
|
Some(nonce)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A single signed extension
|
/// A single signed extension
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ExtrinsicSignedExtension<'a> {
|
pub struct ExtrinsicSignedExtension<'a, T: Config> {
|
||||||
bytes: &'a [u8],
|
bytes: &'a [u8],
|
||||||
ty_id: u32,
|
ty_id: u32,
|
||||||
identifier: &'a str,
|
identifier: &'a str,
|
||||||
metadata: &'a Metadata,
|
metadata: &'a Metadata,
|
||||||
|
_marker: std::marker::PhantomData<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ExtrinsicSignedExtension<'a> {
|
impl<'a, T: Config> ExtrinsicSignedExtension<'a, T> {
|
||||||
/// The bytes representing this signed extension.
|
/// The bytes representing this signed extension.
|
||||||
pub fn bytes(&self) -> &'a [u8] {
|
pub fn bytes(&self) -> &'a [u8] {
|
||||||
self.bytes
|
self.bytes
|
||||||
@@ -709,11 +735,23 @@ impl<'a> ExtrinsicSignedExtension<'a> {
|
|||||||
|
|
||||||
/// Signed Extension as a [`scale_value::Value`]
|
/// Signed Extension as a [`scale_value::Value`]
|
||||||
pub fn value(&self) -> Result<DecodedValue, Error> {
|
pub fn value(&self) -> Result<DecodedValue, Error> {
|
||||||
let value =
|
self.as_type()
|
||||||
DecodedValue::decode_as_type(&mut &self.bytes[..], self.ty_id, self.metadata.types())?;
|
}
|
||||||
|
|
||||||
|
/// Decodes the `extra` bytes of this Signed Extension into a static type.
|
||||||
|
fn as_type<E: DecodeAsType>(&self) -> Result<E, Error> {
|
||||||
|
let value = E::decode_as_type(&mut &self.bytes[..], self.ty_id, self.metadata.types())?;
|
||||||
Ok(value)
|
Ok(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Decodes the `extra` bytes of this Signed Extension into its associated `Decoded` type.
|
||||||
|
/// Returns `Ok(None)` if the identitfier of this Signed Extension object does not line up with the `NAME` constant of the provided Signed Extension type.
|
||||||
|
pub fn as_signed_extra<S: SignedExtension<T>>(&self) -> Result<Option<S::Decoded>, Error> {
|
||||||
|
if self.identifier != S::NAME {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
self.as_type::<S::Decoded>().map(Some)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ pub mod substrate;
|
|||||||
|
|
||||||
use codec::{Decode, Encode};
|
use codec::{Decode, Encode};
|
||||||
use core::fmt::Debug;
|
use core::fmt::Debug;
|
||||||
|
use scale_decode::DecodeAsType;
|
||||||
use serde::{de::DeserializeOwned, Serialize};
|
use serde::{de::DeserializeOwned, Serialize};
|
||||||
|
|
||||||
pub use default_extrinsic_params::{DefaultExtrinsicParams, DefaultExtrinsicParamsBuilder};
|
pub use default_extrinsic_params::{DefaultExtrinsicParams, DefaultExtrinsicParamsBuilder};
|
||||||
@@ -53,7 +54,7 @@ pub trait Config: Sized + Send + Sync + 'static {
|
|||||||
type ExtrinsicParams: ExtrinsicParams<Self>;
|
type ExtrinsicParams: ExtrinsicParams<Self>;
|
||||||
|
|
||||||
/// This is used to identify an asset in the `ChargeAssetTxPayment` signed extension.
|
/// This is used to identify an asset in the `ChargeAssetTxPayment` signed extension.
|
||||||
type AssetId: Debug + Encode;
|
type AssetId: Debug + Encode + DecodeAsType;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// given some [`Config`], this return the other params needed for its `ExtrinsicParams`.
|
/// given some [`Config`], this return the other params needed for its `ExtrinsicParams`.
|
||||||
|
|||||||
@@ -12,6 +12,9 @@ use crate::utils::Era;
|
|||||||
use crate::{client::OfflineClientT, Config};
|
use crate::{client::OfflineClientT, Config};
|
||||||
use codec::{Compact, Encode};
|
use codec::{Compact, Encode};
|
||||||
use core::fmt::Debug;
|
use core::fmt::Debug;
|
||||||
|
|
||||||
|
use scale_decode::DecodeAsType;
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
/// A single [`SignedExtension`] has a unique name, but is otherwise the
|
/// A single [`SignedExtension`] has a unique name, but is otherwise the
|
||||||
@@ -21,6 +24,11 @@ pub trait SignedExtension<T: Config>: ExtrinsicParams<T> {
|
|||||||
/// The name of the signed extension. This is used to associate it
|
/// The name of the signed extension. This is used to associate it
|
||||||
/// with the signed extensions that the node is making use of.
|
/// with the signed extensions that the node is making use of.
|
||||||
const NAME: &'static str;
|
const NAME: &'static str;
|
||||||
|
|
||||||
|
/// The type representing the `extra` bytes of a signed extension.
|
||||||
|
/// Decoding from this type should be symmetrical to the respective
|
||||||
|
/// `ExtrinsicParamsEncoder::encode_extra_to()` implementation of this signed extension.
|
||||||
|
type Decoded: DecodeAsType;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The [`CheckSpecVersion`] signed extension.
|
/// The [`CheckSpecVersion`] signed extension.
|
||||||
@@ -48,6 +56,7 @@ impl ExtrinsicParamsEncoder for CheckSpecVersion {
|
|||||||
|
|
||||||
impl<T: Config> SignedExtension<T> for CheckSpecVersion {
|
impl<T: Config> SignedExtension<T> for CheckSpecVersion {
|
||||||
const NAME: &'static str = "CheckSpecVersion";
|
const NAME: &'static str = "CheckSpecVersion";
|
||||||
|
type Decoded = ();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The [`CheckNonce`] signed extension.
|
/// The [`CheckNonce`] signed extension.
|
||||||
@@ -75,6 +84,7 @@ impl ExtrinsicParamsEncoder for CheckNonce {
|
|||||||
|
|
||||||
impl<T: Config> SignedExtension<T> for CheckNonce {
|
impl<T: Config> SignedExtension<T> for CheckNonce {
|
||||||
const NAME: &'static str = "CheckNonce";
|
const NAME: &'static str = "CheckNonce";
|
||||||
|
type Decoded = Compact<u64>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The [`CheckTxVersion`] signed extension.
|
/// The [`CheckTxVersion`] signed extension.
|
||||||
@@ -102,6 +112,7 @@ impl ExtrinsicParamsEncoder for CheckTxVersion {
|
|||||||
|
|
||||||
impl<T: Config> SignedExtension<T> for CheckTxVersion {
|
impl<T: Config> SignedExtension<T> for CheckTxVersion {
|
||||||
const NAME: &'static str = "CheckTxVersion";
|
const NAME: &'static str = "CheckTxVersion";
|
||||||
|
type Decoded = ();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The [`CheckGenesis`] signed extension.
|
/// The [`CheckGenesis`] signed extension.
|
||||||
@@ -134,6 +145,7 @@ impl<T: Config> ExtrinsicParamsEncoder for CheckGenesis<T> {
|
|||||||
|
|
||||||
impl<T: Config> SignedExtension<T> for CheckGenesis<T> {
|
impl<T: Config> SignedExtension<T> for CheckGenesis<T> {
|
||||||
const NAME: &'static str = "CheckGenesis";
|
const NAME: &'static str = "CheckGenesis";
|
||||||
|
type Decoded = ();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The [`CheckMortality`] signed extension.
|
/// The [`CheckMortality`] signed extension.
|
||||||
@@ -213,15 +225,29 @@ impl<T: Config> ExtrinsicParamsEncoder for CheckMortality<T> {
|
|||||||
|
|
||||||
impl<T: Config> SignedExtension<T> for CheckMortality<T> {
|
impl<T: Config> SignedExtension<T> for CheckMortality<T> {
|
||||||
const NAME: &'static str = "CheckMortality";
|
const NAME: &'static str = "CheckMortality";
|
||||||
|
type Decoded = Era;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The [`ChargeAssetTxPayment`] signed extension.
|
/// The [`ChargeAssetTxPayment`] signed extension.
|
||||||
#[derive(Debug)]
|
#[derive(Debug, DecodeAsType)]
|
||||||
|
#[decode_as_type(trait_bounds = "T::AssetId: DecodeAsType")]
|
||||||
pub struct ChargeAssetTxPayment<T: Config> {
|
pub struct ChargeAssetTxPayment<T: Config> {
|
||||||
tip: Compact<u128>,
|
tip: Compact<u128>,
|
||||||
asset_id: Option<T::AssetId>,
|
asset_id: Option<T::AssetId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: Config> ChargeAssetTxPayment<T> {
|
||||||
|
/// Tip to the extrinsic author in the native chain token.
|
||||||
|
pub fn tip(&self) -> u128 {
|
||||||
|
self.tip.0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tip to the extrinsic author using the asset ID given.
|
||||||
|
pub fn asset_id(&self) -> Option<&T::AssetId> {
|
||||||
|
self.asset_id.as_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Parameters to configure the [`ChargeAssetTxPayment`] signed extension.
|
/// Parameters to configure the [`ChargeAssetTxPayment`] signed extension.
|
||||||
pub struct ChargeAssetTxPaymentParams<T: Config> {
|
pub struct ChargeAssetTxPaymentParams<T: Config> {
|
||||||
tip: u128,
|
tip: u128,
|
||||||
@@ -285,14 +311,22 @@ impl<T: Config> ExtrinsicParamsEncoder for ChargeAssetTxPayment<T> {
|
|||||||
|
|
||||||
impl<T: Config> SignedExtension<T> for ChargeAssetTxPayment<T> {
|
impl<T: Config> SignedExtension<T> for ChargeAssetTxPayment<T> {
|
||||||
const NAME: &'static str = "ChargeAssetTxPayment";
|
const NAME: &'static str = "ChargeAssetTxPayment";
|
||||||
|
type Decoded = Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The [`ChargeTransactionPayment`] signed extension.
|
/// The [`ChargeTransactionPayment`] signed extension.
|
||||||
#[derive(Debug)]
|
#[derive(Debug, DecodeAsType)]
|
||||||
pub struct ChargeTransactionPayment {
|
pub struct ChargeTransactionPayment {
|
||||||
tip: Compact<u128>,
|
tip: Compact<u128>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ChargeTransactionPayment {
|
||||||
|
/// Tip to the extrinsic author in the native chain token.
|
||||||
|
pub fn tip(&self) -> u128 {
|
||||||
|
self.tip.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Parameters to configure the [`ChargeTransactionPayment`] signed extension.
|
/// Parameters to configure the [`ChargeTransactionPayment`] signed extension.
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct ChargeTransactionPaymentParams {
|
pub struct ChargeTransactionPaymentParams {
|
||||||
@@ -333,6 +367,7 @@ impl ExtrinsicParamsEncoder for ChargeTransactionPayment {
|
|||||||
|
|
||||||
impl<T: Config> SignedExtension<T> for ChargeTransactionPayment {
|
impl<T: Config> SignedExtension<T> for ChargeTransactionPayment {
|
||||||
const NAME: &'static str = "ChargeTransactionPayment";
|
const NAME: &'static str = "ChargeTransactionPayment";
|
||||||
|
type Decoded = Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This accepts a tuple of [`SignedExtension`]s, and will dynamically make use of whichever
|
/// This accepts a tuple of [`SignedExtension`]s, and will dynamically make use of whichever
|
||||||
|
|||||||
@@ -2,9 +2,13 @@
|
|||||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||||
// see LICENSE for license details.
|
// see LICENSE for license details.
|
||||||
|
|
||||||
|
use scale_decode::DecodeAsType;
|
||||||
|
|
||||||
// Dev note: This and related bits taken from `sp_runtime::generic::Era`
|
// Dev note: This and related bits taken from `sp_runtime::generic::Era`
|
||||||
/// An era to describe the longevity of a transaction.
|
/// An era to describe the longevity of a transaction.
|
||||||
#[derive(PartialEq, Default, Eq, Clone, Copy, Debug, serde::Serialize, serde::Deserialize)]
|
#[derive(
|
||||||
|
PartialEq, Default, Eq, Clone, Copy, Debug, serde::Serialize, serde::Deserialize, DecodeAsType,
|
||||||
|
)]
|
||||||
pub enum Era {
|
pub enum Era {
|
||||||
/// The transaction is valid forever. The genesis hash must be present in the signed content.
|
/// The transaction is valid forever. The genesis hash must be present in the signed content.
|
||||||
#[default]
|
#[default]
|
||||||
|
|||||||
@@ -5,8 +5,10 @@
|
|||||||
use crate::{test_context, utils::node_runtime};
|
use crate::{test_context, utils::node_runtime};
|
||||||
use codec::{Compact, Encode};
|
use codec::{Compact, Encode};
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
|
use subxt::config::signed_extensions::{ChargeAssetTxPayment, CheckMortality, CheckNonce};
|
||||||
use subxt::config::DefaultExtrinsicParamsBuilder;
|
use subxt::config::DefaultExtrinsicParamsBuilder;
|
||||||
|
use subxt::config::SubstrateConfig;
|
||||||
|
use subxt::utils::Era;
|
||||||
use subxt_metadata::Metadata;
|
use subxt_metadata::Metadata;
|
||||||
use subxt_signer::sr25519::dev;
|
use subxt_signer::sr25519::dev;
|
||||||
|
|
||||||
@@ -272,6 +274,37 @@ 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>>()
|
||||||
|
.unwrap()
|
||||||
|
.unwrap()
|
||||||
|
.tip();
|
||||||
|
|
||||||
|
let transaction2 = submit_transfer_extrinsic_and_get_it_back!(5678);
|
||||||
|
let extensions2 = transaction2.signed_extensions().unwrap();
|
||||||
|
let nonce2 = extensions2.nonce().unwrap();
|
||||||
|
let nonce2_static = extensions2.find::<CheckNonce>().unwrap().unwrap().0;
|
||||||
|
let tip2 = extensions2.tip().unwrap();
|
||||||
|
let tip2_static: u128 = extensions2
|
||||||
|
.find::<ChargeAssetTxPayment<SubstrateConfig>>()
|
||||||
|
.unwrap()
|
||||||
|
.unwrap()
|
||||||
|
.tip();
|
||||||
|
|
||||||
|
assert_eq!(nonce1, 0);
|
||||||
|
assert_eq!(nonce1, nonce1_static);
|
||||||
|
assert_eq!(tip1, 1234);
|
||||||
|
assert_eq!(tip1, tip1_static);
|
||||||
|
assert_eq!(nonce2, 1);
|
||||||
|
assert_eq!(nonce2, nonce2_static);
|
||||||
|
assert_eq!(tip2, 5678);
|
||||||
|
assert_eq!(tip2, tip2_static);
|
||||||
|
|
||||||
let expected_signed_extensions = [
|
let expected_signed_extensions = [
|
||||||
"CheckNonZeroSender",
|
"CheckNonZeroSender",
|
||||||
"CheckSpecVersion",
|
"CheckSpecVersion",
|
||||||
@@ -283,27 +316,22 @@ async fn decode_signed_extensions_from_blocks() {
|
|||||||
"ChargeAssetTxPayment",
|
"ChargeAssetTxPayment",
|
||||||
];
|
];
|
||||||
|
|
||||||
let transaction1 = submit_transfer_extrinsic_and_get_it_back!(1234);
|
|
||||||
let extensions1 = transaction1.signed_extensions().unwrap();
|
|
||||||
let nonce1 = extensions1.nonce().unwrap();
|
|
||||||
let tip1 = extensions1.tip().unwrap();
|
|
||||||
|
|
||||||
let transaction2 = submit_transfer_extrinsic_and_get_it_back!(5678);
|
|
||||||
let extensions2 = transaction2.signed_extensions().unwrap();
|
|
||||||
let nonce2 = extensions2.nonce().unwrap();
|
|
||||||
let tip2 = extensions2.tip().unwrap();
|
|
||||||
|
|
||||||
assert_eq!(nonce1, 0);
|
|
||||||
assert_eq!(tip1, 1234);
|
|
||||||
assert_eq!(nonce2, 1);
|
|
||||||
assert_eq!(tip2, 5678);
|
|
||||||
|
|
||||||
assert_eq!(extensions1.iter().count(), expected_signed_extensions.len());
|
assert_eq!(extensions1.iter().count(), expected_signed_extensions.len());
|
||||||
for (e, expected_name) in extensions1.iter().zip(expected_signed_extensions.iter()) {
|
for (e, expected_name) in extensions1.iter().zip(expected_signed_extensions.iter()) {
|
||||||
assert_eq!(e.unwrap().name(), *expected_name);
|
assert_eq!(e.unwrap().name(), *expected_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_eq!(extensions2.iter().count(), expected_signed_extensions.len());
|
assert_eq!(extensions2.iter().count(), expected_signed_extensions.len());
|
||||||
for (e, expected_name) in extensions2.iter().zip(expected_signed_extensions.iter()) {
|
for (e, expected_name) in extensions2.iter().zip(expected_signed_extensions.iter()) {
|
||||||
assert_eq!(e.unwrap().name(), *expected_name);
|
assert_eq!(e.unwrap().name(), *expected_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check that era decodes:
|
||||||
|
for extensions in [&extensions1, &extensions2] {
|
||||||
|
let era: Era = extensions
|
||||||
|
.find::<CheckMortality<SubstrateConfig>>()
|
||||||
|
.unwrap()
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(era, Era::Immortal)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user