From ffec0b1400f442d285b3907cb3431b2a7bef2155 Mon Sep 17 00:00:00 2001 From: Tadeo hepperle Date: Thu, 12 Oct 2023 15:32:37 +0200 Subject: [PATCH] signed extension decoding --- subxt/src/blocks/extrinsic_types.rs | 141 +++++++++++++++++++++++----- subxt/src/config/polkadot.rs | 1 + subxt/src/config/substrate.rs | 1 + 3 files changed, 119 insertions(+), 24 deletions(-) diff --git a/subxt/src/blocks/extrinsic_types.rs b/subxt/src/blocks/extrinsic_types.rs index 1e3bc2702f..62496f6222 100644 --- a/subxt/src/blocks/extrinsic_types.rs +++ b/subxt/src/blocks/extrinsic_types.rs @@ -14,12 +14,14 @@ use crate::{ use std::marker::PhantomData; use std::ops::Range; -use crate::dynamic::DecodedValueThunk; +use crate::dynamic::{DecodedValue, DecodedValueThunk}; use crate::metadata::DecodeWithMetadata; use crate::utils::strip_compact_prefix; -use codec::Decode; +use codec::{Compact, Decode}; use derivative::Derivative; use scale_decode::{DecodeAsFields, DecodeAsType}; +use scale_info::form::PortableForm; +use scale_info::{PortableRegistry, TypeDef}; use std::sync::Arc; /// Trait to uniquely identify the extrinsic's identity from the runtime metadata. @@ -374,16 +376,71 @@ where } /// Returns a reference to the signed extensions. - pub fn signed_extensions(&self) -> Option<()> { - let extra_bytes = self.extra_bytes()?; - let cursor: &mut &[u8] = &mut &extra_bytes[..]; - let signed = self.signed.as_ref().unwrap(); - let d = DecodedValueThunk::decode_with_metadata(cursor, signed.ids.extra, &self.metadata) - .expect("decoding err"); + /// + /// Returns `None` if the extrinsic is not signed. + /// Returns `Some(Err(..))` if the extrinsic is singed but something went wrong decoding the signed extensions. + pub fn signed_extensions(&self) -> Option, Error>> { + fn signed_extensions_or_err<'a, T: Config>( + extra_bytes: &'a [u8], + extra_ty_id: u32, + metadata: &'a Metadata, + ) -> Result, Error> { + let extra_type = metadata + .types() + .resolve(extra_ty_id) + .ok_or(MetadataError::TypeNotFound(extra_ty_id))?; + // the type behind this is expected to be a tuple that holds all the individual signed extensions + let TypeDef::Tuple(extra_tuple_type_def) = &extra_type.type_def else { + return Err(Error::Other( + "singed extra type def should be a tuple".into(), + )); + }; - let val = d.to_value().expect("ok"); - println!("{}", val.to_string()); - Some(()) + let mut signed_extensions: Vec> = vec![]; + + let mut cursor: &mut &[u8] = &mut &extra_bytes[..]; + let mut start_idx: usize = 0; + for field in extra_tuple_type_def.fields.iter() { + let ty_id = field.id; + let ty = metadata + .types() + .resolve(ty_id) + .ok_or(MetadataError::TypeNotFound(ty_id))?; + let name = ty.path.segments.last().ok_or_else(|| Error::Other("signed extension path segments should contain the signed extension name as the last element".into()))?; + scale_decode::visitor::decode_with_visitor( + cursor, + ty_id, + metadata.types(), + scale_decode::visitor::IgnoreVisitor, + ) + .map_err(|e| Error::Decode(e.into()))?; + let end_idx = extra_bytes.len() - cursor.len(); + let bytes = &extra_bytes[start_idx..end_idx]; + start_idx = end_idx; + signed_extensions.push(ExtrinsicSignedExtension { + bytes, + ty_id, + name, + metadata, + phantom: PhantomData, + }); + } + + Ok(ExtrinsicSignedExtensions { + bytes: extra_bytes, + signed_extensions, + metadata, + phantom: PhantomData, + }) + } + + let signed = self.signed.as_ref()?; + let extra_bytes = &self.bytes[signed.signature_end_idx..signed.extra_end_idx]; + Some(signed_extensions_or_err( + extra_bytes, + signed.ids.extra, + &self.metadata, + )) } /// The index of the pallet that the extrinsic originated from. @@ -614,44 +671,80 @@ impl ExtrinsicEvents { } } +#[derive(Debug, Clone)] pub struct ExtrinsicSignedExtensions<'a, T: Config> { + signed_extensions: Vec>, bytes: &'a [u8], + metadata: &'a Metadata, phantom: PhantomData, } -pub struct ExtrinsicSignedExtension { +#[derive(Debug, Clone)] +pub struct ExtrinsicSignedExtension<'a, T: Config> { + bytes: &'a [u8], + ty_id: u32, + name: &'a str, + metadata: &'a Metadata, phantom: PhantomData, } impl<'a, T: Config> ExtrinsicSignedExtensions<'a, T> { + /// Returns the slice of bytes pub fn bytes(&self) -> &[u8] { - &self.bytes + self.bytes } - pub fn find(&self, signed_extension: impl AsRef) -> ExtrinsicSignedExtension { - todo!() + /// Returns a slice of all signed extensions + pub fn signed_extensions(&self) -> &[ExtrinsicSignedExtension] { + &self.signed_extensions } - pub fn tip() -> Option { - todo!() + /// Get a certain signed extension by its name. + pub fn find(&self, signed_extension: impl AsRef) -> Option<&ExtrinsicSignedExtension> { + self.signed_extensions + .iter() + .find(|e| e.name == signed_extension.as_ref()) } - pub fn nonce() -> Option { - todo!() + /// The tip of an extrinsic, extracted from the ChargeTransactionPayment signed extension. + pub fn tip(&self) -> Option { + let tip = self.find("ChargeTransactionPayment")?; + let tip = Compact::::decode(&mut tip.bytes()).ok()?.0; + Some(tip) + } + + /// The nonce of the account that submitted the extrinsic, extracted from the CheckNonce signed extension. + pub fn nonce(&self) -> Option { + let nonce = self.find("CheckNonce")?; + let nonce = Compact::::decode(&mut nonce.bytes()).ok()?.0; + Some(nonce) } } -impl ExtrinsicSignedExtension { +impl<'a, T: Config> ExtrinsicSignedExtension<'a, T> { pub fn bytes(&self) -> &[u8] { - todo!() + self.bytes + } + + pub fn name(&self) -> &str { + self.name } pub fn type_id(&self) -> u32 { - todo!() + self.ty_id } - pub fn value(&self) -> scale_value::Value { - todo!() + pub fn decoded(&self) -> Result { + let decoded_value_thunk = DecodedValueThunk::decode_with_metadata( + &mut &self.bytes[..], + self.ty_id, + self.metadata, + )?; + Ok(decoded_value_thunk) + } + pub fn value(&self) -> Result { + let value = self.decoded()?.to_value()?; + Ok(value) } } diff --git a/subxt/src/config/polkadot.rs b/subxt/src/config/polkadot.rs index 5fb3390483..f47ae176f2 100644 --- a/subxt/src/config/polkadot.rs +++ b/subxt/src/config/polkadot.rs @@ -11,6 +11,7 @@ use crate::SubstrateConfig; pub use primitive_types::{H256, U256}; /// Default set of commonly used types by Polkadot nodes. +#[derive(Debug)] pub enum PolkadotConfig {} impl Config for PolkadotConfig { diff --git a/subxt/src/config/substrate.rs b/subxt/src/config/substrate.rs index 609b20a778..ea1d4a0a95 100644 --- a/subxt/src/config/substrate.rs +++ b/subxt/src/config/substrate.rs @@ -14,6 +14,7 @@ pub use primitive_types::{H256, U256}; /// Default set of commonly used types by Substrate runtimes. // Note: We only use this at the type level, so it should be impossible to // create an instance of it. +#[derive(Debug)] pub enum SubstrateConfig {} impl Config for SubstrateConfig {