use crate::{
scale_info::{MetaType, StaticTypeInfo},
transaction_validity::{
TransactionSource, TransactionValidity, TransactionValidityError, ValidTransaction,
},
DispatchResult,
};
use alloc::vec::Vec;
use codec::{Codec, Decode, DecodeWithMemTracking, Encode};
use core::fmt::Debug;
#[doc(hidden)]
pub use core::marker::PhantomData;
use impl_trait_for_tuples::impl_for_tuples;
use sp_weights::Weight;
use tuplex::{PopFront, PushBack};
use super::{
DispatchInfoOf, DispatchOriginOf, Dispatchable, ExtensionPostDispatchWeightHandler,
PostDispatchInfoOf, RefundWeight,
};
mod as_transaction_extension;
mod dispatch_transaction;
#[allow(deprecated)]
pub use as_transaction_extension::AsTransactionExtension;
pub use dispatch_transaction::DispatchTransaction;
mod private {
pub trait Sealed {}
}
#[derive(Encode)]
pub struct TxBaseImplication<T>(pub T);
impl<T: Encode> Implication for TxBaseImplication<T> {
fn parts(&self) -> ImplicationParts<&impl Encode, &impl Encode, &impl Encode> {
ImplicationParts { base: self, explicit: &(), implicit: &() }
}
}
impl<T> private::Sealed for TxBaseImplication<T> {}
#[derive(Encode)]
pub struct ImplicationParts<Base, Explicit, Implicit> {
pub base: Base,
pub explicit: Explicit,
pub implicit: Implicit,
}
impl<Base: Encode, Explicit: Encode, Implicit: Encode> Implication
for ImplicationParts<Base, Explicit, Implicit>
{
fn parts(&self) -> ImplicationParts<&impl Encode, &impl Encode, &impl Encode> {
ImplicationParts { base: &self.base, explicit: &self.explicit, implicit: &self.implicit }
}
}
impl<Base, Explicit, Implicit> private::Sealed for ImplicationParts<Base, Explicit, Implicit> {}
pub trait Implication: Encode + private::Sealed {
fn parts(&self) -> ImplicationParts<&impl Encode, &impl Encode, &impl Encode>;
}
pub type ValidateResult<Val, Call> =
Result<(ValidTransaction, Val, DispatchOriginOf<Call>), TransactionValidityError>;
pub trait TransactionExtension<Call: Dispatchable>:
Codec + DecodeWithMemTracking + Debug + Sync + Send + Clone + Eq + PartialEq + StaticTypeInfo
{
const IDENTIFIER: &'static str;
type Implicit: Codec + StaticTypeInfo;
fn implicit(&self) -> Result<Self::Implicit, TransactionValidityError> {
use crate::transaction_validity::InvalidTransaction::IndeterminateImplicit;
Ok(Self::Implicit::decode(&mut &[][..]).map_err(|_| IndeterminateImplicit)?)
}
fn metadata() -> Vec<TransactionExtensionMetadata> {
alloc::vec![TransactionExtensionMetadata {
identifier: Self::IDENTIFIER,
ty: scale_info::meta_type::<Self>(),
implicit: scale_info::meta_type::<Self::Implicit>()
}]
}
type Val;
type Pre;
fn weight(&self, call: &Call) -> Weight;
fn validate(
&self,
origin: DispatchOriginOf<Call>,
call: &Call,
info: &DispatchInfoOf<Call>,
len: usize,
self_implicit: Self::Implicit,
inherited_implication: &impl Implication,
source: TransactionSource,
) -> ValidateResult<Self::Val, Call>;
fn prepare(
self,
val: Self::Val,
origin: &DispatchOriginOf<Call>,
call: &Call,
info: &DispatchInfoOf<Call>,
len: usize,
) -> Result<Self::Pre, TransactionValidityError>;
fn post_dispatch_details(
_pre: Self::Pre,
_info: &DispatchInfoOf<Call>,
_post_info: &PostDispatchInfoOf<Call>,
_len: usize,
_result: &DispatchResult,
) -> Result<Weight, TransactionValidityError> {
Ok(Weight::zero())
}
fn post_dispatch(
pre: Self::Pre,
info: &DispatchInfoOf<Call>,
post_info: &mut PostDispatchInfoOf<Call>,
len: usize,
result: &DispatchResult,
) -> Result<(), TransactionValidityError> {
let unspent_weight = Self::post_dispatch_details(pre, info, &post_info, len, result)?;
post_info.refund(unspent_weight);
Ok(())
}
fn bare_validate(
_call: &Call,
_info: &DispatchInfoOf<Call>,
_len: usize,
) -> TransactionValidity {
Ok(ValidTransaction::default())
}
fn bare_validate_and_prepare(
_call: &Call,
_info: &DispatchInfoOf<Call>,
_len: usize,
) -> Result<(), TransactionValidityError> {
Ok(())
}
fn bare_post_dispatch(
_info: &DispatchInfoOf<Call>,
_post_info: &mut PostDispatchInfoOf<Call>,
_len: usize,
_result: &DispatchResult,
) -> Result<(), TransactionValidityError> {
Ok(())
}
}
#[macro_export]
macro_rules! impl_tx_ext_default {
($call:ty ; , $( $rest:tt )*) => {
$crate::impl_tx_ext_default!{$call ; $( $rest )*}
};
($call:ty ; validate $( $rest:tt )*) => {
fn validate(
&self,
origin: $crate::traits::DispatchOriginOf<$call>,
_call: &$call,
_info: &$crate::traits::DispatchInfoOf<$call>,
_len: usize,
_self_implicit: Self::Implicit,
_inherited_implication: &impl $crate::codec::Encode,
_source: $crate::transaction_validity::TransactionSource,
) -> $crate::traits::ValidateResult<Self::Val, $call> {
Ok((Default::default(), Default::default(), origin))
}
$crate::impl_tx_ext_default!{$call ; $( $rest )*}
};
($call:ty ; prepare $( $rest:tt )*) => {
fn prepare(
self,
_val: Self::Val,
_origin: &$crate::traits::DispatchOriginOf<$call>,
_call: &$call,
_info: &$crate::traits::DispatchInfoOf<$call>,
_len: usize,
) -> Result<Self::Pre, $crate::transaction_validity::TransactionValidityError> {
Ok(Default::default())
}
$crate::impl_tx_ext_default!{$call ; $( $rest )*}
};
($call:ty ; weight $( $rest:tt )*) => {
fn weight(&self, _call: &$call) -> $crate::Weight {
$crate::Weight::zero()
}
$crate::impl_tx_ext_default!{$call ; $( $rest )*}
};
($call:ty ;) => {};
}
pub struct TransactionExtensionMetadata {
pub identifier: &'static str,
pub ty: MetaType,
pub implicit: MetaType,
}
#[impl_for_tuples(1, 12)]
impl<Call: Dispatchable> TransactionExtension<Call> for Tuple {
const IDENTIFIER: &'static str = "Use `metadata()`!";
for_tuples!( type Implicit = ( #( Tuple::Implicit ),* ); );
fn implicit(&self) -> Result<Self::Implicit, TransactionValidityError> {
Ok(for_tuples!( ( #( Tuple.implicit()? ),* ) ))
}
fn metadata() -> Vec<TransactionExtensionMetadata> {
let mut ids = Vec::new();
for_tuples!( #( ids.extend(Tuple::metadata()); )* );
ids
}
for_tuples!( type Val = ( #( Tuple::Val ),* ); );
for_tuples!( type Pre = ( #( Tuple::Pre ),* ); );
fn weight(&self, call: &Call) -> Weight {
let mut weight = Weight::zero();
for_tuples!( #( weight = weight.saturating_add(Tuple.weight(call)); )* );
weight
}
fn validate(
&self,
origin: <Call as Dispatchable>::RuntimeOrigin,
call: &Call,
info: &DispatchInfoOf<Call>,
len: usize,
self_implicit: Self::Implicit,
inherited_implication: &impl Implication,
source: TransactionSource,
) -> Result<
(ValidTransaction, Self::Val, <Call as Dispatchable>::RuntimeOrigin),
TransactionValidityError,
> {
let valid = ValidTransaction::default();
let val = ();
let following_explicit_implications = for_tuples!( ( #( &self.Tuple ),* ) );
let following_implicit_implications = self_implicit;
let implication_parts = inherited_implication.parts();
for_tuples!(#(
let (_item, following_explicit_implications) = following_explicit_implications.pop_front();
let (item_implicit, following_implicit_implications) = following_implicit_implications.pop_front();
let (item_valid, item_val, origin) = {
Tuple.validate(origin, call, info, len, item_implicit,
&ImplicationParts {
base: implication_parts.base,
explicit: (&following_explicit_implications, implication_parts.explicit),
implicit: (&following_implicit_implications, implication_parts.implicit),
},
source)?
};
let valid = valid.combine_with(item_valid);
let val = val.push_back(item_val);
)* );
Ok((valid, val, origin))
}
fn prepare(
self,
val: Self::Val,
origin: &<Call as Dispatchable>::RuntimeOrigin,
call: &Call,
info: &DispatchInfoOf<Call>,
len: usize,
) -> Result<Self::Pre, TransactionValidityError> {
Ok(for_tuples!( ( #(
Tuple::prepare(self.Tuple, val.Tuple, origin, call, info, len)?
),* ) ))
}
fn post_dispatch_details(
pre: Self::Pre,
info: &DispatchInfoOf<Call>,
post_info: &PostDispatchInfoOf<Call>,
len: usize,
result: &DispatchResult,
) -> Result<Weight, TransactionValidityError> {
let mut total_unspent_weight = Weight::zero();
for_tuples!( #({
let unspent_weight = Tuple::post_dispatch_details(pre.Tuple, info, post_info, len, result)?;
total_unspent_weight = total_unspent_weight.saturating_add(unspent_weight);
})* );
Ok(total_unspent_weight)
}
fn post_dispatch(
pre: Self::Pre,
info: &DispatchInfoOf<Call>,
post_info: &mut PostDispatchInfoOf<Call>,
len: usize,
result: &DispatchResult,
) -> Result<(), TransactionValidityError> {
for_tuples!( #( Tuple::post_dispatch(pre.Tuple, info, post_info, len, result)?; )* );
Ok(())
}
fn bare_validate(call: &Call, info: &DispatchInfoOf<Call>, len: usize) -> TransactionValidity {
let valid = ValidTransaction::default();
for_tuples!(#(
let item_valid = Tuple::bare_validate(call, info, len)?;
let valid = valid.combine_with(item_valid);
)* );
Ok(valid)
}
fn bare_validate_and_prepare(
call: &Call,
info: &DispatchInfoOf<Call>,
len: usize,
) -> Result<(), TransactionValidityError> {
for_tuples!( #( Tuple::bare_validate_and_prepare(call, info, len)?; )* );
Ok(())
}
fn bare_post_dispatch(
info: &DispatchInfoOf<Call>,
post_info: &mut PostDispatchInfoOf<Call>,
len: usize,
result: &DispatchResult,
) -> Result<(), TransactionValidityError> {
for_tuples!( #( Tuple::bare_post_dispatch(info, post_info, len, result)?; )* );
Ok(())
}
}
impl<Call: Dispatchable> TransactionExtension<Call> for () {
const IDENTIFIER: &'static str = "UnitTransactionExtension";
type Implicit = ();
fn implicit(&self) -> core::result::Result<Self::Implicit, TransactionValidityError> {
Ok(())
}
type Val = ();
type Pre = ();
fn weight(&self, _call: &Call) -> Weight {
Weight::zero()
}
fn validate(
&self,
origin: <Call as Dispatchable>::RuntimeOrigin,
_call: &Call,
_info: &DispatchInfoOf<Call>,
_len: usize,
_self_implicit: Self::Implicit,
_inherited_implication: &impl Implication,
_source: TransactionSource,
) -> Result<
(ValidTransaction, (), <Call as Dispatchable>::RuntimeOrigin),
TransactionValidityError,
> {
Ok((ValidTransaction::default(), (), origin))
}
fn prepare(
self,
_val: (),
_origin: &<Call as Dispatchable>::RuntimeOrigin,
_call: &Call,
_info: &DispatchInfoOf<Call>,
_len: usize,
) -> Result<(), TransactionValidityError> {
Ok(())
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_implications_on_nested_structure() {
use scale_info::TypeInfo;
use std::cell::RefCell;
#[derive(Clone, Debug, Eq, PartialEq, Encode, Decode, DecodeWithMemTracking, TypeInfo)]
struct MockExtension {
also_implicit: u8,
explicit: u8,
}
const CALL_IMPLICIT: u8 = 23;
thread_local! {
static COUNTER: RefCell<u8> = RefCell::new(1);
}
impl TransactionExtension<()> for MockExtension {
const IDENTIFIER: &'static str = "MockExtension";
type Implicit = u8;
fn implicit(&self) -> Result<Self::Implicit, TransactionValidityError> {
Ok(self.also_implicit)
}
type Val = ();
type Pre = ();
fn weight(&self, _call: &()) -> Weight {
Weight::zero()
}
fn prepare(
self,
_val: Self::Val,
_origin: &DispatchOriginOf<()>,
_call: &(),
_info: &DispatchInfoOf<()>,
_len: usize,
) -> Result<Self::Pre, TransactionValidityError> {
Ok(())
}
fn validate(
&self,
origin: DispatchOriginOf<()>,
_call: &(),
_info: &DispatchInfoOf<()>,
_len: usize,
self_implicit: Self::Implicit,
inherited_implication: &impl Implication,
_source: TransactionSource,
) -> ValidateResult<Self::Val, ()> {
COUNTER.with(|c| {
let mut counter = c.borrow_mut();
assert_eq!(self_implicit, *counter);
assert_eq!(
self,
&MockExtension { also_implicit: *counter, explicit: *counter + 1 }
);
let mut assert_implications = Vec::new();
assert_implications.push(CALL_IMPLICIT);
for i in *counter + 2..23 {
assert_implications.push(i);
}
for i in *counter + 2..23 {
if i % 2 == 1 {
assert_implications.push(i);
}
}
assert_eq!(inherited_implication.encode(), assert_implications);
*counter += 2;
});
Ok((ValidTransaction::default(), (), origin))
}
fn post_dispatch_details(
_pre: Self::Pre,
_info: &DispatchInfoOf<()>,
_post_info: &PostDispatchInfoOf<()>,
_len: usize,
_result: &DispatchResult,
) -> Result<Weight, TransactionValidityError> {
Ok(Weight::zero())
}
}
let ext = (
MockExtension { also_implicit: 1, explicit: 2 },
MockExtension { also_implicit: 3, explicit: 4 },
(
MockExtension { also_implicit: 5, explicit: 6 },
MockExtension { also_implicit: 7, explicit: 8 },
(
MockExtension { also_implicit: 9, explicit: 10 },
MockExtension { also_implicit: 11, explicit: 12 },
),
MockExtension { also_implicit: 13, explicit: 14 },
MockExtension { also_implicit: 15, explicit: 16 },
),
MockExtension { also_implicit: 17, explicit: 18 },
(MockExtension { also_implicit: 19, explicit: 20 },),
MockExtension { also_implicit: 21, explicit: 22 },
);
let implicit = ext.implicit().unwrap();
let res = ext
.validate(
(),
&(),
&DispatchInfoOf::<()>::default(),
0,
implicit,
&TxBaseImplication(CALL_IMPLICIT),
TransactionSource::Local,
)
.expect("valid");
assert_eq!(res.0, ValidTransaction::default());
COUNTER.with(|c| {
*c.borrow_mut() = 1;
});
let ext = (
MockExtension { also_implicit: 1, explicit: 2 },
MockExtension { also_implicit: 3, explicit: 4 },
MockExtension { also_implicit: 5, explicit: 6 },
MockExtension { also_implicit: 7, explicit: 8 },
MockExtension { also_implicit: 9, explicit: 10 },
MockExtension { also_implicit: 11, explicit: 12 },
(
MockExtension { also_implicit: 13, explicit: 14 },
MockExtension { also_implicit: 15, explicit: 16 },
MockExtension { also_implicit: 17, explicit: 18 },
MockExtension { also_implicit: 19, explicit: 20 },
MockExtension { also_implicit: 21, explicit: 22 },
),
);
let implicit = ext.implicit().unwrap();
let res = ext
.validate(
(),
&(),
&DispatchInfoOf::<()>::default(),
0,
implicit,
&TxBaseImplication(CALL_IMPLICIT),
TransactionSource::Local,
)
.expect("valid");
assert_eq!(res.0, ValidTransaction::default());
}
}