Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clean up extrinsic params builder #405

Merged
merged 17 commits into from
Jan 6, 2023
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions examples/examples/compose_extrinsic_offline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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, GetHeader, PolkadotAdditionalParams,
SubmitAndWatch, XtStatus,
};

Expand All @@ -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::<Runtime>::new()
let tx_params = PolkadotAdditionalParams::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();
Expand Down
8 changes: 4 additions & 4 deletions examples/examples/custom_nonce.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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, GetHeader, PolkadotAdditionalParams,
SubmitAndWatch, XtStatus,
};

Expand All @@ -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::<Runtime>::new()
let tx_params = PolkadotAdditionalParams::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();
Expand Down
141 changes: 75 additions & 66 deletions primitives/src/extrinsic_params.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand All @@ -27,111 +27,121 @@ pub type AssetBalanceFor<Runtime> = <Runtime as crate::AssetsConfig>::Balance;
pub type HashFor<Runtime> = <Runtime as crate::FrameSystemConfig>::Hash;
pub type IndexFor<Runtime> = <Runtime as crate::FrameSystemConfig>::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<Runtime> = PolkadotExtrinsicParams<
AssetTip<AssetBalanceFor<Runtime>>,
IndexFor<Runtime>,
HashFor<Runtime>,
>;

/// A struct representing the signed extra and additional parameters required
/// to construct a transaction and pay in token fees.
pub type PlainTipExtrinsicParams<Runtime> =
PolkadotExtrinsicParams<PlainTip<BalanceFor<Runtime>>, IndexFor<Runtime>, HashFor<Runtime>>;

/// SignedExtra that is compatible with the 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<Tip, Index> {
pub struct PolkadotSignedExtra<Tip, Index> {
pub era: Era,
#[codec(compact)]
pub nonce: Index,
pub tip: Tip,
}

impl<Tip, Index> SubstrateDefaultSignedExtra<Tip, Index> {
impl<Tip, Index> PolkadotSignedExtra<Tip, Index> {
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<Hash> = ((), 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 PolkadotAdditionalSigned<Hash> = ((), 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<Index, Hash> {
/// 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<Runtime> =
BaseExtrinsicParams<AssetTip<AssetBalanceFor<Runtime>>, IndexFor<Runtime>, HashFor<Runtime>>;
/// A builder which leads to [`AssetTipExtrinsicParams`] being constructed.
/// This is what you provide to methods like `sign_and_submit()`.
pub type AssetTipExtrinsicParamsBuilder<Runtime> =
BaseExtrinsicParamsBuilder<AssetTip<AssetBalanceFor<Runtime>>, HashFor<Runtime>>;

/// A struct representing the signed extra and additional parameters required
/// to construct a transaction and pay in token fees
pub type PlainTipExtrinsicParams<Runtime> =
BaseExtrinsicParams<PlainTip<BalanceFor<Runtime>>, IndexFor<Runtime>, HashFor<Runtime>>;
/// A builder which leads to [`PlainTipExtrinsicParams`] being constructed.
/// This is what you provide to methods like `sign_and_submit()`.
pub type PlainTipExtrinsicParamsBuilder<Runtime> =
BaseExtrinsicParamsBuilder<PlainTip<BalanceFor<Runtime>>, HashFor<Runtime>>;

/// 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<Tip, Index, Hash> {
pub struct PolkadotExtrinsicParams<Tip, Index, Hash> {
era: Era,
nonce: Index,
tip: Tip,
spec_version: u32,
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 Polkadot (and default Substrate) node additional params,
/// needed for constructing an extrinsic with the trait `ExtrinsicParams`.
#[derive(Decode, Encode, Copy, Clone, Eq, PartialEq, Debug)]
pub struct BaseExtrinsicParamsBuilder<Tip, Hash> {
pub struct PolkadotAdditionalParams<Tip, Hash> {
era: Era,
mortality_checkpoint: Option<Hash>,
tip: Tip,
}

impl<Tip: Default, Hash> BaseExtrinsicParamsBuilder<Tip, Hash> {
/// Instantiate the default set of [`BaseExtrinsicParamsBuilder`]
impl<Tip: Default, Hash> PolkadotAdditionalParams<Tip, Hash> {
/// Instantiate the default set of [`PolkadotAdditionalParams`]
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 {
Expand All @@ -141,47 +151,46 @@ impl<Tip: Default, Hash> BaseExtrinsicParamsBuilder<Tip, Hash> {
}

/// 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<Tip>) -> Self {
self.tip = tip.into();
self
}
}

impl<Tip: Default, Hash> Default for BaseExtrinsicParamsBuilder<Tip, Hash> {
impl<Tip: Default, Hash> Default for PolkadotAdditionalParams<Tip, Hash> {
fn default() -> Self {
Self { era: Era::Immortal, mortality_checkpoint: None, tip: Tip::default() }
}
}

impl<Tip, Index, Hash> ExtrinsicParams<Index, Hash> for BaseExtrinsicParams<Tip, Index, Hash>
impl<Tip, Index, Hash> ExtrinsicParams<Index, Hash> for PolkadotExtrinsicParams<Tip, Index, Hash>
where
u128: From<Tip>,
Tip: Copy + Default + Encode,
Index: Copy + Default + Encode,
Hash: HashTrait + Encode + Copy,
SubstrateDefaultSignedExtra<Tip, Index>: Encode,
PolkadotSignedExtra<Tip, Index>: Encode,
{
type OtherParams = BaseExtrinsicParamsBuilder<Tip, Hash>;
type SignedExtra = SubstrateDefaultSignedExtra<Tip, Index>;
type AdditionalSigned = SubstrateDefaultAdditionalSigned<Hash>;
type AdditionalParams = PolkadotAdditionalParams<Tip, Hash>;
type SignedExtra = PolkadotSignedExtra<Tip, Index>;
type AdditionalSigned = PolkadotAdditionalSigned<Hash>;

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,
PolkadotExtrinsicParams {
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(),
}
}

Expand Down Expand Up @@ -232,7 +241,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<Balance> {
#[codec(compact)]
Expand All @@ -258,7 +267,7 @@ impl From<PlainTip<u128>> 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<Balance> {
#[codec(compact)]
Expand Down
10 changes: 4 additions & 6 deletions primitives/src/extrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@

//! Primitives for substrate extrinsics.

extern crate alloc;

use alloc::vec::Vec;
use codec::{Decode, Encode, Error, Input};
use core::fmt;
Expand Down Expand Up @@ -146,8 +144,7 @@ fn encode_with_vec_prefix<T: Encode, F: Fn(&mut Vec<u8>)>(encoder: F) -> Vec<u8>
#[cfg(test)]
mod tests {
use super::*;
use crate::{BaseExtrinsicParams, ExtrinsicParams, PlainTipExtrinsicParamsBuilder};
use node_template_runtime::Runtime;
use crate::{ExtrinsicParams, PlainTip, PolkadotAdditionalParams, PolkadotExtrinsicParams};
use sp_core::{Pair, H256 as Hash};
use sp_runtime::{generic::Era, testing::sr25519, MultiSignature};

Expand All @@ -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::<Runtime>::new()
let tx_params = PolkadotAdditionalParams::<PlainTip<u128>, 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 =
PolkadotExtrinsicParams::new(0, 0, 0u32, Hash::from([0u8; 32]), tx_params);
let xt = UncheckedExtrinsicV4::new_signed(
vec![1, 1, 1],
account.into(),
Expand Down
Loading