subxt_core/blocks/
extrinsics.rs

1// Copyright 2019-2024 Parity Technologies (UK) Ltd.
2// This file is dual-licensed as Apache-2.0 or GPL-3.0.
3// see LICENSE for license details.
4
5use super::BlockError;
6use crate::blocks::extrinsic_transaction_extensions::ExtrinsicTransactionExtensions;
7use crate::{
8    config::{Config, Hasher},
9    error::{Error, MetadataError},
10    Metadata,
11};
12use alloc::sync::Arc;
13use alloc::vec::Vec;
14use core::ops::Deref;
15use frame_decode::extrinsics::Extrinsic;
16use scale_decode::DecodeAsType;
17use subxt_metadata::PalletMetadata;
18
19pub use crate::blocks::StaticExtrinsic;
20
21/// The body of a block.
22pub struct Extrinsics<T: Config> {
23    extrinsics: Vec<Arc<(Extrinsic<'static, u32>, Vec<u8>)>>,
24    metadata: Metadata,
25    _marker: core::marker::PhantomData<T>,
26}
27
28impl<T: Config> Extrinsics<T> {
29    /// Instantiate a new [`Extrinsics`] object, given a vector containing
30    /// each extrinsic hash (in the form of bytes) and some metadata that
31    /// we'll use to decode them.
32    pub fn decode_from(extrinsics: Vec<Vec<u8>>, metadata: Metadata) -> Result<Self, Error> {
33        let extrinsics = extrinsics
34            .into_iter()
35            .enumerate()
36            .map(|(extrinsic_index, bytes)| {
37                let cursor = &mut &*bytes;
38
39                // Try to decode the extrinsic.
40                let decoded_info = frame_decode::extrinsics::decode_extrinsic(
41                    cursor,
42                    metadata.deref(),
43                    metadata.types(),
44                )
45                .map_err(|error| BlockError::ExtrinsicDecodeError {
46                    extrinsic_index,
47                    error,
48                })?
49                .into_owned();
50
51                // We didn't consume all bytes, so decoding probably failed.
52                if !cursor.is_empty() {
53                    return Err(BlockError::LeftoverBytes {
54                        extrinsic_index,
55                        num_leftover_bytes: cursor.len(),
56                    }
57                    .into());
58                }
59
60                Ok(Arc::new((decoded_info, bytes)))
61            })
62            .collect::<Result<_, Error>>()?;
63
64        Ok(Self {
65            extrinsics,
66            metadata,
67            _marker: core::marker::PhantomData,
68        })
69    }
70
71    /// The number of extrinsics.
72    pub fn len(&self) -> usize {
73        self.extrinsics.len()
74    }
75
76    /// Are there no extrinsics in this block?
77    // Note: mainly here to satisfy clippy.
78    pub fn is_empty(&self) -> bool {
79        self.extrinsics.is_empty()
80    }
81
82    /// Returns an iterator over the extrinsics in the block body.
83    // Dev note: The returned iterator is 'static + Send so that we can box it up and make
84    // use of it with our `FilterExtrinsic` stuff.
85    pub fn iter(&self) -> impl Iterator<Item = ExtrinsicDetails<T>> + Send + Sync + 'static {
86        let extrinsics = self.extrinsics.clone();
87        let num_extrinsics = self.extrinsics.len();
88        let metadata = self.metadata.clone();
89
90        (0..num_extrinsics).map(move |index| {
91            ExtrinsicDetails::new(index as u32, extrinsics[index].clone(), metadata.clone())
92        })
93    }
94
95    /// Iterate through the extrinsics using metadata to dynamically decode and skip
96    /// them, and return only those which should decode to the provided `E` type.
97    /// If an error occurs, all subsequent iterations return `None`.
98    pub fn find<E: StaticExtrinsic>(
99        &self,
100    ) -> impl Iterator<Item = Result<FoundExtrinsic<T, E>, Error>> + '_ {
101        self.iter().filter_map(|details| {
102            match details.as_extrinsic::<E>() {
103                // Failed to decode extrinsic:
104                Err(err) => Some(Err(err)),
105                // Extrinsic for a different pallet / different call (skip):
106                Ok(None) => None,
107                Ok(Some(value)) => Some(Ok(FoundExtrinsic { details, value })),
108            }
109        })
110    }
111
112    /// Iterate through the extrinsics using metadata to dynamically decode and skip
113    /// them, and return the first extrinsic found which decodes to the provided `E` type.
114    pub fn find_first<E: StaticExtrinsic>(&self) -> Result<Option<FoundExtrinsic<T, E>>, Error> {
115        self.find::<E>().next().transpose()
116    }
117
118    /// Iterate through the extrinsics using metadata to dynamically decode and skip
119    /// them, and return the last extrinsic found which decodes to the provided `Ev` type.
120    pub fn find_last<E: StaticExtrinsic>(&self) -> Result<Option<FoundExtrinsic<T, E>>, Error> {
121        self.find::<E>().last().transpose()
122    }
123
124    /// Find an extrinsics that decodes to the type provided. Returns true if it was found.
125    pub fn has<E: StaticExtrinsic>(&self) -> Result<bool, Error> {
126        Ok(self.find::<E>().next().transpose()?.is_some())
127    }
128}
129
130/// A single extrinsic in a block.
131pub struct ExtrinsicDetails<T: Config> {
132    /// The index of the extrinsic in the block.
133    index: u32,
134    /// Extrinsic bytes and decode info.
135    ext: Arc<(Extrinsic<'static, u32>, Vec<u8>)>,
136    /// Subxt metadata to fetch the extrinsic metadata.
137    metadata: Metadata,
138    _marker: core::marker::PhantomData<T>,
139}
140
141impl<T> ExtrinsicDetails<T>
142where
143    T: Config,
144{
145    // Attempt to dynamically decode a single extrinsic from the given input.
146    #[doc(hidden)]
147    pub fn new(
148        index: u32,
149        ext: Arc<(Extrinsic<'static, u32>, Vec<u8>)>,
150        metadata: Metadata,
151    ) -> ExtrinsicDetails<T> {
152        ExtrinsicDetails {
153            index,
154            ext,
155            metadata,
156            _marker: core::marker::PhantomData,
157        }
158    }
159
160    /// Calculate and return the hash of the extrinsic, based on the configured hasher.
161    pub fn hash(&self) -> T::Hash {
162        // Use hash(), not hash_of(), because we don't want to double encode the bytes.
163        T::Hasher::hash(self.bytes())
164    }
165
166    /// Is the extrinsic signed?
167    pub fn is_signed(&self) -> bool {
168        self.decoded_info().is_signed()
169    }
170
171    /// The index of the extrinsic in the block.
172    pub fn index(&self) -> u32 {
173        self.index
174    }
175
176    /// Return _all_ of the bytes representing this extrinsic, which include, in order:
177    /// - First byte: abbbbbbb (a = 0 for unsigned, 1 for signed, b = version)
178    /// - SignatureType (if the payload is signed)
179    ///   - Address
180    ///   - Signature
181    ///   - Extra fields
182    /// - Extrinsic call bytes
183    pub fn bytes(&self) -> &[u8] {
184        &self.ext.1
185    }
186
187    /// Return only the bytes representing this extrinsic call:
188    /// - First byte is the pallet index
189    /// - Second byte is the variant (call) index
190    /// - Followed by field bytes.
191    ///
192    /// # Note
193    ///
194    /// Please use [`Self::bytes`] if you want to get all extrinsic bytes.
195    pub fn call_bytes(&self) -> &[u8] {
196        &self.bytes()[self.decoded_info().call_data_range()]
197    }
198
199    /// Return the bytes representing the fields stored in this extrinsic.
200    ///
201    /// # Note
202    ///
203    /// This is a subset of [`Self::call_bytes`] that does not include the
204    /// first two bytes that denote the pallet index and the variant index.
205    pub fn field_bytes(&self) -> &[u8] {
206        // Note: this cannot panic because we checked the extrinsic bytes
207        // to contain at least two bytes.
208        &self.bytes()[self.decoded_info().call_data_args_range()]
209    }
210
211    /// Return only the bytes of the address that signed this extrinsic.
212    ///
213    /// # Note
214    ///
215    /// Returns `None` if the extrinsic is not signed.
216    pub fn address_bytes(&self) -> Option<&[u8]> {
217        self.decoded_info()
218            .signature_payload()
219            .map(|s| &self.bytes()[s.address_range()])
220    }
221
222    /// Returns Some(signature_bytes) if the extrinsic was signed otherwise None is returned.
223    pub fn signature_bytes(&self) -> Option<&[u8]> {
224        self.decoded_info()
225            .signature_payload()
226            .map(|s| &self.bytes()[s.signature_range()])
227    }
228
229    /// Returns the signed extension `extra` bytes of the extrinsic.
230    /// Each signed extension has an `extra` type (May be zero-sized).
231    /// These bytes are the scale encoded `extra` fields of each signed extension in order of the signed extensions.
232    /// They do *not* include the `additional` signed bytes that are used as part of the payload that is signed.
233    ///
234    /// Note: Returns `None` if the extrinsic is not signed.
235    pub fn transaction_extensions_bytes(&self) -> Option<&[u8]> {
236        self.decoded_info()
237            .transaction_extension_payload()
238            .map(|t| &self.bytes()[t.range()])
239    }
240
241    /// Returns `None` if the extrinsic is not signed.
242    pub fn transaction_extensions(&self) -> Option<ExtrinsicTransactionExtensions<'_, T>> {
243        self.decoded_info()
244            .transaction_extension_payload()
245            .map(|t| ExtrinsicTransactionExtensions::new(self.bytes(), &self.metadata, t))
246    }
247
248    /// The index of the pallet that the extrinsic originated from.
249    pub fn pallet_index(&self) -> u8 {
250        self.decoded_info().pallet_index()
251    }
252
253    /// The index of the extrinsic variant that the extrinsic originated from.
254    pub fn variant_index(&self) -> u8 {
255        self.decoded_info().call_index()
256    }
257
258    /// The name of the pallet from whence the extrinsic originated.
259    pub fn pallet_name(&self) -> Result<&str, Error> {
260        Ok(self.extrinsic_metadata()?.pallet.name())
261    }
262
263    /// The name of the call (ie the name of the variant that it corresponds to).
264    pub fn variant_name(&self) -> Result<&str, Error> {
265        Ok(&self.extrinsic_metadata()?.variant.name)
266    }
267
268    /// Fetch the metadata for this extrinsic.
269    pub fn extrinsic_metadata(&self) -> Result<ExtrinsicMetadataDetails, Error> {
270        let pallet = self.metadata.pallet_by_index_err(self.pallet_index())?;
271        let variant = pallet
272            .call_variant_by_index(self.variant_index())
273            .ok_or_else(|| MetadataError::VariantIndexNotFound(self.variant_index()))?;
274
275        Ok(ExtrinsicMetadataDetails { pallet, variant })
276    }
277
278    /// Decode and provide the extrinsic fields back in the form of a [`scale_value::Composite`]
279    /// type which represents the named or unnamed fields that were present in the extrinsic.
280    pub fn field_values(&self) -> Result<scale_value::Composite<u32>, Error> {
281        let bytes = &mut self.field_bytes();
282        let extrinsic_metadata = self.extrinsic_metadata()?;
283
284        let mut fields = extrinsic_metadata
285            .variant
286            .fields
287            .iter()
288            .map(|f| scale_decode::Field::new(f.ty.id, f.name.as_deref()));
289        let decoded =
290            scale_value::scale::decode_as_fields(bytes, &mut fields, self.metadata.types())?;
291
292        Ok(decoded)
293    }
294
295    /// Attempt to decode these [`ExtrinsicDetails`] into a type representing the extrinsic fields.
296    /// Such types are exposed in the codegen as `pallet_name::calls::types::CallName` types.
297    pub fn as_extrinsic<E: StaticExtrinsic>(&self) -> Result<Option<E>, Error> {
298        let extrinsic_metadata = self.extrinsic_metadata()?;
299        if extrinsic_metadata.pallet.name() == E::PALLET
300            && extrinsic_metadata.variant.name == E::CALL
301        {
302            let mut fields = extrinsic_metadata
303                .variant
304                .fields
305                .iter()
306                .map(|f| scale_decode::Field::new(f.ty.id, f.name.as_deref()));
307            let decoded =
308                E::decode_as_fields(&mut self.field_bytes(), &mut fields, self.metadata.types())?;
309            Ok(Some(decoded))
310        } else {
311            Ok(None)
312        }
313    }
314
315    /// Attempt to decode these [`ExtrinsicDetails`] into an outer call enum type (which includes
316    /// the pallet and extrinsic enum variants as well as the extrinsic fields). A compatible
317    /// type for this is exposed via static codegen as a root level `Call` type.
318    pub fn as_root_extrinsic<E: DecodeAsType>(&self) -> Result<E, Error> {
319        let decoded = E::decode_as_type(
320            &mut &self.call_bytes()[..],
321            self.metadata.outer_enums().call_enum_ty(),
322            self.metadata.types(),
323        )?;
324
325        Ok(decoded)
326    }
327
328    fn decoded_info(&self) -> &Extrinsic<'static, u32> {
329        &self.ext.0
330    }
331}
332
333/// A Static Extrinsic found in a block coupled with it's details.
334pub struct FoundExtrinsic<T: Config, E> {
335    /// Details for the extrinsic.
336    pub details: ExtrinsicDetails<T>,
337    /// The decoded extrinsic value.
338    pub value: E,
339}
340
341/// Details for the given extrinsic plucked from the metadata.
342pub struct ExtrinsicMetadataDetails<'a> {
343    /// Metadata for the pallet that the extrinsic belongs to.
344    pub pallet: PalletMetadata<'a>,
345    /// Metadata for the variant which describes the pallet extrinsics.
346    pub variant: &'a scale_info::Variant<scale_info::form::PortableForm>,
347}
348
349#[cfg(test)]
350mod tests {
351    use super::*;
352    use crate::config::SubstrateConfig;
353    use assert_matches::assert_matches;
354    use codec::{Decode, Encode};
355    use frame_metadata::v15::{CustomMetadata, OuterEnums};
356    use frame_metadata::{
357        v15::{ExtrinsicMetadata, PalletCallMetadata, PalletMetadata, RuntimeMetadataV15},
358        RuntimeMetadataPrefixed,
359    };
360    use scale_info::{meta_type, TypeInfo};
361    use scale_value::Value;
362
363    // Extrinsic needs to contain at least the generic type parameter "Call"
364    // for the metadata to be valid.
365    // The "Call" type from the metadata is used to decode extrinsics.
366    #[allow(unused)]
367    #[derive(TypeInfo)]
368    struct ExtrinsicType<Address, Call, Signature, Extra> {
369        pub signature: Option<(Address, Signature, Extra)>,
370        pub function: Call,
371    }
372
373    // Because this type is used to decode extrinsics, we expect this to be a TypeDefVariant.
374    // Each pallet must contain one single variant.
375    #[allow(unused)]
376    #[derive(
377        Encode,
378        Decode,
379        TypeInfo,
380        Clone,
381        Debug,
382        PartialEq,
383        Eq,
384        scale_encode::EncodeAsType,
385        scale_decode::DecodeAsType,
386    )]
387    enum RuntimeCall {
388        Test(Pallet),
389    }
390
391    // The calls of the pallet.
392    #[allow(unused)]
393    #[derive(
394        Encode,
395        Decode,
396        TypeInfo,
397        Clone,
398        Debug,
399        PartialEq,
400        Eq,
401        scale_encode::EncodeAsType,
402        scale_decode::DecodeAsType,
403    )]
404    enum Pallet {
405        #[allow(unused)]
406        #[codec(index = 2)]
407        TestCall {
408            value: u128,
409            signed: bool,
410            name: String,
411        },
412    }
413
414    #[allow(unused)]
415    #[derive(
416        Encode,
417        Decode,
418        TypeInfo,
419        Clone,
420        Debug,
421        PartialEq,
422        Eq,
423        scale_encode::EncodeAsType,
424        scale_decode::DecodeAsType,
425    )]
426    struct TestCallExtrinsic {
427        value: u128,
428        signed: bool,
429        name: String,
430    }
431
432    impl StaticExtrinsic for TestCallExtrinsic {
433        const PALLET: &'static str = "Test";
434        const CALL: &'static str = "TestCall";
435    }
436
437    /// Build fake metadata consisting the types needed to represent an extrinsic.
438    fn metadata() -> Metadata {
439        let pallets = vec![PalletMetadata {
440            name: "Test",
441            storage: None,
442            calls: Some(PalletCallMetadata {
443                ty: meta_type::<Pallet>(),
444            }),
445            event: None,
446            constants: vec![],
447            error: None,
448            index: 0,
449            docs: vec![],
450        }];
451
452        let extrinsic = ExtrinsicMetadata {
453            version: 4,
454            signed_extensions: vec![],
455            address_ty: meta_type::<()>(),
456            call_ty: meta_type::<RuntimeCall>(),
457            signature_ty: meta_type::<()>(),
458            extra_ty: meta_type::<()>(),
459        };
460
461        let meta = RuntimeMetadataV15::new(
462            pallets,
463            extrinsic,
464            meta_type::<()>(),
465            vec![],
466            OuterEnums {
467                call_enum_ty: meta_type::<RuntimeCall>(),
468                event_enum_ty: meta_type::<()>(),
469                error_enum_ty: meta_type::<()>(),
470            },
471            CustomMetadata {
472                map: Default::default(),
473            },
474        );
475        let runtime_metadata: RuntimeMetadataPrefixed = meta.into();
476        let metadata: subxt_metadata::Metadata = runtime_metadata.try_into().unwrap();
477
478        Metadata::from(metadata)
479    }
480
481    #[test]
482    fn extrinsic_metadata_consistency() {
483        let metadata = metadata();
484
485        // Except our metadata to contain the registered types.
486        let pallet = metadata.pallet_by_index(0).expect("pallet exists");
487        let extrinsic = pallet
488            .call_variant_by_index(2)
489            .expect("metadata contains the RuntimeCall enum with this pallet");
490
491        assert_eq!(pallet.name(), "Test");
492        assert_eq!(&extrinsic.name, "TestCall");
493    }
494
495    #[test]
496    fn insufficient_extrinsic_bytes() {
497        let metadata = metadata();
498
499        // Decode with empty bytes.
500        let result = Extrinsics::<SubstrateConfig>::decode_from(vec![vec![]], metadata);
501        assert_matches!(
502            result.err(),
503            Some(crate::Error::Block(
504                crate::error::BlockError::ExtrinsicDecodeError {
505                    extrinsic_index: 0,
506                    error: _
507                }
508            ))
509        );
510    }
511
512    #[test]
513    fn unsupported_version_extrinsic() {
514        use frame_decode::extrinsics::ExtrinsicDecodeError;
515
516        let metadata = metadata();
517
518        // Decode with invalid version.
519        let result = Extrinsics::<SubstrateConfig>::decode_from(vec![vec![3u8].encode()], metadata);
520
521        assert_matches!(
522            result.err(),
523            Some(crate::Error::Block(
524                crate::error::BlockError::ExtrinsicDecodeError {
525                    extrinsic_index: 0,
526                    error: ExtrinsicDecodeError::VersionNotSupported(3),
527                }
528            ))
529        );
530    }
531
532    #[test]
533    fn tx_hashes_line_up() {
534        let metadata = metadata();
535
536        let tx = crate::dynamic::tx(
537            "Test",
538            "TestCall",
539            vec![
540                Value::u128(10),
541                Value::bool(true),
542                Value::string("SomeValue"),
543            ],
544        );
545
546        // Encoded TX ready to submit.
547        let tx_encoded = crate::tx::create_v4_unsigned::<SubstrateConfig, _>(&tx, &metadata)
548            .expect("Valid dynamic parameters are provided");
549
550        // Extrinsic details ready to decode.
551        let extrinsics = Extrinsics::<SubstrateConfig>::decode_from(
552            vec![tx_encoded.encoded().to_owned()],
553            metadata,
554        )
555        .expect("Valid extrinsic");
556
557        let extrinsic = extrinsics.iter().next().unwrap();
558
559        // Both of these types should produce the same bytes.
560        assert_eq!(tx_encoded.encoded(), extrinsic.bytes(), "bytes should eq");
561        // Both of these types should produce the same hash.
562        assert_eq!(tx_encoded.hash(), extrinsic.hash(), "hashes should eq");
563    }
564
565    #[test]
566    fn statically_decode_extrinsic() {
567        let metadata = metadata();
568
569        let tx = crate::dynamic::tx(
570            "Test",
571            "TestCall",
572            vec![
573                Value::u128(10),
574                Value::bool(true),
575                Value::string("SomeValue"),
576            ],
577        );
578        let tx_encoded = crate::tx::create_v4_unsigned::<SubstrateConfig, _>(&tx, &metadata)
579            .expect("Valid dynamic parameters are provided");
580
581        // Note: `create_unsigned` produces the extrinsic bytes by prefixing the extrinsic length.
582        // The length is handled deserializing `ChainBlockExtrinsic`, therefore the first byte is not needed.
583        let extrinsics = Extrinsics::<SubstrateConfig>::decode_from(
584            vec![tx_encoded.encoded().to_owned()],
585            metadata,
586        )
587        .expect("Valid extrinsic");
588
589        let extrinsic = extrinsics.iter().next().unwrap();
590
591        assert!(!extrinsic.is_signed());
592
593        assert_eq!(extrinsic.index(), 0);
594
595        assert_eq!(extrinsic.pallet_index(), 0);
596        assert_eq!(
597            extrinsic
598                .pallet_name()
599                .expect("Valid metadata contains pallet name"),
600            "Test"
601        );
602
603        assert_eq!(extrinsic.variant_index(), 2);
604        assert_eq!(
605            extrinsic
606                .variant_name()
607                .expect("Valid metadata contains variant name"),
608            "TestCall"
609        );
610
611        // Decode the extrinsic to the root enum.
612        let decoded_extrinsic = extrinsic
613            .as_root_extrinsic::<RuntimeCall>()
614            .expect("can decode extrinsic to root enum");
615
616        assert_eq!(
617            decoded_extrinsic,
618            RuntimeCall::Test(Pallet::TestCall {
619                value: 10,
620                signed: true,
621                name: "SomeValue".into(),
622            })
623        );
624
625        // Decode the extrinsic to the extrinsic variant.
626        let decoded_extrinsic = extrinsic
627            .as_extrinsic::<TestCallExtrinsic>()
628            .expect("can decode extrinsic to extrinsic variant")
629            .expect("value cannot be None");
630
631        assert_eq!(
632            decoded_extrinsic,
633            TestCallExtrinsic {
634                value: 10,
635                signed: true,
636                name: "SomeValue".into(),
637            }
638        );
639    }
640}