static decoding of signed extensions

This commit is contained in:
Tadeo hepperle
2023-10-26 18:48:47 +02:00
parent 1dd8f53656
commit 245aca78ee
4 changed files with 149 additions and 41 deletions
+30 -13
View File
@@ -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)
}
}
+99 -27
View File
@@ -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
View File
@@ -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]