mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-31 07:31:02 +00:00
static decoding of signed extensions
This commit is contained in:
@@ -19,6 +19,8 @@ use codec::{Compact, Decode};
|
|||||||
use derivative::Derivative;
|
use derivative::Derivative;
|
||||||
use scale_decode::{DecodeAsFields, DecodeAsType};
|
use scale_decode::{DecodeAsFields, DecodeAsType};
|
||||||
|
|
||||||
|
use crate::config::signed_extensions::{ChargeAssetTxPayment, ChargeTransactionPayment};
|
||||||
|
use crate::config::SignedExtension;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
/// Trait to uniquely identify the extrinsic's identity from the runtime metadata.
|
/// Trait to uniquely identify the extrinsic's identity from the runtime metadata.
|
||||||
@@ -371,12 +373,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.clone(),
|
metadata: self.metadata.clone(),
|
||||||
|
_marker: std::marker::PhantomData,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -610,24 +613,26 @@ 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: Metadata,
|
metadata: Metadata,
|
||||||
|
_marker: std::marker::PhantomData<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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: Metadata,
|
metadata: Metadata,
|
||||||
|
_marker: std::marker::PhantomData<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ExtrinsicSignedExtensions<'a> {
|
impl<'a, T: Config> ExtrinsicSignedExtensions<'a, T> {
|
||||||
/// Returns an iterator ove all signed extensions, allowing access to their names, bytes and types.
|
/// Returns an iterator ove all signed extensions, allowing access to their names, bytes and types.
|
||||||
/// 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(&'a self) -> impl Iterator<Item = Result<ExtrinsicSignedExtension<'a>, Error>> {
|
pub fn iter(&'a self) -> impl Iterator<Item = Result<ExtrinsicSignedExtension<'a, 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 mut index = 0;
|
let mut index = 0;
|
||||||
@@ -661,17 +666,29 @@ impl<'a> ExtrinsicSignedExtensions<'a> {
|
|||||||
ty_id,
|
ty_id,
|
||||||
identifier: extension.identifier(),
|
identifier: extension.identifier(),
|
||||||
metadata: self.metadata.clone(),
|
metadata: self.metadata.clone(),
|
||||||
|
_marker: std::marker::PhantomData,
|
||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn find_by_name(&self, name: impl AsRef<str>) -> Option<ExtrinsicSignedExtension<'_, T>> {
|
||||||
|
let signed_extension = self
|
||||||
|
.iter()
|
||||||
|
.find_map(|e| e.ok().filter(|e| e.name() == name.as_ref()))?;
|
||||||
|
Some(signed_extension)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn find<S: SignedExtension<T>>(&self) -> Option<Result<S::Extra, Error>> {
|
||||||
|
let signed_extension = self.find_by_name(S::NAME)?;
|
||||||
|
Some(signed_extension.as_type().map_err(Into::into))
|
||||||
|
}
|
||||||
|
|
||||||
/// The tip of an extrinsic, extracted from the ChargeTransactionPayment or ChargeAssetTxPayment signed extension, depending on which is present.
|
/// The tip of an extrinsic, extracted from the ChargeTransactionPayment or ChargeAssetTxPayment signed extension, depending on which is present.
|
||||||
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| {
|
let tip = self
|
||||||
e.name() == "ChargeTransactionPayment" || e.name() == "ChargeAssetTxPayment"
|
.find_by_name(<ChargeTransactionPayment as SignedExtension<T>>::NAME)
|
||||||
})
|
.or_else(|| self.find_by_name(<ChargeAssetTxPayment as SignedExtension<T>>::NAME))?;
|
||||||
})?;
|
|
||||||
// Note: ChargeAssetTxPayment might have addition information in it (asset_id).
|
// Note: ChargeAssetTxPayment might have addition information in it (asset_id).
|
||||||
// But both should start with a compact encoded u128, so this decoding is fine.
|
// But both should start with a compact encoded u128, so this decoding is fine.
|
||||||
let tip = Compact::<u128>::decode(&mut tip.bytes()).ok()?.0;
|
let tip = Compact::<u128>::decode(&mut tip.bytes()).ok()?.0;
|
||||||
@@ -688,7 +705,7 @@ impl<'a> ExtrinsicSignedExtensions<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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) -> &[u8] {
|
pub fn bytes(&self) -> &[u8] {
|
||||||
self.bytes
|
self.bytes
|
||||||
@@ -719,8 +736,8 @@ impl<'a> ExtrinsicSignedExtension<'a> {
|
|||||||
self.decoded()?.to_value()
|
self.decoded()?.to_value()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_type<T: DecodeAsType>(&self) -> Result<T, Error> {
|
pub fn as_type<E: DecodeAsType>(&self) -> Result<E, Error> {
|
||||||
self.decoded()?.as_type::<T>().map_err(Into::into)
|
self.decoded()?.as_type::<E>().map_err(Into::into)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,19 +10,45 @@
|
|||||||
use super::extrinsic_params::{ExtrinsicParams, ExtrinsicParamsEncoder, ExtrinsicParamsError};
|
use super::extrinsic_params::{ExtrinsicParams, ExtrinsicParamsEncoder, ExtrinsicParamsError};
|
||||||
use crate::utils::Era;
|
use crate::utils::Era;
|
||||||
use crate::{client::OfflineClientT, Config};
|
use crate::{client::OfflineClientT, Config};
|
||||||
use codec::{Compact, Encode};
|
use codec::{Compact, Decode, 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
|
||||||
/// same as [`ExtrinsicParams`] in describing how to encode the extra and
|
/// same as [`ExtrinsicParams`] in describing how to encode the extra and
|
||||||
/// additional data.
|
/// additional data.
|
||||||
pub trait SignedExtension<T: Config>: ExtrinsicParams<T> {
|
pub trait SignedExtension<T: Config>: ExtrinsicParams<T> + SignedExtensionDecoder {
|
||||||
/// 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Specifies the Extra and Additional data types of the signed extension.
|
||||||
|
/// `ExtrinsicParamsEncoder` is implemented for all `SignedExtensionDecoder`s.
|
||||||
|
pub trait SignedExtensionDecoder {
|
||||||
|
/// Signed Extra parameter of a Signed Extension.
|
||||||
|
/// Included in a signed extension behind the signature.
|
||||||
|
type Extra: Encode + DecodeAsType;
|
||||||
|
/// Additional Signed parameter of a Signed Extension.
|
||||||
|
/// Used as part of the payload for signing an extrinsic.
|
||||||
|
type Additional: Encode;
|
||||||
|
/// Retrieves the signed extra parameter.
|
||||||
|
fn extra(&self) -> &Self::Extra;
|
||||||
|
/// Retrieves the additional signed parameter.
|
||||||
|
fn additional(&self) -> &Self::Additional;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S: SignedExtensionDecoder + 'static> ExtrinsicParamsEncoder for S {
|
||||||
|
fn encode_extra_to(&self, v: &mut Vec<u8>) {
|
||||||
|
self.extra().encode_to(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn encode_additional_to(&self, v: &mut Vec<u8>) {
|
||||||
|
self.additional().encode_to(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The [`CheckSpecVersion`] signed extension.
|
/// The [`CheckSpecVersion`] signed extension.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct CheckSpecVersion(u32);
|
pub struct CheckSpecVersion(u32);
|
||||||
@@ -40,9 +66,16 @@ impl<T: Config> ExtrinsicParams<T> for CheckSpecVersion {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExtrinsicParamsEncoder for CheckSpecVersion {
|
impl SignedExtensionDecoder for CheckSpecVersion {
|
||||||
fn encode_additional_to(&self, v: &mut Vec<u8>) {
|
type Extra = ();
|
||||||
self.0.encode_to(v);
|
type Additional = u32;
|
||||||
|
|
||||||
|
fn extra(&self) -> &Self::Extra {
|
||||||
|
&()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn additional(&self) -> &Self::Additional {
|
||||||
|
&self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,9 +100,16 @@ impl<T: Config> ExtrinsicParams<T> for CheckNonce {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExtrinsicParamsEncoder for CheckNonce {
|
impl SignedExtensionDecoder for CheckNonce {
|
||||||
fn encode_extra_to(&self, v: &mut Vec<u8>) {
|
type Extra = Compact<u64>;
|
||||||
self.0.encode_to(v);
|
type Additional = ();
|
||||||
|
|
||||||
|
fn extra(&self) -> &Self::Extra {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn additional(&self) -> &Self::Additional {
|
||||||
|
&()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,9 +134,16 @@ impl<T: Config> ExtrinsicParams<T> for CheckTxVersion {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExtrinsicParamsEncoder for CheckTxVersion {
|
impl SignedExtensionDecoder for CheckTxVersion {
|
||||||
fn encode_additional_to(&self, v: &mut Vec<u8>) {
|
type Extra = ();
|
||||||
self.0.encode_to(v);
|
type Additional = u32;
|
||||||
|
|
||||||
|
fn extra(&self) -> &Self::Extra {
|
||||||
|
&()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn additional(&self) -> &Self::Additional {
|
||||||
|
&self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,9 +173,16 @@ impl<T: Config> ExtrinsicParams<T> for CheckGenesis<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Config> ExtrinsicParamsEncoder for CheckGenesis<T> {
|
impl<T: Config> SignedExtensionDecoder for CheckGenesis<T> {
|
||||||
fn encode_additional_to(&self, v: &mut Vec<u8>) {
|
type Extra = ();
|
||||||
self.0.encode_to(v);
|
type Additional = T::Hash;
|
||||||
|
|
||||||
|
fn extra(&self) -> &Self::Extra {
|
||||||
|
&()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn additional(&self) -> &Self::Additional {
|
||||||
|
&self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -202,12 +256,16 @@ impl<T: Config> ExtrinsicParams<T> for CheckMortality<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Config> ExtrinsicParamsEncoder for CheckMortality<T> {
|
impl<T: Config> SignedExtensionDecoder for CheckMortality<T> {
|
||||||
fn encode_extra_to(&self, v: &mut Vec<u8>) {
|
type Extra = Era;
|
||||||
self.era.encode_to(v);
|
type Additional = T::Hash;
|
||||||
|
|
||||||
|
fn extra(&self) -> &Self::Extra {
|
||||||
|
&self.era
|
||||||
}
|
}
|
||||||
fn encode_additional_to(&self, v: &mut Vec<u8>) {
|
|
||||||
self.checkpoint.encode_to(v)
|
fn additional(&self) -> &Self::Additional {
|
||||||
|
&self.checkpoint
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,7 +274,7 @@ impl<T: Config> SignedExtension<T> for CheckMortality<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The [`ChargeAssetTxPayment`] signed extension.
|
/// The [`ChargeAssetTxPayment`] signed extension.
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Encode, Decode, DecodeAsType)]
|
||||||
pub struct ChargeAssetTxPayment {
|
pub struct ChargeAssetTxPayment {
|
||||||
tip: Compact<u128>,
|
tip: Compact<u128>,
|
||||||
asset_id: Option<u32>,
|
asset_id: Option<u32>,
|
||||||
@@ -269,9 +327,16 @@ impl<T: Config> ExtrinsicParams<T> for ChargeAssetTxPayment {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExtrinsicParamsEncoder for ChargeAssetTxPayment {
|
impl SignedExtensionDecoder for ChargeAssetTxPayment {
|
||||||
fn encode_extra_to(&self, v: &mut Vec<u8>) {
|
type Extra = Self;
|
||||||
(self.tip, self.asset_id).encode_to(v);
|
type Additional = ();
|
||||||
|
|
||||||
|
fn extra(&self) -> &Self::Extra {
|
||||||
|
&self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn additional(&self) -> &Self::Additional {
|
||||||
|
&()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -280,7 +345,7 @@ impl<T: Config> SignedExtension<T> for ChargeAssetTxPayment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The [`ChargeTransactionPayment`] signed extension.
|
/// The [`ChargeTransactionPayment`] signed extension.
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Encode, Decode, DecodeAsType)]
|
||||||
pub struct ChargeTransactionPayment {
|
pub struct ChargeTransactionPayment {
|
||||||
tip: Compact<u128>,
|
tip: Compact<u128>,
|
||||||
}
|
}
|
||||||
@@ -317,9 +382,16 @@ impl<T: Config> ExtrinsicParams<T> for ChargeTransactionPayment {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExtrinsicParamsEncoder for ChargeTransactionPayment {
|
impl SignedExtensionDecoder for ChargeTransactionPayment {
|
||||||
fn encode_extra_to(&self, v: &mut Vec<u8>) {
|
type Extra = Self;
|
||||||
self.tip.encode_to(v);
|
type Additional = ();
|
||||||
|
|
||||||
|
fn extra(&self) -> &Self::Extra {
|
||||||
|
&self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn additional(&self) -> &Self::Additional {
|
||||||
|
&()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+11
-1
@@ -4,7 +4,17 @@
|
|||||||
|
|
||||||
// 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,
|
||||||
|
scale_decode::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]
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ 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, CheckNonce};
|
||||||
use subxt::config::DefaultExtrinsicParamsBuilder;
|
use subxt::config::DefaultExtrinsicParamsBuilder;
|
||||||
use subxt_metadata::Metadata;
|
use subxt_metadata::Metadata;
|
||||||
use subxt_signer::sr25519::dev;
|
use subxt_signer::sr25519::dev;
|
||||||
@@ -286,17 +287,25 @@ async fn decode_signed_extensions_from_blocks() {
|
|||||||
let transaction1 = submit_transfer_extrinsic_and_get_it_back!(1234);
|
let transaction1 = submit_transfer_extrinsic_and_get_it_back!(1234);
|
||||||
let extensions1 = transaction1.signed_extensions().unwrap();
|
let extensions1 = transaction1.signed_extensions().unwrap();
|
||||||
let nonce1 = extensions1.nonce().unwrap();
|
let nonce1 = extensions1.nonce().unwrap();
|
||||||
|
let nonce1_static = extensions1.find::<CheckNonce>().0;
|
||||||
let tip1 = extensions1.tip().unwrap();
|
let tip1 = extensions1.tip().unwrap();
|
||||||
|
let tip1_static: u128 = extensions1.find::<ChargeAssetTxPayment>().tip.0;
|
||||||
|
|
||||||
let transaction2 = submit_transfer_extrinsic_and_get_it_back!(5678);
|
let transaction2 = submit_transfer_extrinsic_and_get_it_back!(5678);
|
||||||
let extensions2 = transaction2.signed_extensions().unwrap();
|
let extensions2 = transaction2.signed_extensions().unwrap();
|
||||||
let nonce2 = extensions2.nonce().unwrap();
|
let nonce2 = extensions2.nonce().unwrap();
|
||||||
|
let nonce2_static: u64 = extensions2.find::<CheckNonce>().0;
|
||||||
let tip2 = extensions2.tip().unwrap();
|
let tip2 = extensions2.tip().unwrap();
|
||||||
|
let tip2_static: u128 = extensions2.find::<ChargeAssetTxPayment>().tip.0;
|
||||||
|
|
||||||
assert_eq!(nonce1, 0);
|
assert_eq!(nonce1, 0);
|
||||||
|
assert_eq!(nonce1, nonce1_static);
|
||||||
assert_eq!(tip1, 1234);
|
assert_eq!(tip1, 1234);
|
||||||
|
assert_eq!(tip1, tip1_static);
|
||||||
assert_eq!(nonce2, 1);
|
assert_eq!(nonce2, 1);
|
||||||
|
assert_eq!(nonce2, nonce2_static);
|
||||||
assert_eq!(tip2, 5678);
|
assert_eq!(tip2, 5678);
|
||||||
|
assert_eq!(tip2, tip2_static);
|
||||||
|
|
||||||
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()) {
|
||||||
|
|||||||
Reference in New Issue
Block a user