subxt_core/blocks/extrinsic_signed_extensions.rs
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
// 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 frame_decode::extrinsics::ExtrinsicExtensions;
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,
decoded_info: &'a ExtrinsicExtensions<'static, u32>,
_marker: core::marker::PhantomData<T>,
}
impl<'a, T: Config> ExtrinsicSignedExtensions<'a, T> {
pub(crate) fn new(
bytes: &'a [u8],
metadata: &'a Metadata,
decoded_info: &'a ExtrinsicExtensions<'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.
pub fn iter(&self) -> impl Iterator<Item = ExtrinsicSignedExtension<T>> {
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.
/// 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() {
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)
}
}