mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-31 11:01:01 +00:00
static decoding of signed extensions
This commit is contained in:
@@ -19,6 +19,8 @@ use codec::{Compact, Decode};
|
||||
use derivative::Derivative;
|
||||
use scale_decode::{DecodeAsFields, DecodeAsType};
|
||||
|
||||
use crate::config::signed_extensions::{ChargeAssetTxPayment, ChargeTransactionPayment};
|
||||
use crate::config::SignedExtension;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// 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.
|
||||
pub fn signed_extensions(&self) -> Option<ExtrinsicSignedExtensions> {
|
||||
pub fn signed_extensions(&self) -> Option<ExtrinsicSignedExtensions<'_, T>> {
|
||||
let signed = self.signed_details.as_ref()?;
|
||||
let extra_bytes = &self.bytes[signed.signature_end_idx..signed.extra_end_idx];
|
||||
Some(ExtrinsicSignedExtensions {
|
||||
bytes: extra_bytes,
|
||||
metadata: self.metadata.clone(),
|
||||
_marker: std::marker::PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -610,24 +613,26 @@ impl<T: Config> ExtrinsicEvents<T> {
|
||||
|
||||
/// The signed extensions of an extrinsic.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ExtrinsicSignedExtensions<'a> {
|
||||
pub struct ExtrinsicSignedExtensions<'a, T: Config> {
|
||||
bytes: &'a [u8],
|
||||
metadata: Metadata,
|
||||
_marker: std::marker::PhantomData<T>,
|
||||
}
|
||||
|
||||
/// A single signed extension
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ExtrinsicSignedExtension<'a> {
|
||||
pub struct ExtrinsicSignedExtension<'a, T: Config> {
|
||||
bytes: &'a [u8],
|
||||
ty_id: u32,
|
||||
identifier: &'a str,
|
||||
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.
|
||||
/// 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 num_signed_extensions = signed_extension_types.len();
|
||||
let mut index = 0;
|
||||
@@ -661,17 +666,29 @@ impl<'a> ExtrinsicSignedExtensions<'a> {
|
||||
ty_id,
|
||||
identifier: extension.identifier(),
|
||||
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.
|
||||
pub fn tip(&self) -> Option<u128> {
|
||||
let tip = self.iter().find_map(|e| {
|
||||
e.ok().filter(|e| {
|
||||
e.name() == "ChargeTransactionPayment" || e.name() == "ChargeAssetTxPayment"
|
||||
})
|
||||
})?;
|
||||
// Note: the overhead of iterating twice should be negligible.
|
||||
let tip = self
|
||||
.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).
|
||||
// But both should start with a compact encoded u128, so this decoding is fine.
|
||||
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.
|
||||
pub fn bytes(&self) -> &[u8] {
|
||||
self.bytes
|
||||
@@ -719,8 +736,8 @@ impl<'a> ExtrinsicSignedExtension<'a> {
|
||||
self.decoded()?.to_value()
|
||||
}
|
||||
|
||||
pub fn as_type<T: DecodeAsType>(&self) -> Result<T, Error> {
|
||||
self.decoded()?.as_type::<T>().map_err(Into::into)
|
||||
pub fn as_type<E: DecodeAsType>(&self) -> Result<E, Error> {
|
||||
self.decoded()?.as_type::<E>().map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,19 +10,45 @@
|
||||
use super::extrinsic_params::{ExtrinsicParams, ExtrinsicParamsEncoder, ExtrinsicParamsError};
|
||||
use crate::utils::Era;
|
||||
use crate::{client::OfflineClientT, Config};
|
||||
use codec::{Compact, Encode};
|
||||
use codec::{Compact, Decode, Encode};
|
||||
use core::fmt::Debug;
|
||||
use scale_decode::DecodeAsType;
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// A single [`SignedExtension`] has a unique name, but is otherwise the
|
||||
/// same as [`ExtrinsicParams`] in describing how to encode the extra and
|
||||
/// 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
|
||||
/// with the signed extensions that the node is making use of.
|
||||
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.
|
||||
#[derive(Debug)]
|
||||
pub struct CheckSpecVersion(u32);
|
||||
@@ -40,9 +66,16 @@ impl<T: Config> ExtrinsicParams<T> for CheckSpecVersion {
|
||||
}
|
||||
}
|
||||
|
||||
impl ExtrinsicParamsEncoder for CheckSpecVersion {
|
||||
fn encode_additional_to(&self, v: &mut Vec<u8>) {
|
||||
self.0.encode_to(v);
|
||||
impl SignedExtensionDecoder for CheckSpecVersion {
|
||||
type Extra = ();
|
||||
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 {
|
||||
fn encode_extra_to(&self, v: &mut Vec<u8>) {
|
||||
self.0.encode_to(v);
|
||||
impl SignedExtensionDecoder for CheckNonce {
|
||||
type Extra = Compact<u64>;
|
||||
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 {
|
||||
fn encode_additional_to(&self, v: &mut Vec<u8>) {
|
||||
self.0.encode_to(v);
|
||||
impl SignedExtensionDecoder for CheckTxVersion {
|
||||
type Extra = ();
|
||||
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> {
|
||||
fn encode_additional_to(&self, v: &mut Vec<u8>) {
|
||||
self.0.encode_to(v);
|
||||
impl<T: Config> SignedExtensionDecoder for CheckGenesis<T> {
|
||||
type Extra = ();
|
||||
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> {
|
||||
fn encode_extra_to(&self, v: &mut Vec<u8>) {
|
||||
self.era.encode_to(v);
|
||||
impl<T: Config> SignedExtensionDecoder for CheckMortality<T> {
|
||||
type Extra = Era;
|
||||
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.
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Encode, Decode, DecodeAsType)]
|
||||
pub struct ChargeAssetTxPayment {
|
||||
tip: Compact<u128>,
|
||||
asset_id: Option<u32>,
|
||||
@@ -269,9 +327,16 @@ impl<T: Config> ExtrinsicParams<T> for ChargeAssetTxPayment {
|
||||
}
|
||||
}
|
||||
|
||||
impl ExtrinsicParamsEncoder for ChargeAssetTxPayment {
|
||||
fn encode_extra_to(&self, v: &mut Vec<u8>) {
|
||||
(self.tip, self.asset_id).encode_to(v);
|
||||
impl SignedExtensionDecoder for ChargeAssetTxPayment {
|
||||
type Extra = Self;
|
||||
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.
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Encode, Decode, DecodeAsType)]
|
||||
pub struct ChargeTransactionPayment {
|
||||
tip: Compact<u128>,
|
||||
}
|
||||
@@ -317,9 +382,16 @@ impl<T: Config> ExtrinsicParams<T> for ChargeTransactionPayment {
|
||||
}
|
||||
}
|
||||
|
||||
impl ExtrinsicParamsEncoder for ChargeTransactionPayment {
|
||||
fn encode_extra_to(&self, v: &mut Vec<u8>) {
|
||||
self.tip.encode_to(v);
|
||||
impl SignedExtensionDecoder for ChargeTransactionPayment {
|
||||
type Extra = Self;
|
||||
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`
|
||||
/// 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 {
|
||||
/// The transaction is valid forever. The genesis hash must be present in the signed content.
|
||||
#[default]
|
||||
|
||||
@@ -6,6 +6,7 @@ use crate::{test_context, utils::node_runtime};
|
||||
use codec::{Compact, Encode};
|
||||
use futures::StreamExt;
|
||||
|
||||
use subxt::config::signed_extensions::{ChargeAssetTxPayment, CheckNonce};
|
||||
use subxt::config::DefaultExtrinsicParamsBuilder;
|
||||
use subxt_metadata::Metadata;
|
||||
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 extensions1 = transaction1.signed_extensions().unwrap();
|
||||
let nonce1 = extensions1.nonce().unwrap();
|
||||
let nonce1_static = extensions1.find::<CheckNonce>().0;
|
||||
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 extensions2 = transaction2.signed_extensions().unwrap();
|
||||
let nonce2 = extensions2.nonce().unwrap();
|
||||
let nonce2_static: u64 = extensions2.find::<CheckNonce>().0;
|
||||
let tip2 = extensions2.tip().unwrap();
|
||||
let tip2_static: u128 = extensions2.find::<ChargeAssetTxPayment>().tip.0;
|
||||
|
||||
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);
|
||||
|
||||
assert_eq!(extensions1.iter().count(), expected_signed_extensions.len());
|
||||
for (e, expected_name) in extensions1.iter().zip(expected_signed_extensions.iter()) {
|
||||
|
||||
Reference in New Issue
Block a user