From 4dbcc79fcc3794b2e807c06e512be4afa95f1a0a Mon Sep 17 00:00:00 2001 From: James Wilson Date: Thu, 26 Sep 2024 16:06:21 +0100 Subject: [PATCH 01/16] WIP using frame-decode for core extrinsic decode logic --- Cargo.lock | 47 ++-- Cargo.toml | 1 + core/Cargo.toml | 1 + .../src/blocks/extrinsic_signed_extensions.rs | 54 +---- core/src/blocks/extrinsics.rs | 205 ++++-------------- core/src/blocks/mod.rs | 6 +- core/src/error.rs | 28 +-- metadata/Cargo.toml | 1 + metadata/src/lib.rs | 44 ++++ subxt/examples/block_decoding_dynamic.rs | 1 - subxt/src/blocks/block_types.rs | 2 +- subxt/src/blocks/extrinsic_types.rs | 10 +- subxt/src/error/mod.rs | 19 +- 13 files changed, 154 insertions(+), 265 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 79d256b1fa..602f168be9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1675,6 +1675,21 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "frame-decode" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "849eebd821dbed6a05afdf96f4ab4d38bbf2c90f1d726f9c43ef21cf17cc52b5" +dependencies = [ + "frame-metadata 16.0.0", + "hex", + "parity-scale-codec", + "scale-decode", + "scale-info", + "scale-type-resolver", + "sp-crypto-hashing", +] + [[package]] name = "frame-metadata" version = "15.1.0" @@ -2982,9 +2997,9 @@ dependencies = [ [[package]] name = "parity-scale-codec" -version = "3.6.9" +version = "3.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "881331e34fa842a2fb61cc2db9643a8fedc615e47cfcc52597d1af0db9a7e8fe" +checksum = "306800abfa29c7f16596b5970a588435e3d5b3149683d00c12b699cc19f895ee" dependencies = [ "arrayvec 0.7.4", "bitvec", @@ -2997,11 +3012,11 @@ dependencies = [ [[package]] name = "parity-scale-codec-derive" -version = "3.6.9" +version = "3.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be30eaf4b0a9fba5336683b38de57bb86d179a35862ba6bfcf57625d006bde5b" +checksum = "d830939c76d294956402033aee57a6da7b438f2294eb94864c37b0569053a42c" dependencies = [ - "proc-macro-crate 2.0.0", + "proc-macro-crate 3.1.0", "proc-macro2", "quote", "syn 1.0.109", @@ -3311,15 +3326,6 @@ dependencies = [ "toml_edit 0.19.15", ] -[[package]] -name = "proc-macro-crate" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e8366a6159044a37876a2b9817124296703c586a5c92e2c53751fa06d8d43e8" -dependencies = [ - "toml_edit 0.20.7", -] - [[package]] name = "proc-macro-crate" version = "3.1.0" @@ -4939,6 +4945,7 @@ dependencies = [ "bitvec", "blake2", "derive-where", + "frame-decode", "frame-metadata 16.0.0", "hashbrown 0.14.5", "hex", @@ -5008,6 +5015,7 @@ dependencies = [ "assert_matches", "bitvec", "criterion", + "frame-decode", "frame-metadata 16.0.0", "hashbrown 0.14.5", "parity-scale-codec", @@ -5286,17 +5294,6 @@ dependencies = [ "winnow 0.5.40", ] -[[package]] -name = "toml_edit" -version = "0.20.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" -dependencies = [ - "indexmap 2.2.6", - "toml_datetime", - "winnow 0.5.40", -] - [[package]] name = "toml_edit" version = "0.21.1" diff --git a/Cargo.toml b/Cargo.toml index aae32ab408..df2256d13d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -77,6 +77,7 @@ console_error_panic_hook = "0.1.7" darling = "0.20.10" derive-where = "1.2.7" either = { version = "1.13.0", default-features = false } +frame-decode = { version = "0.0.6", default-features = false } frame-metadata = { version = "16.0.0", default-features = false } futures = { version = "0.3.30", default-features = false, features = ["std"] } getrandom = { version = "0.2", default-features = false } diff --git a/core/Cargo.toml b/core/Cargo.toml index 638fa570c4..78ca200f75 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -36,6 +36,7 @@ substrate-compat = ["sp-core", "sp-runtime"] [dependencies] codec = { package = "parity-scale-codec", workspace = true, default-features = false, features = ["derive"] } +frame-decode = { workspace = true } scale-info = { workspace = true, default-features = false, features = ["bit-vec"] } scale-value = { workspace = true, default-features = false } scale-bits = { workspace = true, default-features = false } diff --git a/core/src/blocks/extrinsic_signed_extensions.rs b/core/src/blocks/extrinsic_signed_extensions.rs index fbda944cff..bea1574576 100644 --- a/core/src/blocks/extrinsic_signed_extensions.rs +++ b/core/src/blocks/extrinsic_signed_extensions.rs @@ -9,64 +9,37 @@ use crate::config::SignedExtension; use crate::dynamic::Value; use crate::{config::Config, error::Error, Metadata}; use scale_decode::DecodeAsType; +use frame_decode::extrinsics::ExtrinsicSignature; /// The signed extensions of an extrinsic. #[derive(Debug, Clone)] pub struct ExtrinsicSignedExtensions<'a, T: Config> { bytes: &'a [u8], metadata: &'a Metadata, + decoded_info: &'a ExtrinsicSignature<'static, u32>, _marker: core::marker::PhantomData, } impl<'a, T: Config> ExtrinsicSignedExtensions<'a, T> { - pub(crate) fn new(bytes: &'a [u8], metadata: &'a Metadata) -> Self { + pub(crate) fn new(bytes: &'a [u8], metadata: &'a Metadata, decoded_info: &'a ExtrinsicSignature<'static, u32>) -> Self { Self { bytes, metadata, + decoded_info, _marker: core::marker::PhantomData, } } /// 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. - pub fn iter(&self) -> impl Iterator, Error>> { - let signed_extension_types = self.metadata.extrinsic().signed_extensions(); - let num_signed_extensions = signed_extension_types.len(); - let bytes = self.bytes; - let mut index = 0; - let mut byte_start_idx = 0; - let metadata = &self.metadata; - - core::iter::from_fn(move || { - if index == num_signed_extensions { - return None; - } - - let extension = &signed_extension_types[index]; - let ty_id = extension.extra_ty(); - let cursor = &mut &bytes[byte_start_idx..]; - if let Err(err) = scale_decode::visitor::decode_with_visitor( - cursor, - ty_id, - metadata.types(), - scale_decode::visitor::IgnoreVisitor::new(), - ) - .map_err(|e| Error::Decode(e.into())) - { - index = num_signed_extensions; // (such that None is returned in next iteration) - return Some(Err(err)); - } - let byte_end_idx = bytes.len() - cursor.len(); - let bytes = &bytes[byte_start_idx..byte_end_idx]; - byte_start_idx = byte_end_idx; - index += 1; - Some(Ok(ExtrinsicSignedExtension { - bytes, - ty_id, - identifier: extension.identifier(), - metadata, + pub fn iter(&self) -> impl Iterator> { + self.decoded_info.signed_extensions().map(|s| { + ExtrinsicSignedExtension { + bytes: &self.bytes[s.range()], + ty_id: *s.ty(), + identifier: s.name(), + metadata: self.metadata, _marker: core::marker::PhantomData, - })) + } }) } @@ -75,9 +48,6 @@ impl<'a, T: Config> ExtrinsicSignedExtensions<'a, T> { /// If the Signed Extension is found but decoding failed `Err(_)` is returned. pub fn find>(&self) -> Result, Error> { for ext in self.iter() { - // If we encounter an error while iterating, we won't get any more results - // back, so just return that error as we won't find the signed ext anyway. - let ext = ext?; match ext.as_signed_extension::() { // We found a match; return it: Ok(Some(e)) => return Ok(Some(e)), diff --git a/core/src/blocks/extrinsics.rs b/core/src/blocks/extrinsics.rs index 0af853fc8b..c6b7a07181 100644 --- a/core/src/blocks/extrinsics.rs +++ b/core/src/blocks/extrinsics.rs @@ -2,25 +2,27 @@ // This file is dual-licensed as Apache-2.0 or GPL-3.0. // see LICENSE for license details. +use core::ops::Deref; + use crate::blocks::extrinsic_signed_extensions::ExtrinsicSignedExtensions; use crate::{ config::{Config, Hasher}, - error::{BlockError, Error, MetadataError}, + error::{Error, MetadataError}, Metadata, }; use alloc::sync::Arc; use alloc::vec::Vec; -use codec::{Compact, CompactLen, Decode}; use scale_decode::DecodeAsType; use subxt_metadata::PalletMetadata; pub use crate::blocks::StaticExtrinsic; +use super::BlockError; + /// The body of a block. pub struct Extrinsics { extrinsics: Vec>, metadata: Metadata, - ids: ExtrinsicPartTypeIds, _marker: core::marker::PhantomData, } @@ -28,15 +30,12 @@ impl Extrinsics { /// Instantiate a new [`Extrinsics`] object, given a vector containing /// each extrinsic hash (in the form of bytes) and some metadata that /// we'll use to decode them. - pub fn decode_from(extrinsics: Vec>, metadata: Metadata) -> Result { - let ids = ExtrinsicPartTypeIds::new(&metadata)?; - - Ok(Self { + pub fn decode_from(extrinsics: Vec>, metadata: Metadata) -> Self { + Self { extrinsics, metadata, - ids, _marker: core::marker::PhantomData, - }) + } } /// The number of extrinsics. @@ -59,7 +58,6 @@ impl Extrinsics { let extrinsics = self.extrinsics.clone(); let num_extrinsics = self.extrinsics.len(); let metadata = self.metadata.clone(); - let ids = self.ids; let mut index = 0; core::iter::from_fn(move || { @@ -70,7 +68,6 @@ impl Extrinsics { index as u32, &extrinsics[index], metadata.clone(), - ids, ) { Ok(extrinsic_details) => { index += 1; @@ -127,31 +124,13 @@ pub struct ExtrinsicDetails { index: u32, /// Extrinsic bytes. bytes: Arc<[u8]>, - /// Some if the extrinsic payload is signed. - signed_details: Option, - /// The start index in the `bytes` from which the call is encoded. - call_start_idx: usize, - /// The pallet index. - pallet_index: u8, - /// The variant index. - variant_index: u8, + /// Decoded information about the extrinsic. + decoded_info: frame_decode::extrinsics::Extrinsic<'static, u32>, /// Subxt metadata to fetch the extrinsic metadata. metadata: Metadata, _marker: core::marker::PhantomData, } -/// Details only available in signed extrinsics. -pub struct SignedExtrinsicDetails { - /// start index of the range in `bytes` of `ExtrinsicDetails` that encodes the address. - address_start_idx: usize, - /// end index of the range in `bytes` of `ExtrinsicDetails` that encodes the address. Equivalent to signature_start_idx. - address_end_idx: usize, - /// end index of the range in `bytes` of `ExtrinsicDetails` that encodes the signature. Equivalent to extra_start_idx. - signature_end_idx: usize, - /// end index of the range in `bytes` of `ExtrinsicDetails` that encodes the signature. - extra_end_idx: usize, -} - impl ExtrinsicDetails where T: Config, @@ -162,90 +141,26 @@ where index: u32, extrinsic_bytes: &[u8], metadata: Metadata, - ids: ExtrinsicPartTypeIds, ) -> Result, Error> { - const SIGNATURE_MASK: u8 = 0b1000_0000; - const VERSION_MASK: u8 = 0b0111_1111; - const LATEST_EXTRINSIC_VERSION: u8 = 4; + let cursor = &mut &*extrinsic_bytes; + let decoded_info = frame_decode::extrinsics::decode_extrinsic( + cursor, + metadata.deref(), + metadata.types() + ).map_err(BlockError::ExtrinsicDecodeError)?.into_owned(); + + // We didn't consume all bytes. + if !cursor.is_empty() { + return Err(BlockError::LeftoverBytes(cursor.len()).into()) + } // Wrap all of the bytes in Arc for easy sharing. let bytes: Arc<[u8]> = Arc::from(extrinsic_bytes); - // The compact encoded length prefix. - let prefix = >::decode(&mut &*extrinsic_bytes)?; - let prefix_len = >::compact_len(&prefix.0); - - // Extrinsic are encoded in memory in the following way: - // - first byte: abbbbbbb (a = 0 for unsigned, 1 for signed, b = version) - // - signature: [unknown TBD with metadata]. - // - extrinsic data - let version_byte: u8 = Decode::decode(&mut &bytes[prefix_len..])?; - - let version = version_byte & VERSION_MASK; - if version != LATEST_EXTRINSIC_VERSION { - return Err(BlockError::UnsupportedVersion(version).into()); - } - - let is_signed = version_byte & SIGNATURE_MASK != 0; - - // Skip over the prefix and first byte which denotes the version and signing. - let cursor = &mut &bytes[prefix_len + 1..]; - - let signed_details = is_signed - .then(|| -> Result { - let address_start_idx = bytes.len() - cursor.len(); - // Skip over the address, signature and extra fields. - scale_decode::visitor::decode_with_visitor( - cursor, - ids.address, - metadata.types(), - scale_decode::visitor::IgnoreVisitor::new(), - ) - .map_err(scale_decode::Error::from)?; - let address_end_idx = bytes.len() - cursor.len(); - - scale_decode::visitor::decode_with_visitor( - cursor, - ids.signature, - metadata.types(), - scale_decode::visitor::IgnoreVisitor::new(), - ) - .map_err(scale_decode::Error::from)?; - let signature_end_idx = bytes.len() - cursor.len(); - - scale_decode::visitor::decode_with_visitor( - cursor, - ids.extra, - metadata.types(), - scale_decode::visitor::IgnoreVisitor::new(), - ) - .map_err(scale_decode::Error::from)?; - let extra_end_idx = bytes.len() - cursor.len(); - - Ok(SignedExtrinsicDetails { - address_start_idx, - address_end_idx, - signature_end_idx, - extra_end_idx, - }) - }) - .transpose()?; - - let call_start_idx = bytes.len() - cursor.len(); - - // Decode the pallet index, then the call variant. - let cursor = &mut &bytes[call_start_idx..]; - - let pallet_index: u8 = Decode::decode(cursor)?; - let variant_index: u8 = Decode::decode(cursor)?; - Ok(ExtrinsicDetails { index, bytes, - signed_details, - call_start_idx, - pallet_index, - variant_index, + decoded_info, metadata, _marker: core::marker::PhantomData, }) @@ -259,7 +174,7 @@ where /// Is the extrinsic signed? pub fn is_signed(&self) -> bool { - self.signed_details.is_some() + self.decoded_info.is_signed() } /// The index of the extrinsic in the block. @@ -287,7 +202,7 @@ where /// /// Please use [`Self::bytes`] if you want to get all extrinsic bytes. pub fn call_bytes(&self) -> &[u8] { - &self.bytes[self.call_start_idx..] + &self.bytes[self.decoded_info.call_data_range()] } /// Return the bytes representing the fields stored in this extrinsic. @@ -308,16 +223,16 @@ where /// /// Returns `None` if the extrinsic is not signed. pub fn address_bytes(&self) -> Option<&[u8]> { - self.signed_details - .as_ref() - .map(|e| &self.bytes[e.address_start_idx..e.address_end_idx]) + self.decoded_info.signature_payload().map(|s| { + &self.bytes[s.address_range()] + }) } /// Returns Some(signature_bytes) if the extrinsic was signed otherwise None is returned. pub fn signature_bytes(&self) -> Option<&[u8]> { - self.signed_details - .as_ref() - .map(|e| &self.bytes[e.address_end_idx..e.signature_end_idx]) + self.decoded_info.signature_payload().map(|s| { + &self.bytes[s.signature_range()] + }) } /// Returns the signed extension `extra` bytes of the extrinsic. @@ -327,26 +242,26 @@ where /// /// Note: Returns `None` if the extrinsic is not signed. pub fn signed_extensions_bytes(&self) -> Option<&[u8]> { - self.signed_details - .as_ref() - .map(|e| &self.bytes[e.signature_end_idx..e.extra_end_idx]) + self.decoded_info.signature_payload().map(|_| { + &self.bytes[self.decoded_info.signed_extensions_range()] + }) } /// Returns `None` if the extrinsic is not signed. pub fn signed_extensions(&self) -> Option> { - let signed = self.signed_details.as_ref()?; - let extra_bytes = &self.bytes[signed.signature_end_idx..signed.extra_end_idx]; - Some(ExtrinsicSignedExtensions::new(extra_bytes, &self.metadata)) + self.decoded_info.signature_payload().map(|s| { + ExtrinsicSignedExtensions::new(&self.bytes, &self.metadata, s) + }) } /// The index of the pallet that the extrinsic originated from. pub fn pallet_index(&self) -> u8 { - self.pallet_index + self.decoded_info.pallet_index() } /// The index of the extrinsic variant that the extrinsic originated from. pub fn variant_index(&self) -> u8 { - self.variant_index + self.decoded_info.call_index() } /// The name of the pallet from whence the extrinsic originated. @@ -436,36 +351,6 @@ pub struct ExtrinsicMetadataDetails<'a> { pub variant: &'a scale_info::Variant, } -/// The type IDs extracted from the metadata that represent the -/// generic type parameters passed to the `UncheckedExtrinsic` from -/// the substrate-based chain. -#[doc(hidden)] -#[derive(Debug, Copy, Clone)] -pub struct ExtrinsicPartTypeIds { - /// The address (source) of the extrinsic. - address: u32, - /// The extrinsic call type. - // Note: the call type can be used to skip over the extrinsic bytes to check - // they are in line with our metadata. This operation is currently postponed. - _call: u32, - /// The signature of the extrinsic. - signature: u32, - /// The extra parameters of the extrinsic. - extra: u32, -} - -impl ExtrinsicPartTypeIds { - /// Extract the generic type parameters IDs from the extrinsic type. - fn new(metadata: &Metadata) -> Result { - Ok(ExtrinsicPartTypeIds { - address: metadata.extrinsic().address_ty(), - _call: metadata.extrinsic().call_ty(), - signature: metadata.extrinsic().signature_ty(), - extra: metadata.extrinsic().extra_ty(), - }) - } -} - #[cfg(test)] mod tests { use super::*; @@ -615,26 +500,26 @@ mod tests { #[test] fn insufficient_extrinsic_bytes() { let metadata = metadata(); - let ids = ExtrinsicPartTypeIds::new(&metadata).unwrap(); // Decode with empty bytes. - let result = ExtrinsicDetails::::decode_from(0, &[], metadata, ids); + let result = ExtrinsicDetails::::decode_from(0, &[], metadata); assert_matches!(result.err(), Some(crate::Error::Codec(_))); } #[test] fn unsupported_version_extrinsic() { + use frame_decode::extrinsics::ExtrinsicDecodeError; + let metadata = metadata(); - let ids = ExtrinsicPartTypeIds::new(&metadata).unwrap(); // Decode with invalid version. let result = - ExtrinsicDetails::::decode_from(0, &vec![3u8].encode(), metadata, ids); + ExtrinsicDetails::::decode_from(0, &vec![3u8].encode(), metadata); assert_matches!( result.err(), Some(crate::Error::Block( - crate::error::BlockError::UnsupportedVersion(3) + crate::error::BlockError::ExtrinsicDecodeError(ExtrinsicDecodeError::VersionNotSupported { extrinsic_version: 3 }) )) ); } @@ -642,7 +527,6 @@ mod tests { #[test] fn tx_hashes_line_up() { let metadata = metadata(); - let ids = ExtrinsicPartTypeIds::new(&metadata).unwrap(); let tx = crate::dynamic::tx( "Test", @@ -663,7 +547,6 @@ mod tests { 1, tx_encoded.encoded(), metadata, - ids, ) .expect("Valid extrinsic"); @@ -676,7 +559,6 @@ mod tests { #[test] fn statically_decode_extrinsic() { let metadata = metadata(); - let ids = ExtrinsicPartTypeIds::new(&metadata).unwrap(); let tx = crate::dynamic::tx( "Test", @@ -696,7 +578,6 @@ mod tests { 1, tx_encoded.encoded(), metadata, - ids, ) .expect("Valid extrinsic"); diff --git a/core/src/blocks/mod.rs b/core/src/blocks/mod.rs index 8a66966c6a..7aa94b05aa 100644 --- a/core/src/blocks/mod.rs +++ b/core/src/blocks/mod.rs @@ -71,15 +71,15 @@ mod extrinsics; mod static_extrinsic; use crate::config::Config; -use crate::error::BlockError; use crate::Metadata; use alloc::vec::Vec; pub use extrinsic_signed_extensions::{ExtrinsicSignedExtension, ExtrinsicSignedExtensions}; pub use extrinsics::{ - ExtrinsicDetails, ExtrinsicMetadataDetails, Extrinsics, FoundExtrinsic, SignedExtrinsicDetails, + ExtrinsicDetails, ExtrinsicMetadataDetails, Extrinsics, FoundExtrinsic, }; pub use static_extrinsic::StaticExtrinsic; +pub use crate::error::BlockError; /// Instantiate a new [`Extrinsics`] object, given a vector containing each extrinsic hash (in the /// form of bytes) and some metadata that we'll use to decode them. @@ -88,6 +88,6 @@ pub use static_extrinsic::StaticExtrinsic; pub fn decode_from( extrinsics: Vec>, metadata: Metadata, -) -> Result, BlockError> { +) -> Extrinsics { Extrinsics::decode_from(extrinsics, metadata) } diff --git a/core/src/error.rs b/core/src/error.rs index 9824c49a84..2407291e9d 100644 --- a/core/src/error.rs +++ b/core/src/error.rs @@ -56,29 +56,29 @@ impl_from!(StorageAddressError => Error::StorageAddress); impl_from!(codec::Error => Error::Codec); /// Block error -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Debug)] pub enum BlockError { - /// Extrinsic type ID cannot be resolved with the provided metadata. - MissingType, - /// Unsupported signature. - /// The extrinsic has an unsupported version. - UnsupportedVersion(u8), - /// Decoding error. - DecodingError(codec::Error), + /// Leftover bytes found after decoding the extrinsic. + LeftoverBytes(usize), + /// Something went wrong decoding the extrinsic. + ExtrinsicDecodeError(ExtrinsicDecodeError) } - impl Display for BlockError { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { - BlockError::MissingType => write!(f, "Extrinsic type ID cannot be resolved with the provided metadata. Make sure this is a valid metadata"), - BlockError::UnsupportedVersion(_) => write!(f, "Unsupported extrinsic version, only version 4 is supported currently"), - BlockError::DecodingError(e) => write!(f, "Cannot decode extrinsic: {e}"), + BlockError::LeftoverBytes(n) => { + write!(f, "After decoding, {n} bytes were left, suggesting that decoding may have failed") + }, + BlockError::ExtrinsicDecodeError(e) => { + write!(f, "{e}") + } } } } -#[cfg(feature = "std")] -impl std::error::Error for BlockError {} +/// An alias for [`frame_decode::extrinsics::ExtrinsicDecodeError`]. +/// +pub type ExtrinsicDecodeError = frame_decode::extrinsics::ExtrinsicDecodeError; /// Something went wrong trying to access details in the metadata. #[derive(Clone, Debug, PartialEq)] diff --git a/metadata/Cargo.toml b/metadata/Cargo.toml index 0ad5316100..d55660d372 100644 --- a/metadata/Cargo.toml +++ b/metadata/Cargo.toml @@ -19,6 +19,7 @@ std = ["scale-info/std", "frame-metadata/std"] [dependencies] scale-info = { workspace = true, default-features = false } +frame-decode = { workspace = true } frame-metadata = { workspace = true, default-features = false, features = ["current", "decode"] } codec = { package = "parity-scale-codec", workspace = true, default-features = false, features = ["derive"] } sp-crypto-hashing = { workspace = true } diff --git a/metadata/src/lib.rs b/metadata/src/lib.rs index fb86ddcbef..58f3f9fb02 100644 --- a/metadata/src/lib.rs +++ b/metadata/src/lib.rs @@ -22,6 +22,7 @@ extern crate alloc; mod from_into; mod utils; +use alloc::borrow::Cow; use alloc::string::String; use alloc::sync::Arc; use alloc::vec::Vec; @@ -29,6 +30,7 @@ use hashbrown::HashMap; use scale_info::{form::PortableForm, PortableRegistry, Variant}; use utils::variant_index::VariantIndex; use utils::{ordered_map::OrderedMap, validation::outer_enum_hashes::OuterEnumHashes}; +use frame_decode::extrinsics::{ ExtrinsicInfo, ExtrinsicSignatureInfo, ExtrinsicInfoError, ExtrinsicInfoArg }; type ArcStr = Arc; @@ -61,6 +63,48 @@ pub struct Metadata { custom: frame_metadata::v15::CustomMetadata, } +// Since we've abstracted away from frame-metadatas, impl this on our custom metadata +// so that it can be used by `frame-decode` to obtain the relevant extrinsic info. +impl frame_decode::extrinsics::ExtrinsicTypeInfo for Metadata { + type TypeId = u32; + + fn get_extrinsic_info<'a>( + &'a self, + pallet_index: u8, + call_index: u8, + ) -> Result, ExtrinsicInfoError<'a>> { + let pallet = self + .pallet_by_index(pallet_index) + .ok_or_else(|| ExtrinsicInfoError::PalletNotFound { index: pallet_index })?; + + let call = pallet + .call_variant_by_index(call_index) + .ok_or_else(|| ExtrinsicInfoError::CallNotFound { index: call_index, pallet_index: pallet_index, pallet_name: Cow::Borrowed(pallet.name()) })?; + + Ok(ExtrinsicInfo { + pallet_name: Cow::Borrowed(pallet.name()), + call_name: Cow::Borrowed(&call.name), + args: call.fields.iter().map(|f| ExtrinsicInfoArg { + name: Cow::Borrowed(f.name.as_ref().map(|n| n.as_str()).unwrap_or("")), + id: f.ty.id + }).collect(), + }) + } + + fn get_signature_info<'a>( + &'a self, + ) -> Result, ExtrinsicInfoError<'a>> { + Ok(ExtrinsicSignatureInfo { + address_id: self.extrinsic().address_ty(), + signature_id: self.extrinsic().signature_ty(), + signed_extension_ids: self.extrinsic().signed_extensions().iter().map(|f| ExtrinsicInfoArg { + name: Cow::Borrowed(f.identifier()), + id: f.extra_ty() + }).collect() + }) + } +} + impl Metadata { /// Access the underlying type registry. pub fn types(&self) -> &PortableRegistry { diff --git a/subxt/examples/block_decoding_dynamic.rs b/subxt/examples/block_decoding_dynamic.rs index 8bd54d4afb..9d97eb24fe 100644 --- a/subxt/examples/block_decoding_dynamic.rs +++ b/subxt/examples/block_decoding_dynamic.rs @@ -29,7 +29,6 @@ async fn main() -> Result<(), Box> { println!(" {}/{}", meta.pallet.name(), meta.variant.name); println!(" Signed Extensions:"); for signed_ext in signed_extensions.iter() { - let signed_ext = signed_ext?; // We only want to take a look at these 3 signed extensions, because the others all just have unit fields. if ["CheckMortality", "CheckNonce", "ChargeTransactionPayment"] .contains(&signed_ext.name()) diff --git a/subxt/src/blocks/block_types.rs b/subxt/src/blocks/block_types.rs index b93a05ba3e..0102c7ee19 100644 --- a/subxt/src/blocks/block_types.rs +++ b/subxt/src/blocks/block_types.rs @@ -89,7 +89,7 @@ where extrinsics, self.cached_events.clone(), block_hash, - )?) + )) } /// Work with storage. diff --git a/subxt/src/blocks/extrinsic_types.rs b/subxt/src/blocks/extrinsic_types.rs index e7c876b679..2cb00c76e3 100644 --- a/subxt/src/blocks/extrinsic_types.rs +++ b/subxt/src/blocks/extrinsic_types.rs @@ -6,7 +6,7 @@ use crate::{ blocks::block_types::{get_events, CachedEvents}, client::{OfflineClientT, OnlineClientT}, config::{Config, Hasher}, - error::{BlockError, Error}, + error::Error, events, }; @@ -37,14 +37,14 @@ where extrinsics: Vec>, cached_events: CachedEvents, hash: T::Hash, - ) -> Result { - let inner = CoreExtrinsics::decode_from(extrinsics, client.metadata())?; - Ok(Self { + ) -> Self { + let inner = CoreExtrinsics::decode_from(extrinsics, client.metadata()); + Self { inner, client, cached_events, hash, - }) + } } /// See [`subxt_core::blocks::Extrinsics::len()`]. diff --git a/subxt/src/error/mod.rs b/subxt/src/error/mod.rs index 53803450f1..c66dbd6868 100644 --- a/subxt/src/error/mod.rs +++ b/subxt/src/error/mod.rs @@ -165,30 +165,25 @@ impl RpcError { } /// Block error -#[derive(Clone, Debug, Eq, thiserror::Error, PartialEq)] +#[derive(Clone, Debug, thiserror::Error)] #[non_exhaustive] pub enum BlockError { /// An error containing the hash of the block that was not found. #[error("Could not find a block with hash {0} (perhaps it was on a non-finalized fork?)")] NotFound(String), - /// Extrinsic type ID cannot be resolved with the provided metadata. - #[error("Extrinsic type ID cannot be resolved with the provided metadata. Make sure this is a valid metadata")] - MissingType, - /// Unsupported signature. - #[error("Unsupported extrinsic version, only version 4 is supported currently")] - /// The extrinsic has an unsupported version. - UnsupportedVersion(u8), + /// Leftover bytes found after decoding the extrinsic. + #[error("After decoding, {0} bytes were left, suggesting that decoding may have failed")] + LeftoverBytes(usize), /// Decoding error. #[error("Cannot decode extrinsic: {0}")] - DecodingError(codec::Error), + ExtrinsicDecodeError(subxt_core::error::ExtrinsicDecodeError), } impl From for BlockError { fn from(value: CoreBlockError) -> Self { match value { - CoreBlockError::MissingType => BlockError::MissingType, - CoreBlockError::UnsupportedVersion(n) => BlockError::UnsupportedVersion(n), - CoreBlockError::DecodingError(e) => BlockError::DecodingError(e), + CoreBlockError::LeftoverBytes(n) => BlockError::LeftoverBytes(n), + CoreBlockError::ExtrinsicDecodeError(e) => BlockError::ExtrinsicDecodeError(e), } } } From 8220d46636c19a16e880bcf6f0e8406f41e1c3bc Mon Sep 17 00:00:00 2001 From: James Wilson Date: Thu, 26 Sep 2024 16:11:06 +0100 Subject: [PATCH 02/16] fmt --- .../src/blocks/extrinsic_signed_extensions.rs | 16 +++-- core/src/blocks/extrinsics.rs | 59 +++++++++---------- core/src/blocks/mod.rs | 11 +--- core/src/error.rs | 11 ++-- metadata/src/lib.rs | 55 +++++++++++------ 5 files changed, 84 insertions(+), 68 deletions(-) diff --git a/core/src/blocks/extrinsic_signed_extensions.rs b/core/src/blocks/extrinsic_signed_extensions.rs index bea1574576..e1bded55c2 100644 --- a/core/src/blocks/extrinsic_signed_extensions.rs +++ b/core/src/blocks/extrinsic_signed_extensions.rs @@ -8,8 +8,8 @@ use crate::config::signed_extensions::{ use crate::config::SignedExtension; use crate::dynamic::Value; use crate::{config::Config, error::Error, Metadata}; -use scale_decode::DecodeAsType; use frame_decode::extrinsics::ExtrinsicSignature; +use scale_decode::DecodeAsType; /// The signed extensions of an extrinsic. #[derive(Debug, Clone)] @@ -21,7 +21,11 @@ pub struct ExtrinsicSignedExtensions<'a, T: Config> { } impl<'a, T: Config> ExtrinsicSignedExtensions<'a, T> { - pub(crate) fn new(bytes: &'a [u8], metadata: &'a Metadata, decoded_info: &'a ExtrinsicSignature<'static, u32>) -> Self { + pub(crate) fn new( + bytes: &'a [u8], + metadata: &'a Metadata, + decoded_info: &'a ExtrinsicSignature<'static, u32>, + ) -> Self { Self { bytes, metadata, @@ -32,15 +36,15 @@ impl<'a, T: Config> ExtrinsicSignedExtensions<'a, T> { /// Returns an iterator over each of the signed extension details of the extrinsic. pub fn iter(&self) -> impl Iterator> { - self.decoded_info.signed_extensions().map(|s| { - ExtrinsicSignedExtension { + self.decoded_info + .signed_extensions() + .map(|s| ExtrinsicSignedExtension { bytes: &self.bytes[s.range()], ty_id: *s.ty(), identifier: s.name(), metadata: self.metadata, _marker: core::marker::PhantomData, - } - }) + }) } /// Searches through all signed extensions to find a specific one. diff --git a/core/src/blocks/extrinsics.rs b/core/src/blocks/extrinsics.rs index c6b7a07181..8f576259cf 100644 --- a/core/src/blocks/extrinsics.rs +++ b/core/src/blocks/extrinsics.rs @@ -143,15 +143,14 @@ where metadata: Metadata, ) -> Result, Error> { let cursor = &mut &*extrinsic_bytes; - let decoded_info = frame_decode::extrinsics::decode_extrinsic( - cursor, - metadata.deref(), - metadata.types() - ).map_err(BlockError::ExtrinsicDecodeError)?.into_owned(); + let decoded_info = + frame_decode::extrinsics::decode_extrinsic(cursor, metadata.deref(), metadata.types()) + .map_err(BlockError::ExtrinsicDecodeError)? + .into_owned(); // We didn't consume all bytes. if !cursor.is_empty() { - return Err(BlockError::LeftoverBytes(cursor.len()).into()) + return Err(BlockError::LeftoverBytes(cursor.len()).into()); } // Wrap all of the bytes in Arc for easy sharing. @@ -223,16 +222,16 @@ where /// /// Returns `None` if the extrinsic is not signed. pub fn address_bytes(&self) -> Option<&[u8]> { - self.decoded_info.signature_payload().map(|s| { - &self.bytes[s.address_range()] - }) + self.decoded_info + .signature_payload() + .map(|s| &self.bytes[s.address_range()]) } /// Returns Some(signature_bytes) if the extrinsic was signed otherwise None is returned. pub fn signature_bytes(&self) -> Option<&[u8]> { - self.decoded_info.signature_payload().map(|s| { - &self.bytes[s.signature_range()] - }) + self.decoded_info + .signature_payload() + .map(|s| &self.bytes[s.signature_range()]) } /// Returns the signed extension `extra` bytes of the extrinsic. @@ -242,16 +241,16 @@ where /// /// Note: Returns `None` if the extrinsic is not signed. pub fn signed_extensions_bytes(&self) -> Option<&[u8]> { - self.decoded_info.signature_payload().map(|_| { - &self.bytes[self.decoded_info.signed_extensions_range()] - }) + self.decoded_info + .signature_payload() + .map(|_| &self.bytes[self.decoded_info.signed_extensions_range()]) } /// Returns `None` if the extrinsic is not signed. pub fn signed_extensions(&self) -> Option> { - self.decoded_info.signature_payload().map(|s| { - ExtrinsicSignedExtensions::new(&self.bytes, &self.metadata, s) - }) + self.decoded_info + .signature_payload() + .map(|s| ExtrinsicSignedExtensions::new(&self.bytes, &self.metadata, s)) } /// The index of the pallet that the extrinsic originated from. @@ -519,7 +518,11 @@ mod tests { assert_matches!( result.err(), Some(crate::Error::Block( - crate::error::BlockError::ExtrinsicDecodeError(ExtrinsicDecodeError::VersionNotSupported { extrinsic_version: 3 }) + crate::error::BlockError::ExtrinsicDecodeError( + ExtrinsicDecodeError::VersionNotSupported { + extrinsic_version: 3 + } + ) )) ); } @@ -543,12 +546,9 @@ mod tests { .expect("Valid dynamic parameters are provided"); // Extrinsic details ready to decode. - let extrinsic = ExtrinsicDetails::::decode_from( - 1, - tx_encoded.encoded(), - metadata, - ) - .expect("Valid extrinsic"); + let extrinsic = + ExtrinsicDetails::::decode_from(1, tx_encoded.encoded(), metadata) + .expect("Valid extrinsic"); // Both of these types should produce the same bytes. assert_eq!(tx_encoded.encoded(), extrinsic.bytes(), "bytes should eq"); @@ -574,12 +574,9 @@ mod tests { // Note: `create_unsigned` produces the extrinsic bytes by prefixing the extrinsic length. // The length is handled deserializing `ChainBlockExtrinsic`, therefore the first byte is not needed. - let extrinsic = ExtrinsicDetails::::decode_from( - 1, - tx_encoded.encoded(), - metadata, - ) - .expect("Valid extrinsic"); + let extrinsic = + ExtrinsicDetails::::decode_from(1, tx_encoded.encoded(), metadata) + .expect("Valid extrinsic"); assert!(!extrinsic.is_signed()); diff --git a/core/src/blocks/mod.rs b/core/src/blocks/mod.rs index 7aa94b05aa..19d4f7cfaf 100644 --- a/core/src/blocks/mod.rs +++ b/core/src/blocks/mod.rs @@ -74,20 +74,15 @@ use crate::config::Config; use crate::Metadata; use alloc::vec::Vec; +pub use crate::error::BlockError; pub use extrinsic_signed_extensions::{ExtrinsicSignedExtension, ExtrinsicSignedExtensions}; -pub use extrinsics::{ - ExtrinsicDetails, ExtrinsicMetadataDetails, Extrinsics, FoundExtrinsic, -}; +pub use extrinsics::{ExtrinsicDetails, ExtrinsicMetadataDetails, Extrinsics, FoundExtrinsic}; pub use static_extrinsic::StaticExtrinsic; -pub use crate::error::BlockError; /// Instantiate a new [`Extrinsics`] object, given a vector containing each extrinsic hash (in the /// form of bytes) and some metadata that we'll use to decode them. /// /// This is a shortcut for [`Extrinsics::decode_from`]. -pub fn decode_from( - extrinsics: Vec>, - metadata: Metadata, -) -> Extrinsics { +pub fn decode_from(extrinsics: Vec>, metadata: Metadata) -> Extrinsics { Extrinsics::decode_from(extrinsics, metadata) } diff --git a/core/src/error.rs b/core/src/error.rs index 2407291e9d..8b9b8a83d5 100644 --- a/core/src/error.rs +++ b/core/src/error.rs @@ -61,14 +61,17 @@ pub enum BlockError { /// Leftover bytes found after decoding the extrinsic. LeftoverBytes(usize), /// Something went wrong decoding the extrinsic. - ExtrinsicDecodeError(ExtrinsicDecodeError) + ExtrinsicDecodeError(ExtrinsicDecodeError), } impl Display for BlockError { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { BlockError::LeftoverBytes(n) => { - write!(f, "After decoding, {n} bytes were left, suggesting that decoding may have failed") - }, + write!( + f, + "After decoding, {n} bytes were left, suggesting that decoding may have failed" + ) + } BlockError::ExtrinsicDecodeError(e) => { write!(f, "{e}") } @@ -77,7 +80,7 @@ impl Display for BlockError { } /// An alias for [`frame_decode::extrinsics::ExtrinsicDecodeError`]. -/// +/// pub type ExtrinsicDecodeError = frame_decode::extrinsics::ExtrinsicDecodeError; /// Something went wrong trying to access details in the metadata. diff --git a/metadata/src/lib.rs b/metadata/src/lib.rs index 58f3f9fb02..846879f9c6 100644 --- a/metadata/src/lib.rs +++ b/metadata/src/lib.rs @@ -26,11 +26,13 @@ use alloc::borrow::Cow; use alloc::string::String; use alloc::sync::Arc; use alloc::vec::Vec; +use frame_decode::extrinsics::{ + ExtrinsicInfo, ExtrinsicInfoArg, ExtrinsicInfoError, ExtrinsicSignatureInfo, +}; use hashbrown::HashMap; use scale_info::{form::PortableForm, PortableRegistry, Variant}; use utils::variant_index::VariantIndex; use utils::{ordered_map::OrderedMap, validation::outer_enum_hashes::OuterEnumHashes}; -use frame_decode::extrinsics::{ ExtrinsicInfo, ExtrinsicSignatureInfo, ExtrinsicInfoError, ExtrinsicInfoArg }; type ArcStr = Arc; @@ -73,34 +75,49 @@ impl frame_decode::extrinsics::ExtrinsicTypeInfo for Metadata { pallet_index: u8, call_index: u8, ) -> Result, ExtrinsicInfoError<'a>> { - let pallet = self - .pallet_by_index(pallet_index) - .ok_or_else(|| ExtrinsicInfoError::PalletNotFound { index: pallet_index })?; - - let call = pallet - .call_variant_by_index(call_index) - .ok_or_else(|| ExtrinsicInfoError::CallNotFound { index: call_index, pallet_index: pallet_index, pallet_name: Cow::Borrowed(pallet.name()) })?; + let pallet = self.pallet_by_index(pallet_index).ok_or_else(|| { + ExtrinsicInfoError::PalletNotFound { + index: pallet_index, + } + })?; + + let call = pallet.call_variant_by_index(call_index).ok_or_else(|| { + ExtrinsicInfoError::CallNotFound { + index: call_index, + pallet_index: pallet_index, + pallet_name: Cow::Borrowed(pallet.name()), + } + })?; Ok(ExtrinsicInfo { pallet_name: Cow::Borrowed(pallet.name()), call_name: Cow::Borrowed(&call.name), - args: call.fields.iter().map(|f| ExtrinsicInfoArg { - name: Cow::Borrowed(f.name.as_ref().map(|n| n.as_str()).unwrap_or("")), - id: f.ty.id - }).collect(), + args: call + .fields + .iter() + .map(|f| ExtrinsicInfoArg { + name: Cow::Borrowed(f.name.as_ref().map(|n| n.as_str()).unwrap_or("")), + id: f.ty.id, + }) + .collect(), }) } fn get_signature_info<'a>( &'a self, ) -> Result, ExtrinsicInfoError<'a>> { - Ok(ExtrinsicSignatureInfo { - address_id: self.extrinsic().address_ty(), - signature_id: self.extrinsic().signature_ty(), - signed_extension_ids: self.extrinsic().signed_extensions().iter().map(|f| ExtrinsicInfoArg { - name: Cow::Borrowed(f.identifier()), - id: f.extra_ty() - }).collect() + Ok(ExtrinsicSignatureInfo { + address_id: self.extrinsic().address_ty(), + signature_id: self.extrinsic().signature_ty(), + signed_extension_ids: self + .extrinsic() + .signed_extensions() + .iter() + .map(|f| ExtrinsicInfoArg { + name: Cow::Borrowed(f.identifier()), + id: f.extra_ty(), + }) + .collect(), }) } } From f9b9fead5d4210d91ad8a1353fc2348def52202b Mon Sep 17 00:00:00 2001 From: James Wilson Date: Thu, 26 Sep 2024 16:12:54 +0100 Subject: [PATCH 03/16] Fix dependabot config --- .github/dependabot.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index c4b5ffb92b..9d0bf3886e 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -6,6 +6,7 @@ updates: schedule: interval: weekly - package-ecosystem: github-actions - directory: "**/*" + directories: + - "**/*" schedule: interval: weekly From 393c819ce6a335c25c6aac2d85d8cdafccd81af9 Mon Sep 17 00:00:00 2001 From: James Wilson Date: Thu, 26 Sep 2024 16:20:37 +0100 Subject: [PATCH 04/16] clippy --- metadata/src/lib.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/metadata/src/lib.rs b/metadata/src/lib.rs index 846879f9c6..6979b45c9d 100644 --- a/metadata/src/lib.rs +++ b/metadata/src/lib.rs @@ -65,17 +65,17 @@ pub struct Metadata { custom: frame_metadata::v15::CustomMetadata, } -// Since we've abstracted away from frame-metadatas, impl this on our custom metadata +// Since we've abstracted away from frame-metadatas, we impl this on our custom Metadata // so that it can be used by `frame-decode` to obtain the relevant extrinsic info. impl frame_decode::extrinsics::ExtrinsicTypeInfo for Metadata { type TypeId = u32; - fn get_extrinsic_info<'a>( - &'a self, + fn get_extrinsic_info( + &self, pallet_index: u8, call_index: u8, - ) -> Result, ExtrinsicInfoError<'a>> { - let pallet = self.pallet_by_index(pallet_index).ok_or_else(|| { + ) -> Result, ExtrinsicInfoError<'_>> { + let pallet = self.pallet_by_index(pallet_index).ok_or({ ExtrinsicInfoError::PalletNotFound { index: pallet_index, } @@ -84,7 +84,7 @@ impl frame_decode::extrinsics::ExtrinsicTypeInfo for Metadata { let call = pallet.call_variant_by_index(call_index).ok_or_else(|| { ExtrinsicInfoError::CallNotFound { index: call_index, - pallet_index: pallet_index, + pallet_index, pallet_name: Cow::Borrowed(pallet.name()), } })?; @@ -96,16 +96,16 @@ impl frame_decode::extrinsics::ExtrinsicTypeInfo for Metadata { .fields .iter() .map(|f| ExtrinsicInfoArg { - name: Cow::Borrowed(f.name.as_ref().map(|n| n.as_str()).unwrap_or("")), + name: Cow::Borrowed(f.name.as_deref().unwrap_or("")), id: f.ty.id, }) .collect(), }) } - fn get_signature_info<'a>( - &'a self, - ) -> Result, ExtrinsicInfoError<'a>> { + fn get_signature_info( + &self, + ) -> Result, ExtrinsicInfoError<'_>> { Ok(ExtrinsicSignatureInfo { address_id: self.extrinsic().address_ty(), signature_id: self.extrinsic().signature_ty(), From 307afbd311296c610554d26a777537209f8e2707 Mon Sep 17 00:00:00 2001 From: James Wilson Date: Thu, 26 Sep 2024 16:24:08 +0100 Subject: [PATCH 05/16] tidy some imports --- core/src/blocks/extrinsics.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/core/src/blocks/extrinsics.rs b/core/src/blocks/extrinsics.rs index 8f576259cf..9e272af111 100644 --- a/core/src/blocks/extrinsics.rs +++ b/core/src/blocks/extrinsics.rs @@ -2,8 +2,7 @@ // This file is dual-licensed as Apache-2.0 or GPL-3.0. // see LICENSE for license details. -use core::ops::Deref; - +use super::BlockError; use crate::blocks::extrinsic_signed_extensions::ExtrinsicSignedExtensions; use crate::{ config::{Config, Hasher}, @@ -12,13 +11,12 @@ use crate::{ }; use alloc::sync::Arc; use alloc::vec::Vec; +use core::ops::Deref; use scale_decode::DecodeAsType; use subxt_metadata::PalletMetadata; pub use crate::blocks::StaticExtrinsic; -use super::BlockError; - /// The body of a block. pub struct Extrinsics { extrinsics: Vec>, From ffafbf2f84f04e651baa0ed8d5a359b5be8dc7fb Mon Sep 17 00:00:00 2001 From: James Wilson Date: Thu, 26 Sep 2024 17:00:21 +0100 Subject: [PATCH 06/16] Fix a couple of tests --- subxt/examples/blocks_subscribing.rs | 1 - testing/integration-tests/src/full_client/blocks/mod.rs | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/subxt/examples/blocks_subscribing.rs b/subxt/examples/blocks_subscribing.rs index 3402a08827..e06c05f86c 100644 --- a/subxt/examples/blocks_subscribing.rs +++ b/subxt/examples/blocks_subscribing.rs @@ -52,7 +52,6 @@ async fn main() -> Result<(), Box> { println!(" Signed Extensions:"); if let Some(signed_extensions) = ext.signed_extensions() { for signed_extension in signed_extensions.iter() { - let signed_extension = signed_extension?; let name = signed_extension.name(); let value = signed_extension.value()?.to_string(); println!(" {name}: {value}"); diff --git a/testing/integration-tests/src/full_client/blocks/mod.rs b/testing/integration-tests/src/full_client/blocks/mod.rs index 145cb693ec..24c7ac0a0e 100644 --- a/testing/integration-tests/src/full_client/blocks/mod.rs +++ b/testing/integration-tests/src/full_client/blocks/mod.rs @@ -351,12 +351,12 @@ async fn decode_signed_extensions_from_blocks() { assert_eq!(extensions1.iter().count(), expected_signed_extensions.len()); for (e, expected_name) in extensions1.iter().zip(expected_signed_extensions.iter()) { - assert_eq!(e.unwrap().name(), *expected_name); + assert_eq!(e.name(), *expected_name); } assert_eq!(extensions2.iter().count(), expected_signed_extensions.len()); for (e, expected_name) in extensions2.iter().zip(expected_signed_extensions.iter()) { - assert_eq!(e.unwrap().name(), *expected_name); + assert_eq!(e.name(), *expected_name); } // check that era decodes: From 543a6ec9fc70b7d9d41a3aa90688e19cf67f911b Mon Sep 17 00:00:00 2001 From: James Wilson Date: Thu, 26 Sep 2024 18:20:27 +0100 Subject: [PATCH 07/16] Update to frame-decode 0.0.7 --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- .../src/blocks/extrinsic_signed_extensions.rs | 22 +++++++++---------- core/src/blocks/extrinsics.rs | 12 +++++----- metadata/src/lib.rs | 2 +- 5 files changed, 19 insertions(+), 23 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f858b96fe4..fda2e68775 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1783,9 +1783,9 @@ dependencies = [ [[package]] name = "frame-decode" -version = "0.0.6" +version = "0.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "849eebd821dbed6a05afdf96f4ab4d38bbf2c90f1d726f9c43ef21cf17cc52b5" +checksum = "f126c6a1f3fc3856e1d6ed0322dd44a572862cf8e48f69205b090c83454b4d16" dependencies = [ "frame-metadata 16.0.0", "hex", diff --git a/Cargo.toml b/Cargo.toml index abf5bf868c..e937b9b5ca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -77,7 +77,7 @@ console_error_panic_hook = "0.1.7" darling = "0.20.10" derive-where = "1.2.7" either = { version = "1.13.0", default-features = false } -frame-decode = { version = "0.0.6", default-features = false } +frame-decode = { version = "0.0.7", default-features = false } finito = { version = "0.1.0", default-features = false } frame-metadata = { version = "16.0.0", default-features = false } futures = { version = "0.3.30", default-features = false, features = ["std"] } diff --git a/core/src/blocks/extrinsic_signed_extensions.rs b/core/src/blocks/extrinsic_signed_extensions.rs index e1bded55c2..4731cba6b0 100644 --- a/core/src/blocks/extrinsic_signed_extensions.rs +++ b/core/src/blocks/extrinsic_signed_extensions.rs @@ -8,7 +8,7 @@ use crate::config::signed_extensions::{ use crate::config::SignedExtension; use crate::dynamic::Value; use crate::{config::Config, error::Error, Metadata}; -use frame_decode::extrinsics::ExtrinsicSignature; +use frame_decode::extrinsics::ExtrinsicExtensions; use scale_decode::DecodeAsType; /// The signed extensions of an extrinsic. @@ -16,7 +16,7 @@ use scale_decode::DecodeAsType; pub struct ExtrinsicSignedExtensions<'a, T: Config> { bytes: &'a [u8], metadata: &'a Metadata, - decoded_info: &'a ExtrinsicSignature<'static, u32>, + decoded_info: &'a ExtrinsicExtensions<'static, u32>, _marker: core::marker::PhantomData, } @@ -24,7 +24,7 @@ impl<'a, T: Config> ExtrinsicSignedExtensions<'a, T> { pub(crate) fn new( bytes: &'a [u8], metadata: &'a Metadata, - decoded_info: &'a ExtrinsicSignature<'static, u32>, + decoded_info: &'a ExtrinsicExtensions<'static, u32>, ) -> Self { Self { bytes, @@ -36,15 +36,13 @@ impl<'a, T: Config> ExtrinsicSignedExtensions<'a, T> { /// Returns an iterator over each of the signed extension details of the extrinsic. pub fn iter(&self) -> impl Iterator> { - self.decoded_info - .signed_extensions() - .map(|s| ExtrinsicSignedExtension { - bytes: &self.bytes[s.range()], - ty_id: *s.ty(), - identifier: s.name(), - metadata: self.metadata, - _marker: core::marker::PhantomData, - }) + self.decoded_info.iter().map(|s| ExtrinsicSignedExtension { + bytes: &self.bytes[s.range()], + ty_id: *s.ty(), + identifier: s.name(), + metadata: self.metadata, + _marker: core::marker::PhantomData, + }) } /// Searches through all signed extensions to find a specific one. diff --git a/core/src/blocks/extrinsics.rs b/core/src/blocks/extrinsics.rs index 9e272af111..95bd257f82 100644 --- a/core/src/blocks/extrinsics.rs +++ b/core/src/blocks/extrinsics.rs @@ -240,15 +240,15 @@ where /// Note: Returns `None` if the extrinsic is not signed. pub fn signed_extensions_bytes(&self) -> Option<&[u8]> { self.decoded_info - .signature_payload() - .map(|_| &self.bytes[self.decoded_info.signed_extensions_range()]) + .transaction_extension_payload() + .map(|t| &self.bytes[t.range()]) } /// Returns `None` if the extrinsic is not signed. pub fn signed_extensions(&self) -> Option> { self.decoded_info - .signature_payload() - .map(|s| ExtrinsicSignedExtensions::new(&self.bytes, &self.metadata, s)) + .transaction_extension_payload() + .map(|t| ExtrinsicSignedExtensions::new(&self.bytes, &self.metadata, t)) } /// The index of the pallet that the extrinsic originated from. @@ -517,9 +517,7 @@ mod tests { result.err(), Some(crate::Error::Block( crate::error::BlockError::ExtrinsicDecodeError( - ExtrinsicDecodeError::VersionNotSupported { - extrinsic_version: 3 - } + ExtrinsicDecodeError::VersionNotSupported(3) ) )) ); diff --git a/metadata/src/lib.rs b/metadata/src/lib.rs index 6979b45c9d..8e0900ea7d 100644 --- a/metadata/src/lib.rs +++ b/metadata/src/lib.rs @@ -109,7 +109,7 @@ impl frame_decode::extrinsics::ExtrinsicTypeInfo for Metadata { Ok(ExtrinsicSignatureInfo { address_id: self.extrinsic().address_ty(), signature_id: self.extrinsic().signature_ty(), - signed_extension_ids: self + transaction_extension_ids: self .extrinsic() .signed_extensions() .iter() From 6171cee463cc1f43f93ee5b08aed6d755c1150ed Mon Sep 17 00:00:00 2001 From: James Wilson Date: Thu, 26 Sep 2024 18:21:17 +0100 Subject: [PATCH 08/16] fix docs --- core/src/blocks/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/blocks/mod.rs b/core/src/blocks/mod.rs index 19d4f7cfaf..825245e3ce 100644 --- a/core/src/blocks/mod.rs +++ b/core/src/blocks/mod.rs @@ -38,7 +38,7 @@ //! ]; //! //! // Given some chain config and metadata, we know how to decode the bytes. -//! let exts = blocks::decode_from::(ext_bytes, metadata).unwrap(); +//! let exts = blocks::decode_from::(ext_bytes, metadata); //! //! // We'll see 3 extrinsics: //! assert_eq!(exts.len(), 3); From 3d7f02dff4c7c1464114d5de5994397a191f30ae Mon Sep 17 00:00:00 2001 From: James Wilson Date: Fri, 27 Sep 2024 10:59:14 +0100 Subject: [PATCH 09/16] Decode exts earlier to avoid doing it every iter/find step --- core/src/blocks/extrinsics.rs | 144 +++++++++--------- core/src/blocks/mod.rs | 3 +- subxt/examples/block_decoding_dynamic.rs | 2 - subxt/examples/blocks_subscribing.rs | 1 - subxt/src/blocks/block_types.rs | 2 +- subxt/src/blocks/extrinsic_types.rs | 17 +-- .../src/full_client/blocks/mod.rs | 3 +- .../src/full_client/client/unstable_rpcs.rs | 1 - 8 files changed, 81 insertions(+), 92 deletions(-) diff --git a/core/src/blocks/extrinsics.rs b/core/src/blocks/extrinsics.rs index 95bd257f82..ac1fe32481 100644 --- a/core/src/blocks/extrinsics.rs +++ b/core/src/blocks/extrinsics.rs @@ -14,12 +14,13 @@ use alloc::vec::Vec; use core::ops::Deref; use scale_decode::DecodeAsType; use subxt_metadata::PalletMetadata; +use frame_decode::extrinsics::Extrinsic; pub use crate::blocks::StaticExtrinsic; /// The body of a block. pub struct Extrinsics { - extrinsics: Vec>, + extrinsics: Vec, Vec)>>, metadata: Metadata, _marker: core::marker::PhantomData, } @@ -28,12 +29,29 @@ impl Extrinsics { /// Instantiate a new [`Extrinsics`] object, given a vector containing /// each extrinsic hash (in the form of bytes) and some metadata that /// we'll use to decode them. - pub fn decode_from(extrinsics: Vec>, metadata: Metadata) -> Self { - Self { + pub fn decode_from(extrinsics: Vec>, metadata: Metadata) -> Result { + let extrinsics = extrinsics.into_iter().map(|bytes| { + let cursor = &mut &*bytes; + + // Try to decode the extrinsic. + let decoded_info = + frame_decode::extrinsics::decode_extrinsic(cursor, metadata.deref(), metadata.types()) + .map_err(BlockError::ExtrinsicDecodeError)? + .into_owned(); + + // We didn't consume all bytes, so decoding probably failed. + if !cursor.is_empty() { + return Err(BlockError::LeftoverBytes(cursor.len()).into()); + } + + Ok(Arc::new((decoded_info, bytes))) + }).collect::>()?; + + Ok(Self { extrinsics, metadata, _marker: core::marker::PhantomData, - } + }) } /// The number of extrinsics. @@ -52,31 +70,17 @@ impl Extrinsics { // use of it with our `FilterExtrinsic` stuff. pub fn iter( &self, - ) -> impl Iterator, Error>> + Send + Sync + 'static { + ) -> impl Iterator> + Send + Sync + 'static { let extrinsics = self.extrinsics.clone(); let num_extrinsics = self.extrinsics.len(); let metadata = self.metadata.clone(); - let mut index = 0; - - core::iter::from_fn(move || { - if index == num_extrinsics { - None - } else { - match ExtrinsicDetails::decode_from( - index as u32, - &extrinsics[index], - metadata.clone(), - ) { - Ok(extrinsic_details) => { - index += 1; - Some(Ok(extrinsic_details)) - } - Err(e) => { - index = num_extrinsics; - Some(Err(e)) - } - } - } + + (0..num_extrinsics).map(move |index| { + ExtrinsicDetails::new( + index as u32, + extrinsics[index].clone(), + metadata.clone(), + ) }) } @@ -86,15 +90,14 @@ impl Extrinsics { pub fn find( &self, ) -> impl Iterator, Error>> + '_ { - self.iter().filter_map(|res| match res { - Err(err) => Some(Err(err)), - Ok(details) => match details.as_extrinsic::() { + self.iter().filter_map(|details| { + match details.as_extrinsic::() { // Failed to decode extrinsic: Err(err) => Some(Err(err)), // Extrinsic for a different pallet / different call (skip): Ok(None) => None, Ok(Some(value)) => Some(Ok(FoundExtrinsic { details, value })), - }, + } }) } @@ -120,10 +123,8 @@ impl Extrinsics { pub struct ExtrinsicDetails { /// The index of the extrinsic in the block. index: u32, - /// Extrinsic bytes. - bytes: Arc<[u8]>, - /// Decoded information about the extrinsic. - decoded_info: frame_decode::extrinsics::Extrinsic<'static, u32>, + /// Extrinsic bytes and decode info. + ext: Arc<(Extrinsic<'static, u32>, Vec)>, /// Subxt metadata to fetch the extrinsic metadata. metadata: Metadata, _marker: core::marker::PhantomData, @@ -135,43 +136,28 @@ where { // Attempt to dynamically decode a single extrinsic from the given input. #[doc(hidden)] - pub fn decode_from( + pub fn new( index: u32, - extrinsic_bytes: &[u8], + ext: Arc<(Extrinsic<'static, u32>, Vec)>, metadata: Metadata, - ) -> Result, Error> { - let cursor = &mut &*extrinsic_bytes; - let decoded_info = - frame_decode::extrinsics::decode_extrinsic(cursor, metadata.deref(), metadata.types()) - .map_err(BlockError::ExtrinsicDecodeError)? - .into_owned(); - - // We didn't consume all bytes. - if !cursor.is_empty() { - return Err(BlockError::LeftoverBytes(cursor.len()).into()); - } - - // Wrap all of the bytes in Arc for easy sharing. - let bytes: Arc<[u8]> = Arc::from(extrinsic_bytes); - - Ok(ExtrinsicDetails { + ) -> ExtrinsicDetails { + ExtrinsicDetails { index, - bytes, - decoded_info, + ext, metadata, _marker: core::marker::PhantomData, - }) + } } /// Calculate and return the hash of the extrinsic, based on the configured hasher. pub fn hash(&self) -> T::Hash { // Use hash(), not hash_of(), because we don't want to double encode the bytes. - T::Hasher::hash(&self.bytes) + T::Hasher::hash(&self.bytes()) } /// Is the extrinsic signed? pub fn is_signed(&self) -> bool { - self.decoded_info.is_signed() + self.decoded_info().is_signed() } /// The index of the extrinsic in the block. @@ -187,7 +173,7 @@ where /// - Extra fields /// - Extrinsic call bytes pub fn bytes(&self) -> &[u8] { - &self.bytes + &self.ext.1 } /// Return only the bytes representing this extrinsic call: @@ -199,7 +185,7 @@ where /// /// Please use [`Self::bytes`] if you want to get all extrinsic bytes. pub fn call_bytes(&self) -> &[u8] { - &self.bytes[self.decoded_info.call_data_range()] + &self.bytes()[self.decoded_info().call_data_range()] } /// Return the bytes representing the fields stored in this extrinsic. @@ -220,16 +206,16 @@ where /// /// Returns `None` if the extrinsic is not signed. pub fn address_bytes(&self) -> Option<&[u8]> { - self.decoded_info + self.decoded_info() .signature_payload() - .map(|s| &self.bytes[s.address_range()]) + .map(|s| &self.bytes()[s.address_range()]) } /// Returns Some(signature_bytes) if the extrinsic was signed otherwise None is returned. pub fn signature_bytes(&self) -> Option<&[u8]> { - self.decoded_info + self.decoded_info() .signature_payload() - .map(|s| &self.bytes[s.signature_range()]) + .map(|s| &self.bytes()[s.signature_range()]) } /// Returns the signed extension `extra` bytes of the extrinsic. @@ -239,26 +225,26 @@ where /// /// Note: Returns `None` if the extrinsic is not signed. pub fn signed_extensions_bytes(&self) -> Option<&[u8]> { - self.decoded_info + self.decoded_info() .transaction_extension_payload() - .map(|t| &self.bytes[t.range()]) + .map(|t| &self.bytes()[t.range()]) } /// Returns `None` if the extrinsic is not signed. pub fn signed_extensions(&self) -> Option> { - self.decoded_info + self.decoded_info() .transaction_extension_payload() - .map(|t| ExtrinsicSignedExtensions::new(&self.bytes, &self.metadata, t)) + .map(|t| ExtrinsicSignedExtensions::new(self.bytes(), &self.metadata, t)) } /// The index of the pallet that the extrinsic originated from. pub fn pallet_index(&self) -> u8 { - self.decoded_info.pallet_index() + self.decoded_info().pallet_index() } /// The index of the extrinsic variant that the extrinsic originated from. pub fn variant_index(&self) -> u8 { - self.decoded_info.call_index() + self.decoded_info().call_index() } /// The name of the pallet from whence the extrinsic originated. @@ -330,6 +316,10 @@ where Ok(decoded) } + + fn decoded_info(&self) -> &Extrinsic<'static, u32> { + &self.ext.0 + } } /// A Static Extrinsic found in a block coupled with it's details. @@ -499,7 +489,7 @@ mod tests { let metadata = metadata(); // Decode with empty bytes. - let result = ExtrinsicDetails::::decode_from(0, &[], metadata); + let result = Extrinsics::::decode_from(vec![vec![]], metadata); assert_matches!(result.err(), Some(crate::Error::Codec(_))); } @@ -511,7 +501,7 @@ mod tests { // Decode with invalid version. let result = - ExtrinsicDetails::::decode_from(0, &vec![3u8].encode(), metadata); + Extrinsics::::decode_from(vec![vec![3u8].encode()], metadata); assert_matches!( result.err(), @@ -542,10 +532,12 @@ mod tests { .expect("Valid dynamic parameters are provided"); // Extrinsic details ready to decode. - let extrinsic = - ExtrinsicDetails::::decode_from(1, tx_encoded.encoded(), metadata) + let extrinsics = + Extrinsics::::decode_from(vec![tx_encoded.encoded().to_owned()], metadata) .expect("Valid extrinsic"); + let extrinsic = extrinsics.iter().next().unwrap(); + // Both of these types should produce the same bytes. assert_eq!(tx_encoded.encoded(), extrinsic.bytes(), "bytes should eq"); // Both of these types should produce the same hash. @@ -570,10 +562,12 @@ mod tests { // Note: `create_unsigned` produces the extrinsic bytes by prefixing the extrinsic length. // The length is handled deserializing `ChainBlockExtrinsic`, therefore the first byte is not needed. - let extrinsic = - ExtrinsicDetails::::decode_from(1, tx_encoded.encoded(), metadata) + let extrinsics = + Extrinsics::::decode_from(vec![tx_encoded.encoded().to_owned()], metadata) .expect("Valid extrinsic"); + let extrinsic = extrinsics.iter().next().unwrap(); + assert!(!extrinsic.is_signed()); assert_eq!(extrinsic.index(), 1); diff --git a/core/src/blocks/mod.rs b/core/src/blocks/mod.rs index 825245e3ce..ef28c72efa 100644 --- a/core/src/blocks/mod.rs +++ b/core/src/blocks/mod.rs @@ -70,6 +70,7 @@ mod extrinsic_signed_extensions; mod extrinsics; mod static_extrinsic; +use crate::error::Error; use crate::config::Config; use crate::Metadata; use alloc::vec::Vec; @@ -83,6 +84,6 @@ pub use static_extrinsic::StaticExtrinsic; /// form of bytes) and some metadata that we'll use to decode them. /// /// This is a shortcut for [`Extrinsics::decode_from`]. -pub fn decode_from(extrinsics: Vec>, metadata: Metadata) -> Extrinsics { +pub fn decode_from(extrinsics: Vec>, metadata: Metadata) -> Result, Error> { Extrinsics::decode_from(extrinsics, metadata) } diff --git a/subxt/examples/block_decoding_dynamic.rs b/subxt/examples/block_decoding_dynamic.rs index 9d97eb24fe..3826d189ce 100644 --- a/subxt/examples/block_decoding_dynamic.rs +++ b/subxt/examples/block_decoding_dynamic.rs @@ -17,8 +17,6 @@ async fn main() -> Result<(), Box> { // Decode each signed extrinsic in the block dynamically let extrinsics = block.extrinsics().await?; for ext in extrinsics.iter() { - let ext = ext?; - let Some(signed_extensions) = ext.signed_extensions() else { continue; // we do not look at inherents in this example }; diff --git a/subxt/examples/blocks_subscribing.rs b/subxt/examples/blocks_subscribing.rs index e06c05f86c..4a6163d80f 100644 --- a/subxt/examples/blocks_subscribing.rs +++ b/subxt/examples/blocks_subscribing.rs @@ -26,7 +26,6 @@ async fn main() -> Result<(), Box> { // Log each of the extrinsic with it's associated events: let extrinsics = block.extrinsics().await?; for ext in extrinsics.iter() { - let ext = ext?; let idx = ext.index(); let events = ext.events().await?; let bytes_hex = format!("0x{}", hex::encode(ext.bytes())); diff --git a/subxt/src/blocks/block_types.rs b/subxt/src/blocks/block_types.rs index 0102c7ee19..b93a05ba3e 100644 --- a/subxt/src/blocks/block_types.rs +++ b/subxt/src/blocks/block_types.rs @@ -89,7 +89,7 @@ where extrinsics, self.cached_events.clone(), block_hash, - )) + )?) } /// Work with storage. diff --git a/subxt/src/blocks/extrinsic_types.rs b/subxt/src/blocks/extrinsic_types.rs index 2cb00c76e3..fbd891c313 100644 --- a/subxt/src/blocks/extrinsic_types.rs +++ b/subxt/src/blocks/extrinsic_types.rs @@ -37,14 +37,14 @@ where extrinsics: Vec>, cached_events: CachedEvents, hash: T::Hash, - ) -> Self { - let inner = CoreExtrinsics::decode_from(extrinsics, client.metadata()); - Self { + ) -> Result { + let inner = CoreExtrinsics::decode_from(extrinsics, client.metadata())?; + Ok(Self { inner, client, cached_events, hash, - } + }) } /// See [`subxt_core::blocks::Extrinsics::len()`]. @@ -67,19 +67,18 @@ where // use of it with our `FilterExtrinsic` stuff. pub fn iter( &self, - ) -> impl Iterator, Error>> + Send + Sync + 'static { + ) -> impl Iterator> + Send + Sync + 'static { let client = self.client.clone(); let cached_events = self.cached_events.clone(); let block_hash = self.hash; - self.inner.iter().map(move |res| { - let inner = res?; - Ok(ExtrinsicDetails::new( + self.inner.iter().map(move |inner| { + ExtrinsicDetails::new( inner, client.clone(), block_hash, cached_events.clone(), - )) + ) }) } diff --git a/testing/integration-tests/src/full_client/blocks/mod.rs b/testing/integration-tests/src/full_client/blocks/mod.rs index 24c7ac0a0e..8e69ffe98f 100644 --- a/testing/integration-tests/src/full_client/blocks/mod.rs +++ b/testing/integration-tests/src/full_client/blocks/mod.rs @@ -224,7 +224,6 @@ async fn fetch_block_and_decode_extrinsic_details() { let block_extrinsics = extrinsics .iter() - .map(|res| res.unwrap()) .collect::>(); let mut balance = None; @@ -299,7 +298,7 @@ async fn decode_signed_extensions_from_blocks() { let extrinsics = block.extrinsics().await.unwrap(); let extrinsic_details = extrinsics .iter() - .find_map(|e| e.ok().filter(|e| e.is_signed())) + .find(|e| e.is_signed()) .unwrap(); extrinsic_details }}; diff --git a/testing/integration-tests/src/full_client/client/unstable_rpcs.rs b/testing/integration-tests/src/full_client/client/unstable_rpcs.rs index 0e8ceb74ba..c11a4afa3d 100644 --- a/testing/integration-tests/src/full_client/client/unstable_rpcs.rs +++ b/testing/integration-tests/src/full_client/client/unstable_rpcs.rs @@ -369,7 +369,6 @@ async fn transaction_v1_broadcast() { let extrinsics = finalized.extrinsics().await.unwrap(); let block_extrinsics = extrinsics .iter() - .map(|res| res.unwrap()) .collect::>(); let Some(ext) = block_extrinsics From 3501374b8fd331ff85cb4abdf6d660cfeb4793a2 Mon Sep 17 00:00:00 2001 From: James Wilson Date: Mon, 30 Sep 2024 09:47:26 +0100 Subject: [PATCH 10/16] frame-decode to 0.1.0 --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fda2e68775..9f3172e0f7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1783,9 +1783,9 @@ dependencies = [ [[package]] name = "frame-decode" -version = "0.0.7" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f126c6a1f3fc3856e1d6ed0322dd44a572862cf8e48f69205b090c83454b4d16" +checksum = "80ed7b529f5154ac817a32f6a660e1125f9c75112ad9496c5aa6d52b22f4257b" dependencies = [ "frame-metadata 16.0.0", "hex", diff --git a/Cargo.toml b/Cargo.toml index e937b9b5ca..6e5bf3d632 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -77,8 +77,8 @@ console_error_panic_hook = "0.1.7" darling = "0.20.10" derive-where = "1.2.7" either = { version = "1.13.0", default-features = false } -frame-decode = { version = "0.0.7", default-features = false } finito = { version = "0.1.0", default-features = false } +frame-decode = { version = "0.1.0", default-features = false } frame-metadata = { version = "16.0.0", default-features = false } futures = { version = "0.3.30", default-features = false, features = ["std"] } getrandom = { version = "0.2", default-features = false } From fe4121661ae3a1517a7654d4b2d2003422f8f5ad Mon Sep 17 00:00:00 2001 From: James Wilson Date: Mon, 30 Sep 2024 11:04:34 +0100 Subject: [PATCH 11/16] fmt --- core/src/blocks/extrinsics.rs | 67 ++++++++++--------- core/src/blocks/mod.rs | 7 +- subxt/src/blocks/extrinsic_types.rs | 11 +-- .../src/full_client/blocks/mod.rs | 9 +-- .../src/full_client/client/unstable_rpcs.rs | 4 +- 5 files changed, 45 insertions(+), 53 deletions(-) diff --git a/core/src/blocks/extrinsics.rs b/core/src/blocks/extrinsics.rs index ac1fe32481..5c970aa241 100644 --- a/core/src/blocks/extrinsics.rs +++ b/core/src/blocks/extrinsics.rs @@ -12,9 +12,9 @@ use crate::{ use alloc::sync::Arc; use alloc::vec::Vec; use core::ops::Deref; +use frame_decode::extrinsics::Extrinsic; use scale_decode::DecodeAsType; use subxt_metadata::PalletMetadata; -use frame_decode::extrinsics::Extrinsic; pub use crate::blocks::StaticExtrinsic; @@ -30,22 +30,28 @@ impl Extrinsics { /// each extrinsic hash (in the form of bytes) and some metadata that /// we'll use to decode them. pub fn decode_from(extrinsics: Vec>, metadata: Metadata) -> Result { - let extrinsics = extrinsics.into_iter().map(|bytes| { - let cursor = &mut &*bytes; - - // Try to decode the extrinsic. - let decoded_info = - frame_decode::extrinsics::decode_extrinsic(cursor, metadata.deref(), metadata.types()) - .map_err(BlockError::ExtrinsicDecodeError)? - .into_owned(); - - // We didn't consume all bytes, so decoding probably failed. - if !cursor.is_empty() { - return Err(BlockError::LeftoverBytes(cursor.len()).into()); - } + let extrinsics = extrinsics + .into_iter() + .map(|bytes| { + let cursor = &mut &*bytes; + + // Try to decode the extrinsic. + let decoded_info = frame_decode::extrinsics::decode_extrinsic( + cursor, + metadata.deref(), + metadata.types(), + ) + .map_err(BlockError::ExtrinsicDecodeError)? + .into_owned(); - Ok(Arc::new((decoded_info, bytes))) - }).collect::>()?; + // We didn't consume all bytes, so decoding probably failed. + if !cursor.is_empty() { + return Err(BlockError::LeftoverBytes(cursor.len()).into()); + } + + Ok(Arc::new((decoded_info, bytes))) + }) + .collect::>()?; Ok(Self { extrinsics, @@ -68,19 +74,13 @@ impl Extrinsics { /// Returns an iterator over the extrinsics in the block body. // Dev note: The returned iterator is 'static + Send so that we can box it up and make // use of it with our `FilterExtrinsic` stuff. - pub fn iter( - &self, - ) -> impl Iterator> + Send + Sync + 'static { + pub fn iter(&self) -> impl Iterator> + Send + Sync + 'static { let extrinsics = self.extrinsics.clone(); let num_extrinsics = self.extrinsics.len(); let metadata = self.metadata.clone(); (0..num_extrinsics).map(move |index| { - ExtrinsicDetails::new( - index as u32, - extrinsics[index].clone(), - metadata.clone(), - ) + ExtrinsicDetails::new(index as u32, extrinsics[index].clone(), metadata.clone()) }) } @@ -500,8 +500,7 @@ mod tests { let metadata = metadata(); // Decode with invalid version. - let result = - Extrinsics::::decode_from(vec![vec![3u8].encode()], metadata); + let result = Extrinsics::::decode_from(vec![vec![3u8].encode()], metadata); assert_matches!( result.err(), @@ -532,9 +531,11 @@ mod tests { .expect("Valid dynamic parameters are provided"); // Extrinsic details ready to decode. - let extrinsics = - Extrinsics::::decode_from(vec![tx_encoded.encoded().to_owned()], metadata) - .expect("Valid extrinsic"); + let extrinsics = Extrinsics::::decode_from( + vec![tx_encoded.encoded().to_owned()], + metadata, + ) + .expect("Valid extrinsic"); let extrinsic = extrinsics.iter().next().unwrap(); @@ -562,9 +563,11 @@ mod tests { // Note: `create_unsigned` produces the extrinsic bytes by prefixing the extrinsic length. // The length is handled deserializing `ChainBlockExtrinsic`, therefore the first byte is not needed. - let extrinsics = - Extrinsics::::decode_from(vec![tx_encoded.encoded().to_owned()], metadata) - .expect("Valid extrinsic"); + let extrinsics = Extrinsics::::decode_from( + vec![tx_encoded.encoded().to_owned()], + metadata, + ) + .expect("Valid extrinsic"); let extrinsic = extrinsics.iter().next().unwrap(); diff --git a/core/src/blocks/mod.rs b/core/src/blocks/mod.rs index ef28c72efa..727a4f5ecc 100644 --- a/core/src/blocks/mod.rs +++ b/core/src/blocks/mod.rs @@ -70,8 +70,8 @@ mod extrinsic_signed_extensions; mod extrinsics; mod static_extrinsic; -use crate::error::Error; use crate::config::Config; +use crate::error::Error; use crate::Metadata; use alloc::vec::Vec; @@ -84,6 +84,9 @@ pub use static_extrinsic::StaticExtrinsic; /// form of bytes) and some metadata that we'll use to decode them. /// /// This is a shortcut for [`Extrinsics::decode_from`]. -pub fn decode_from(extrinsics: Vec>, metadata: Metadata) -> Result, Error> { +pub fn decode_from( + extrinsics: Vec>, + metadata: Metadata, +) -> Result, Error> { Extrinsics::decode_from(extrinsics, metadata) } diff --git a/subxt/src/blocks/extrinsic_types.rs b/subxt/src/blocks/extrinsic_types.rs index fbd891c313..e03b489257 100644 --- a/subxt/src/blocks/extrinsic_types.rs +++ b/subxt/src/blocks/extrinsic_types.rs @@ -65,20 +65,13 @@ where /// Returns an iterator over the extrinsics in the block body. // Dev note: The returned iterator is 'static + Send so that we can box it up and make // use of it with our `FilterExtrinsic` stuff. - pub fn iter( - &self, - ) -> impl Iterator> + Send + Sync + 'static { + pub fn iter(&self) -> impl Iterator> + Send + Sync + 'static { let client = self.client.clone(); let cached_events = self.cached_events.clone(); let block_hash = self.hash; self.inner.iter().map(move |inner| { - ExtrinsicDetails::new( - inner, - client.clone(), - block_hash, - cached_events.clone(), - ) + ExtrinsicDetails::new(inner, client.clone(), block_hash, cached_events.clone()) }) } diff --git a/testing/integration-tests/src/full_client/blocks/mod.rs b/testing/integration-tests/src/full_client/blocks/mod.rs index 8e69ffe98f..55b05ec512 100644 --- a/testing/integration-tests/src/full_client/blocks/mod.rs +++ b/testing/integration-tests/src/full_client/blocks/mod.rs @@ -222,9 +222,7 @@ async fn fetch_block_and_decode_extrinsic_details() { .unwrap() .is_some()); - let block_extrinsics = extrinsics - .iter() - .collect::>(); + let block_extrinsics = extrinsics.iter().collect::>(); let mut balance = None; let mut timestamp = None; @@ -296,10 +294,7 @@ async fn decode_signed_extensions_from_blocks() { let block_hash = in_block.block_hash(); let block = api.blocks().at(block_hash).await.unwrap(); let extrinsics = block.extrinsics().await.unwrap(); - let extrinsic_details = extrinsics - .iter() - .find(|e| e.is_signed()) - .unwrap(); + let extrinsic_details = extrinsics.iter().find(|e| e.is_signed()).unwrap(); extrinsic_details }}; } diff --git a/testing/integration-tests/src/full_client/client/unstable_rpcs.rs b/testing/integration-tests/src/full_client/client/unstable_rpcs.rs index c11a4afa3d..3df3696e94 100644 --- a/testing/integration-tests/src/full_client/client/unstable_rpcs.rs +++ b/testing/integration-tests/src/full_client/client/unstable_rpcs.rs @@ -367,9 +367,7 @@ async fn transaction_v1_broadcast() { } let extrinsics = finalized.extrinsics().await.unwrap(); - let block_extrinsics = extrinsics - .iter() - .collect::>(); + let block_extrinsics = extrinsics.iter().collect::>(); let Some(ext) = block_extrinsics .iter() From 7dfb66b677bb03e6e56793e86abd702d74b5a5a4 Mon Sep 17 00:00:00 2001 From: James Wilson Date: Mon, 30 Sep 2024 11:48:40 +0100 Subject: [PATCH 12/16] clippy --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- core/src/blocks/extrinsics.rs | 2 +- subxt/src/blocks/block_types.rs | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9f3172e0f7..2c060fdcff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1783,9 +1783,9 @@ dependencies = [ [[package]] name = "frame-decode" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80ed7b529f5154ac817a32f6a660e1125f9c75112ad9496c5aa6d52b22f4257b" +checksum = "5b1734d01c40fca62e70e7bc72c96441af596ffcde7f7aac7507b5de07e0307f" dependencies = [ "frame-metadata 16.0.0", "hex", diff --git a/Cargo.toml b/Cargo.toml index 6e5bf3d632..a66e83825b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -78,7 +78,7 @@ darling = "0.20.10" derive-where = "1.2.7" either = { version = "1.13.0", default-features = false } finito = { version = "0.1.0", default-features = false } -frame-decode = { version = "0.1.0", default-features = false } +frame-decode = { version = "0.2.0", default-features = false } frame-metadata = { version = "16.0.0", default-features = false } futures = { version = "0.3.30", default-features = false, features = ["std"] } getrandom = { version = "0.2", default-features = false } diff --git a/core/src/blocks/extrinsics.rs b/core/src/blocks/extrinsics.rs index 5c970aa241..476e651f86 100644 --- a/core/src/blocks/extrinsics.rs +++ b/core/src/blocks/extrinsics.rs @@ -152,7 +152,7 @@ where /// Calculate and return the hash of the extrinsic, based on the configured hasher. pub fn hash(&self) -> T::Hash { // Use hash(), not hash_of(), because we don't want to double encode the bytes. - T::Hasher::hash(&self.bytes()) + T::Hasher::hash(self.bytes()) } /// Is the extrinsic signed? diff --git a/subxt/src/blocks/block_types.rs b/subxt/src/blocks/block_types.rs index b93a05ba3e..c80fc8b6a6 100644 --- a/subxt/src/blocks/block_types.rs +++ b/subxt/src/blocks/block_types.rs @@ -84,12 +84,12 @@ where return Err(BlockError::not_found(block_hash).into()); }; - Ok(Extrinsics::new( + Extrinsics::new( self.client.clone(), extrinsics, self.cached_events.clone(), block_hash, - )?) + ) } /// Work with storage. From 7a2358cee24efa8566add2beb005fd6433165151 Mon Sep 17 00:00:00 2001 From: James Wilson Date: Mon, 30 Sep 2024 11:55:07 +0100 Subject: [PATCH 13/16] fix wasm example --- examples/wasm-example/src/services.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/wasm-example/src/services.rs b/examples/wasm-example/src/services.rs index 5b8df203ef..7897b05d95 100644 --- a/examples/wasm-example/src/services.rs +++ b/examples/wasm-example/src/services.rs @@ -49,7 +49,6 @@ pub(crate) async fn subscribe_to_finalized_blocks( writeln!(output, " Extrinsics:").ok(); let extrinsics = block.extrinsics().await?; for ext in extrinsics.iter() { - let ext = ext?; let idx = ext.index(); let events = ext.events().await?; let bytes_hex = format!("0x{}", hex::encode(ext.bytes())); From bc373827594409ba0d0ca20226ad414145cfb872 Mon Sep 17 00:00:00 2001 From: James Wilson Date: Mon, 30 Sep 2024 14:24:46 +0100 Subject: [PATCH 14/16] doc test fixes --- core/src/blocks/mod.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/core/src/blocks/mod.rs b/core/src/blocks/mod.rs index 727a4f5ecc..5bfc90d02d 100644 --- a/core/src/blocks/mod.rs +++ b/core/src/blocks/mod.rs @@ -38,21 +38,19 @@ //! ]; //! //! // Given some chain config and metadata, we know how to decode the bytes. -//! let exts = blocks::decode_from::(ext_bytes, metadata); +//! let exts = blocks::decode_from::(ext_bytes, metadata).unwrap(); //! //! // We'll see 3 extrinsics: //! assert_eq!(exts.len(), 3); //! //! // We can iterate over them and decode various details out of them. //! for ext in exts.iter() { -//! let ext = ext.unwrap(); //! println!("Pallet: {}", ext.pallet_name().unwrap()); //! println!("Call: {}", ext.variant_name().unwrap()); //! } //! //! # let ext_details: Vec<_> = exts.iter() //! # .map(|ext| { -//! # let ext = ext.unwrap(); //! # let pallet = ext.pallet_name().unwrap().to_string(); //! # let call = ext.variant_name().unwrap().to_string(); //! # (pallet, call) From c9129a83d21bfd2d533f81b79f7a2ae541a89877 Mon Sep 17 00:00:00 2001 From: James Wilson Date: Mon, 30 Sep 2024 16:41:50 +0100 Subject: [PATCH 15/16] Fix test --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- core/src/blocks/extrinsics.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2117678ac0..8797dd84c5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1782,9 +1782,9 @@ dependencies = [ [[package]] name = "frame-decode" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b1734d01c40fca62e70e7bc72c96441af596ffcde7f7aac7507b5de07e0307f" +checksum = "ed90459016b06a2855321469cb01fbc74208c80c06b085d1ed13162cf8bd7e1b" dependencies = [ "frame-metadata 16.0.0", "hex", diff --git a/Cargo.toml b/Cargo.toml index bd2d13e5d7..158d83e213 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -78,7 +78,7 @@ darling = "0.20.10" derive-where = "1.2.7" either = { version = "1.13.0", default-features = false } finito = { version = "0.1.0", default-features = false } -frame-decode = { version = "0.2.0", default-features = false } +frame-decode = { version = "0.3.0", default-features = false } frame-metadata = { version = "16.0.0", default-features = false } futures = { version = "0.3.30", default-features = false, features = ["std"] } getrandom = { version = "0.2", default-features = false } diff --git a/core/src/blocks/extrinsics.rs b/core/src/blocks/extrinsics.rs index 476e651f86..87d68fcb75 100644 --- a/core/src/blocks/extrinsics.rs +++ b/core/src/blocks/extrinsics.rs @@ -197,7 +197,7 @@ where pub fn field_bytes(&self) -> &[u8] { // Note: this cannot panic because we checked the extrinsic bytes // to contain at least two bytes. - &self.call_bytes()[2..] + &self.bytes()[self.decoded_info().call_data_args_range()] } /// Return only the bytes of the address that signed this extrinsic. From 84ce2565ce5b00f6fecf001150bd6bb89d2f6c43 Mon Sep 17 00:00:00 2001 From: James Wilson Date: Mon, 30 Sep 2024 17:11:26 +0100 Subject: [PATCH 16/16] Fix a couple of subxt_core tests --- core/src/blocks/extrinsics.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/core/src/blocks/extrinsics.rs b/core/src/blocks/extrinsics.rs index 87d68fcb75..2b82d2eb1b 100644 --- a/core/src/blocks/extrinsics.rs +++ b/core/src/blocks/extrinsics.rs @@ -490,7 +490,12 @@ mod tests { // Decode with empty bytes. let result = Extrinsics::::decode_from(vec![vec![]], metadata); - assert_matches!(result.err(), Some(crate::Error::Codec(_))); + assert_matches!( + result.err(), + Some(crate::Error::Block( + crate::error::BlockError::ExtrinsicDecodeError(_) + )) + ); } #[test] @@ -573,7 +578,7 @@ mod tests { assert!(!extrinsic.is_signed()); - assert_eq!(extrinsic.index(), 1); + assert_eq!(extrinsic.index(), 0); assert_eq!(extrinsic.pallet_index(), 0); assert_eq!(