mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-26 01:47:55 +00:00
FRAME: Create TransactionExtension as a replacement for SignedExtension (#2280)
Closes #2160 First part of [Extrinsic Horizon](https://github.com/paritytech/polkadot-sdk/issues/2415) Introduces a new trait `TransactionExtension` to replace `SignedExtension`. Introduce the idea of transactions which obey the runtime's extensions and have according Extension data (né Extra data) yet do not have hard-coded signatures. Deprecate the terminology of "Unsigned" when used for transactions/extrinsics owing to there now being "proper" unsigned transactions which obey the extension framework and "old-style" unsigned which do not. Instead we have __*General*__ for the former and __*Bare*__ for the latter. (Ultimately, the latter will be phased out as a type of transaction, and Bare will only be used for Inherents.) Types of extrinsic are now therefore: - Bare (no hardcoded signature, no Extra data; used to be known as "Unsigned") - Bare transactions (deprecated): Gossiped, validated with `ValidateUnsigned` (deprecated) and the `_bare_compat` bits of `TransactionExtension` (deprecated). - Inherents: Not gossiped, validated with `ProvideInherent`. - Extended (Extra data): Gossiped, validated via `TransactionExtension`. - Signed transactions (with a hardcoded signature). - General transactions (without a hardcoded signature). `TransactionExtension` differs from `SignedExtension` because: - A signature on the underlying transaction may validly not be present. - It may alter the origin during validation. - `pre_dispatch` is renamed to `prepare` and need not contain the checks present in `validate`. - `validate` and `prepare` is passed an `Origin` rather than a `AccountId`. - `validate` may pass arbitrary information into `prepare` via a new user-specifiable type `Val`. - `AdditionalSigned`/`additional_signed` is renamed to `Implicit`/`implicit`. It is encoded *for the entire transaction* and passed in to each extension as a new argument to `validate`. This facilitates the ability of extensions to acts as underlying crypto. There is a new `DispatchTransaction` trait which contains only default function impls and is impl'ed for any `TransactionExtension` impler. It provides several utility functions which reduce some of the tedium from using `TransactionExtension` (indeed, none of its regular functions should now need to be called directly). Three transaction version discriminator ("versions") are now permissible: - 0b000000100: Bare (used to be called "Unsigned"): contains Signature or Extra (extension data). After bare transactions are no longer supported, this will strictly identify an Inherents only. - 0b100000100: Old-school "Signed" Transaction: contains Signature and Extra (extension data). - 0b010000100: New-school "General" Transaction: contains Extra (extension data), but no Signature. For the New-school General Transaction, it becomes trivial for authors to publish extensions to the mechanism for authorizing an Origin, e.g. through new kinds of key-signing schemes, ZK proofs, pallet state, mutations over pre-authenticated origins or any combination of the above. ## Code Migration ### NOW: Getting it to build Wrap your `SignedExtension`s in `AsTransactionExtension`. This should be accompanied by renaming your aggregate type in line with the new terminology. E.g. Before: ```rust /// The SignedExtension to the basic transaction logic. pub type SignedExtra = ( /* snip */ MySpecialSignedExtension, ); /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = generic::UncheckedExtrinsic<Address, RuntimeCall, Signature, SignedExtra>; ``` After: ```rust /// The extension to the basic transaction logic. pub type TxExtension = ( /* snip */ AsTransactionExtension<MySpecialSignedExtension>, ); /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = generic::UncheckedExtrinsic<Address, RuntimeCall, Signature, TxExtension>; ``` You'll also need to alter any transaction building logic to add a `.into()` to make the conversion happen. E.g. Before: ```rust fn construct_extrinsic( /* snip */ ) -> UncheckedExtrinsic { let extra: SignedExtra = ( /* snip */ MySpecialSignedExtension::new(/* snip */), ); let payload = SignedPayload::new(call.clone(), extra.clone()).unwrap(); let signature = payload.using_encoded(|e| sender.sign(e)); UncheckedExtrinsic::new_signed( /* snip */ Signature::Sr25519(signature), extra, ) } ``` After: ```rust fn construct_extrinsic( /* snip */ ) -> UncheckedExtrinsic { let tx_ext: TxExtension = ( /* snip */ MySpecialSignedExtension::new(/* snip */).into(), ); let payload = SignedPayload::new(call.clone(), tx_ext.clone()).unwrap(); let signature = payload.using_encoded(|e| sender.sign(e)); UncheckedExtrinsic::new_signed( /* snip */ Signature::Sr25519(signature), tx_ext, ) } ``` ### SOON: Migrating to `TransactionExtension` Most `SignedExtension`s can be trivially converted to become a `TransactionExtension`. There are a few things to know. - Instead of a single trait like `SignedExtension`, you should now implement two traits individually: `TransactionExtensionBase` and `TransactionExtension`. - Weights are now a thing and must be provided via the new function `fn weight`. #### `TransactionExtensionBase` This trait takes care of anything which is not dependent on types specific to your runtime, most notably `Call`. - `AdditionalSigned`/`additional_signed` is renamed to `Implicit`/`implicit`. - Weight must be returned by implementing the `weight` function. If your extension is associated with a pallet, you'll probably want to do this via the pallet's existing benchmarking infrastructure. #### `TransactionExtension` Generally: - `pre_dispatch` is now `prepare` and you *should not reexecute the `validate` functionality in there*! - You don't get an account ID any more; you get an origin instead. If you need to presume an account ID, then you can use the trait function `AsSystemOriginSigner::as_system_origin_signer`. - You get an additional ticket, similar to `Pre`, called `Val`. This defines data which is passed from `validate` into `prepare`. This is important since you should not be duplicating logic from `validate` to `prepare`, you need a way of passing your working from the former into the latter. This is it. - This trait takes two type parameters: `Call` and `Context`. `Call` is the runtime call type which used to be an associated type; you can just move it to become a type parameter for your trait impl. `Context` is not currently used and you can safely implement over it as an unbounded type. - There's no `AccountId` associated type any more. Just remove it. Regarding `validate`: - You get three new parameters in `validate`; all can be ignored when migrating from `SignedExtension`. - `validate` returns a tuple on success; the second item in the tuple is the new ticket type `Self::Val` which gets passed in to `prepare`. If you use any information extracted during `validate` (off-chain and on-chain, non-mutating) in `prepare` (on-chain, mutating) then you can pass it through with this. For the tuple's last item, just return the `origin` argument. Regarding `prepare`: - This is renamed from `pre_dispatch`, but there is one change: - FUNCTIONALITY TO VALIDATE THE TRANSACTION NEED NOT BE DUPLICATED FROM `validate`!! - (This is different to `SignedExtension` which was required to run the same checks in `pre_dispatch` as in `validate`.) Regarding `post_dispatch`: - Since there are no unsigned transactions handled by `TransactionExtension`, `Pre` is always defined, so the first parameter is `Self::Pre` rather than `Option<Self::Pre>`. If you make use of `SignedExtension::validate_unsigned` or `SignedExtension::pre_dispatch_unsigned`, then: - Just use the regular versions of these functions instead. - Have your logic execute in the case that the `origin` is `None`. - Ensure your transaction creation logic creates a General Transaction rather than a Bare Transaction; this means having to include all `TransactionExtension`s' data. - `ValidateUnsigned` can still be used (for now) if you need to be able to construct transactions which contain none of the extension data, however these will be phased out in stage 2 of the Transactions Horizon, so you should consider moving to an extension-centric design. ## TODO - [x] Introduce `CheckSignature` impl of `TransactionExtension` to ensure it's possible to have crypto be done wholly in a `TransactionExtension`. - [x] Deprecate `SignedExtension` and move all uses in codebase to `TransactionExtension`. - [x] `ChargeTransactionPayment` - [x] `DummyExtension` - [x] `ChargeAssetTxPayment` (asset-tx-payment) - [x] `ChargeAssetTxPayment` (asset-conversion-tx-payment) - [x] `CheckWeight` - [x] `CheckTxVersion` - [x] `CheckSpecVersion` - [x] `CheckNonce` - [x] `CheckNonZeroSender` - [x] `CheckMortality` - [x] `CheckGenesis` - [x] `CheckOnlySudoAccount` - [x] `WatchDummy` - [x] `PrevalidateAttests` - [x] `GenericSignedExtension` - [x] `SignedExtension` (chain-polkadot-bulletin) - [x] `RefundSignedExtensionAdapter` - [x] Implement `fn weight` across the board. - [ ] Go through all pre-existing extensions which assume an account signer and explicitly handle the possibility of another kind of origin. - [x] `CheckNonce` should probably succeed in the case of a non-account origin. - [x] `CheckNonZeroSender` should succeed in the case of a non-account origin. - [x] `ChargeTransactionPayment` and family should fail in the case of a non-account origin. - [ ] - [x] Fix any broken tests. --------- Signed-off-by: georgepisaltu <george.pisaltu@parity.io> Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> Signed-off-by: dependabot[bot] <support@github.com> Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> Signed-off-by: Alexandru Gheorghe <alexandru.gheorghe@parity.io> Signed-off-by: Andrei Sandu <andrei-mihail@parity.io> Co-authored-by: Nikhil Gupta <17176722+gupnik@users.noreply.github.com> Co-authored-by: georgepisaltu <52418509+georgepisaltu@users.noreply.github.com> Co-authored-by: Chevdor <chevdor@users.noreply.github.com> Co-authored-by: Bastian Köcher <git@kchr.de> Co-authored-by: Maciej <maciej.zyszkiewicz@parity.io> Co-authored-by: Javier Viola <javier@parity.io> Co-authored-by: Marcin S. <marcin@realemail.net> Co-authored-by: Tsvetomir Dimitrov <tsvetomir@parity.io> Co-authored-by: Javier Bullrich <javier@bullrich.dev> Co-authored-by: Koute <koute@users.noreply.github.com> Co-authored-by: Adrian Catangiu <adrian@parity.io> Co-authored-by: Vladimir Istyufeev <vladimir@parity.io> Co-authored-by: Ross Bulat <ross@parity.io> Co-authored-by: Gonçalo Pestana <g6pestana@gmail.com> Co-authored-by: Liam Aharon <liam.aharon@hotmail.com> Co-authored-by: Svyatoslav Nikolsky <svyatonik@gmail.com> Co-authored-by: André Silva <123550+andresilva@users.noreply.github.com> Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> Co-authored-by: s0me0ne-unkn0wn <48632512+s0me0ne-unkn0wn@users.noreply.github.com> Co-authored-by: ordian <write@reusable.software> Co-authored-by: Sebastian Kunert <skunert49@gmail.com> Co-authored-by: Aaro Altonen <48052676+altonen@users.noreply.github.com> Co-authored-by: Dmitry Markin <dmitry@markin.tech> Co-authored-by: Alexandru Vasile <60601340+lexnv@users.noreply.github.com> Co-authored-by: Alexander Samusev <41779041+alvicsam@users.noreply.github.com> Co-authored-by: Julian Eager <eagr@tutanota.com> Co-authored-by: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com> Co-authored-by: Davide Galassi <davxy@datawok.net> Co-authored-by: Dónal Murray <donal.murray@parity.io> Co-authored-by: yjh <yjh465402634@gmail.com> Co-authored-by: Tom Mi <tommi@niemi.lol> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Will | Paradox | ParaNodes.io <79228812+paradox-tt@users.noreply.github.com> Co-authored-by: Bastian Köcher <info@kchr.de> Co-authored-by: Joshy Orndorff <JoshOrndorff@users.noreply.github.com> Co-authored-by: Joshy Orndorff <git-user-email.h0ly5@simplelogin.com> Co-authored-by: PG Herveou <pgherveou@gmail.com> Co-authored-by: Alexander Theißen <alex.theissen@me.com> Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> Co-authored-by: Juan Girini <juangirini@gmail.com> Co-authored-by: bader y <ibnbassem@gmail.com> Co-authored-by: James Wilson <james@jsdw.me> Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> Co-authored-by: asynchronous rob <rphmeier@gmail.com> Co-authored-by: Parth <desaiparth08@gmail.com> Co-authored-by: Andrew Jones <ascjones@gmail.com> Co-authored-by: Jonathan Udd <jonathan@dwellir.com> Co-authored-by: Serban Iorga <serban@parity.io> Co-authored-by: Egor_P <egor@parity.io> Co-authored-by: Branislav Kontur <bkontur@gmail.com> Co-authored-by: Evgeny Snitko <evgeny@parity.io> Co-authored-by: Just van Stam <vstam1@users.noreply.github.com> Co-authored-by: Francisco Aguirre <franciscoaguirreperez@gmail.com> Co-authored-by: gupnik <nikhilgupta.iitk@gmail.com> Co-authored-by: dzmitry-lahoda <dzmitry@lahoda.pro> Co-authored-by: zhiqiangxu <652732310@qq.com> Co-authored-by: Nazar Mokrynskyi <nazar@mokrynskyi.com> Co-authored-by: Anwesh <anweshknayak@gmail.com> Co-authored-by: cheme <emericchevalier.pro@gmail.com> Co-authored-by: Sam Johnson <sam@durosoft.com> Co-authored-by: kianenigma <kian@parity.io> Co-authored-by: Jegor Sidorenko <5252494+jsidorenko@users.noreply.github.com> Co-authored-by: Muharem <ismailov.m.h@gmail.com> Co-authored-by: joepetrowski <joe@parity.io> Co-authored-by: Alexandru Gheorghe <49718502+alexggh@users.noreply.github.com> Co-authored-by: Gabriel Facco de Arruda <arrudagates@gmail.com> Co-authored-by: Squirrel <gilescope@gmail.com> Co-authored-by: Andrei Sandu <54316454+sandreim@users.noreply.github.com> Co-authored-by: georgepisaltu <george.pisaltu@parity.io> Co-authored-by: command-bot <>
This commit is contained in:
@@ -18,81 +18,115 @@
|
||||
//! Generic implementation of an extrinsic that has passed the verification
|
||||
//! stage.
|
||||
|
||||
use codec::Encode;
|
||||
|
||||
use crate::{
|
||||
traits::{
|
||||
self, DispatchInfoOf, Dispatchable, MaybeDisplay, Member, PostDispatchInfoOf,
|
||||
SignedExtension, ValidateUnsigned,
|
||||
self, transaction_extension::TransactionExtension, DispatchInfoOf, DispatchTransaction,
|
||||
Dispatchable, MaybeDisplay, Member, PostDispatchInfoOf, ValidateUnsigned,
|
||||
},
|
||||
transaction_validity::{TransactionSource, TransactionValidity},
|
||||
};
|
||||
|
||||
/// The kind of extrinsic this is, including any fields required of that kind. This is basically
|
||||
/// the full extrinsic except the `Call`.
|
||||
#[derive(PartialEq, Eq, Clone, sp_core::RuntimeDebug)]
|
||||
pub enum ExtrinsicFormat<AccountId, Extension> {
|
||||
/// Extrinsic is bare; it must pass either the bare forms of `TransactionExtension` or
|
||||
/// `ValidateUnsigned`, both deprecated, or alternatively a `ProvideInherent`.
|
||||
Bare,
|
||||
/// Extrinsic has a default `Origin` of `Signed(AccountId)` and must pass all
|
||||
/// `TransactionExtension`s regular checks and includes all extension data.
|
||||
Signed(AccountId, Extension),
|
||||
/// Extrinsic has a default `Origin` of `None` and must pass all `TransactionExtension`s.
|
||||
/// regular checks and includes all extension data.
|
||||
General(Extension),
|
||||
}
|
||||
|
||||
// TODO: Rename ValidateUnsigned to ValidateInherent
|
||||
// TODO: Consider changing ValidateInherent API to avoid need for duplicating validate
|
||||
// code into pre_dispatch (rename that to `prepare`).
|
||||
// TODO: New extrinsic type corresponding to `ExtrinsicFormat::General`, which is
|
||||
// unsigned but includes extension data.
|
||||
// TODO: Move usage of `signed` to `format`:
|
||||
// - Inherent instead of None.
|
||||
// - Signed(id, extension) instead of Some((id, extra)).
|
||||
// - Introduce General(extension) for one without a signature.
|
||||
|
||||
/// Definition of something that the external world might want to say; its existence implies that it
|
||||
/// has been checked and is good, particularly with regards to the signature.
|
||||
///
|
||||
/// This is typically passed into [`traits::Applyable::apply`], which should execute
|
||||
/// [`CheckedExtrinsic::function`], alongside all other bits and bobs.
|
||||
#[derive(PartialEq, Eq, Clone, sp_core::RuntimeDebug)]
|
||||
pub struct CheckedExtrinsic<AccountId, Call, Extra> {
|
||||
pub struct CheckedExtrinsic<AccountId, Call, Extension> {
|
||||
/// Who this purports to be from and the number of extrinsics have come before
|
||||
/// from the same signer, if anyone (note this is not a signature).
|
||||
pub signed: Option<(AccountId, Extra)>,
|
||||
pub format: ExtrinsicFormat<AccountId, Extension>,
|
||||
|
||||
/// The function that should be called.
|
||||
pub function: Call,
|
||||
}
|
||||
|
||||
impl<AccountId, Call, Extra, RuntimeOrigin> traits::Applyable
|
||||
for CheckedExtrinsic<AccountId, Call, Extra>
|
||||
impl<AccountId, Call, Extension, RuntimeOrigin> traits::Applyable
|
||||
for CheckedExtrinsic<AccountId, Call, Extension>
|
||||
where
|
||||
AccountId: Member + MaybeDisplay,
|
||||
Call: Member + Dispatchable<RuntimeOrigin = RuntimeOrigin>,
|
||||
Extra: SignedExtension<AccountId = AccountId, Call = Call>,
|
||||
Call: Member + Dispatchable<RuntimeOrigin = RuntimeOrigin> + Encode,
|
||||
Extension: TransactionExtension<Call, ()>,
|
||||
RuntimeOrigin: From<Option<AccountId>>,
|
||||
{
|
||||
type Call = Call;
|
||||
|
||||
fn validate<U: ValidateUnsigned<Call = Self::Call>>(
|
||||
fn validate<I: ValidateUnsigned<Call = Self::Call>>(
|
||||
&self,
|
||||
// TODO [#5006;ToDr] should source be passed to `SignedExtension`s?
|
||||
// Perhaps a change for 2.0 to avoid breaking too much APIs?
|
||||
source: TransactionSource,
|
||||
info: &DispatchInfoOf<Self::Call>,
|
||||
len: usize,
|
||||
) -> TransactionValidity {
|
||||
if let Some((ref id, ref extra)) = self.signed {
|
||||
Extra::validate(extra, id, &self.function, info, len)
|
||||
} else {
|
||||
let valid = Extra::validate_unsigned(&self.function, info, len)?;
|
||||
let unsigned_validation = U::validate_unsigned(source, &self.function)?;
|
||||
Ok(valid.combine_with(unsigned_validation))
|
||||
match self.format {
|
||||
ExtrinsicFormat::Bare => {
|
||||
let inherent_validation = I::validate_unsigned(source, &self.function)?;
|
||||
#[allow(deprecated)]
|
||||
let legacy_validation = Extension::validate_bare_compat(&self.function, info, len)?;
|
||||
Ok(legacy_validation.combine_with(inherent_validation))
|
||||
},
|
||||
ExtrinsicFormat::Signed(ref signer, ref extension) => {
|
||||
let origin = Some(signer.clone()).into();
|
||||
extension.validate_only(origin, &self.function, info, len).map(|x| x.0)
|
||||
},
|
||||
ExtrinsicFormat::General(ref extension) =>
|
||||
extension.validate_only(None.into(), &self.function, info, len).map(|x| x.0),
|
||||
}
|
||||
}
|
||||
|
||||
fn apply<U: ValidateUnsigned<Call = Self::Call>>(
|
||||
fn apply<I: ValidateUnsigned<Call = Self::Call>>(
|
||||
self,
|
||||
info: &DispatchInfoOf<Self::Call>,
|
||||
len: usize,
|
||||
) -> crate::ApplyExtrinsicResultWithInfo<PostDispatchInfoOf<Self::Call>> {
|
||||
let (maybe_who, maybe_pre) = if let Some((id, extra)) = self.signed {
|
||||
let pre = Extra::pre_dispatch(extra, &id, &self.function, info, len)?;
|
||||
(Some(id), Some(pre))
|
||||
} else {
|
||||
Extra::pre_dispatch_unsigned(&self.function, info, len)?;
|
||||
U::pre_dispatch(&self.function)?;
|
||||
(None, None)
|
||||
};
|
||||
let res = self.function.dispatch(RuntimeOrigin::from(maybe_who));
|
||||
let post_info = match res {
|
||||
Ok(info) => info,
|
||||
Err(err) => err.post_info,
|
||||
};
|
||||
Extra::post_dispatch(
|
||||
maybe_pre,
|
||||
info,
|
||||
&post_info,
|
||||
len,
|
||||
&res.map(|_| ()).map_err(|e| e.error),
|
||||
)?;
|
||||
Ok(res)
|
||||
match self.format {
|
||||
ExtrinsicFormat::Bare => {
|
||||
I::pre_dispatch(&self.function)?;
|
||||
// TODO: Remove below once `pre_dispatch_unsigned` is removed from `LegacyExtension`
|
||||
// or `LegacyExtension` is removed.
|
||||
#[allow(deprecated)]
|
||||
Extension::validate_bare_compat(&self.function, info, len)?;
|
||||
#[allow(deprecated)]
|
||||
Extension::pre_dispatch_bare_compat(&self.function, info, len)?;
|
||||
let res = self.function.dispatch(None.into());
|
||||
let post_info = res.unwrap_or_else(|err| err.post_info);
|
||||
let pd_res = res.map(|_| ()).map_err(|e| e.error);
|
||||
// TODO: Remove below once `pre_dispatch_unsigned` is removed from `LegacyExtension`
|
||||
// or `LegacyExtension` is removed.
|
||||
#[allow(deprecated)]
|
||||
Extension::post_dispatch_bare_compat(info, &post_info, len, &pd_res)?;
|
||||
Ok(res)
|
||||
},
|
||||
ExtrinsicFormat::Signed(signer, extension) =>
|
||||
extension.dispatch_transaction(Some(signer).into(), self.function, info, len),
|
||||
ExtrinsicFormat::General(extension) =>
|
||||
extension.dispatch_transaction(None.into(), self.function, info, len),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,9 +29,9 @@ mod unchecked_extrinsic;
|
||||
|
||||
pub use self::{
|
||||
block::{Block, BlockId, SignedBlock},
|
||||
checked_extrinsic::CheckedExtrinsic,
|
||||
checked_extrinsic::{CheckedExtrinsic, ExtrinsicFormat},
|
||||
digest::{Digest, DigestItem, DigestItemRef, OpaqueDigestItemId},
|
||||
era::{Era, Phase},
|
||||
header::Header,
|
||||
unchecked_extrinsic::{SignedPayload, UncheckedExtrinsic},
|
||||
unchecked_extrinsic::{Preamble, SignedPayload, UncheckedExtrinsic},
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -125,6 +125,11 @@ pub use sp_arithmetic::{
|
||||
Perquintill, Rational128, Rounding, UpperOf,
|
||||
};
|
||||
|
||||
pub use transaction_validity::{
|
||||
InvalidTransaction, TransactionSource, TransactionValidityError, UnknownTransaction,
|
||||
ValidTransaction,
|
||||
};
|
||||
|
||||
pub use either::Either;
|
||||
|
||||
/// The number of bytes of the module-specific `error` field defined in [`ModuleError`].
|
||||
@@ -443,21 +448,21 @@ impl std::fmt::Display for MultiSigner {
|
||||
impl Verify for MultiSignature {
|
||||
type Signer = MultiSigner;
|
||||
fn verify<L: Lazy<[u8]>>(&self, mut msg: L, signer: &AccountId32) -> bool {
|
||||
match (self, signer) {
|
||||
(Self::Ed25519(ref sig), who) => match ed25519::Public::from_slice(who.as_ref()) {
|
||||
match self {
|
||||
Self::Ed25519(ref sig) => match ed25519::Public::from_slice(signer.as_ref()) {
|
||||
Ok(signer) => sig.verify(msg, &signer),
|
||||
Err(()) => false,
|
||||
},
|
||||
(Self::Sr25519(ref sig), who) => match sr25519::Public::from_slice(who.as_ref()) {
|
||||
Self::Sr25519(ref sig) => match sr25519::Public::from_slice(signer.as_ref()) {
|
||||
Ok(signer) => sig.verify(msg, &signer),
|
||||
Err(()) => false,
|
||||
},
|
||||
(Self::Ecdsa(ref sig), who) => {
|
||||
Self::Ecdsa(ref sig) => {
|
||||
let m = sp_io::hashing::blake2_256(msg.get());
|
||||
match sp_io::crypto::secp256k1_ecdsa_recover_compressed(sig.as_ref(), &m) {
|
||||
Ok(pubkey) =>
|
||||
&sp_io::hashing::blake2_256(pubkey.as_ref()) ==
|
||||
<dyn AsRef<[u8; 32]>>::as_ref(who),
|
||||
<dyn AsRef<[u8; 32]>>::as_ref(signer),
|
||||
_ => false,
|
||||
}
|
||||
},
|
||||
@@ -944,9 +949,13 @@ impl<'a> ::serde::Deserialize<'a> for OpaqueExtrinsic {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: OpaqueExtrinsics cannot act like regular extrinsics, right?!
|
||||
impl traits::Extrinsic for OpaqueExtrinsic {
|
||||
type Call = ();
|
||||
type SignaturePayload = ();
|
||||
fn is_bare(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Print something that implements `Printable` from the runtime.
|
||||
|
||||
@@ -21,24 +21,16 @@ use crate::{
|
||||
codec::{Codec, Decode, Encode, MaxEncodedLen},
|
||||
generic,
|
||||
scale_info::TypeInfo,
|
||||
traits::{
|
||||
self, Applyable, BlakeTwo256, Checkable, DispatchInfoOf, Dispatchable, OpaqueKeys,
|
||||
PostDispatchInfoOf, SignaturePayload, SignedExtension, ValidateUnsigned,
|
||||
},
|
||||
transaction_validity::{TransactionSource, TransactionValidity, TransactionValidityError},
|
||||
ApplyExtrinsicResultWithInfo, KeyTypeId,
|
||||
traits::{self, BlakeTwo256, Dispatchable, OpaqueKeys},
|
||||
DispatchResultWithInfo, KeyTypeId,
|
||||
};
|
||||
use serde::{de::Error as DeError, Deserialize, Deserializer, Serialize, Serializer};
|
||||
use serde::{de::Error as DeError, Deserialize, Deserializer, Serialize};
|
||||
use sp_core::{
|
||||
crypto::{key_types, ByteArray, CryptoType, Dummy},
|
||||
U256,
|
||||
};
|
||||
pub use sp_core::{sr25519, H256};
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
fmt::{self, Debug},
|
||||
ops::Deref,
|
||||
};
|
||||
use std::{cell::RefCell, fmt::Debug};
|
||||
|
||||
/// A dummy type which can be used instead of regular cryptographic primitives.
|
||||
///
|
||||
@@ -198,42 +190,6 @@ impl Header {
|
||||
}
|
||||
}
|
||||
|
||||
/// An opaque extrinsic wrapper type.
|
||||
#[derive(PartialEq, Eq, Clone, Debug, Encode, Decode)]
|
||||
pub struct ExtrinsicWrapper<Xt>(Xt);
|
||||
|
||||
impl<Xt> traits::Extrinsic for ExtrinsicWrapper<Xt> {
|
||||
type Call = ();
|
||||
type SignaturePayload = ();
|
||||
|
||||
fn is_signed(&self) -> Option<bool> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl<Xt: Encode> serde::Serialize for ExtrinsicWrapper<Xt> {
|
||||
fn serialize<S>(&self, seq: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: ::serde::Serializer,
|
||||
{
|
||||
self.using_encoded(|bytes| seq.serialize_bytes(bytes))
|
||||
}
|
||||
}
|
||||
|
||||
impl<Xt> From<Xt> for ExtrinsicWrapper<Xt> {
|
||||
fn from(xt: Xt) -> Self {
|
||||
ExtrinsicWrapper(xt)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Xt> Deref for ExtrinsicWrapper<Xt> {
|
||||
type Target = Xt;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
/// Testing block
|
||||
#[derive(PartialEq, Eq, Clone, Serialize, Debug, Encode, Decode, TypeInfo)]
|
||||
pub struct Block<Xt> {
|
||||
@@ -283,139 +239,22 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// The signature payload of a `TestXt`.
|
||||
type TxSingaturePayload<Extra> = (u64, Extra);
|
||||
/// Wrapper over a `u64` that can be used as a `RuntimeCall`.
|
||||
#[derive(PartialEq, Eq, Debug, Clone, Encode, Decode, TypeInfo)]
|
||||
pub struct MockCallU64(pub u64);
|
||||
|
||||
impl<Extra: TypeInfo> SignaturePayload for TxSingaturePayload<Extra> {
|
||||
type SignatureAddress = u64;
|
||||
type Signature = ();
|
||||
type SignatureExtra = Extra;
|
||||
}
|
||||
|
||||
/// Test transaction, tuple of (sender, call, signed_extra)
|
||||
/// with index only used if sender is some.
|
||||
///
|
||||
/// If sender is some then the transaction is signed otherwise it is unsigned.
|
||||
#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo)]
|
||||
pub struct TestXt<Call, Extra> {
|
||||
/// Signature of the extrinsic.
|
||||
pub signature: Option<TxSingaturePayload<Extra>>,
|
||||
/// Call of the extrinsic.
|
||||
pub call: Call,
|
||||
}
|
||||
|
||||
impl<Call, Extra> TestXt<Call, Extra> {
|
||||
/// Create a new `TextXt`.
|
||||
pub fn new(call: Call, signature: Option<(u64, Extra)>) -> Self {
|
||||
Self { call, signature }
|
||||
impl Dispatchable for MockCallU64 {
|
||||
type RuntimeOrigin = u64;
|
||||
type Config = ();
|
||||
type Info = ();
|
||||
type PostInfo = ();
|
||||
fn dispatch(self, _origin: Self::RuntimeOrigin) -> DispatchResultWithInfo<Self::PostInfo> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<Call, Extra> Serialize for TestXt<Call, Extra>
|
||||
where
|
||||
TestXt<Call, Extra>: Encode,
|
||||
{
|
||||
fn serialize<S>(&self, seq: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
self.using_encoded(|bytes| seq.serialize_bytes(bytes))
|
||||
}
|
||||
}
|
||||
|
||||
impl<Call, Extra> Debug for TestXt<Call, Extra> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "TestXt({:?}, ...)", self.signature.as_ref().map(|x| &x.0))
|
||||
}
|
||||
}
|
||||
|
||||
impl<Call: Codec + Sync + Send, Context, Extra> Checkable<Context> for TestXt<Call, Extra> {
|
||||
type Checked = Self;
|
||||
fn check(self, _: &Context) -> Result<Self::Checked, TransactionValidityError> {
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
#[cfg(feature = "try-runtime")]
|
||||
fn unchecked_into_checked_i_know_what_i_am_doing(
|
||||
self,
|
||||
_: &Context,
|
||||
) -> Result<Self::Checked, TransactionValidityError> {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Call: Codec + Sync + Send + TypeInfo, Extra: TypeInfo> traits::Extrinsic
|
||||
for TestXt<Call, Extra>
|
||||
{
|
||||
type Call = Call;
|
||||
type SignaturePayload = TxSingaturePayload<Extra>;
|
||||
|
||||
fn is_signed(&self) -> Option<bool> {
|
||||
Some(self.signature.is_some())
|
||||
}
|
||||
|
||||
fn new(c: Call, sig: Option<Self::SignaturePayload>) -> Option<Self> {
|
||||
Some(TestXt { signature: sig, call: c })
|
||||
}
|
||||
}
|
||||
|
||||
impl<Call, Extra> traits::ExtrinsicMetadata for TestXt<Call, Extra>
|
||||
where
|
||||
Call: Codec + Sync + Send,
|
||||
Extra: SignedExtension<AccountId = u64, Call = Call>,
|
||||
{
|
||||
type SignedExtensions = Extra;
|
||||
const VERSION: u8 = 0u8;
|
||||
}
|
||||
|
||||
impl<Origin, Call, Extra> Applyable for TestXt<Call, Extra>
|
||||
where
|
||||
Call: 'static
|
||||
+ Sized
|
||||
+ Send
|
||||
+ Sync
|
||||
+ Clone
|
||||
+ Eq
|
||||
+ Codec
|
||||
+ Debug
|
||||
+ Dispatchable<RuntimeOrigin = Origin>,
|
||||
Extra: SignedExtension<AccountId = u64, Call = Call>,
|
||||
Origin: From<Option<u64>>,
|
||||
{
|
||||
type Call = Call;
|
||||
|
||||
/// Checks to see if this is a valid *transaction*. It returns information on it if so.
|
||||
fn validate<U: ValidateUnsigned<Call = Self::Call>>(
|
||||
&self,
|
||||
source: TransactionSource,
|
||||
info: &DispatchInfoOf<Self::Call>,
|
||||
len: usize,
|
||||
) -> TransactionValidity {
|
||||
if let Some((ref id, ref extra)) = self.signature {
|
||||
Extra::validate(extra, id, &self.call, info, len)
|
||||
} else {
|
||||
let valid = Extra::validate_unsigned(&self.call, info, len)?;
|
||||
let unsigned_validation = U::validate_unsigned(source, &self.call)?;
|
||||
Ok(valid.combine_with(unsigned_validation))
|
||||
}
|
||||
}
|
||||
|
||||
/// Executes all necessary logic needed prior to dispatch and deconstructs into function call,
|
||||
/// index and sender.
|
||||
fn apply<U: ValidateUnsigned<Call = Self::Call>>(
|
||||
self,
|
||||
info: &DispatchInfoOf<Self::Call>,
|
||||
len: usize,
|
||||
) -> ApplyExtrinsicResultWithInfo<PostDispatchInfoOf<Self::Call>> {
|
||||
let maybe_who = if let Some((who, extra)) = self.signature {
|
||||
Extra::pre_dispatch(extra, &who, &self.call, info, len)?;
|
||||
Some(who)
|
||||
} else {
|
||||
Extra::pre_dispatch_unsigned(&self.call, info, len)?;
|
||||
U::pre_dispatch(&self.call)?;
|
||||
None
|
||||
};
|
||||
|
||||
Ok(self.call.dispatch(maybe_who.into()))
|
||||
impl From<u64> for MockCallU64 {
|
||||
fn from(value: u64) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
+98
-146
@@ -19,7 +19,7 @@
|
||||
|
||||
use crate::{
|
||||
generic::Digest,
|
||||
scale_info::{MetaType, StaticTypeInfo, TypeInfo},
|
||||
scale_info::{StaticTypeInfo, TypeInfo},
|
||||
transaction_validity::{
|
||||
TransactionSource, TransactionValidity, TransactionValidityError, UnknownTransaction,
|
||||
ValidTransaction,
|
||||
@@ -52,6 +52,12 @@ use std::fmt::Display;
|
||||
#[cfg(feature = "std")]
|
||||
use std::str::FromStr;
|
||||
|
||||
pub mod transaction_extension;
|
||||
pub use transaction_extension::{
|
||||
DispatchTransaction, TransactionExtension, TransactionExtensionBase,
|
||||
TransactionExtensionInterior, TransactionExtensionMetadata, ValidateResult,
|
||||
};
|
||||
|
||||
/// A lazy value.
|
||||
pub trait Lazy<T: ?Sized> {
|
||||
/// Get a reference to the underlying value.
|
||||
@@ -1253,7 +1259,7 @@ pub trait Header:
|
||||
// that is then used to define `UncheckedExtrinsic`.
|
||||
// ```ignore
|
||||
// pub type UncheckedExtrinsic =
|
||||
// generic::UncheckedExtrinsic<Address, RuntimeCall, Signature, SignedExtra>;
|
||||
// generic::UncheckedExtrinsic<Address, RuntimeCall, Signature, TxExtension>;
|
||||
// ```
|
||||
// This `UncheckedExtrinsic` is supplied to the `Block`.
|
||||
// ```ignore
|
||||
@@ -1323,19 +1329,31 @@ pub trait Extrinsic: Sized {
|
||||
|
||||
/// Is this `Extrinsic` signed?
|
||||
/// If no information are available about signed/unsigned, `None` should be returned.
|
||||
#[deprecated = "Use and implement `!is_bare()` instead"]
|
||||
fn is_signed(&self) -> Option<bool> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Create new instance of the extrinsic.
|
||||
///
|
||||
/// Extrinsics can be split into:
|
||||
/// 1. Inherents (no signature; created by validators during block production)
|
||||
/// 2. Unsigned Transactions (no signature; represent "system calls" or other special kinds of
|
||||
/// calls) 3. Signed Transactions (with signature; a regular transactions with known origin)
|
||||
/// Returns `true` if this `Extrinsic` is bare.
|
||||
fn is_bare(&self) -> bool {
|
||||
#[allow(deprecated)]
|
||||
!self
|
||||
.is_signed()
|
||||
.expect("`is_signed` must return `Some` on production extrinsics; qed")
|
||||
}
|
||||
|
||||
/// Create a new old-school extrinsic, either a bare extrinsic if `_signed_data` is `None` or
|
||||
/// a signed transaction is it is `Some`.
|
||||
#[deprecated = "Use `new_inherent` or the `CreateTransaction` trait instead"]
|
||||
fn new(_call: Self::Call, _signed_data: Option<Self::SignaturePayload>) -> Option<Self> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Create a new inherent extrinsic.
|
||||
fn new_inherent(function: Self::Call) -> Self {
|
||||
#[allow(deprecated)]
|
||||
Self::new(function, None).expect("Extrinsic must provide inherents; qed")
|
||||
}
|
||||
}
|
||||
|
||||
/// Something that acts like a [`SignaturePayload`](Extrinsic::SignaturePayload) of an
|
||||
@@ -1371,7 +1389,8 @@ pub trait ExtrinsicMetadata {
|
||||
const VERSION: u8;
|
||||
|
||||
/// Signed extensions attached to this `Extrinsic`.
|
||||
type SignedExtensions: SignedExtension;
|
||||
// TODO: metadata-v16: rename to `Extension`.
|
||||
type Extra;
|
||||
}
|
||||
|
||||
/// Extract the hashing type for a block.
|
||||
@@ -1456,6 +1475,8 @@ pub trait Dispatchable {
|
||||
-> crate::DispatchResultWithInfo<Self::PostInfo>;
|
||||
}
|
||||
|
||||
/// Shortcut to reference the `Origin` type of a `Dispatchable`.
|
||||
pub type OriginOf<T> = <T as Dispatchable>::RuntimeOrigin;
|
||||
/// Shortcut to reference the `Info` type of a `Dispatchable`.
|
||||
pub type DispatchInfoOf<T> = <T as Dispatchable>::Info;
|
||||
/// Shortcut to reference the `PostInfo` type of a `Dispatchable`.
|
||||
@@ -1474,8 +1495,49 @@ impl Dispatchable for () {
|
||||
}
|
||||
}
|
||||
|
||||
/// Dispatchable impl containing an arbitrary value which panics if it actually is dispatched.
|
||||
#[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)]
|
||||
pub struct FakeDispatchable<Inner>(pub Inner);
|
||||
impl<Inner> From<Inner> for FakeDispatchable<Inner> {
|
||||
fn from(inner: Inner) -> Self {
|
||||
Self(inner)
|
||||
}
|
||||
}
|
||||
impl<Inner> FakeDispatchable<Inner> {
|
||||
/// Take `self` and return the underlying inner value.
|
||||
pub fn deconstruct(self) -> Inner {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
impl<Inner> AsRef<Inner> for FakeDispatchable<Inner> {
|
||||
fn as_ref(&self) -> &Inner {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<Inner> Dispatchable for FakeDispatchable<Inner> {
|
||||
type RuntimeOrigin = ();
|
||||
type Config = ();
|
||||
type Info = ();
|
||||
type PostInfo = ();
|
||||
fn dispatch(
|
||||
self,
|
||||
_origin: Self::RuntimeOrigin,
|
||||
) -> crate::DispatchResultWithInfo<Self::PostInfo> {
|
||||
panic!("This implementation should not be used for actual dispatch.");
|
||||
}
|
||||
}
|
||||
|
||||
/// Runtime Origin which includes a System Origin variant whose `AccountId` is the parameter.
|
||||
pub trait AsSystemOriginSigner<AccountId> {
|
||||
/// Extract a reference of the inner value of the System `Origin::Signed` variant, if self has
|
||||
/// that variant.
|
||||
fn as_system_origin_signer(&self) -> Option<&AccountId>;
|
||||
}
|
||||
|
||||
/// Means by which a transaction may be extended. This type embodies both the data and the logic
|
||||
/// that should be additionally associated with the transaction. It should be plain old data.
|
||||
#[deprecated = "Use `TransactionExtension` instead."]
|
||||
pub trait SignedExtension:
|
||||
Codec + Debug + Sync + Send + Clone + Eq + PartialEq + StaticTypeInfo
|
||||
{
|
||||
@@ -1493,7 +1555,7 @@ pub trait SignedExtension:
|
||||
|
||||
/// Any additional data that will go into the signed payload. This may be created dynamically
|
||||
/// from the transaction using the `additional_signed` function.
|
||||
type AdditionalSigned: Encode + TypeInfo;
|
||||
type AdditionalSigned: Codec + TypeInfo;
|
||||
|
||||
/// The type that encodes information that can be passed from pre_dispatch to post-dispatch.
|
||||
type Pre;
|
||||
@@ -1532,38 +1594,6 @@ pub trait SignedExtension:
|
||||
len: usize,
|
||||
) -> Result<Self::Pre, TransactionValidityError>;
|
||||
|
||||
/// Validate an unsigned transaction for the transaction queue.
|
||||
///
|
||||
/// This function can be called frequently by the transaction queue
|
||||
/// to obtain transaction validity against current state.
|
||||
/// It should perform all checks that determine a valid unsigned transaction,
|
||||
/// and quickly eliminate ones that are stale or incorrect.
|
||||
///
|
||||
/// Make sure to perform the same checks in `pre_dispatch_unsigned` function.
|
||||
fn validate_unsigned(
|
||||
_call: &Self::Call,
|
||||
_info: &DispatchInfoOf<Self::Call>,
|
||||
_len: usize,
|
||||
) -> TransactionValidity {
|
||||
Ok(ValidTransaction::default())
|
||||
}
|
||||
|
||||
/// Do any pre-flight stuff for a unsigned transaction.
|
||||
///
|
||||
/// Note this function by default delegates to `validate_unsigned`, so that
|
||||
/// all checks performed for the transaction queue are also performed during
|
||||
/// the dispatch phase (applying the extrinsic).
|
||||
///
|
||||
/// If you ever override this function, you need to make sure to always
|
||||
/// perform the same validation as in `validate_unsigned`.
|
||||
fn pre_dispatch_unsigned(
|
||||
call: &Self::Call,
|
||||
info: &DispatchInfoOf<Self::Call>,
|
||||
len: usize,
|
||||
) -> Result<(), TransactionValidityError> {
|
||||
Self::validate_unsigned(call, info, len).map(|_| ()).map_err(Into::into)
|
||||
}
|
||||
|
||||
/// Do any post-flight stuff for an extrinsic.
|
||||
///
|
||||
/// If the transaction is signed, then `_pre` will contain the output of `pre_dispatch`,
|
||||
@@ -1594,126 +1624,47 @@ pub trait SignedExtension:
|
||||
///
|
||||
/// As a [`SignedExtension`] can be a tuple of [`SignedExtension`]s we need to return a `Vec`
|
||||
/// that holds the metadata of each one. Each individual `SignedExtension` must return
|
||||
/// *exactly* one [`SignedExtensionMetadata`].
|
||||
/// *exactly* one [`TransactionExtensionMetadata`].
|
||||
///
|
||||
/// This method provides a default implementation that returns a vec containing a single
|
||||
/// [`SignedExtensionMetadata`].
|
||||
fn metadata() -> Vec<SignedExtensionMetadata> {
|
||||
sp_std::vec![SignedExtensionMetadata {
|
||||
/// [`TransactionExtensionMetadata`].
|
||||
fn metadata() -> Vec<TransactionExtensionMetadata> {
|
||||
sp_std::vec![TransactionExtensionMetadata {
|
||||
identifier: Self::IDENTIFIER,
|
||||
ty: scale_info::meta_type::<Self>(),
|
||||
additional_signed: scale_info::meta_type::<Self::AdditionalSigned>()
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
/// Information about a [`SignedExtension`] for the runtime metadata.
|
||||
pub struct SignedExtensionMetadata {
|
||||
/// The unique identifier of the [`SignedExtension`].
|
||||
pub identifier: &'static str,
|
||||
/// The type of the [`SignedExtension`].
|
||||
pub ty: MetaType,
|
||||
/// The type of the [`SignedExtension`] additional signed data for the payload.
|
||||
pub additional_signed: MetaType,
|
||||
}
|
||||
|
||||
#[impl_for_tuples(1, 12)]
|
||||
impl<AccountId, Call: Dispatchable> SignedExtension for Tuple {
|
||||
for_tuples!( where #( Tuple: SignedExtension<AccountId=AccountId, Call=Call,> )* );
|
||||
type AccountId = AccountId;
|
||||
type Call = Call;
|
||||
const IDENTIFIER: &'static str = "You should call `identifier()`!";
|
||||
for_tuples!( type AdditionalSigned = ( #( Tuple::AdditionalSigned ),* ); );
|
||||
for_tuples!( type Pre = ( #( Tuple::Pre ),* ); );
|
||||
|
||||
fn additional_signed(&self) -> Result<Self::AdditionalSigned, TransactionValidityError> {
|
||||
Ok(for_tuples!( ( #( Tuple.additional_signed()? ),* ) ))
|
||||
}
|
||||
|
||||
fn validate(
|
||||
&self,
|
||||
who: &Self::AccountId,
|
||||
call: &Self::Call,
|
||||
info: &DispatchInfoOf<Self::Call>,
|
||||
len: usize,
|
||||
) -> TransactionValidity {
|
||||
let valid = ValidTransaction::default();
|
||||
for_tuples!( #( let valid = valid.combine_with(Tuple.validate(who, call, info, len)?); )* );
|
||||
Ok(valid)
|
||||
}
|
||||
|
||||
fn pre_dispatch(
|
||||
self,
|
||||
who: &Self::AccountId,
|
||||
call: &Self::Call,
|
||||
info: &DispatchInfoOf<Self::Call>,
|
||||
len: usize,
|
||||
) -> Result<Self::Pre, TransactionValidityError> {
|
||||
Ok(for_tuples!( ( #( Tuple.pre_dispatch(who, call, info, len)? ),* ) ))
|
||||
}
|
||||
|
||||
/// Validate an unsigned transaction for the transaction queue.
|
||||
///
|
||||
/// This function can be called frequently by the transaction queue
|
||||
/// to obtain transaction validity against current state.
|
||||
/// It should perform all checks that determine a valid unsigned transaction,
|
||||
/// and quickly eliminate ones that are stale or incorrect.
|
||||
fn validate_unsigned(
|
||||
call: &Self::Call,
|
||||
info: &DispatchInfoOf<Self::Call>,
|
||||
len: usize,
|
||||
_call: &Self::Call,
|
||||
_info: &DispatchInfoOf<Self::Call>,
|
||||
_len: usize,
|
||||
) -> TransactionValidity {
|
||||
let valid = ValidTransaction::default();
|
||||
for_tuples!( #( let valid = valid.combine_with(Tuple::validate_unsigned(call, info, len)?); )* );
|
||||
Ok(valid)
|
||||
Ok(ValidTransaction::default())
|
||||
}
|
||||
|
||||
/// Do any pre-flight stuff for an unsigned transaction.
|
||||
///
|
||||
/// Note this function by default delegates to `validate_unsigned`, so that
|
||||
/// all checks performed for the transaction queue are also performed during
|
||||
/// the dispatch phase (applying the extrinsic).
|
||||
///
|
||||
/// If you ever override this function, you need not perform the same validation as in
|
||||
/// `validate_unsigned`.
|
||||
fn pre_dispatch_unsigned(
|
||||
call: &Self::Call,
|
||||
info: &DispatchInfoOf<Self::Call>,
|
||||
len: usize,
|
||||
_call: &Self::Call,
|
||||
_info: &DispatchInfoOf<Self::Call>,
|
||||
_len: usize,
|
||||
) -> Result<(), TransactionValidityError> {
|
||||
for_tuples!( #( Tuple::pre_dispatch_unsigned(call, info, len)?; )* );
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn post_dispatch(
|
||||
pre: Option<Self::Pre>,
|
||||
info: &DispatchInfoOf<Self::Call>,
|
||||
post_info: &PostDispatchInfoOf<Self::Call>,
|
||||
len: usize,
|
||||
result: &DispatchResult,
|
||||
) -> Result<(), TransactionValidityError> {
|
||||
match pre {
|
||||
Some(x) => {
|
||||
for_tuples!( #( Tuple::post_dispatch(Some(x.Tuple), info, post_info, len, result)?; )* );
|
||||
},
|
||||
None => {
|
||||
for_tuples!( #( Tuple::post_dispatch(None, info, post_info, len, result)?; )* );
|
||||
},
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn metadata() -> Vec<SignedExtensionMetadata> {
|
||||
let mut ids = Vec::new();
|
||||
for_tuples!( #( ids.extend(Tuple::metadata()); )* );
|
||||
ids
|
||||
}
|
||||
}
|
||||
|
||||
impl SignedExtension for () {
|
||||
type AccountId = u64;
|
||||
type AdditionalSigned = ();
|
||||
type Call = ();
|
||||
type Pre = ();
|
||||
const IDENTIFIER: &'static str = "UnitSignedExtension";
|
||||
fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {
|
||||
Ok(())
|
||||
}
|
||||
fn pre_dispatch(
|
||||
self,
|
||||
who: &Self::AccountId,
|
||||
call: &Self::Call,
|
||||
info: &DispatchInfoOf<Self::Call>,
|
||||
len: usize,
|
||||
) -> Result<Self::Pre, TransactionValidityError> {
|
||||
self.validate(who, call, info, len).map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
/// An "executable" piece of information, used by the standard Substrate Executive in order to
|
||||
@@ -1762,6 +1713,7 @@ pub trait GetNodeBlockType {
|
||||
/// function is called right before dispatching the call wrapped by an unsigned extrinsic. The
|
||||
/// [`validate_unsigned`](Self::validate_unsigned) function is mainly being used in the context of
|
||||
/// the transaction pool to check the validity of the call wrapped by an unsigned extrinsic.
|
||||
// TODO: Rename to ValidateBareTransaction (or just remove).
|
||||
pub trait ValidateUnsigned {
|
||||
/// The call to validate
|
||||
type Call;
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! The [AsTransactionExtension] adapter struct for adapting [SignedExtension]s to
|
||||
//! [TransactionExtension]s.
|
||||
|
||||
#![allow(deprecated)]
|
||||
|
||||
use scale_info::TypeInfo;
|
||||
use sp_core::RuntimeDebug;
|
||||
|
||||
use crate::{
|
||||
traits::{AsSystemOriginSigner, SignedExtension, ValidateResult},
|
||||
InvalidTransaction,
|
||||
};
|
||||
|
||||
use super::*;
|
||||
|
||||
/// Adapter to use a `SignedExtension` in the place of a `TransactionExtension`.
|
||||
#[derive(TypeInfo, Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)]
|
||||
#[deprecated = "Convert your SignedExtension to a TransactionExtension."]
|
||||
pub struct AsTransactionExtension<SE: SignedExtension>(pub SE);
|
||||
|
||||
impl<SE: SignedExtension + Default> Default for AsTransactionExtension<SE> {
|
||||
fn default() -> Self {
|
||||
Self(SE::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<SE: SignedExtension> From<SE> for AsTransactionExtension<SE> {
|
||||
fn from(value: SE) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<SE: SignedExtension> TransactionExtensionBase for AsTransactionExtension<SE> {
|
||||
const IDENTIFIER: &'static str = SE::IDENTIFIER;
|
||||
type Implicit = SE::AdditionalSigned;
|
||||
|
||||
fn implicit(&self) -> Result<Self::Implicit, TransactionValidityError> {
|
||||
self.0.additional_signed()
|
||||
}
|
||||
fn metadata() -> Vec<TransactionExtensionMetadata> {
|
||||
SE::metadata()
|
||||
}
|
||||
}
|
||||
|
||||
impl<SE: SignedExtension, Context> TransactionExtension<SE::Call, Context>
|
||||
for AsTransactionExtension<SE>
|
||||
where
|
||||
<SE::Call as Dispatchable>::RuntimeOrigin: AsSystemOriginSigner<SE::AccountId> + Clone,
|
||||
{
|
||||
type Val = ();
|
||||
type Pre = SE::Pre;
|
||||
|
||||
fn validate(
|
||||
&self,
|
||||
origin: <SE::Call as Dispatchable>::RuntimeOrigin,
|
||||
call: &SE::Call,
|
||||
info: &DispatchInfoOf<SE::Call>,
|
||||
len: usize,
|
||||
_context: &mut Context,
|
||||
_self_implicit: Self::Implicit,
|
||||
_inherited_implication: &impl Encode,
|
||||
) -> ValidateResult<Self::Val, SE::Call> {
|
||||
let who = origin.as_system_origin_signer().ok_or(InvalidTransaction::BadSigner)?;
|
||||
let r = self.0.validate(who, call, info, len)?;
|
||||
Ok((r, (), origin))
|
||||
}
|
||||
|
||||
fn prepare(
|
||||
self,
|
||||
_: (),
|
||||
origin: &<SE::Call as Dispatchable>::RuntimeOrigin,
|
||||
call: &SE::Call,
|
||||
info: &DispatchInfoOf<SE::Call>,
|
||||
len: usize,
|
||||
_context: &Context,
|
||||
) -> Result<Self::Pre, TransactionValidityError> {
|
||||
let who = origin.as_system_origin_signer().ok_or(InvalidTransaction::BadSigner)?;
|
||||
self.0.pre_dispatch(who, call, info, len)
|
||||
}
|
||||
|
||||
fn post_dispatch(
|
||||
pre: Self::Pre,
|
||||
info: &DispatchInfoOf<SE::Call>,
|
||||
post_info: &PostDispatchInfoOf<SE::Call>,
|
||||
len: usize,
|
||||
result: &DispatchResult,
|
||||
_context: &Context,
|
||||
) -> Result<(), TransactionValidityError> {
|
||||
SE::post_dispatch(Some(pre), info, post_info, len, result)
|
||||
}
|
||||
|
||||
fn validate_bare_compat(
|
||||
call: &SE::Call,
|
||||
info: &DispatchInfoOf<SE::Call>,
|
||||
len: usize,
|
||||
) -> TransactionValidity {
|
||||
SE::validate_unsigned(call, info, len)
|
||||
}
|
||||
|
||||
fn pre_dispatch_bare_compat(
|
||||
call: &SE::Call,
|
||||
info: &DispatchInfoOf<SE::Call>,
|
||||
len: usize,
|
||||
) -> Result<(), TransactionValidityError> {
|
||||
SE::pre_dispatch_unsigned(call, info, len)
|
||||
}
|
||||
|
||||
fn post_dispatch_bare_compat(
|
||||
info: &DispatchInfoOf<SE::Call>,
|
||||
post_info: &PostDispatchInfoOf<SE::Call>,
|
||||
len: usize,
|
||||
result: &DispatchResult,
|
||||
) -> Result<(), TransactionValidityError> {
|
||||
SE::post_dispatch(None, info, post_info, len, result)
|
||||
}
|
||||
}
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! The [DispatchTransaction] trait.
|
||||
|
||||
use super::*;
|
||||
|
||||
/// Single-function utility trait with a blanket impl over [TransactionExtension] in order to
|
||||
/// provide transaction dispatching functionality. We avoid implementing this directly on the trait
|
||||
/// since we never want it to be overriden by the trait implementation.
|
||||
pub trait DispatchTransaction<Call: Dispatchable> {
|
||||
/// The origin type of the transaction.
|
||||
type Origin;
|
||||
/// The info type.
|
||||
type Info;
|
||||
/// The resultant type.
|
||||
type Result;
|
||||
/// The `Val` of the extension.
|
||||
type Val;
|
||||
/// The `Pre` of the extension.
|
||||
type Pre;
|
||||
/// Just validate a transaction.
|
||||
///
|
||||
/// The is basically the same as [validate](TransactionExtension::validate), except that there
|
||||
/// is no need to supply the bond data.
|
||||
fn validate_only(
|
||||
&self,
|
||||
origin: Self::Origin,
|
||||
call: &Call,
|
||||
info: &Self::Info,
|
||||
len: usize,
|
||||
) -> Result<(ValidTransaction, Self::Val, Self::Origin), TransactionValidityError>;
|
||||
/// Prepare and validate a transaction, ready for dispatch.
|
||||
fn validate_and_prepare(
|
||||
self,
|
||||
origin: Self::Origin,
|
||||
call: &Call,
|
||||
info: &Self::Info,
|
||||
len: usize,
|
||||
) -> Result<(Self::Pre, Self::Origin), TransactionValidityError>;
|
||||
/// Dispatch a transaction with the given base origin and call.
|
||||
fn dispatch_transaction(
|
||||
self,
|
||||
origin: Self::Origin,
|
||||
call: Call,
|
||||
info: &Self::Info,
|
||||
len: usize,
|
||||
) -> Self::Result;
|
||||
/// Do everything which would be done in a [dispatch_transaction](Self::dispatch_transaction),
|
||||
/// but instead of executing the call, execute `substitute` instead. Since this doesn't actually
|
||||
/// dispatch the call, it doesn't need to consume it and so `call` can be passed as a reference.
|
||||
fn test_run(
|
||||
self,
|
||||
origin: Self::Origin,
|
||||
call: &Call,
|
||||
info: &Self::Info,
|
||||
len: usize,
|
||||
substitute: impl FnOnce(
|
||||
Self::Origin,
|
||||
) -> crate::DispatchResultWithInfo<<Call as Dispatchable>::PostInfo>,
|
||||
) -> Self::Result;
|
||||
}
|
||||
|
||||
impl<T: TransactionExtension<Call, ()>, Call: Dispatchable + Encode> DispatchTransaction<Call>
|
||||
for T
|
||||
{
|
||||
type Origin = <Call as Dispatchable>::RuntimeOrigin;
|
||||
type Info = DispatchInfoOf<Call>;
|
||||
type Result = crate::ApplyExtrinsicResultWithInfo<PostDispatchInfoOf<Call>>;
|
||||
type Val = T::Val;
|
||||
type Pre = T::Pre;
|
||||
|
||||
fn validate_only(
|
||||
&self,
|
||||
origin: Self::Origin,
|
||||
call: &Call,
|
||||
info: &DispatchInfoOf<Call>,
|
||||
len: usize,
|
||||
) -> Result<(ValidTransaction, T::Val, Self::Origin), TransactionValidityError> {
|
||||
self.validate(origin, call, info, len, &mut (), self.implicit()?, call)
|
||||
}
|
||||
fn validate_and_prepare(
|
||||
self,
|
||||
origin: Self::Origin,
|
||||
call: &Call,
|
||||
info: &DispatchInfoOf<Call>,
|
||||
len: usize,
|
||||
) -> Result<(T::Pre, Self::Origin), TransactionValidityError> {
|
||||
let (_, val, origin) = self.validate_only(origin, call, info, len)?;
|
||||
let pre = self.prepare(val, &origin, &call, info, len, &())?;
|
||||
Ok((pre, origin))
|
||||
}
|
||||
fn dispatch_transaction(
|
||||
self,
|
||||
origin: <Call as Dispatchable>::RuntimeOrigin,
|
||||
call: Call,
|
||||
info: &DispatchInfoOf<Call>,
|
||||
len: usize,
|
||||
) -> Self::Result {
|
||||
let (pre, origin) = self.validate_and_prepare(origin, &call, info, len)?;
|
||||
let res = call.dispatch(origin);
|
||||
let post_info = res.unwrap_or_else(|err| err.post_info);
|
||||
let pd_res = res.map(|_| ()).map_err(|e| e.error);
|
||||
T::post_dispatch(pre, info, &post_info, len, &pd_res, &())?;
|
||||
Ok(res)
|
||||
}
|
||||
fn test_run(
|
||||
self,
|
||||
origin: Self::Origin,
|
||||
call: &Call,
|
||||
info: &Self::Info,
|
||||
len: usize,
|
||||
substitute: impl FnOnce(
|
||||
Self::Origin,
|
||||
) -> crate::DispatchResultWithInfo<<Call as Dispatchable>::PostInfo>,
|
||||
) -> Self::Result {
|
||||
let (pre, origin) = self.validate_and_prepare(origin, &call, info, len)?;
|
||||
let res = substitute(origin);
|
||||
let post_info = match res {
|
||||
Ok(info) => info,
|
||||
Err(err) => err.post_info,
|
||||
};
|
||||
let pd_res = res.map(|_| ()).map_err(|e| e.error);
|
||||
T::post_dispatch(pre, info, &post_info, len, &pd_res, &())?;
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,526 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! The transaction extension trait.
|
||||
|
||||
use crate::{
|
||||
scale_info::{MetaType, StaticTypeInfo},
|
||||
transaction_validity::{TransactionValidity, TransactionValidityError, ValidTransaction},
|
||||
DispatchResult,
|
||||
};
|
||||
use codec::{Codec, Decode, Encode};
|
||||
use impl_trait_for_tuples::impl_for_tuples;
|
||||
#[doc(hidden)]
|
||||
pub use sp_std::marker::PhantomData;
|
||||
use sp_std::{self, fmt::Debug, prelude::*};
|
||||
use sp_weights::Weight;
|
||||
use tuplex::{PopFront, PushBack};
|
||||
|
||||
use super::{DispatchInfoOf, Dispatchable, OriginOf, PostDispatchInfoOf};
|
||||
|
||||
mod as_transaction_extension;
|
||||
mod dispatch_transaction;
|
||||
#[allow(deprecated)]
|
||||
pub use as_transaction_extension::AsTransactionExtension;
|
||||
pub use dispatch_transaction::DispatchTransaction;
|
||||
|
||||
/// Shortcut for the result value of the `validate` function.
|
||||
pub type ValidateResult<Val, Call> =
|
||||
Result<(ValidTransaction, Val, OriginOf<Call>), TransactionValidityError>;
|
||||
|
||||
/// Simple blanket implementation trait to denote the bounds of a type which can be contained within
|
||||
/// a [`TransactionExtension`].
|
||||
pub trait TransactionExtensionInterior:
|
||||
Codec + Debug + Sync + Send + Clone + Eq + PartialEq + StaticTypeInfo
|
||||
{
|
||||
}
|
||||
impl<T: Codec + Debug + Sync + Send + Clone + Eq + PartialEq + StaticTypeInfo>
|
||||
TransactionExtensionInterior for T
|
||||
{
|
||||
}
|
||||
|
||||
/// Base for [TransactionExtension]s; this contains the associated types and does not require any
|
||||
/// generic parameterization.
|
||||
pub trait TransactionExtensionBase: TransactionExtensionInterior {
|
||||
/// Unique identifier of this signed extension.
|
||||
///
|
||||
/// This will be exposed in the metadata to identify the signed extension used in an extrinsic.
|
||||
const IDENTIFIER: &'static str;
|
||||
|
||||
/// Any additional data which was known at the time of transaction construction and can be
|
||||
/// useful in authenticating the transaction. This is determined dynamically in part from the
|
||||
/// on-chain environment using the `implicit` function and not directly contained in the
|
||||
/// transaction itself and therefore is considered "implicit".
|
||||
type Implicit: Codec + StaticTypeInfo;
|
||||
|
||||
/// Determine any additional data which was known at the time of transaction construction and
|
||||
/// can be useful in authenticating the transaction. The expected usage of this is to include in
|
||||
/// any data which is signed and verified as part of transactiob validation. Also perform any
|
||||
/// pre-signature-verification checks and return an error if needed.
|
||||
fn implicit(&self) -> Result<Self::Implicit, TransactionValidityError> {
|
||||
use crate::InvalidTransaction::IndeterminateImplicit;
|
||||
Ok(Self::Implicit::decode(&mut &[][..]).map_err(|_| IndeterminateImplicit)?)
|
||||
}
|
||||
|
||||
/// The weight consumed by executing this extension instance fully during transaction dispatch.
|
||||
fn weight(&self) -> Weight {
|
||||
Weight::zero()
|
||||
}
|
||||
|
||||
/// Returns the metadata for this extension.
|
||||
///
|
||||
/// As a [`TransactionExtension`] can be a tuple of [`TransactionExtension`]s we need to return
|
||||
/// a `Vec` that holds the metadata of each one. Each individual `TransactionExtension` must
|
||||
/// return *exactly* one [`TransactionExtensionMetadata`].
|
||||
///
|
||||
/// This method provides a default implementation that returns a vec containing a single
|
||||
/// [`TransactionExtensionMetadata`].
|
||||
fn metadata() -> Vec<TransactionExtensionMetadata> {
|
||||
sp_std::vec![TransactionExtensionMetadata {
|
||||
identifier: Self::IDENTIFIER,
|
||||
ty: scale_info::meta_type::<Self>(),
|
||||
// TODO: Metadata-v16: Rename to "implicit"
|
||||
additional_signed: scale_info::meta_type::<Self::Implicit>()
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
/// Means by which a transaction may be extended. This type embodies both the data and the logic
|
||||
/// that should be additionally associated with the transaction. It should be plain old data.
|
||||
///
|
||||
/// The simplest transaction extension would be the Unit type (and empty pipeline) `()`. This
|
||||
/// executes no additional logic and implies a dispatch of the transaction's call using the
|
||||
/// inherited origin (either `None` or `Signed`, depending on whether this is a signed or general
|
||||
/// transaction).
|
||||
///
|
||||
/// Transaction extensions are capable of altering certain associated semantics:
|
||||
///
|
||||
/// - They may define the origin with which the transaction's call should be dispatched.
|
||||
/// - They may define various parameters used by the transction queue to determine under what
|
||||
/// conditions the transaction should be retained and introduced on-chain.
|
||||
/// - They may define whether this transaction is acceptable for introduction on-chain at all.
|
||||
///
|
||||
/// Each of these semantics are defined by the `validate` function.
|
||||
///
|
||||
/// **NOTE: Transaction extensions cannot under any circumctances alter the call itself.**
|
||||
///
|
||||
/// Transaction extensions are capable of defining logic which is executed additionally to the
|
||||
/// dispatch of the call:
|
||||
///
|
||||
/// - They may define logic which must be executed prior to the dispatch of the call.
|
||||
/// - They may also define logic which must be executed after the dispatch of the call.
|
||||
///
|
||||
/// Each of these semantics are defined by the `prepare` and `post_dispatch` functions respectively.
|
||||
///
|
||||
/// Finally, transaction extensions may define additional data to help define the implications of
|
||||
/// the logic they introduce. This additional data may be explicitly defined by the transaction
|
||||
/// author (in which case it is included as part of the transaction body), or it may be implicitly
|
||||
/// defined by the transaction extension based around the on-chain state (which the transaction
|
||||
/// author is assumed to know). This data may be utilized by the above logic to alter how a node's
|
||||
/// transaction queue treats this transaction.
|
||||
///
|
||||
/// ## Default implementations
|
||||
///
|
||||
/// Of the 5 functions in this trait, 3 of them must return a value of an associated type on
|
||||
/// success, and none of these types implement [Default] or anything like it. This means that
|
||||
/// default implementations cannot be provided for these functions. However, a macro is provided
|
||||
/// [impl_tx_ext_default](crate::impl_tx_ext_default) which is capable of generating default
|
||||
/// implementations for each of these 3 functions. If you do not wish to introduce additional logic
|
||||
/// into the transaction pipeline, then it is recommended that you use this macro to implement these
|
||||
/// functions.
|
||||
///
|
||||
/// ## Pipelines, Inherited Implications, and Authorized Origins
|
||||
///
|
||||
/// Requiring a single transaction extension to define all of the above semantics would be
|
||||
/// cumbersome and would lead to a lot of boilerplate. Instead, transaction extensions are
|
||||
/// aggregated into pipelines, which are tuples of transaction extensions. Each extension in the
|
||||
/// pipeline is executed in order, and the output of each extension is aggregated and/or relayed as
|
||||
/// the input to the next extension in the pipeline.
|
||||
///
|
||||
/// This ordered composition happens with all datatypes ([Val](TransactionExtension::Val),
|
||||
/// [Pre](TransactionExtension::Pre) and [Implicit](TransactionExtensionBase::Implicit)) as well as
|
||||
/// all functions. There are important consequences stemming from how the composition affects the
|
||||
/// meaning of the `origin` and `implication` parameters as well as the results. Whereas the
|
||||
/// [prepare](TransactionExtension::prepare) and
|
||||
/// [post_dispatch](TransactionExtension::post_dispatch) functions are clear in their meaning, the
|
||||
/// [validate](TransactionExtension::validate) function is fairly sophisticated and warrants
|
||||
/// further explanation.
|
||||
///
|
||||
/// Firstly, the `origin` parameter. The `origin` passed into the first item in a pipeline is simply
|
||||
/// that passed into the tuple itself. It represents an authority who has authorized the implication
|
||||
/// of the transaction, as of the extension it has been passed into *and any further extensions it
|
||||
/// may pass though, all the way to, and including, the transaction's dispatch call itself. Each
|
||||
/// following item in the pipeline is passed the origin which the previous item returned. The origin
|
||||
/// returned from the final item in the pipeline is the origin which is returned by the tuple
|
||||
/// itself.
|
||||
///
|
||||
/// This means that if a constituent extension returns a different origin to the one it was called
|
||||
/// with, then (assuming no other extension changes it further) *this new origin will be used for
|
||||
/// all extensions following it in the pipeline, and will be returned from the pipeline to be used
|
||||
/// as the origin for the call's dispatch*. The call itself as well as all these extensions
|
||||
/// following may each imply consequence for this origin. We call this the *inherited implication*.
|
||||
///
|
||||
/// The *inherited implication* is the cumulated on-chain effects born by whatever origin is
|
||||
/// returned. It is expressed to the [validate](TransactionExtension::validate) function only as the
|
||||
/// `implication` argument which implements the [Encode] trait. A transaction extension may define
|
||||
/// its own implications through its own fields and the
|
||||
/// [implicit](TransactionExtensionBase::implicit) function. This is only utilized by extensions
|
||||
/// which preceed it in a pipeline or, if the transaction is an old-school signed trasnaction, the
|
||||
/// underlying transaction verification logic.
|
||||
///
|
||||
/// **The inherited implication passed as the `implication` parameter to
|
||||
/// [validate](TransactionExtension::validate) does not include the extension's inner data itself
|
||||
/// nor does it include the result of the extension's `implicit` function.** If you both provide an
|
||||
/// implication and rely on the implication, then you need to manually aggregate your extensions
|
||||
/// implication with the aggregated implication passed in.
|
||||
pub trait TransactionExtension<Call: Dispatchable, Context>: TransactionExtensionBase {
|
||||
/// The type that encodes information that can be passed from validate to prepare.
|
||||
type Val;
|
||||
|
||||
/// The type that encodes information that can be passed from prepare to post-dispatch.
|
||||
type Pre;
|
||||
|
||||
/// Validate a transaction for the transaction queue.
|
||||
///
|
||||
/// This function can be called frequently by the transaction queue to obtain transaction
|
||||
/// validity against current state. It should perform all checks that determine a valid
|
||||
/// transaction, that can pay for its execution and quickly eliminate ones that are stale or
|
||||
/// incorrect.
|
||||
///
|
||||
/// Parameters:
|
||||
/// - `origin`: The origin of the transaction which this extension inherited; coming from an
|
||||
/// "old-school" *signed transaction*, this will be a system `RawOrigin::Signed` value. If the
|
||||
/// transaction is a "new-school" *General Transaction*, then this will be a system
|
||||
/// `RawOrigin::None` value. If this extension is an item in a composite, then it could be
|
||||
/// anything which was previously returned as an `origin` value in the result of a `validate`
|
||||
/// call.
|
||||
/// - `call`: The `Call` wrapped by this extension.
|
||||
/// - `info`: Information concerning, and inherent to, the transaction's call.
|
||||
/// - `len`: The total length of the encoded transaction.
|
||||
/// - `inherited_implication`: The *implication* which this extension inherits. This is a tuple
|
||||
/// of the transaction's call and some additional opaque-but-encodable data. Coming directly
|
||||
/// from a transaction, the latter is [()]. However, if this extension is expressed as part of
|
||||
/// a composite type, then the latter component is equal to any further implications to which
|
||||
/// the returned `origin` could potentially apply. See Pipelines, Inherited Implications, and
|
||||
/// Authorized Origins for more information.
|
||||
/// - `context`: Some opaque mutable context, as yet unused.
|
||||
///
|
||||
/// Returns a [ValidateResult], which is a [Result] whose success type is a tuple of
|
||||
/// [ValidTransaction] (defining useful metadata for the transaction queue), the [Self::Val]
|
||||
/// token of this transaction, which gets passed into [prepare](TransactionExtension::prepare),
|
||||
/// and the origin of the transaction, which gets passed into
|
||||
/// [prepare](TransactionExtension::prepare) and is ultimately used for dispatch.
|
||||
fn validate(
|
||||
&self,
|
||||
origin: OriginOf<Call>,
|
||||
call: &Call,
|
||||
info: &DispatchInfoOf<Call>,
|
||||
len: usize,
|
||||
context: &mut Context,
|
||||
self_implicit: Self::Implicit,
|
||||
inherited_implication: &impl Encode,
|
||||
) -> ValidateResult<Self::Val, Call>;
|
||||
|
||||
/// Do any pre-flight stuff for a transaction after validation.
|
||||
///
|
||||
/// This is for actions which do not happen in the transaction queue but only immediately prior
|
||||
/// to the point of dispatch on-chain. This should not return an error, since errors should
|
||||
/// already have been identified during the [validate](TransactionExtension::validate) call. If
|
||||
/// an error is returned, the transaction will be considered invalid but no state changes will
|
||||
/// happen and therefore work done in [validate](TransactionExtension::validate) will not be
|
||||
/// paid for.
|
||||
///
|
||||
/// Unlike `validate`, this function may consume `self`.
|
||||
///
|
||||
/// Parameters:
|
||||
/// - `val`: `Self::Val` returned by the result of the `validate` call.
|
||||
/// - `origin`: The origin returned by the result of the `validate` call.
|
||||
/// - `call`: The `Call` wrapped by this extension.
|
||||
/// - `info`: Information concerning, and inherent to, the transaction's call.
|
||||
/// - `len`: The total length of the encoded transaction.
|
||||
/// - `context`: Some opaque mutable context, as yet unused.
|
||||
///
|
||||
/// Returns a [Self::Pre] value on success, which gets passed into
|
||||
/// [post_dispatch](TransactionExtension::post_dispatch) and after the call is dispatched.
|
||||
///
|
||||
/// IMPORTANT: **Checks made in validation need not be repeated here.**
|
||||
fn prepare(
|
||||
self,
|
||||
val: Self::Val,
|
||||
origin: &OriginOf<Call>,
|
||||
call: &Call,
|
||||
info: &DispatchInfoOf<Call>,
|
||||
len: usize,
|
||||
context: &Context,
|
||||
) -> Result<Self::Pre, TransactionValidityError>;
|
||||
|
||||
/// Do any post-flight stuff for an extrinsic.
|
||||
///
|
||||
/// `_pre` contains the output of `prepare`.
|
||||
///
|
||||
/// This gets given the `DispatchResult` `_result` from the extrinsic and can, if desired,
|
||||
/// introduce a `TransactionValidityError`, causing the block to become invalid for including
|
||||
/// it.
|
||||
///
|
||||
/// Parameters:
|
||||
/// - `pre`: `Self::Pre` returned by the result of the `prepare` call prior to dispatch.
|
||||
/// - `info`: Information concerning, and inherent to, the transaction's call.
|
||||
/// - `post_info`: Information concerning the dispatch of the transaction's call.
|
||||
/// - `len`: The total length of the encoded transaction.
|
||||
/// - `result`: The result of the dispatch.
|
||||
/// - `context`: Some opaque mutable context, as yet unused.
|
||||
///
|
||||
/// WARNING: It is dangerous to return an error here. To do so will fundamentally invalidate the
|
||||
/// transaction and any block that it is included in, causing the block author to not be
|
||||
/// compensated for their work in validating the transaction or producing the block so far. It
|
||||
/// can only be used safely when you *know* that the transaction is one that would only be
|
||||
/// introduced by the current block author.
|
||||
fn post_dispatch(
|
||||
_pre: Self::Pre,
|
||||
_info: &DispatchInfoOf<Call>,
|
||||
_post_info: &PostDispatchInfoOf<Call>,
|
||||
_len: usize,
|
||||
_result: &DispatchResult,
|
||||
_context: &Context,
|
||||
) -> Result<(), TransactionValidityError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Compatibility function for supporting the `SignedExtension::validate_unsigned` function.
|
||||
///
|
||||
/// DO NOT USE! THIS MAY BE REMOVED AT ANY TIME!
|
||||
#[deprecated = "Only for compatibility. DO NOT USE."]
|
||||
fn validate_bare_compat(
|
||||
_call: &Call,
|
||||
_info: &DispatchInfoOf<Call>,
|
||||
_len: usize,
|
||||
) -> TransactionValidity {
|
||||
Ok(ValidTransaction::default())
|
||||
}
|
||||
|
||||
/// Compatibility function for supporting the `SignedExtension::pre_dispatch_unsigned` function.
|
||||
///
|
||||
/// DO NOT USE! THIS MAY BE REMOVED AT ANY TIME!
|
||||
#[deprecated = "Only for compatibility. DO NOT USE."]
|
||||
fn pre_dispatch_bare_compat(
|
||||
_call: &Call,
|
||||
_info: &DispatchInfoOf<Call>,
|
||||
_len: usize,
|
||||
) -> Result<(), TransactionValidityError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Compatibility function for supporting the `SignedExtension::post_dispatch` function where
|
||||
/// `pre` is `None`.
|
||||
///
|
||||
/// DO NOT USE! THIS MAY BE REMOVED AT ANY TIME!
|
||||
#[deprecated = "Only for compatibility. DO NOT USE."]
|
||||
fn post_dispatch_bare_compat(
|
||||
_info: &DispatchInfoOf<Call>,
|
||||
_post_info: &PostDispatchInfoOf<Call>,
|
||||
_len: usize,
|
||||
_result: &DispatchResult,
|
||||
) -> Result<(), TransactionValidityError> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Implict
|
||||
#[macro_export]
|
||||
macro_rules! impl_tx_ext_default {
|
||||
($call:ty ; $context:ty ; , $( $rest:tt )*) => {
|
||||
impl_tx_ext_default!{$call ; $context ; $( $rest )*}
|
||||
};
|
||||
($call:ty ; $context:ty ; validate $( $rest:tt )*) => {
|
||||
fn validate(
|
||||
&self,
|
||||
origin: $crate::traits::OriginOf<$call>,
|
||||
_call: &$call,
|
||||
_info: &$crate::traits::DispatchInfoOf<$call>,
|
||||
_len: usize,
|
||||
_context: &mut $context,
|
||||
_self_implicit: Self::Implicit,
|
||||
_inherited_implication: &impl $crate::codec::Encode,
|
||||
) -> $crate::traits::ValidateResult<Self::Val, $call> {
|
||||
Ok((Default::default(), Default::default(), origin))
|
||||
}
|
||||
impl_tx_ext_default!{$call ; $context ; $( $rest )*}
|
||||
};
|
||||
($call:ty ; $context:ty ; prepare $( $rest:tt )*) => {
|
||||
fn prepare(
|
||||
self,
|
||||
_val: Self::Val,
|
||||
_origin: &$crate::traits::OriginOf<$call>,
|
||||
_call: &$call,
|
||||
_info: &$crate::traits::DispatchInfoOf<$call>,
|
||||
_len: usize,
|
||||
_context: & $context,
|
||||
) -> Result<Self::Pre, $crate::TransactionValidityError> {
|
||||
Ok(Default::default())
|
||||
}
|
||||
impl_tx_ext_default!{$call ; $context ; $( $rest )*}
|
||||
};
|
||||
($call:ty ; $context:ty ;) => {};
|
||||
}
|
||||
|
||||
/// Information about a [`TransactionExtension`] for the runtime metadata.
|
||||
pub struct TransactionExtensionMetadata {
|
||||
/// The unique identifier of the [`TransactionExtension`].
|
||||
pub identifier: &'static str,
|
||||
/// The type of the [`TransactionExtension`].
|
||||
pub ty: MetaType,
|
||||
/// The type of the [`TransactionExtension`] additional signed data for the payload.
|
||||
// TODO: Rename "implicit"
|
||||
pub additional_signed: MetaType,
|
||||
}
|
||||
|
||||
#[impl_for_tuples(1, 12)]
|
||||
impl TransactionExtensionBase for Tuple {
|
||||
for_tuples!( where #( Tuple: TransactionExtensionBase )* );
|
||||
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 weight(&self) -> Weight {
|
||||
let mut weight = Weight::zero();
|
||||
for_tuples!( #( weight += Tuple.weight(); )* );
|
||||
weight
|
||||
}
|
||||
fn metadata() -> Vec<TransactionExtensionMetadata> {
|
||||
let mut ids = Vec::new();
|
||||
for_tuples!( #( ids.extend(Tuple::metadata()); )* );
|
||||
ids
|
||||
}
|
||||
}
|
||||
|
||||
#[impl_for_tuples(1, 12)]
|
||||
impl<Call: Dispatchable, Context> TransactionExtension<Call, Context> for Tuple {
|
||||
for_tuples!( where #( Tuple: TransactionExtension<Call, Context> )* );
|
||||
for_tuples!( type Val = ( #( Tuple::Val ),* ); );
|
||||
for_tuples!( type Pre = ( #( Tuple::Pre ),* ); );
|
||||
|
||||
fn validate(
|
||||
&self,
|
||||
origin: <Call as Dispatchable>::RuntimeOrigin,
|
||||
call: &Call,
|
||||
info: &DispatchInfoOf<Call>,
|
||||
len: usize,
|
||||
context: &mut Context,
|
||||
self_implicit: Self::Implicit,
|
||||
inherited_implication: &impl Encode,
|
||||
) -> 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;
|
||||
|
||||
for_tuples!(#(
|
||||
// Implication of this pipeline element not relevant for later items, so we pop it.
|
||||
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) = {
|
||||
let implications = (
|
||||
// The first is the implications born of the fact we return the mutated
|
||||
// origin.
|
||||
inherited_implication,
|
||||
// This is the explicitly made implication born of the fact the new origin is
|
||||
// passed into the next items in this pipeline-tuple.
|
||||
&following_explicit_implications,
|
||||
// This is the implicitly made implication born of the fact the new origin is
|
||||
// passed into the next items in this pipeline-tuple.
|
||||
&following_implicit_implications,
|
||||
);
|
||||
Tuple.validate(origin, call, info, len, context, item_implicit, &implications)?
|
||||
};
|
||||
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,
|
||||
context: &Context,
|
||||
) -> Result<Self::Pre, TransactionValidityError> {
|
||||
Ok(for_tuples!( ( #(
|
||||
Tuple::prepare(self.Tuple, val.Tuple, origin, call, info, len, context)?
|
||||
),* ) ))
|
||||
}
|
||||
|
||||
fn post_dispatch(
|
||||
pre: Self::Pre,
|
||||
info: &DispatchInfoOf<Call>,
|
||||
post_info: &PostDispatchInfoOf<Call>,
|
||||
len: usize,
|
||||
result: &DispatchResult,
|
||||
context: &Context,
|
||||
) -> Result<(), TransactionValidityError> {
|
||||
for_tuples!( #( Tuple::post_dispatch(pre.Tuple, info, post_info, len, result, context)?; )* );
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl TransactionExtensionBase for () {
|
||||
const IDENTIFIER: &'static str = "UnitTransactionExtension";
|
||||
type Implicit = ();
|
||||
fn implicit(&self) -> sp_std::result::Result<Self::Implicit, TransactionValidityError> {
|
||||
Ok(())
|
||||
}
|
||||
fn weight(&self) -> Weight {
|
||||
Weight::zero()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Call: Dispatchable, Context> TransactionExtension<Call, Context> for () {
|
||||
type Val = ();
|
||||
type Pre = ();
|
||||
fn validate(
|
||||
&self,
|
||||
origin: <Call as Dispatchable>::RuntimeOrigin,
|
||||
_call: &Call,
|
||||
_info: &DispatchInfoOf<Call>,
|
||||
_len: usize,
|
||||
_context: &mut Context,
|
||||
_self_implicit: Self::Implicit,
|
||||
_inherited_implication: &impl Encode,
|
||||
) -> 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,
|
||||
_context: &Context,
|
||||
) -> Result<(), TransactionValidityError> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -82,6 +82,8 @@ pub enum InvalidTransaction {
|
||||
MandatoryValidation,
|
||||
/// The sending address is disabled or known to be invalid.
|
||||
BadSigner,
|
||||
/// The implicit data was unable to be calculated.
|
||||
IndeterminateImplicit,
|
||||
}
|
||||
|
||||
impl InvalidTransaction {
|
||||
@@ -113,6 +115,8 @@ impl From<InvalidTransaction> for &'static str {
|
||||
"Transaction dispatch is mandatory; transactions must not be validated.",
|
||||
InvalidTransaction::Custom(_) => "InvalidTransaction custom error",
|
||||
InvalidTransaction::BadSigner => "Invalid signing address",
|
||||
InvalidTransaction::IndeterminateImplicit =>
|
||||
"The implicit data was unable to be calculated",
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -338,7 +342,7 @@ pub struct ValidTransactionBuilder {
|
||||
impl ValidTransactionBuilder {
|
||||
/// Set the priority of a transaction.
|
||||
///
|
||||
/// Note that the final priority for `FRAME` is combined from all `SignedExtension`s.
|
||||
/// Note that the final priority for `FRAME` is combined from all `TransactionExtension`s.
|
||||
/// Most likely for unsigned transactions you want the priority to be higher
|
||||
/// than for regular transactions. We recommend exposing a base priority for unsigned
|
||||
/// transactions as a runtime module parameter, so that the runtime can tune inter-module
|
||||
|
||||
Reference in New Issue
Block a user