diff --git a/examples/examples/compose_extrinsic_offline.rs b/examples/examples/compose_extrinsic_offline.rs index 800e20002..198133fc2 100644 --- a/examples/examples/compose_extrinsic_offline.rs +++ b/examples/examples/compose_extrinsic_offline.rs @@ -21,7 +21,7 @@ use kitchensink_runtime::{BalancesCall, Runtime, RuntimeCall}; use sp_keyring::AccountKeyring; use sp_runtime::{generic::Era, MultiAddress}; use substrate_api_client::{ - rpc::JsonrpseeClient, Api, AssetTipExtrinsicParams, AssetTipExtrinsicParamsBuilder, GetHeader, + rpc::JsonrpseeClient, Api, AssetTipExtrinsicParams, GenericAdditionalParams, GetHeader, SubmitAndWatch, XtStatus, }; @@ -45,12 +45,12 @@ async fn main() { let last_finalized_header_hash = api.get_finalized_head().unwrap().unwrap(); let header = api.get_header(Some(last_finalized_header_hash)).unwrap().unwrap(); let period = 5; - let tx_params = AssetTipExtrinsicParamsBuilder::::new() + let tx_params = GenericAdditionalParams::new() .era(Era::mortal(period, header.number.into()), last_finalized_header_hash) .tip(0); - // Set the custom params builder. - api.set_extrinsic_params_builder(tx_params); + // Set the additional params. + api.set_additional_params(tx_params); // Get the nonce of the signer account (online). let signer_nonce = api.get_nonce().unwrap(); diff --git a/examples/examples/custom_nonce.rs b/examples/examples/custom_nonce.rs index 56b107991..ff76613b6 100644 --- a/examples/examples/custom_nonce.rs +++ b/examples/examples/custom_nonce.rs @@ -21,7 +21,7 @@ use kitchensink_runtime::{BalancesCall, Runtime, RuntimeCall}; use sp_keyring::AccountKeyring; use sp_runtime::{generic::Era, MultiAddress}; use substrate_api_client::{ - rpc::JsonrpseeClient, Api, AssetTipExtrinsicParams, AssetTipExtrinsicParamsBuilder, GetHeader, + rpc::JsonrpseeClient, Api, AssetTipExtrinsicParams, GenericAdditionalParams, GetHeader, SubmitAndWatch, XtStatus, }; @@ -41,12 +41,12 @@ async fn main() { let last_finalized_header_hash = api.get_finalized_head().unwrap().unwrap(); let header = api.get_header(Some(last_finalized_header_hash)).unwrap().unwrap(); let period = 5; - let tx_params = AssetTipExtrinsicParamsBuilder::::new() + let tx_params = GenericAdditionalParams::new() .era(Era::mortal(period, header.number.into()), last_finalized_header_hash) .tip(0); - // Set the custom parmas builder. - api.set_extrinsic_params_builder(tx_params); + // Set the custom additional params. + api.set_additional_params(tx_params); // Get the nonce of Alice. let signer_nonce = api.get_nonce().unwrap(); diff --git a/primitives/src/extrinsic_params.rs b/primitives/src/extrinsic_params.rs index f99e29f89..4f3adb2f9 100644 --- a/primitives/src/extrinsic_params.rs +++ b/primitives/src/extrinsic_params.rs @@ -16,7 +16,7 @@ */ use codec::{Decode, Encode}; -use core::{hash::Hash as HashTrait, marker::PhantomData}; +use core::hash::Hash as HashTrait; use sp_runtime::{ generic::Era, traits::{BlakeTwo256, Hash}, @@ -27,83 +27,91 @@ pub type AssetBalanceFor = ::Balance; pub type HashFor = ::Hash; pub type IndexFor = ::Index; -/// Default SignedExtra. -/// Simple generic extra mirroring the SignedExtra currently used in extrinsics. +/// A struct representing the signed extra and additional parameters required +/// to construct a transaction and pay in asset fees. +pub type AssetTipExtrinsicParams = + GenericExtrinsicParams>, IndexFor, HashFor>; + +/// A struct representing the signed extra and additional parameters required +/// to construct a transaction and pay in token fees. +pub type PlainTipExtrinsicParams = + GenericExtrinsicParams>, IndexFor, HashFor>; + +/// SignedExtra that is compatible with a default Substrate / Polkadot node. +// Unlike the SignedExtra on the node side, which seemingly contains a lot more parameters +// see: https://github.com/paritytech/substrate/blob/cbd8f1b56fd8ab9af0d9317432cc735264c89d70/bin/node/runtime/src/lib.rs#L1779-L1788 +// The SignedExtra on the client side mirrors the actual values contained. E.g. +// CheckNonZeroSender does not hold any value inside (see link below) +// https://github.com/paritytech/substrate/blob/23bb5a6255bbcd7ce2999044710428bc4a7a924f/frame/system/src/extensions/check_non_zero_sender.rs#L33 +// and is therefore not represented on this side of the SignedExtra. +// The Era however is actually defined in the CheckMortality part: +// https://github.com/paritytech/substrate/blob/23bb5a6255bbcd7ce2999044710428bc4a7a924f/frame/system/src/extensions/check_mortality.rs#L36 +// and needs to be defined here. Be sure the order matches the one on the node side. #[derive(Decode, Encode, Copy, Clone, Eq, PartialEq, Debug)] -pub struct SubstrateDefaultSignedExtra { +pub struct GenericSignedExtra { pub era: Era, #[codec(compact)] pub nonce: Index, pub tip: Tip, } -impl SubstrateDefaultSignedExtra { +impl GenericSignedExtra { pub fn new(era: Era, nonce: Index, tip: Tip) -> Self { Self { era, nonce, tip } } } -/// Default AdditionalSigned fields of the respective SignedExtra fields. -/// The Order is (CheckNonZeroSender, CheckSpecVersion, CheckTxVersion, CheckGenesis, Check::Era, CheckNonce, CheckWeight, transactionPayment::ChargeTransactionPayment). -pub type SubstrateDefaultAdditionalSigned = ((), u32, u32, Hash, Hash, (), (), ()); +/// Default AdditionalSigned fields of a Polkadot/Substrate node. +/// Order: (CheckNonZeroSender, CheckSpecVersion, CheckTxVersion, CheckGenesis, Check::Era, CheckNonce, CheckWeight, transactionPayment::ChargeTransactionPayment). +// The order and types can must match the one defined in the runtime. +// Example: https://github.com/paritytech/substrate/blob/cbd8f1b56fd8ab9af0d9317432cc735264c89d70/bin/node/runtime/src/lib.rs#L1779-L1788 +// The `AdditionalSigned` is the tuple returned from the call SignedExtra::additional_signed(). +// Each member defined in the `SignedExtra` on the node side implements the trait `SignedExtension`, which +// defines what is returned upon the `additional_signed` call. The AdditionalSigned defined here +// must mirror these return values. +// Example: https://github.com/paritytech/substrate/blob/23bb5a6255bbcd7ce2999044710428bc4a7a924f/frame/system/src/extensions/check_non_zero_sender.rs#L64-L66 +pub type GenericAdditionalSigned = ((), u32, u32, Hash, Hash, (), (), ()); /// This trait allows you to configure the "signed extra" and -/// "additional" parameters that are signed and used in transactions. -/// see [`BaseExtrinsicParams`] for an implementation that is compatible with -/// a Polkadot node. +/// "additional" parameters that are signed and used in substrate extrinsics. pub trait ExtrinsicParams { - /// These parameters can be provided to the constructor along with - /// some default parameters in order to help construct your [`ExtrinsicParams`] object. - type OtherParams: Default + Clone; - - /// SignedExtra format of the node. + /// These params represent optional / additional params which are most likely + /// subject to change. This way, the trait does not need to be adapted if one of + /// these params is updated. + type AdditionalParams: Default + Clone; + + /// Extra mirroring the `SignedExtra` defined on the node side. + /// These parameters are sent along with the extrinsic and are taken into account + /// when signing the extrinsic. + /// It represents the inner values of the SignedExtra, PhantomData is ignored. type SignedExtra: Copy + Encode; - /// Additional Signed format of the node + /// AdditionalSigned format of the node, which is returned upon the call `additional_signed`. + /// These parameters are not sent along with the extrinsic, but are taken into account + /// when signing it, meaning the client and node must agree on their values. type AdditionalSigned: Encode; - /// Construct a new instance of our [`ExtrinsicParams`] + /// Construct a new instance. fn new( spec_version: u32, transaction_version: u32, nonce: Index, genesis_hash: Hash, - other_params: Self::OtherParams, + additional_params: Self::AdditionalParams, ) -> Self; - /// These are the parameters which are sent along with the transaction, - /// as well as taken into account when signing the transaction. + /// Construct the signed extra needed for constructing an extrinsic. fn signed_extra(&self) -> Self::SignedExtra; - /// 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. + /// Construct any additional data that should be in the signed payload of the extrinsic. fn additional_signed(&self) -> Self::AdditionalSigned; } -/// A struct representing the signed extra and additional parameters required -/// to construct a transaction and pay in asset fees -pub type AssetTipExtrinsicParams = - BaseExtrinsicParams>, IndexFor, HashFor>; -/// A builder which leads to [`AssetTipExtrinsicParams`] being constructed. -/// This is what you provide to methods like `sign_and_submit()`. -pub type AssetTipExtrinsicParamsBuilder = - BaseExtrinsicParamsBuilder>, HashFor>; - -/// A struct representing the signed extra and additional parameters required -/// to construct a transaction and pay in token fees -pub type PlainTipExtrinsicParams = - BaseExtrinsicParams>, IndexFor, HashFor>; -/// A builder which leads to [`PlainTipExtrinsicParams`] being constructed. -/// This is what you provide to methods like `sign_and_submit()`. -pub type PlainTipExtrinsicParamsBuilder = - BaseExtrinsicParamsBuilder>, HashFor>; - /// An implementation of [`ExtrinsicParams`] that is suitable for constructing /// extrinsics that can be sent to a node with the same signed extra and additional /// parameters as a Polkadot/Substrate node. #[derive(Decode, Encode, Clone, Eq, PartialEq, Debug)] -pub struct BaseExtrinsicParams { +pub struct GenericExtrinsicParams { era: Era, nonce: Index, tip: Tip, @@ -111,27 +119,26 @@ pub struct BaseExtrinsicParams { transaction_version: u32, genesis_hash: Hash, mortality_checkpoint: Hash, - marker: PhantomData<()>, } -/// This builder allows you to provide the parameters that can be configured in order to -/// construct a [`BaseExtrinsicParams`] value. +/// Representation of the default Substrate / Polkadot node additional params, +/// needed for constructing an extrinsic with the trait `ExtrinsicParams`. #[derive(Decode, Encode, Copy, Clone, Eq, PartialEq, Debug)] -pub struct BaseExtrinsicParamsBuilder { +pub struct GenericAdditionalParams { era: Era, mortality_checkpoint: Option, tip: Tip, } -impl BaseExtrinsicParamsBuilder { - /// Instantiate the default set of [`BaseExtrinsicParamsBuilder`] +impl GenericAdditionalParams { + /// Instantiate the default set of [`GenericAdditionalParams`] pub fn new() -> Self { Self::default() } - /// Set the [`Era`], which defines how long the transaction will be valid for + /// Set the [`Era`], which defines how long the extrinsic will be valid for /// (it can be either immortal, or it can be mortal and expire after a certain amount - /// of time). The second argument is the block hash after which the transaction + /// of time). The second argument is the block hash after which the extrinsic /// becomes valid, and must align with the era phase (see the [`Era::Mortal`] docs /// for more detail on that). pub fn era(mut self, era: Era, checkpoint: Hash) -> Self { @@ -141,47 +148,46 @@ impl BaseExtrinsicParamsBuilder { } /// Set the tip you'd like to give to the block author - /// for this transaction. + /// for this extrinsic. pub fn tip(mut self, tip: impl Into) -> Self { self.tip = tip.into(); self } } -impl Default for BaseExtrinsicParamsBuilder { +impl Default for GenericAdditionalParams { fn default() -> Self { Self { era: Era::Immortal, mortality_checkpoint: None, tip: Tip::default() } } } -impl ExtrinsicParams for BaseExtrinsicParams +impl ExtrinsicParams for GenericExtrinsicParams where u128: From, Tip: Copy + Default + Encode, Index: Copy + Default + Encode, Hash: HashTrait + Encode + Copy, - SubstrateDefaultSignedExtra: Encode, + GenericSignedExtra: Encode, { - type OtherParams = BaseExtrinsicParamsBuilder; - type SignedExtra = SubstrateDefaultSignedExtra; - type AdditionalSigned = SubstrateDefaultAdditionalSigned; + type AdditionalParams = GenericAdditionalParams; + type SignedExtra = GenericSignedExtra; + type AdditionalSigned = GenericAdditionalSigned; fn new( spec_version: u32, transaction_version: u32, nonce: Index, genesis_hash: Hash, - other_params: Self::OtherParams, + additional_params: Self::AdditionalParams, ) -> Self { - BaseExtrinsicParams { - era: other_params.era, - tip: other_params.tip, + GenericExtrinsicParams { + era: additional_params.era, + tip: additional_params.tip, spec_version, transaction_version, genesis_hash, - mortality_checkpoint: other_params.mortality_checkpoint.unwrap_or(genesis_hash), + mortality_checkpoint: additional_params.mortality_checkpoint.unwrap_or(genesis_hash), nonce, - marker: Default::default(), } } @@ -232,7 +238,7 @@ where } } -/// A tip payment. +/// Default tip payment for a substrate node using the balance pallet. #[derive(Copy, Clone, Debug, Default, Decode, Encode, Eq, PartialEq)] pub struct PlainTip { #[codec(compact)] @@ -258,7 +264,7 @@ impl From> for u128 { } } -/// A tip payment made in the form of a specific asset. +/// Default tip payment for substrate nodes that use the asset payment pallet. #[derive(Copy, Clone, Debug, Default, Decode, Encode, Eq, PartialEq)] pub struct AssetTip { #[codec(compact)] diff --git a/primitives/src/extrinsics.rs b/primitives/src/extrinsics.rs index b757dc557..4876ae338 100644 --- a/primitives/src/extrinsics.rs +++ b/primitives/src/extrinsics.rs @@ -17,8 +17,6 @@ //! Primitives for substrate extrinsics. -extern crate alloc; - use alloc::vec::Vec; use codec::{Decode, Encode, Error, Input}; use core::fmt; @@ -146,8 +144,7 @@ fn encode_with_vec_prefix)>(encoder: F) -> Vec #[cfg(test)] mod tests { use super::*; - use crate::{BaseExtrinsicParams, ExtrinsicParams, PlainTipExtrinsicParamsBuilder}; - use node_template_runtime::Runtime; + use crate::{ExtrinsicParams, GenericAdditionalParams, GenericExtrinsicParams, PlainTip}; use sp_core::{Pair, H256 as Hash}; use sp_runtime::{generic::Era, testing::sr25519, MultiSignature}; @@ -158,10 +155,11 @@ mod tests { let signature = pair.sign(msg); let multi_sig = MultiSignature::from(signature); let account: AccountId = pair.public().into(); - let tx_params = PlainTipExtrinsicParamsBuilder::::new() + let tx_params = GenericAdditionalParams::, Hash>::new() .era(Era::mortal(8, 0), Hash::from([0u8; 32])); - let default_extra = BaseExtrinsicParams::new(0, 0, 0u32, Hash::from([0u8; 32]), tx_params); + let default_extra = + GenericExtrinsicParams::new(0, 0, 0u32, Hash::from([0u8; 32]), tx_params); let xt = UncheckedExtrinsicV4::new_signed( vec![1, 1, 1], account.into(), diff --git a/src/api/api_client.rs b/src/api/api_client.rs index 566403b13..4a8eea082 100644 --- a/src/api/api_client.rs +++ b/src/api/api_client.rs @@ -93,7 +93,7 @@ where metadata: Metadata, runtime_version: RuntimeVersion, client: Client, - extrinsic_params_builder: Option, + additional_extrinsic_params: Option, } impl Api @@ -114,7 +114,7 @@ where metadata, runtime_version, client, - extrinsic_params_builder: None, + additional_extrinsic_params: None, } } @@ -153,20 +153,22 @@ where &self.client } - /// Set the extrinscs param builder. - pub fn set_extrinsic_params_builder(&mut self, extrinsic_params: Params::OtherParams) { - self.extrinsic_params_builder = Some(extrinsic_params); + /// Set the additional params. + pub fn set_additional_params(&mut self, extrinsic_params: Params::AdditionalParams) { + self.additional_extrinsic_params = Some(extrinsic_params); } - /// Get the extrinsic params, built with the set or if none, the default Params Builder. + /// Get the extrinsic params with the set additional params. If no additional params are set, + /// the default is taken. pub fn extrinsic_params(&self, nonce: Runtime::Index) -> Params { - let extrinsic_params_builder = self.extrinsic_params_builder.clone().unwrap_or_default(); + let additional_extrinsic_params = + self.additional_extrinsic_params.clone().unwrap_or_default(); >::new( self.runtime_version.spec_version, self.runtime_version.transaction_version, nonce, self.genesis_hash, - extrinsic_params_builder, + additional_extrinsic_params, ) } } @@ -264,8 +266,8 @@ where mod tests { use super::*; use crate::{ - rpc::mocks::RpcClientMock, utils::ToHexString, PlainTipExtrinsicParams, - PlainTipExtrinsicParamsBuilder, + rpc::mocks::RpcClientMock, utils::ToHexString, GenericAdditionalParams, + PlainTipExtrinsicParams, }; use kitchensink_runtime::Runtime; use sp_core::{sr25519::Pair, H256}; @@ -298,8 +300,8 @@ mod tests { create_mock_api(genesis_hash, runtime_version.clone(), metadata, Default::default()); // Information for Era for mortal transactions. - let builder = PlainTipExtrinsicParamsBuilder::::new(); - api.set_extrinsic_params_builder(builder); + let additional_params = GenericAdditionalParams::new(); + api.set_additional_params(additional_params); let nonce = 6; let retrieved_params = api.extrinsic_params(nonce);