1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
// Copyright 2019-2024 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.

use crate::config::signed_extensions::{
    ChargeAssetTxPayment, ChargeTransactionPayment, CheckNonce,
};
use crate::config::SignedExtension;
use crate::dynamic::Value;
use crate::{config::Config, error::Error, Metadata};
use scale_decode::DecodeAsType;

/// The signed extensions of an extrinsic.
#[derive(Debug, Clone)]
pub struct ExtrinsicSignedExtensions<'a, T: Config> {
    bytes: &'a [u8],
    metadata: &'a Metadata,
    _marker: core::marker::PhantomData<T>,
}

impl<'a, T: Config> ExtrinsicSignedExtensions<'a, T> {
    pub(crate) fn new(bytes: &'a [u8], metadata: &'a Metadata) -> Self {
        Self {
            bytes,
            metadata,
            _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<Item = Result<ExtrinsicSignedExtension<T>, 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,
                _marker: core::marker::PhantomData,
            }))
        })
    }

    /// Searches through all signed extensions to find a specific one.
    /// If the Signed Extension is not found `Ok(None)` is returned.
    /// If the Signed Extension is found but decoding failed `Err(_)` is returned.
    pub fn find<S: SignedExtension<T>>(&self) -> Result<Option<S::Decoded>, 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::<S>() {
                // We found a match; return it:
                Ok(Some(e)) => return Ok(Some(e)),
                // No error, but no match either; next!
                Ok(None) => continue,
                // Error? return it
                Err(e) => return Err(e),
            }
        }
        Ok(None)
    }

    /// The tip of an extrinsic, extracted from the ChargeTransactionPayment or ChargeAssetTxPayment
    /// signed extension, depending on which is present.
    ///
    /// Returns `None` if  `tip` was not found or decoding failed.
    pub fn tip(&self) -> Option<u128> {
        // Note: the overhead of iterating multiple time should be negligible.
        self.find::<ChargeTransactionPayment>()
            .ok()
            .flatten()
            .map(|e| e.tip())
            .or_else(|| {
                self.find::<ChargeAssetTxPayment<T>>()
                    .ok()
                    .flatten()
                    .map(|e| e.tip())
            })
    }

    /// The nonce of the account that submitted the extrinsic, extracted from the CheckNonce signed extension.
    ///
    /// Returns `None` if `nonce` was not found or decoding failed.
    pub fn nonce(&self) -> Option<u64> {
        self.find::<CheckNonce>().ok()?
    }
}

/// A single signed extension
#[derive(Debug, Clone)]
pub struct ExtrinsicSignedExtension<'a, T: Config> {
    bytes: &'a [u8],
    ty_id: u32,
    identifier: &'a str,
    metadata: &'a Metadata,
    _marker: core::marker::PhantomData<T>,
}

impl<'a, T: Config> ExtrinsicSignedExtension<'a, T> {
    /// The bytes representing this signed extension.
    pub fn bytes(&self) -> &'a [u8] {
        self.bytes
    }

    /// The name of the signed extension.
    pub fn name(&self) -> &'a str {
        self.identifier
    }

    /// The type id of the signed extension.
    pub fn type_id(&self) -> u32 {
        self.ty_id
    }

    /// Signed Extension as a [`scale_value::Value`]
    pub fn value(&self) -> Result<Value<u32>, Error> {
        let value = scale_value::scale::decode_as_type(
            &mut &self.bytes[..],
            self.ty_id,
            self.metadata.types(),
        )?;
        Ok(value)
    }

    /// Decodes the bytes of this Signed Extension into its associated `Decoded` type.
    /// Returns `Ok(None)` if the data we have doesn't match the Signed Extension we're asking to
    /// decode with.
    pub fn as_signed_extension<S: SignedExtension<T>>(&self) -> Result<Option<S::Decoded>, Error> {
        if !S::matches(self.identifier, self.ty_id, self.metadata.types()) {
            return Ok(None);
        }
        self.as_type::<S::Decoded>().map(Some)
    }

    fn as_type<E: DecodeAsType>(&self) -> Result<E, Error> {
        let value = E::decode_as_type(&mut &self.bytes[..], self.ty_id, self.metadata.types())?;
        Ok(value)
    }
}