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)
}
}