frame_decode/methods/
extrinsic_decoder.rs

1// Copyright (C) 2022-2023 Parity Technologies (UK) Ltd. (admin@parity.io)
2// This file is a part of the scale-value crate.
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8//         http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16use crate::methods::extrinsic_type_info::ExtrinsicInfoError;
17use crate::methods::extrinsic_type_info::ExtrinsicTypeInfo;
18use crate::utils::{decode_with_error_tracing, DecodeErrorTrace};
19use alloc::borrow::Cow;
20use alloc::string::{String, ToString};
21use alloc::vec;
22use alloc::vec::Vec;
23use core::ops::Range;
24use parity_scale_codec::{Compact, Decode};
25use scale_type_resolver::TypeResolver;
26
27/// An error returned trying to decode extrinsic bytes.
28#[non_exhaustive]
29#[allow(missing_docs)]
30#[derive(Debug, Clone, thiserror::Error)]
31pub enum ExtrinsicDecodeError {
32    #[error("Cannot decode the compact-encoded extrinsic length.")]
33    CannotDecodeLength,
34    #[error(
35        "The actual number of bytes does not match the compact-encoded extrinsic length; expected {expected_len} bytes but got {actual_len} bytes."
36    )]
37    WrongLength {
38        expected_len: usize,
39        actual_len: usize,
40    },
41    #[error("Not enough bytes to decode a valid extrinsic.")]
42    NotEnoughBytes,
43    #[error("This extrinsic version ({0}) is not supported.")]
44    VersionNotSupported(u8),
45    #[error("The extrinsic type 0b{extrinsic_type:02b} is not supported (given extrinsic version {version}).")]
46    ExtrinsicTypeNotSupported { version: u8, extrinsic_type: u8 },
47    #[error("Cannot get extrinsic info:\n\n{0}")]
48    CannotGetInfo(ExtrinsicInfoError<'static>),
49    #[error("Cannot decode signature:\n\n{0}")]
50    CannotDecodeSignature(DecodeErrorTrace),
51    #[error("Cannot decode pallet index byte:\n\n{0}")]
52    CannotDecodePalletIndex(parity_scale_codec::Error),
53    #[error("Cannot decode call index byte:\n\n{0}")]
54    CannotDecodeCallIndex(parity_scale_codec::Error),
55    #[error("Cannot decode transaction extensions version byte:\n\n{0}")]
56    CannotDecodeExtensionsVersion(parity_scale_codec::Error),
57    #[error("Cannot decode call data for argument {argument_name} in {pallet_name}.{call_name}:\n\n{reason}")]
58    CannotDecodeCallData {
59        pallet_name: String,
60        call_name: String,
61        argument_name: String,
62        reason: DecodeErrorTrace,
63    },
64}
65
66/// An owned variant of an Extrinsic (note: this may still contain
67/// references if the visitor used to decode the extrinsic contents holds
68/// onto any)
69pub type ExtrinsicOwned<TypeId> = Extrinsic<'static, TypeId>;
70
71/// Information about the extrinsic.
72#[derive(Clone, Debug)]
73pub struct Extrinsic<'info, TypeId> {
74    compact_prefix_len: u8,
75    version: u8,
76    version_ty: ExtrinsicType,
77    byte_len: u32,
78    signature: Option<ExtrinsicSignature<TypeId>>,
79    extensions: Option<ExtrinsicExtensions<'info, TypeId>>,
80    pallet_name: Cow<'info, str>,
81    pallet_index: u8,
82    pallet_index_idx: u32,
83    call_name: Cow<'info, str>,
84    call_index: u8,
85    call_data: Vec<NamedArg<'info, TypeId>>,
86}
87
88impl<'info, TypeId> Extrinsic<'info, TypeId> {
89    /// Take ownership of the extrinsic, so that it no longer references
90    /// the extrinsic info or bytes.
91    pub fn into_owned(self) -> ExtrinsicOwned<TypeId> {
92        Extrinsic {
93            compact_prefix_len: self.compact_prefix_len,
94            version: self.version,
95            version_ty: self.version_ty,
96            byte_len: self.byte_len,
97            signature: self.signature,
98            extensions: self.extensions.map(|e| e.into_owned()),
99            pallet_name: Cow::Owned(self.pallet_name.into_owned()),
100            pallet_index: self.pallet_index,
101            pallet_index_idx: self.pallet_index_idx,
102            call_name: Cow::Owned(self.call_name.into_owned()),
103            call_index: self.call_index,
104            call_data: self.call_data.into_iter().map(|e| e.into_owned()).collect(),
105        }
106    }
107
108    /// The extrinsic version.
109    pub fn version(&self) -> u8 {
110        self.version
111    }
112
113    /// The type of the extrinsic.
114    pub fn ty(&self) -> ExtrinsicType {
115        self.version_ty
116    }
117
118    /// The length of the extrinsic payload, excluding the prefixed compact-encoded length bytes.
119    #[allow(clippy::len_without_is_empty)]
120    pub fn len(&self) -> usize {
121        self.byte_len as usize
122    }
123
124    /// The name of the pallet that this extrinsic is calling into.
125    pub fn pallet_name(&self) -> &str {
126        &self.pallet_name
127    }
128
129    /// the index of the pallet that this extrinsic is calling into.
130    pub fn pallet_index(&self) -> u8 {
131        self.pallet_index
132    }
133
134    /// The name of the call that the extrinsic is making.
135    pub fn call_name(&self) -> &str {
136        &self.call_name
137    }
138
139    /// the index of the call that the extrinsic is making.
140    pub fn call_index(&self) -> u8 {
141        self.call_index
142    }
143
144    /// Does the extrinsic have a signature.
145    pub fn is_signed(&self) -> bool {
146        self.signature.is_some()
147    }
148
149    /// Return the extrinsic signature payload, if present. This contains the
150    /// address and signature information.
151    pub fn signature_payload(&self) -> Option<&ExtrinsicSignature<TypeId>> {
152        self.signature.as_ref()
153    }
154
155    /// Return the transaction extension payload, if present. This contains the
156    /// transaction extensions.
157    pub fn transaction_extension_payload(&self) -> Option<&ExtrinsicExtensions<'info, TypeId>> {
158        self.extensions.as_ref()
159    }
160
161    /// Iterate over the call data argument names and types.
162    pub fn call_data(&self) -> impl ExactSizeIterator<Item = &NamedArg<'info, TypeId>> {
163        self.call_data.iter()
164    }
165
166    /// Return a range denoting the call data bytes. This includes the pallet index and
167    /// call index bytes and then any encoded arguments for the call.
168    pub fn call_data_range(&self) -> Range<usize> {
169        let start = self.pallet_index_idx as usize;
170        let end = self
171            .call_data()
172            .map(|a| a.range.end as usize)
173            .max()
174            .unwrap_or(start);
175
176        Range { start, end }
177    }
178
179    /// Return a range denoting the arguments given to the call. This does *not* include
180    /// the pallet index and call index bytes.
181    pub fn call_data_args_range(&self) -> Range<usize> {
182        let start = (self.pallet_index_idx + 2) as usize;
183        let end = self
184            .call_data()
185            .map(|a| a.range.end as usize)
186            .max()
187            .unwrap_or(start);
188
189        Range { start, end }
190    }
191
192    /// Map the signature type IDs to something else.
193    pub fn map_type_id<NewTypeId, F>(self, mut f: F) -> Extrinsic<'info, NewTypeId>
194    where
195        F: FnMut(TypeId) -> NewTypeId,
196    {
197        Extrinsic {
198            compact_prefix_len: self.compact_prefix_len,
199            version: self.version,
200            version_ty: self.version_ty,
201            byte_len: self.byte_len,
202            signature: self.signature.map(|s| s.map_type_id(&mut f)),
203            extensions: self.extensions.map(|e| e.map_type_id(&mut f)),
204            pallet_name: self.pallet_name,
205            pallet_index: self.pallet_index,
206            pallet_index_idx: self.pallet_index_idx,
207            call_name: self.call_name,
208            call_index: self.call_index,
209            call_data: self
210                .call_data
211                .into_iter()
212                .map(|s| s.map_type_id(&mut f))
213                .collect(),
214        }
215    }
216}
217
218/// The type of the extrinsic.
219#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Ord, PartialOrd)]
220pub enum ExtrinsicType {
221    /// Only call data
222    Bare,
223    /// Transaction extensions and call data
224    General,
225    /// Address, signature, transaction extensions and call data
226    Signed,
227}
228
229/// Information about the extrinsic signature.
230#[derive(Clone, Debug)]
231pub struct ExtrinsicSignature<TypeId> {
232    // Store byte offsets so people can ask for raw
233    // bytes to do their own thing.
234    address_start_idx: u32,
235    address_end_idx: u32,
236    signature_end_idx: u32,
237    // Also decode and store actual values. We could
238    // do this more "on demand" but it complicates
239    // everything. Ultimately just a couple of vec allocs
240    // that we could perhaps optimise into SmallVecs or
241    // something if desired.
242    address_ty: TypeId,
243    signature_ty: TypeId,
244}
245
246impl<TypeId> ExtrinsicSignature<TypeId> {
247    /// Return a range denoting the address bytes.
248    pub fn address_range(&self) -> Range<usize> {
249        Range {
250            start: self.address_start_idx as usize,
251            end: self.address_end_idx as usize,
252        }
253    }
254
255    /// The decoded address.
256    pub fn address_type(&self) -> &TypeId {
257        &self.address_ty
258    }
259
260    /// Return a range denoting the signature bytes.
261    pub fn signature_range(&self) -> Range<usize> {
262        Range {
263            start: self.address_end_idx as usize,
264            end: self.signature_end_idx as usize,
265        }
266    }
267
268    /// The decoded signature.
269    pub fn signature_type(&self) -> &TypeId {
270        &self.signature_ty
271    }
272
273    /// Map the signature type IDs to something else.
274    pub fn map_type_id<NewTypeId, F>(self, mut f: F) -> ExtrinsicSignature<NewTypeId>
275    where
276        F: FnMut(TypeId) -> NewTypeId,
277    {
278        ExtrinsicSignature {
279            address_start_idx: self.address_start_idx,
280            address_end_idx: self.address_end_idx,
281            signature_end_idx: self.signature_end_idx,
282            address_ty: f(self.address_ty),
283            signature_ty: f(self.signature_ty),
284        }
285    }
286}
287
288/// Information about the extrinsic signed extensions.
289#[derive(Clone, Debug)]
290pub struct ExtrinsicExtensions<'info, TypeId> {
291    transaction_extensions_version: u8,
292    transaction_extensions: Vec<NamedArg<'info, TypeId>>,
293}
294
295impl<'info, TypeId> ExtrinsicExtensions<'info, TypeId> {
296    /// Take ownership of the signature.
297    pub fn into_owned(self) -> ExtrinsicExtensions<'static, TypeId> {
298        ExtrinsicExtensions {
299            transaction_extensions_version: self.transaction_extensions_version,
300            transaction_extensions: self
301                .transaction_extensions
302                .into_iter()
303                .map(|e| e.into_owned())
304                .collect(),
305        }
306    }
307
308    /// The version of the transaction extensions
309    pub fn version(&self) -> u8 {
310        self.transaction_extensions_version
311    }
312
313    /// Iterate over the signed extension argument names and types.
314    pub fn iter(&self) -> impl ExactSizeIterator<Item = &NamedArg<'info, TypeId>> {
315        self.transaction_extensions.iter()
316    }
317
318    /// Return a range denoting the transaction extension bytes. This does
319    /// *not* include any version byte.
320    pub fn range(&self) -> Range<usize> {
321        let start = self
322            .iter()
323            .map(|a| a.range.start as usize)
324            .min()
325            .unwrap_or(0);
326        let end = self
327            .iter()
328            .map(|a| a.range.end as usize)
329            .max()
330            .unwrap_or(start);
331
332        Range { start, end }
333    }
334
335    /// Map the extensions type IDs to something else.
336    pub fn map_type_id<NewTypeId, F>(self, mut f: F) -> ExtrinsicExtensions<'info, NewTypeId>
337    where
338        F: FnMut(TypeId) -> NewTypeId,
339    {
340        ExtrinsicExtensions {
341            transaction_extensions_version: self.transaction_extensions_version,
342            transaction_extensions: self
343                .transaction_extensions
344                .into_iter()
345                .map(|s| s.map_type_id(&mut f))
346                .collect(),
347        }
348    }
349}
350
351/// A single named argument.
352#[derive(Clone, Debug)]
353pub struct NamedArg<'info, TypeId> {
354    name: Cow<'info, str>,
355    range: Range<u32>,
356    ty: TypeId,
357}
358
359impl<'info, TypeId> NamedArg<'info, TypeId> {
360    /// Map the type ID to something else.
361    pub fn map_type_id<NewTypeId, F>(self, mut f: F) -> NamedArg<'info, NewTypeId>
362    where
363        F: FnMut(TypeId) -> NewTypeId,
364    {
365        NamedArg {
366            name: self.name,
367            range: self.range,
368            ty: f(self.ty),
369        }
370    }
371}
372
373impl<TypeId> NamedArg<'_, TypeId> {
374    /// Take ownership of this named argument.
375    pub fn into_owned(self) -> NamedArg<'static, TypeId> {
376        NamedArg {
377            name: Cow::Owned(self.name.into_owned()),
378            range: self.range,
379            ty: self.ty,
380        }
381    }
382
383    /// The name of this argument.
384    pub fn name(&self) -> &str {
385        &self.name
386    }
387
388    /// Return a range denoting the bytes associated with this argument value.
389    pub fn range(&self) -> Range<usize> {
390        Range {
391            start: self.range.start as usize,
392            end: self.range.end as usize,
393        }
394    }
395
396    /// The type ID associated with this argument value.
397    pub fn ty(&self) -> &TypeId {
398        &self.ty
399    }
400}
401
402/// Decode an extrinsic, returning information about it.
403///
404/// This information can be used to then decode the various parts of the extrinsic (address,
405/// signature, transaction extensions and call data) to concrete types.
406///
407/// # Example
408///
409/// Here, we decode all of the extrinsics in a block to a [`scale_value::Value`] type.
410///
411/// ```rust
412/// use frame_decode::extrinsics::decode_extrinsic;
413/// use frame_decode::helpers::decode_with_visitor;
414/// use frame_metadata::RuntimeMetadata;
415/// use parity_scale_codec::Decode;
416/// use scale_value::scale::ValueVisitor;
417///
418/// let metadata_bytes = std::fs::read("artifacts/metadata_10000000_9180.scale").unwrap();
419/// let RuntimeMetadata::V14(metadata) = RuntimeMetadata::decode(&mut &*metadata_bytes).unwrap() else { return };
420///
421/// let extrinsics_bytes = std::fs::read("artifacts/exts_10000000_9180.json").unwrap();
422/// let extrinsics_hex: Vec<String> = serde_json::from_slice(&extrinsics_bytes).unwrap();
423///
424/// for ext_hex in extrinsics_hex {
425///     let ext_bytes = hex::decode(ext_hex.trim_start_matches("0x")).unwrap();
426///
427///     // Decode the extrinsic, returning information about it:
428///     let ext_info = decode_extrinsic(&mut &*ext_bytes, &metadata, &metadata.types).unwrap();
429///
430///     // Decode the signature details to scale_value::Value's.
431///     if let Some(sig) = ext_info.signature_payload() {
432///         let address_bytes =  &ext_bytes[sig.address_range()];
433///         let address_value = decode_with_visitor(
434///             &mut &*address_bytes,
435///             *sig.address_type(),
436///             &metadata.types,
437///             ValueVisitor::new()
438///         ).unwrap();
439///
440///         let signature_bytes = &ext_bytes[sig.signature_range()];
441///         let signature_value = decode_with_visitor(
442///             &mut &*signature_bytes,
443///             *sig.signature_type(),
444///             &metadata.types,
445///             ValueVisitor::new()
446///         ).unwrap();
447///     }
448///
449///     // Decode the transaction extensions to scale_value::Value's.
450///     if let Some(exts) = ext_info.transaction_extension_payload() {
451///         for ext in exts.iter() {
452///             let ext_name = ext.name();
453///             let ext_bytes = &ext_bytes[ext.range()];
454///             let ext_value = decode_with_visitor(
455///                 &mut &*ext_bytes,
456///                 *ext.ty(),
457///                 &metadata.types,
458///                 ValueVisitor::new()
459///             ).unwrap();
460///         }
461///     }
462///
463///     // Decode the call data args to scale_value::Value's.
464///     for arg in ext_info.call_data() {
465///         let arg_name = arg.name();
466///         let arg_bytes = &ext_bytes[arg.range()];
467///         let arg_value = decode_with_visitor(
468///             &mut &*arg_bytes,
469///             *arg.ty(),
470///             &metadata.types,
471///             ValueVisitor::new()
472///         ).unwrap();
473///     }
474/// }
475/// ```
476pub fn decode_extrinsic<'info, Info, Resolver>(
477    cursor: &mut &[u8],
478    info: &'info Info,
479    type_resolver: &Resolver,
480) -> Result<Extrinsic<'info, Info::TypeId>, ExtrinsicDecodeError>
481where
482    Info: ExtrinsicTypeInfo,
483    Info::TypeId: core::fmt::Debug + Clone,
484    Resolver: TypeResolver<TypeId = Info::TypeId>,
485{
486    let bytes = *cursor;
487    let ext_len = Compact::<u64>::decode(cursor)
488        .map_err(|_| ExtrinsicDecodeError::CannotDecodeLength)?
489        .0 as usize;
490
491    let compact_prefix_len = (bytes.len() - cursor.len()) as u8;
492
493    if cursor.len() != ext_len {
494        return Err(ExtrinsicDecodeError::WrongLength {
495            expected_len: ext_len,
496            actual_len: cursor.len(),
497        });
498    }
499
500    if cursor.is_empty() {
501        return Err(ExtrinsicDecodeError::NotEnoughBytes);
502    }
503
504    // Decide how to decode the extrinsic based on the version.
505    // As of https://github.com/paritytech/polkadot-sdk/pull/3685,
506    // only 6 bits used for version. Shouldn't break old impls.
507    let version = cursor[0] & 0b0011_1111;
508    let version_type = cursor[0] >> 6;
509    *cursor = &cursor[1..];
510
511    // We only know how to decode v4 and v5 extrinsics.
512    if version != 4 && version != 5 {
513        return Err(ExtrinsicDecodeError::VersionNotSupported(version));
514    }
515
516    // We know about the following types of extrinsics:
517    // - "Bare": no signature or extensions. V4 inherents encode the same as V5 bare.
518    // - "Signed": an address, signature and extensions. Only exists in V4.
519    // - "General": no signature, just extensions (one of which can include sig). Only exists in V5.
520    let version_ty = match version_type {
521        0b00 => ExtrinsicType::Bare,
522        0b10 if version == 4 => ExtrinsicType::Signed,
523        0b01 if version == 5 => ExtrinsicType::General,
524        _ => {
525            return Err(ExtrinsicDecodeError::ExtrinsicTypeNotSupported {
526                version,
527                extrinsic_type: version_type,
528            })
529        }
530    };
531
532    let curr_idx = |cursor: &mut &[u8]| (bytes.len() - cursor.len()) as u32;
533
534    // Signature part. Present for V4 signed extrinsics
535    let signature = (version_ty == ExtrinsicType::Signed)
536        .then(|| {
537            let signature_info = info
538                .get_signature_info()
539                .map_err(|e| ExtrinsicDecodeError::CannotGetInfo(e.into_owned()))?;
540
541            let address_start_idx = curr_idx(cursor);
542            decode_with_error_tracing(
543                cursor,
544                signature_info.address_id.clone(),
545                type_resolver,
546                scale_decode::visitor::IgnoreVisitor::new(),
547            )
548            .map_err(ExtrinsicDecodeError::CannotDecodeSignature)?;
549            let address_end_idx = curr_idx(cursor);
550
551            decode_with_error_tracing(
552                cursor,
553                signature_info.signature_id.clone(),
554                type_resolver,
555                scale_decode::visitor::IgnoreVisitor::new(),
556            )
557            .map_err(ExtrinsicDecodeError::CannotDecodeSignature)?;
558            let signature_end_idx = curr_idx(cursor);
559
560            Ok(ExtrinsicSignature {
561                address_start_idx,
562                address_end_idx,
563                signature_end_idx,
564                address_ty: signature_info.address_id,
565                signature_ty: signature_info.signature_id,
566            })
567        })
568        .transpose()?;
569
570    // "General" extensions now have a single byte representing the extension version.
571    let extension_version = (version_ty == ExtrinsicType::General)
572        .then(|| u8::decode(cursor).map_err(ExtrinsicDecodeError::CannotDecodeExtensionsVersion))
573        .transpose()?;
574
575    // Signed and General extrinsics both now have a set of transaction extensions.
576    let extensions = (version_ty == ExtrinsicType::General || version_ty == ExtrinsicType::Signed)
577        .then(|| {
578            let extension_info = info
579                .get_extension_info(extension_version)
580                .map_err(|e| ExtrinsicDecodeError::CannotGetInfo(e.into_owned()))?;
581
582            let mut transaction_extensions = vec![];
583            for ext in extension_info.extension_ids {
584                let start_idx = curr_idx(cursor);
585                decode_with_error_tracing(
586                    cursor,
587                    ext.id.clone(),
588                    type_resolver,
589                    scale_decode::visitor::IgnoreVisitor::new(),
590                )
591                .map_err(ExtrinsicDecodeError::CannotDecodeSignature)?;
592                let end_idx = curr_idx(cursor);
593
594                transaction_extensions.push(NamedArg {
595                    name: ext.name,
596                    range: Range {
597                        start: start_idx,
598                        end: end_idx,
599                    },
600                    ty: ext.id,
601                });
602            }
603
604            Ok::<_, ExtrinsicDecodeError>(ExtrinsicExtensions {
605                transaction_extensions_version: extension_version.unwrap_or(0),
606                transaction_extensions,
607            })
608        })
609        .transpose()?;
610
611    // All extrinsics now have the encoded call data.
612    let pallet_index_idx = curr_idx(cursor);
613    let pallet_index: u8 =
614        Decode::decode(cursor).map_err(ExtrinsicDecodeError::CannotDecodePalletIndex)?;
615    let call_index: u8 =
616        Decode::decode(cursor).map_err(ExtrinsicDecodeError::CannotDecodeCallIndex)?;
617    let call_info = info
618        .get_call_info(pallet_index, call_index)
619        .map_err(|e| ExtrinsicDecodeError::CannotGetInfo(e.into_owned()))?;
620
621    let mut call_data = vec![];
622    for arg in call_info.args {
623        let start_idx = curr_idx(cursor);
624        decode_with_error_tracing(
625            cursor,
626            arg.id.clone(),
627            type_resolver,
628            scale_decode::visitor::IgnoreVisitor::new(),
629        )
630        .map_err(|e| ExtrinsicDecodeError::CannotDecodeCallData {
631            pallet_name: call_info.pallet_name.to_string(),
632            call_name: call_info.call_name.to_string(),
633            argument_name: arg.name.to_string(),
634            reason: e,
635        })?;
636        let end_idx = curr_idx(cursor);
637
638        call_data.push(NamedArg {
639            name: arg.name,
640            range: Range {
641                start: start_idx,
642                end: end_idx,
643            },
644            ty: arg.id,
645        })
646    }
647
648    let ext = Extrinsic {
649        compact_prefix_len,
650        version,
651        version_ty,
652        byte_len: bytes.len() as u32,
653        signature,
654        extensions,
655        pallet_name: call_info.pallet_name,
656        pallet_index,
657        pallet_index_idx,
658        call_name: call_info.call_name,
659        call_index,
660        call_data,
661    };
662
663    Ok(ext)
664}