mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-07-12 05:15:44 +00:00
Support constructing and submitting V5 transactions (#1931)
* TransactionExtensions basic support for V5 VerifySignature and renames * WIP: subxt-core v5 transaction support * Subxt to support V5 extrinsics * WIP tests failing with wsm trap error * Actually encode mortality to fix tx encode issue * fmt * rename to sign_with_account_and_signature * Add explicit methods for v4 and v5 ext construction * clippy * fix wasm example and no mut self where not needed * fix doc example * another doc fix * Add tests for tx encoding and fix v5 encode issue * add copyright and todo * refactor APIs to have clear v4/v5 split in core and slightly nicer split in subxt proper * rename Partial/SubmittableExtrinsic to *Transaction * Remove SignerT::address since it's not needed * doc fixes * fmt * doc fixes * Fix comment number * Clarify panic behaviour of inject_signature * fmt
This commit is contained in:
@@ -2,23 +2,23 @@
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
use super::signed_extensions::CheckNonceParams;
|
||||
use super::{signed_extensions, ExtrinsicParams};
|
||||
use super::{Config, Header};
|
||||
use super::Config;
|
||||
use super::{transaction_extensions, ExtrinsicParams};
|
||||
|
||||
/// The default [`super::ExtrinsicParams`] implementation understands common signed extensions
|
||||
/// and how to apply them to a given chain.
|
||||
pub type DefaultExtrinsicParams<T> = signed_extensions::AnyOf<
|
||||
pub type DefaultExtrinsicParams<T> = transaction_extensions::AnyOf<
|
||||
T,
|
||||
(
|
||||
signed_extensions::CheckSpecVersion,
|
||||
signed_extensions::CheckTxVersion,
|
||||
signed_extensions::CheckNonce,
|
||||
signed_extensions::CheckGenesis<T>,
|
||||
signed_extensions::CheckMortality<T>,
|
||||
signed_extensions::ChargeAssetTxPayment<T>,
|
||||
signed_extensions::ChargeTransactionPayment,
|
||||
signed_extensions::CheckMetadataHash,
|
||||
transaction_extensions::VerifySignature<T>,
|
||||
transaction_extensions::CheckSpecVersion,
|
||||
transaction_extensions::CheckTxVersion,
|
||||
transaction_extensions::CheckNonce,
|
||||
transaction_extensions::CheckGenesis<T>,
|
||||
transaction_extensions::CheckMortality<T>,
|
||||
transaction_extensions::ChargeAssetTxPayment<T>,
|
||||
transaction_extensions::ChargeTransactionPayment,
|
||||
transaction_extensions::CheckMetadataHash,
|
||||
),
|
||||
>;
|
||||
|
||||
@@ -26,8 +26,8 @@ pub type DefaultExtrinsicParams<T> = signed_extensions::AnyOf<
|
||||
/// [`DefaultExtrinsicParams`]. This may expose methods that aren't applicable to the current
|
||||
/// chain; such values will simply be ignored if so.
|
||||
pub struct DefaultExtrinsicParamsBuilder<T: Config> {
|
||||
/// `None` means the tx will be immortal.
|
||||
mortality: Option<Mortality<T::Hash>>,
|
||||
/// `None` means the tx will be immortal, else it's mortal for N blocks (if possible).
|
||||
mortality: Option<u64>,
|
||||
/// `None` means the nonce will be automatically set.
|
||||
nonce: Option<u64>,
|
||||
/// `None` means we'll use the native token.
|
||||
@@ -36,16 +36,6 @@ pub struct DefaultExtrinsicParamsBuilder<T: Config> {
|
||||
tip_of: u128,
|
||||
}
|
||||
|
||||
struct Mortality<Hash> {
|
||||
/// Block hash that mortality starts from
|
||||
checkpoint_hash: Hash,
|
||||
/// Block number that mortality starts from (must
|
||||
// point to the same block as the hash above)
|
||||
checkpoint_number: u64,
|
||||
/// How many blocks the tx is mortal for
|
||||
period: u64,
|
||||
}
|
||||
|
||||
impl<T: Config> Default for DefaultExtrinsicParamsBuilder<T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
@@ -65,15 +55,10 @@ impl<T: Config> DefaultExtrinsicParamsBuilder<T> {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
/// Make the transaction mortal, given a block header that it should be mortal from,
|
||||
/// and the number of blocks (roughly; it'll be rounded to a power of two) that it will
|
||||
/// be mortal for.
|
||||
pub fn mortal(mut self, from_block: &T::Header, for_n_blocks: u64) -> Self {
|
||||
self.mortality = Some(Mortality {
|
||||
checkpoint_hash: from_block.hash(),
|
||||
checkpoint_number: from_block.number().into(),
|
||||
period: for_n_blocks,
|
||||
});
|
||||
/// Make the transaction mortal, given a number of blocks it will be mortal for from
|
||||
/// the current block at the time of submission.
|
||||
pub fn mortal(mut self, for_n_blocks: u64) -> Self {
|
||||
self.mortality = Some(for_n_blocks);
|
||||
self
|
||||
}
|
||||
|
||||
@@ -83,26 +68,6 @@ impl<T: Config> DefaultExtrinsicParamsBuilder<T> {
|
||||
self
|
||||
}
|
||||
|
||||
/// Make the transaction mortal, given a block number and block hash (which must both point to
|
||||
/// the same block) that it should be mortal from, and the number of blocks (roughly; it'll be
|
||||
/// rounded to a power of two) that it will be mortal for.
|
||||
///
|
||||
/// Prefer to use [`DefaultExtrinsicParamsBuilder::mortal()`], which ensures that the block hash
|
||||
/// and number align.
|
||||
pub fn mortal_unchecked(
|
||||
mut self,
|
||||
from_block_number: u64,
|
||||
from_block_hash: T::Hash,
|
||||
for_n_blocks: u64,
|
||||
) -> Self {
|
||||
self.mortality = Some(Mortality {
|
||||
checkpoint_hash: from_block_hash,
|
||||
checkpoint_number: from_block_number,
|
||||
period: for_n_blocks,
|
||||
});
|
||||
self
|
||||
}
|
||||
|
||||
/// Provide a tip to the block author in the chain's native token.
|
||||
pub fn tip(mut self, tip: u128) -> Self {
|
||||
self.tip = tip;
|
||||
@@ -123,28 +88,29 @@ impl<T: Config> DefaultExtrinsicParamsBuilder<T> {
|
||||
|
||||
/// Build the extrinsic parameters.
|
||||
pub fn build(self) -> <DefaultExtrinsicParams<T> as ExtrinsicParams<T>>::Params {
|
||||
let check_mortality_params = if let Some(mortality) = self.mortality {
|
||||
signed_extensions::CheckMortalityParams::mortal(
|
||||
mortality.period,
|
||||
mortality.checkpoint_number,
|
||||
mortality.checkpoint_hash,
|
||||
)
|
||||
let check_mortality_params = if let Some(for_n_blocks) = self.mortality {
|
||||
transaction_extensions::CheckMortalityParams::mortal(for_n_blocks)
|
||||
} else {
|
||||
signed_extensions::CheckMortalityParams::immortal()
|
||||
transaction_extensions::CheckMortalityParams::immortal()
|
||||
};
|
||||
|
||||
let charge_asset_tx_params = if let Some(asset_id) = self.tip_of_asset_id {
|
||||
signed_extensions::ChargeAssetTxPaymentParams::tip_of(self.tip, asset_id)
|
||||
transaction_extensions::ChargeAssetTxPaymentParams::tip_of(self.tip, asset_id)
|
||||
} else {
|
||||
signed_extensions::ChargeAssetTxPaymentParams::tip(self.tip)
|
||||
transaction_extensions::ChargeAssetTxPaymentParams::tip(self.tip)
|
||||
};
|
||||
|
||||
let charge_transaction_params =
|
||||
signed_extensions::ChargeTransactionPaymentParams::tip(self.tip);
|
||||
transaction_extensions::ChargeTransactionPaymentParams::tip(self.tip);
|
||||
|
||||
let check_nonce_params = CheckNonceParams(self.nonce);
|
||||
let check_nonce_params = if let Some(nonce) = self.nonce {
|
||||
transaction_extensions::CheckNonceParams::with_nonce(nonce)
|
||||
} else {
|
||||
transaction_extensions::CheckNonceParams::from_chain()
|
||||
};
|
||||
|
||||
(
|
||||
(),
|
||||
(),
|
||||
(),
|
||||
check_nonce_params,
|
||||
@@ -156,3 +122,16 @@ impl<T: Config> DefaultExtrinsicParamsBuilder<T> {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
fn assert_default<T: Default>(_t: T) {}
|
||||
|
||||
#[test]
|
||||
fn params_are_default() {
|
||||
let params = DefaultExtrinsicParamsBuilder::<crate::config::PolkadotConfig>::new().build();
|
||||
assert_default(params)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
//! [`crate::config::DefaultExtrinsicParams`] provides a general-purpose
|
||||
//! implementation of this that will work in many cases.
|
||||
|
||||
use super::refine_params::RefineParams;
|
||||
use crate::{client::ClientState, error::ExtrinsicParamsError, Config};
|
||||
use alloc::vec::Vec;
|
||||
use core::any::Any;
|
||||
|
||||
/// This trait allows you to configure the "signed extra" and
|
||||
/// "additional" parameters that are a part of the transaction payload
|
||||
@@ -18,7 +18,7 @@ pub trait ExtrinsicParams<T: Config>: ExtrinsicParamsEncoder + Sized + Send + 's
|
||||
/// These parameters can be provided to the constructor along with
|
||||
/// some default parameters that `subxt` understands, in order to
|
||||
/// help construct your [`ExtrinsicParams`] object.
|
||||
type Params: RefineParams<T>;
|
||||
type Params: Params<T>;
|
||||
|
||||
/// Construct a new instance of our [`ExtrinsicParams`].
|
||||
fn new(client: &ClientState<T>, params: Self::Params) -> Result<Self, ExtrinsicParamsError>;
|
||||
@@ -28,15 +28,92 @@ pub trait ExtrinsicParams<T: Config>: ExtrinsicParamsEncoder + Sized + Send + 's
|
||||
/// defines how to encode the "additional" and "extra" params. Both functions
|
||||
/// are optional and will encode nothing by default.
|
||||
pub trait ExtrinsicParamsEncoder: 'static {
|
||||
/// This is expected to SCALE encode the "signed extra" parameters
|
||||
/// to some buffer that has been provided. These are the parameters
|
||||
/// which are sent along with the transaction, as well as taken into
|
||||
/// account when signing the transaction.
|
||||
fn encode_extra_to(&self, _v: &mut Vec<u8>) {}
|
||||
/// This is expected to SCALE encode the transaction extension data to some
|
||||
/// buffer that has been provided. This data is attached to the transaction
|
||||
/// and also (by default) attached to the signer payload which is signed to
|
||||
/// provide a signature for the transaction.
|
||||
///
|
||||
/// If [`ExtrinsicParamsEncoder::encode_signer_payload_value_to`] is implemented,
|
||||
/// then that will be used instead when generating a signer payload. Useful for
|
||||
/// eg the `VerifySignature` extension, which is send with the transaction but
|
||||
/// is not a part of the signer payload.
|
||||
fn encode_value_to(&self, _v: &mut Vec<u8>) {}
|
||||
|
||||
/// This is expected to SCALE encode the "additional" parameters
|
||||
/// to some buffer that has been provided. These parameters are _not_
|
||||
/// sent along with the transaction, but are taken into account when
|
||||
/// See [`ExtrinsicParamsEncoder::encode_value_to`]. This defaults to calling that
|
||||
/// method, but if implemented will dictate what is encoded to the signer payload.
|
||||
fn encode_signer_payload_value_to(&self, v: &mut Vec<u8>) {
|
||||
self.encode_value_to(v);
|
||||
}
|
||||
|
||||
/// This is expected to SCALE encode the "implicit" (formally "additional")
|
||||
/// parameters to some buffer that has been provided. These parameters are
|
||||
/// _not_ sent along with the transaction, but are taken into account when
|
||||
/// signing it, meaning the client and node must agree on their values.
|
||||
fn encode_additional_to(&self, _v: &mut Vec<u8>) {}
|
||||
fn encode_implicit_to(&self, _v: &mut Vec<u8>) {}
|
||||
|
||||
/// Set the signature. This happens after we have constructed the extrinsic params,
|
||||
/// and so is defined here rather than on the params, below. We need to use `&dyn Any`
|
||||
/// to keep this trait object safe, but can downcast in the impls.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Implementations of this will likely try to downcast the provided `account_id`
|
||||
/// and `signature` into `T::AccountId` and `T::Signature` (where `T: Config`), and are
|
||||
/// free to panic if this downcasting does not succeed.
|
||||
///
|
||||
/// In typical usage, this is not a problem, since this method is only called internally
|
||||
/// and provided values which line up with the relevant `Config`. In theory though, this
|
||||
/// method can be called manually with any types, hence this warning.
|
||||
fn inject_signature(&mut self, _account_id: &dyn Any, _signature: &dyn Any) {}
|
||||
}
|
||||
|
||||
/// The parameters (ie [`ExtrinsicParams::Params`]) can also have data injected into them,
|
||||
/// allowing Subxt to retrieve data from the chain and amend the parameters with it when
|
||||
/// online.
|
||||
pub trait Params<T: Config> {
|
||||
/// Set the account nonce.
|
||||
fn inject_account_nonce(&mut self, _nonce: u64) {}
|
||||
/// Set the current block.
|
||||
fn inject_block(&mut self, _number: u64, _hash: T::Hash) {}
|
||||
}
|
||||
|
||||
impl<T: Config> Params<T> for () {}
|
||||
|
||||
macro_rules! impl_tuples {
|
||||
($($ident:ident $index:tt),+) => {
|
||||
|
||||
impl <T: Config, $($ident : Params<T>),+> Params<T> for ($($ident,)+){
|
||||
fn inject_account_nonce(&mut self, nonce: u64) {
|
||||
$(self.$index.inject_account_nonce(nonce);)+
|
||||
}
|
||||
fn inject_block(&mut self, number: u64, hash: T::Hash) {
|
||||
$(self.$index.inject_block(number, hash);)+
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
const _: () = {
|
||||
impl_tuples!(A 0);
|
||||
impl_tuples!(A 0, B 1);
|
||||
impl_tuples!(A 0, B 1, C 2);
|
||||
impl_tuples!(A 0, B 1, C 2, D 3);
|
||||
impl_tuples!(A 0, B 1, C 2, D 3, E 4);
|
||||
impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5);
|
||||
impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6);
|
||||
impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7);
|
||||
impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8);
|
||||
impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9);
|
||||
impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10);
|
||||
impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10, L 11);
|
||||
impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10, L 11, M 12);
|
||||
impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10, L 11, M 12, N 13);
|
||||
impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10, L 11, M 12, N 13, O 14);
|
||||
impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10, L 11, M 12, N 13, O 14, P 15);
|
||||
impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10, L 11, M 12, N 13, O 14, P 15, Q 16);
|
||||
impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10, L 11, M 12, N 13, O 14, P 15, Q 16, R 17);
|
||||
impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10, L 11, M 12, N 13, O 14, P 15, Q 16, R 17, S 18);
|
||||
impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10, L 11, M 12, N 13, O 14, P 15, Q 16, R 17, S 18, U 19);
|
||||
impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10, L 11, M 12, N 13, O 14, P 15, Q 16, R 17, S 18, U 19, V 20);
|
||||
};
|
||||
|
||||
@@ -10,11 +10,10 @@
|
||||
|
||||
mod default_extrinsic_params;
|
||||
mod extrinsic_params;
|
||||
mod refine_params;
|
||||
|
||||
pub mod polkadot;
|
||||
pub mod signed_extensions;
|
||||
pub mod substrate;
|
||||
pub mod transaction_extensions;
|
||||
|
||||
use codec::{Decode, Encode};
|
||||
use core::fmt::Debug;
|
||||
@@ -25,9 +24,8 @@ use serde::{de::DeserializeOwned, Serialize};
|
||||
pub use default_extrinsic_params::{DefaultExtrinsicParams, DefaultExtrinsicParamsBuilder};
|
||||
pub use extrinsic_params::{ExtrinsicParams, ExtrinsicParamsEncoder};
|
||||
pub use polkadot::{PolkadotConfig, PolkadotExtrinsicParams, PolkadotExtrinsicParamsBuilder};
|
||||
pub use refine_params::{RefineParams, RefineParamsData};
|
||||
pub use signed_extensions::SignedExtension;
|
||||
pub use substrate::{SubstrateConfig, SubstrateExtrinsicParams, SubstrateExtrinsicParamsBuilder};
|
||||
pub use transaction_extensions::TransactionExtension;
|
||||
|
||||
/// Runtime types.
|
||||
// Note: the `Send + Sync + 'static` bound isn't strictly required, but currently deriving
|
||||
@@ -39,13 +37,13 @@ pub trait Config: Sized + Send + Sync + 'static {
|
||||
type Hash: BlockHash;
|
||||
|
||||
/// The account ID type.
|
||||
type AccountId: Debug + Clone + Encode + Serialize;
|
||||
type AccountId: Debug + Clone + Encode + Decode + Serialize + Send;
|
||||
|
||||
/// The address type.
|
||||
type Address: Debug + Encode + From<Self::AccountId>;
|
||||
|
||||
/// The signature type.
|
||||
type Signature: Debug + Encode;
|
||||
type Signature: Debug + Clone + Encode + Decode + Send;
|
||||
|
||||
/// The hashing system (algorithm) being used in the runtime (e.g. Blake2).
|
||||
type Hasher: Debug + Hasher<Output = Self::Hash>;
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
// Copyright 2019-2024 Parity Technologies (UK) Ltd.
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
//! Refining params with values fetched from the chain
|
||||
|
||||
use crate::Config;
|
||||
|
||||
/// Data that can be used to refine the params of signed extensions.
|
||||
pub struct RefineParamsData<T: Config> {
|
||||
account_nonce: u64,
|
||||
block_number: u64,
|
||||
block_hash: T::Hash,
|
||||
}
|
||||
|
||||
impl<T: Config> RefineParamsData<T> {
|
||||
#[doc(hidden)]
|
||||
/// Creates a new [`RefineParamsData`] instance. Called from `subxt` when refining signed extensions.
|
||||
pub fn new(account_nonce: u64, block_number: u64, block_hash: T::Hash) -> Self {
|
||||
RefineParamsData {
|
||||
account_nonce,
|
||||
block_number,
|
||||
block_hash,
|
||||
}
|
||||
}
|
||||
|
||||
/// account nonce for extrinsic author
|
||||
pub fn account_nonce(&self) -> u64 {
|
||||
self.account_nonce
|
||||
}
|
||||
|
||||
/// latest finalized block number
|
||||
pub fn block_number(&self) -> u64 {
|
||||
self.block_number
|
||||
}
|
||||
|
||||
/// latest finalized block hash
|
||||
pub fn block_hash(&self) -> T::Hash {
|
||||
self.block_hash
|
||||
}
|
||||
}
|
||||
|
||||
/// Types implementing [`RefineParams`] can be modified to reflect live information from the chain.
|
||||
pub trait RefineParams<T: Config> {
|
||||
/// Refine params to an extrinsic. There is usually some notion of 'the param is already set/unset' in types implementing this trait.
|
||||
/// The refinement should most likely not affect cases where a param is in a 'is already set by the user' state.
|
||||
fn refine(&mut self, _data: &RefineParamsData<T>) {}
|
||||
}
|
||||
|
||||
impl<T: Config> RefineParams<T> for () {}
|
||||
|
||||
macro_rules! impl_tuples {
|
||||
($($ident:ident $index:tt),+) => {
|
||||
|
||||
impl <T: Config, $($ident : RefineParams<T>),+> RefineParams<T> for ($($ident,)+){
|
||||
fn refine(&mut self, data: &RefineParamsData<T>) {
|
||||
$(self.$index.refine(data);)+
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
const _: () = {
|
||||
impl_tuples!(A 0);
|
||||
impl_tuples!(A 0, B 1);
|
||||
impl_tuples!(A 0, B 1, C 2);
|
||||
impl_tuples!(A 0, B 1, C 2, D 3);
|
||||
impl_tuples!(A 0, B 1, C 2, D 3, E 4);
|
||||
impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5);
|
||||
impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6);
|
||||
impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7);
|
||||
impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8);
|
||||
impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9);
|
||||
impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10);
|
||||
impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10, L 11);
|
||||
impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10, L 11, M 12);
|
||||
impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10, L 11, M 12, N 13);
|
||||
impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10, L 11, M 12, N 13, O 14);
|
||||
impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10, L 11, M 12, N 13, O 14, P 15);
|
||||
impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10, L 11, M 12, N 13, O 14, P 15, Q 16);
|
||||
impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10, L 11, M 12, N 13, O 14, P 15, Q 16, R 17);
|
||||
impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10, L 11, M 12, N 13, O 14, P 15, Q 16, R 17, S 18);
|
||||
impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10, L 11, M 12, N 13, O 14, P 15, Q 16, R 17, S 18, U 19);
|
||||
impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10, L 11, M 12, N 13, O 14, P 15, Q 16, R 17, S 18, U 19, V 20);
|
||||
};
|
||||
+302
-167
@@ -2,45 +2,116 @@
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
//! This module contains implementations for common signed extensions, each
|
||||
//! of which implements [`SignedExtension`], and can be used in conjunction with
|
||||
//! [`AnyOf`] to configure the set of signed extensions which are known about
|
||||
//! This module contains implementations for common transaction extensions, each
|
||||
//! of which implements [`TransactionExtension`], and can be used in conjunction with
|
||||
//! [`AnyOf`] to configure the set of transaction extensions which are known about
|
||||
//! when interacting with a chain.
|
||||
|
||||
use super::extrinsic_params::ExtrinsicParams;
|
||||
use super::refine_params::RefineParamsData;
|
||||
use super::RefineParams;
|
||||
use crate::client::ClientState;
|
||||
use crate::config::ExtrinsicParamsEncoder;
|
||||
use crate::config::{ExtrinsicParamsEncoder, Header};
|
||||
use crate::error::ExtrinsicParamsError;
|
||||
use crate::utils::Era;
|
||||
use crate::utils::{Era, Static};
|
||||
use crate::Config;
|
||||
use alloc::borrow::ToOwned;
|
||||
use alloc::boxed::Box;
|
||||
use alloc::vec::Vec;
|
||||
use codec::{Compact, Encode};
|
||||
use core::any::Any;
|
||||
use core::fmt::Debug;
|
||||
use derive_where::derive_where;
|
||||
use hashbrown::HashMap;
|
||||
use scale_decode::DecodeAsType;
|
||||
use scale_info::PortableRegistry;
|
||||
|
||||
/// A single [`SignedExtension`] has a unique name, but is otherwise the
|
||||
// Re-export this here; it's a bit generically named to be re-exported from ::config.
|
||||
pub use super::extrinsic_params::Params;
|
||||
|
||||
/// A single [`TransactionExtension`] has a unique name, but is otherwise the
|
||||
/// same as [`ExtrinsicParams`] in describing how to encode the extra and
|
||||
/// additional data.
|
||||
pub trait SignedExtension<T: Config>: ExtrinsicParams<T> {
|
||||
/// The type representing the `extra` bytes of a signed extension.
|
||||
pub trait TransactionExtension<T: Config>: ExtrinsicParams<T> {
|
||||
/// The type representing the `extra` / value bytes of a transaction extension.
|
||||
/// Decoding from this type should be symmetrical to the respective
|
||||
/// `ExtrinsicParamsEncoder::encode_extra_to()` implementation of this signed extension.
|
||||
/// `ExtrinsicParamsEncoder::encode_value_to()` implementation of this transaction extension.
|
||||
type Decoded: DecodeAsType;
|
||||
|
||||
/// This should return true if the signed extension matches the details given.
|
||||
/// This should return true if the transaction extension matches the details given.
|
||||
/// Often, this will involve just checking that the identifier given matches that of the
|
||||
/// extension in question.
|
||||
fn matches(identifier: &str, _type_id: u32, _types: &PortableRegistry) -> bool;
|
||||
}
|
||||
|
||||
/// The [`CheckMetadataHash`] signed extension.
|
||||
/// The [`VerifySignature`] extension. For V5 General transactions, this is how a signature
|
||||
/// is provided. The signature is constructed by signing a payload which contains the
|
||||
/// transaction call data as well as the encoded "additional" bytes for any extensions _after_
|
||||
/// this one in the list.
|
||||
pub struct VerifySignature<T: Config>(VerifySignatureDetails<T>);
|
||||
|
||||
impl<T: Config> ExtrinsicParams<T> for VerifySignature<T> {
|
||||
type Params = ();
|
||||
|
||||
fn new(_client: &ClientState<T>, _params: Self::Params) -> Result<Self, ExtrinsicParamsError> {
|
||||
Ok(VerifySignature(VerifySignatureDetails::Disabled))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> ExtrinsicParamsEncoder for VerifySignature<T> {
|
||||
fn encode_value_to(&self, v: &mut Vec<u8>) {
|
||||
self.0.encode_to(v);
|
||||
}
|
||||
fn encode_signer_payload_value_to(&self, v: &mut Vec<u8>) {
|
||||
// This extension is never encoded to the signer payload, and extensions
|
||||
// prior to this are ignored when creating said payload, so clear anything
|
||||
// we've seen so far.
|
||||
v.clear();
|
||||
}
|
||||
fn encode_implicit_to(&self, v: &mut Vec<u8>) {
|
||||
// We only use the "implicit" data for extensions _after_ this one
|
||||
// in the pipeline to form the signer payload. Thus, clear anything
|
||||
// we've seen so far.
|
||||
v.clear();
|
||||
}
|
||||
|
||||
fn inject_signature(&mut self, account: &dyn Any, signature: &dyn Any) {
|
||||
// Downcast refs back to concrete types (we use `&dyn Any`` so that the trait remains object safe)
|
||||
let account = account
|
||||
.downcast_ref::<T::AccountId>()
|
||||
.expect("A T::AccoundId should have been provided")
|
||||
.clone();
|
||||
let signature = signature
|
||||
.downcast_ref::<T::Signature>()
|
||||
.expect("A T::Signature should have been provided")
|
||||
.clone();
|
||||
|
||||
// The signature is not set through params, only here, once given by a user:
|
||||
self.0 = VerifySignatureDetails::Signed { signature, account }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> TransactionExtension<T> for VerifySignature<T> {
|
||||
type Decoded = Static<VerifySignatureDetails<T>>;
|
||||
fn matches(identifier: &str, _type_id: u32, _types: &PortableRegistry) -> bool {
|
||||
identifier == "VerifySignature"
|
||||
}
|
||||
}
|
||||
|
||||
/// This allows a signature to be provided to the [`VerifySignature`] transaction extension.
|
||||
// Dev note: this must encode identically to https://github.com/paritytech/polkadot-sdk/blob/fd72d58313c297a10600037ce1bb88ec958d722e/substrate/frame/verify-signature/src/extension.rs#L43
|
||||
#[derive(codec::Encode, codec::Decode)]
|
||||
pub enum VerifySignatureDetails<T: Config> {
|
||||
/// A signature has been provided.
|
||||
Signed {
|
||||
/// The signature.
|
||||
signature: T::Signature,
|
||||
/// The account that generated the signature.
|
||||
account: T::AccountId,
|
||||
},
|
||||
/// No signature was provided.
|
||||
Disabled,
|
||||
}
|
||||
|
||||
/// The [`CheckMetadataHash`] transaction extension.
|
||||
pub struct CheckMetadataHash {
|
||||
// Eventually we might provide or calculate the metadata hash here,
|
||||
// but for now we never provide a hash and so this is empty.
|
||||
@@ -55,18 +126,18 @@ impl<T: Config> ExtrinsicParams<T> for CheckMetadataHash {
|
||||
}
|
||||
|
||||
impl ExtrinsicParamsEncoder for CheckMetadataHash {
|
||||
fn encode_extra_to(&self, v: &mut Vec<u8>) {
|
||||
fn encode_value_to(&self, v: &mut Vec<u8>) {
|
||||
// A single 0 byte in the TX payload indicates that the chain should
|
||||
// _not_ expect any metadata hash to exist in the signer payload.
|
||||
0u8.encode_to(v);
|
||||
}
|
||||
fn encode_additional_to(&self, v: &mut Vec<u8>) {
|
||||
fn encode_implicit_to(&self, v: &mut Vec<u8>) {
|
||||
// We provide no metadata hash in the signer payload to align with the above.
|
||||
None::<()>.encode_to(v);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> SignedExtension<T> for CheckMetadataHash {
|
||||
impl<T: Config> TransactionExtension<T> for CheckMetadataHash {
|
||||
type Decoded = CheckMetadataHashMode;
|
||||
fn matches(identifier: &str, _type_id: u32, _types: &PortableRegistry) -> bool {
|
||||
identifier == "CheckMetadataHash"
|
||||
@@ -75,7 +146,7 @@ impl<T: Config> SignedExtension<T> for CheckMetadataHash {
|
||||
|
||||
/// Is metadata checking enabled or disabled?
|
||||
// Dev note: The "Disabled" and "Enabled" variant names match those that the
|
||||
// signed extension will be encoded with, in order that DecodeAsType will work
|
||||
// transaction extension will be encoded with, in order that DecodeAsType will work
|
||||
// properly.
|
||||
#[derive(Copy, Clone, Debug, DecodeAsType)]
|
||||
pub enum CheckMetadataHashMode {
|
||||
@@ -95,7 +166,7 @@ impl CheckMetadataHashMode {
|
||||
}
|
||||
}
|
||||
|
||||
/// The [`CheckSpecVersion`] signed extension.
|
||||
/// The [`CheckSpecVersion`] transaction extension.
|
||||
pub struct CheckSpecVersion(u32);
|
||||
|
||||
impl<T: Config> ExtrinsicParams<T> for CheckSpecVersion {
|
||||
@@ -107,57 +178,66 @@ impl<T: Config> ExtrinsicParams<T> for CheckSpecVersion {
|
||||
}
|
||||
|
||||
impl ExtrinsicParamsEncoder for CheckSpecVersion {
|
||||
fn encode_additional_to(&self, v: &mut Vec<u8>) {
|
||||
fn encode_implicit_to(&self, v: &mut Vec<u8>) {
|
||||
self.0.encode_to(v);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> SignedExtension<T> for CheckSpecVersion {
|
||||
impl<T: Config> TransactionExtension<T> for CheckSpecVersion {
|
||||
type Decoded = ();
|
||||
fn matches(identifier: &str, _type_id: u32, _types: &PortableRegistry) -> bool {
|
||||
identifier == "CheckSpecVersion"
|
||||
}
|
||||
}
|
||||
|
||||
/// The [`CheckNonce`] signed extension.
|
||||
pub struct CheckNonce(Compact<u64>);
|
||||
/// The [`CheckNonce`] transaction extension.
|
||||
pub struct CheckNonce(u64);
|
||||
|
||||
impl<T: Config> ExtrinsicParams<T> for CheckNonce {
|
||||
type Params = CheckNonceParams;
|
||||
|
||||
fn new(_client: &ClientState<T>, params: Self::Params) -> Result<Self, ExtrinsicParamsError> {
|
||||
// If no nonce is set (nor by user nor refinement), use a nonce of 0.
|
||||
let nonce = params.0.unwrap_or(0);
|
||||
Ok(CheckNonce(Compact(nonce)))
|
||||
Ok(CheckNonce(params.0.unwrap_or(0)))
|
||||
}
|
||||
}
|
||||
|
||||
impl ExtrinsicParamsEncoder for CheckNonce {
|
||||
fn encode_extra_to(&self, v: &mut Vec<u8>) {
|
||||
self.0.encode_to(v);
|
||||
fn encode_value_to(&self, v: &mut Vec<u8>) {
|
||||
Compact(self.0).encode_to(v);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> SignedExtension<T> for CheckNonce {
|
||||
impl<T: Config> TransactionExtension<T> for CheckNonce {
|
||||
type Decoded = u64;
|
||||
fn matches(identifier: &str, _type_id: u32, _types: &PortableRegistry) -> bool {
|
||||
identifier == "CheckNonce"
|
||||
}
|
||||
}
|
||||
|
||||
/// Params for [`CheckNonce`]
|
||||
/// Configure the nonce used.
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct CheckNonceParams(pub Option<u64>);
|
||||
pub struct CheckNonceParams(Option<u64>);
|
||||
|
||||
impl<T: Config> RefineParams<T> for CheckNonceParams {
|
||||
fn refine(&mut self, data: &RefineParamsData<T>) {
|
||||
impl CheckNonceParams {
|
||||
/// Retrieve the nonce from the chain and use that.
|
||||
pub fn from_chain() -> Self {
|
||||
Self(None)
|
||||
}
|
||||
/// Manually set an account nonce to use.
|
||||
pub fn with_nonce(nonce: u64) -> Self {
|
||||
Self(Some(nonce))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> Params<T> for CheckNonceParams {
|
||||
fn inject_account_nonce(&mut self, nonce: u64) {
|
||||
if self.0.is_none() {
|
||||
self.0 = Some(data.account_nonce());
|
||||
self.0 = Some(nonce)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The [`CheckTxVersion`] signed extension.
|
||||
/// The [`CheckTxVersion`] transaction extension.
|
||||
pub struct CheckTxVersion(u32);
|
||||
|
||||
impl<T: Config> ExtrinsicParams<T> for CheckTxVersion {
|
||||
@@ -169,19 +249,19 @@ impl<T: Config> ExtrinsicParams<T> for CheckTxVersion {
|
||||
}
|
||||
|
||||
impl ExtrinsicParamsEncoder for CheckTxVersion {
|
||||
fn encode_additional_to(&self, v: &mut Vec<u8>) {
|
||||
fn encode_implicit_to(&self, v: &mut Vec<u8>) {
|
||||
self.0.encode_to(v);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> SignedExtension<T> for CheckTxVersion {
|
||||
impl<T: Config> TransactionExtension<T> for CheckTxVersion {
|
||||
type Decoded = ();
|
||||
fn matches(identifier: &str, _type_id: u32, _types: &PortableRegistry) -> bool {
|
||||
identifier == "CheckTxVersion"
|
||||
}
|
||||
}
|
||||
|
||||
/// The [`CheckGenesis`] signed extension.
|
||||
/// The [`CheckGenesis`] transaction extension.
|
||||
pub struct CheckGenesis<T: Config>(T::Hash);
|
||||
|
||||
impl<T: Config> ExtrinsicParams<T> for CheckGenesis<T> {
|
||||
@@ -193,104 +273,149 @@ impl<T: Config> ExtrinsicParams<T> for CheckGenesis<T> {
|
||||
}
|
||||
|
||||
impl<T: Config> ExtrinsicParamsEncoder for CheckGenesis<T> {
|
||||
fn encode_additional_to(&self, v: &mut Vec<u8>) {
|
||||
fn encode_implicit_to(&self, v: &mut Vec<u8>) {
|
||||
self.0.encode_to(v);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> SignedExtension<T> for CheckGenesis<T> {
|
||||
impl<T: Config> TransactionExtension<T> for CheckGenesis<T> {
|
||||
type Decoded = ();
|
||||
fn matches(identifier: &str, _type_id: u32, _types: &PortableRegistry) -> bool {
|
||||
identifier == "CheckGenesis"
|
||||
}
|
||||
}
|
||||
|
||||
/// The [`CheckMortality`] signed extension.
|
||||
/// The [`CheckMortality`] transaction extension.
|
||||
pub struct CheckMortality<T: Config> {
|
||||
era: Era,
|
||||
checkpoint: T::Hash,
|
||||
}
|
||||
|
||||
/// Parameters to configure the [`CheckMortality`] signed extension.
|
||||
pub struct CheckMortalityParams<T: Config>(Option<CheckMortalityParamsInner<T>>);
|
||||
struct CheckMortalityParamsInner<T: Config> {
|
||||
era: Era,
|
||||
checkpoint: Option<T::Hash>,
|
||||
}
|
||||
|
||||
impl<T: Config> Default for CheckMortalityParams<T> {
|
||||
fn default() -> Self {
|
||||
CheckMortalityParams(None)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> RefineParams<T> for CheckMortalityParams<T> {
|
||||
fn refine(&mut self, data: &RefineParamsData<T>) {
|
||||
if self.0.is_none() {
|
||||
// By default we refine the params to have a mortal transaction valid for 32 blocks.
|
||||
const TX_VALID_FOR: u64 = 32;
|
||||
*self =
|
||||
CheckMortalityParams::mortal(TX_VALID_FOR, data.block_number(), data.block_hash());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> CheckMortalityParams<T> {
|
||||
/// Configure a mortal transaction. The `period` is (roughly) how many
|
||||
/// blocks the transaction will be valid for. The `block_number` and
|
||||
/// `block_hash` should both point to the same block, and are the block that
|
||||
/// the transaction is mortal from.
|
||||
pub fn mortal(period: u64, block_number: u64, block_hash: T::Hash) -> Self {
|
||||
Self(Some(CheckMortalityParamsInner {
|
||||
era: Era::mortal(period, block_number),
|
||||
checkpoint: Some(block_hash),
|
||||
}))
|
||||
}
|
||||
/// An immortal transaction.
|
||||
pub fn immortal() -> Self {
|
||||
Self(Some(CheckMortalityParamsInner {
|
||||
era: Era::Immortal,
|
||||
checkpoint: None,
|
||||
}))
|
||||
}
|
||||
params: CheckMortalityParamsInner<T>,
|
||||
genesis_hash: T::Hash,
|
||||
}
|
||||
|
||||
impl<T: Config> ExtrinsicParams<T> for CheckMortality<T> {
|
||||
type Params = CheckMortalityParams<T>;
|
||||
|
||||
fn new(client: &ClientState<T>, params: Self::Params) -> Result<Self, ExtrinsicParamsError> {
|
||||
let check_mortality = if let Some(params) = params.0 {
|
||||
CheckMortality {
|
||||
era: params.era,
|
||||
checkpoint: params.checkpoint.unwrap_or(client.genesis_hash),
|
||||
}
|
||||
} else {
|
||||
CheckMortality {
|
||||
era: Era::Immortal,
|
||||
checkpoint: client.genesis_hash,
|
||||
}
|
||||
};
|
||||
Ok(check_mortality)
|
||||
Ok(CheckMortality {
|
||||
// if nothing has been explicitly configured, we will have a mortal transaction
|
||||
// valid for 32 blocks if block info is available.
|
||||
params: params.0,
|
||||
genesis_hash: client.genesis_hash,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> ExtrinsicParamsEncoder for CheckMortality<T> {
|
||||
fn encode_extra_to(&self, v: &mut Vec<u8>) {
|
||||
self.era.encode_to(v);
|
||||
fn encode_value_to(&self, v: &mut Vec<u8>) {
|
||||
match &self.params {
|
||||
CheckMortalityParamsInner::MortalFromBlock {
|
||||
for_n_blocks,
|
||||
from_block_n,
|
||||
..
|
||||
} => {
|
||||
Era::mortal(*for_n_blocks, *from_block_n).encode_to(v);
|
||||
}
|
||||
_ => {
|
||||
// Note: if we see `CheckMortalityInner::MortalForBlocks`, then it means the user has
|
||||
// configured a block to be mortal for N blocks, but the current block was never injected,
|
||||
// so we don't know where to start from and default back to building an immortal tx.
|
||||
Era::Immortal.encode_to(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
fn encode_additional_to(&self, v: &mut Vec<u8>) {
|
||||
self.checkpoint.encode_to(v);
|
||||
fn encode_implicit_to(&self, v: &mut Vec<u8>) {
|
||||
match &self.params {
|
||||
CheckMortalityParamsInner::MortalFromBlock {
|
||||
from_block_hash, ..
|
||||
} => {
|
||||
from_block_hash.encode_to(v);
|
||||
}
|
||||
_ => {
|
||||
self.genesis_hash.encode_to(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> SignedExtension<T> for CheckMortality<T> {
|
||||
impl<T: Config> TransactionExtension<T> for CheckMortality<T> {
|
||||
type Decoded = Era;
|
||||
fn matches(identifier: &str, _type_id: u32, _types: &PortableRegistry) -> bool {
|
||||
identifier == "CheckMortality"
|
||||
}
|
||||
}
|
||||
|
||||
/// The [`ChargeAssetTxPayment`] signed extension.
|
||||
/// Parameters to configure the [`CheckMortality`] transaction extension.
|
||||
pub struct CheckMortalityParams<T: Config>(CheckMortalityParamsInner<T>);
|
||||
|
||||
enum CheckMortalityParamsInner<T: Config> {
|
||||
Immortal,
|
||||
MortalForBlocks(u64),
|
||||
MortalFromBlock {
|
||||
for_n_blocks: u64,
|
||||
from_block_n: u64,
|
||||
from_block_hash: T::Hash,
|
||||
},
|
||||
}
|
||||
|
||||
impl<T: Config> Default for CheckMortalityParams<T> {
|
||||
fn default() -> Self {
|
||||
// default to being mortal for 32 blocks if possible:
|
||||
CheckMortalityParams(CheckMortalityParamsInner::MortalForBlocks(32))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> CheckMortalityParams<T> {
|
||||
/// Configure a transaction that will be mortal for the number of blocks given.
|
||||
pub fn mortal(for_n_blocks: u64) -> Self {
|
||||
Self(CheckMortalityParamsInner::MortalForBlocks(for_n_blocks))
|
||||
}
|
||||
/// Configure a transaction that will be mortal for the number of blocks given,
|
||||
/// and from the block header provided.
|
||||
pub fn mortal_from(for_n_blocks: u64, from_block: T::Header) -> Self {
|
||||
Self(CheckMortalityParamsInner::MortalFromBlock {
|
||||
for_n_blocks,
|
||||
from_block_n: from_block.number().into(),
|
||||
from_block_hash: from_block.hash(),
|
||||
})
|
||||
}
|
||||
/// Configure a transaction that will be mortal for the number of blocks given,
|
||||
/// and from the block details provided. Prefer to use [`CheckMortalityParams::mortal()`]
|
||||
/// or [`CheckMortalityParams::mortal_from()`] which both avoid the block number and hash
|
||||
/// from being misaligned.
|
||||
pub fn mortal_from_unchecked(
|
||||
for_n_blocks: u64,
|
||||
from_block_n: u64,
|
||||
from_block_hash: T::Hash,
|
||||
) -> Self {
|
||||
Self(CheckMortalityParamsInner::MortalFromBlock {
|
||||
for_n_blocks,
|
||||
from_block_n,
|
||||
from_block_hash,
|
||||
})
|
||||
}
|
||||
/// An immortal transaction.
|
||||
pub fn immortal() -> Self {
|
||||
Self(CheckMortalityParamsInner::Immortal)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> Params<T> for CheckMortalityParams<T> {
|
||||
fn inject_block(&mut self, from_block_n: u64, from_block_hash: <T as Config>::Hash) {
|
||||
match &self.0 {
|
||||
CheckMortalityParamsInner::MortalForBlocks(n) => {
|
||||
self.0 = CheckMortalityParamsInner::MortalFromBlock {
|
||||
for_n_blocks: *n,
|
||||
from_block_n,
|
||||
from_block_hash,
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// Don't change anything if explicit Immortal or explicit block set.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The [`ChargeAssetTxPayment`] transaction extension.
|
||||
#[derive(DecodeAsType)]
|
||||
#[derive_where(Clone, Debug; T::AssetId)]
|
||||
#[decode_as_type(trait_bounds = "T::AssetId: DecodeAsType")]
|
||||
@@ -311,7 +436,31 @@ impl<T: Config> ChargeAssetTxPayment<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Parameters to configure the [`ChargeAssetTxPayment`] signed extension.
|
||||
impl<T: Config> ExtrinsicParams<T> for ChargeAssetTxPayment<T> {
|
||||
type Params = ChargeAssetTxPaymentParams<T>;
|
||||
|
||||
fn new(_client: &ClientState<T>, params: Self::Params) -> Result<Self, ExtrinsicParamsError> {
|
||||
Ok(ChargeAssetTxPayment {
|
||||
tip: Compact(params.tip),
|
||||
asset_id: params.asset_id,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> ExtrinsicParamsEncoder for ChargeAssetTxPayment<T> {
|
||||
fn encode_value_to(&self, v: &mut Vec<u8>) {
|
||||
(self.tip, &self.asset_id).encode_to(v);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> TransactionExtension<T> for ChargeAssetTxPayment<T> {
|
||||
type Decoded = Self;
|
||||
fn matches(identifier: &str, _type_id: u32, _types: &PortableRegistry) -> bool {
|
||||
identifier == "ChargeAssetTxPayment"
|
||||
}
|
||||
}
|
||||
|
||||
/// Parameters to configure the [`ChargeAssetTxPayment`] transaction extension.
|
||||
pub struct ChargeAssetTxPaymentParams<T: Config> {
|
||||
tip: u128,
|
||||
asset_id: Option<T::AssetId>,
|
||||
@@ -350,33 +499,9 @@ impl<T: Config> ChargeAssetTxPaymentParams<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> ExtrinsicParams<T> for ChargeAssetTxPayment<T> {
|
||||
type Params = ChargeAssetTxPaymentParams<T>;
|
||||
impl<T: Config> Params<T> for ChargeAssetTxPaymentParams<T> {}
|
||||
|
||||
fn new(_client: &ClientState<T>, params: Self::Params) -> Result<Self, ExtrinsicParamsError> {
|
||||
Ok(ChargeAssetTxPayment {
|
||||
tip: Compact(params.tip),
|
||||
asset_id: params.asset_id,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> RefineParams<T> for ChargeAssetTxPaymentParams<T> {}
|
||||
|
||||
impl<T: Config> ExtrinsicParamsEncoder for ChargeAssetTxPayment<T> {
|
||||
fn encode_extra_to(&self, v: &mut Vec<u8>) {
|
||||
(self.tip, &self.asset_id).encode_to(v);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> SignedExtension<T> for ChargeAssetTxPayment<T> {
|
||||
type Decoded = Self;
|
||||
fn matches(identifier: &str, _type_id: u32, _types: &PortableRegistry) -> bool {
|
||||
identifier == "ChargeAssetTxPayment"
|
||||
}
|
||||
}
|
||||
|
||||
/// The [`ChargeTransactionPayment`] signed extension.
|
||||
/// The [`ChargeTransactionPayment`] transaction extension.
|
||||
#[derive(Clone, Debug, DecodeAsType)]
|
||||
pub struct ChargeTransactionPayment {
|
||||
tip: Compact<u128>,
|
||||
@@ -389,7 +514,30 @@ impl ChargeTransactionPayment {
|
||||
}
|
||||
}
|
||||
|
||||
/// Parameters to configure the [`ChargeTransactionPayment`] signed extension.
|
||||
impl<T: Config> ExtrinsicParams<T> for ChargeTransactionPayment {
|
||||
type Params = ChargeTransactionPaymentParams;
|
||||
|
||||
fn new(_client: &ClientState<T>, params: Self::Params) -> Result<Self, ExtrinsicParamsError> {
|
||||
Ok(ChargeTransactionPayment {
|
||||
tip: Compact(params.tip),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ExtrinsicParamsEncoder for ChargeTransactionPayment {
|
||||
fn encode_value_to(&self, v: &mut Vec<u8>) {
|
||||
self.tip.encode_to(v);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> TransactionExtension<T> for ChargeTransactionPayment {
|
||||
type Decoded = Self;
|
||||
fn matches(identifier: &str, _type_id: u32, _types: &PortableRegistry) -> bool {
|
||||
identifier == "ChargeTransactionPayment"
|
||||
}
|
||||
}
|
||||
|
||||
/// Parameters to configure the [`ChargeTransactionPayment`] transaction extension.
|
||||
#[derive(Default)]
|
||||
pub struct ChargeTransactionPaymentParams {
|
||||
tip: u128,
|
||||
@@ -406,32 +554,9 @@ impl ChargeTransactionPaymentParams {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> ExtrinsicParams<T> for ChargeTransactionPayment {
|
||||
type Params = ChargeTransactionPaymentParams;
|
||||
impl<T: Config> Params<T> for ChargeTransactionPaymentParams {}
|
||||
|
||||
fn new(_client: &ClientState<T>, params: Self::Params) -> Result<Self, ExtrinsicParamsError> {
|
||||
Ok(ChargeTransactionPayment {
|
||||
tip: Compact(params.tip),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> RefineParams<T> for ChargeTransactionPaymentParams {}
|
||||
|
||||
impl ExtrinsicParamsEncoder for ChargeTransactionPayment {
|
||||
fn encode_extra_to(&self, v: &mut Vec<u8>) {
|
||||
self.tip.encode_to(v);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> SignedExtension<T> for ChargeTransactionPayment {
|
||||
type Decoded = Self;
|
||||
fn matches(identifier: &str, _type_id: u32, _types: &PortableRegistry) -> bool {
|
||||
identifier == "ChargeTransactionPayment"
|
||||
}
|
||||
}
|
||||
|
||||
/// This accepts a tuple of [`SignedExtension`]s, and will dynamically make use of whichever
|
||||
/// This accepts a tuple of [`TransactionExtension`]s, and will dynamically make use of whichever
|
||||
/// ones are actually required for the chain in the correct order, ignoring the rest. This
|
||||
/// is a sensible default, and allows for a single configuration to work across multiple chains.
|
||||
pub struct AnyOf<T, Params> {
|
||||
@@ -447,7 +572,7 @@ macro_rules! impl_tuples {
|
||||
impl <T, $($ident),+> ExtrinsicParams<T> for AnyOf<T, ($($ident,)+)>
|
||||
where
|
||||
T: Config,
|
||||
$($ident: SignedExtension<T>,)+
|
||||
$($ident: TransactionExtension<T>,)+
|
||||
{
|
||||
type Params = ($($ident::Params,)+);
|
||||
|
||||
@@ -458,11 +583,11 @@ macro_rules! impl_tuples {
|
||||
let metadata = &client.metadata;
|
||||
let types = metadata.types();
|
||||
|
||||
// For each signed extension in the tuple, find the matching index in the metadata, if
|
||||
// For each transaction extension in the tuple, find the matching index in the metadata, if
|
||||
// there is one, and add it to a map with that index as the key.
|
||||
let mut exts_by_index = HashMap::new();
|
||||
$({
|
||||
for (idx, e) in metadata.extrinsic().signed_extensions().iter().enumerate() {
|
||||
for (idx, e) in metadata.extrinsic().transaction_extensions().iter().enumerate() {
|
||||
// Skip over any exts that have a match already:
|
||||
if exts_by_index.contains_key(&idx) {
|
||||
continue
|
||||
@@ -479,12 +604,12 @@ macro_rules! impl_tuples {
|
||||
|
||||
// Next, turn these into an ordered vec, erroring if we haven't matched on any exts yet.
|
||||
let mut params = Vec::new();
|
||||
for (idx, e) in metadata.extrinsic().signed_extensions().iter().enumerate() {
|
||||
for (idx, e) in metadata.extrinsic().transaction_extensions().iter().enumerate() {
|
||||
let Some(ext) = exts_by_index.remove(&idx) else {
|
||||
if is_type_empty(e.extra_ty(), types) {
|
||||
continue
|
||||
} else {
|
||||
return Err(ExtrinsicParamsError::UnknownSignedExtension(e.identifier().to_owned()));
|
||||
return Err(ExtrinsicParamsError::UnknownTransactionExtension(e.identifier().to_owned()));
|
||||
}
|
||||
};
|
||||
params.push(ext);
|
||||
@@ -500,16 +625,26 @@ macro_rules! impl_tuples {
|
||||
impl <T, $($ident),+> ExtrinsicParamsEncoder for AnyOf<T, ($($ident,)+)>
|
||||
where
|
||||
T: Config,
|
||||
$($ident: SignedExtension<T>,)+
|
||||
$($ident: TransactionExtension<T>,)+
|
||||
{
|
||||
fn encode_extra_to(&self, v: &mut Vec<u8>) {
|
||||
fn encode_value_to(&self, v: &mut Vec<u8>) {
|
||||
for ext in &self.params {
|
||||
ext.encode_extra_to(v);
|
||||
ext.encode_value_to(v);
|
||||
}
|
||||
}
|
||||
fn encode_additional_to(&self, v: &mut Vec<u8>) {
|
||||
fn encode_signer_payload_value_to(&self, v: &mut Vec<u8>) {
|
||||
for ext in &self.params {
|
||||
ext.encode_additional_to(v);
|
||||
ext.encode_signer_payload_value_to(v);
|
||||
}
|
||||
}
|
||||
fn encode_implicit_to(&self, v: &mut Vec<u8>) {
|
||||
for ext in &self.params {
|
||||
ext.encode_implicit_to(v);
|
||||
}
|
||||
}
|
||||
fn inject_signature(&mut self, account_id: &dyn Any, signature: &dyn Any) {
|
||||
for ext in &mut self.params {
|
||||
ext.inject_signature(account_id, signature);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user