From 08d6032df7c30ddb74182b1eddae2a166af64060 Mon Sep 17 00:00:00 2001 From: Tadeo hepperle Date: Thu, 22 Feb 2024 18:11:40 +0100 Subject: [PATCH 1/3] refinement based approach to params --- subxt/examples/rpc_legacy.rs | 7 +- subxt/examples/setup_config_custom.rs | 15 ++- .../examples/setup_config_signed_extension.rs | 13 ++- subxt/src/book/setup/config.rs | 2 +- subxt/src/config/default_extrinsic_params.rs | 18 +++- subxt/src/config/extrinsic_params.rs | 7 +- subxt/src/config/mod.rs | 4 +- subxt/src/config/refine_params.rs | 53 +++++++++ subxt/src/config/signed_extensions.rs | 101 +++++++++++------- subxt/src/tx/tx_client.rs | 92 +++++++++++----- 10 files changed, 226 insertions(+), 86 deletions(-) create mode 100644 subxt/src/config/refine_params.rs diff --git a/subxt/examples/rpc_legacy.rs b/subxt/examples/rpc_legacy.rs index f0b831c3b6..84bd837e2d 100644 --- a/subxt/examples/rpc_legacy.rs +++ b/subxt/examples/rpc_legacy.rs @@ -40,7 +40,10 @@ async fn main() -> Result<(), Box> { .await?; let current_header = rpc.chain_get_header(None).await?.unwrap(); - let ext_params = Params::new().mortal(¤t_header, 8).build(); + let ext_params = Params::new() + .mortal(¤t_header, 8) + .nonce(current_nonce) + .build(); let balance_transfer = polkadot::tx() .balances() @@ -48,7 +51,7 @@ async fn main() -> Result<(), Box> { let ext_hash = api .tx() - .create_signed_with_nonce(&balance_transfer, &alice, current_nonce, ext_params)? + .create_signed_offline(&balance_transfer, &alice, ext_params)? .submit() .await?; diff --git a/subxt/examples/setup_config_custom.rs b/subxt/examples/setup_config_custom.rs index 4748202e1f..d50f317250 100644 --- a/subxt/examples/setup_config_custom.rs +++ b/subxt/examples/setup_config_custom.rs @@ -1,7 +1,9 @@ #![allow(missing_docs)] use codec::Encode; use subxt::client::OfflineClientT; -use subxt::config::{Config, ExtrinsicParams, ExtrinsicParamsEncoder, ExtrinsicParamsError}; +use subxt::config::{ + Config, ExtrinsicParams, ExtrinsicParamsEncoder, ExtrinsicParamsError, RefineParams, +}; use subxt_signer::sr25519::dev; #[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata_full.scale")] @@ -51,15 +53,16 @@ impl CustomExtrinsicParamsBuilder { } } +impl RefineParams for CustomExtrinsicParamsBuilder {} + // Describe how to fetch and then encode the params: impl ExtrinsicParams for CustomExtrinsicParams { - type OtherParams = CustomExtrinsicParamsBuilder; + type Params = CustomExtrinsicParamsBuilder; // Gather together all of the params we will need to encode: fn new>( - _nonce: u64, client: Client, - other_params: Self::OtherParams, + other_params: Self::Params, ) -> Result { Ok(Self { genesis_hash: client.genesis_hash(), @@ -69,6 +72,8 @@ impl ExtrinsicParams for CustomExtrinsicParams { } } +impl RefineParams for CustomExtrinsicParams {} + // Encode the relevant params when asked: impl ExtrinsicParamsEncoder for CustomExtrinsicParams { fn encode_extra_to(&self, v: &mut Vec) { @@ -86,7 +91,7 @@ async fn main() { let tx_payload = runtime::tx().system().remark(b"Hello".to_vec()); - // Build your custom "OtherParams": + // Build your custom "Params": let tx_config = CustomExtrinsicParamsBuilder::new().tip(1234).enable_foo(); // And provide them when submitting a transaction: diff --git a/subxt/examples/setup_config_signed_extension.rs b/subxt/examples/setup_config_signed_extension.rs index 2442de629b..b3b756f119 100644 --- a/subxt/examples/setup_config_signed_extension.rs +++ b/subxt/examples/setup_config_signed_extension.rs @@ -58,12 +58,11 @@ impl signed_extensions::SignedExtension for CustomSignedExtension // Gather together any params we need for our signed extension, here none. impl ExtrinsicParams for CustomSignedExtension { - type OtherParams = (); + type Params = (); fn new>( - _nonce: u64, _client: Client, - _other_params: Self::OtherParams, + _other_params: Self::Params, ) -> Result { Ok(CustomSignedExtension) } @@ -80,13 +79,13 @@ impl ExtrinsicParamsEncoder for CustomSignedExtension { } // When composing a tuple of signed extensions, the user parameters we need must -// be able to convert `Into` a tuple of corresponding `OtherParams`. Here, we just -// "hijack" the default param builder, but add the `OtherParams` (`()`) for our +// be able to convert `Into` a tuple of corresponding `Params`. Here, we just +// "hijack" the default param builder, but add the `Params` (`()`) for our // new signed extension at the end, to make the types line up. IN reality you may wish -// to construct an entirely new interface to provide the relevant `OtherParams`. +// to construct an entirely new interface to provide the relevant `Params`. pub fn custom( params: DefaultExtrinsicParamsBuilder, -) -> <::ExtrinsicParams as ExtrinsicParams>::OtherParams { +) -> <::ExtrinsicParams as ExtrinsicParams>::Params { let (a, b, c, d, e, f, g) = params.build(); (a, b, c, d, e, f, g, ()) } diff --git a/subxt/src/book/setup/config.rs b/subxt/src/book/setup/config.rs index 6e6e811a55..a26ee75a94 100644 --- a/subxt/src/book/setup/config.rs +++ b/subxt/src/book/setup/config.rs @@ -71,7 +71,7 @@ //! //! The `ExtrinsicParams` config type expects to be given an implementation of the [`crate::config::ExtrinsicParams`] trait. //! Implementations of the [`crate::config::ExtrinsicParams`] trait are handed some parameters from Subxt itself, and can -//! accept arbitrary `OtherParams` from users, and are then expected to provide this "extra" and "additional" data when asked +//! accept arbitrary other `Params` from users, and are then expected to provide this "extra" and "additional" data when asked //! via the required [`crate::config::ExtrinsicParamsEncoder`] impl. //! //! **In most cases, the default [crate::config::DefaultExtrinsicParams] type will work**: it understands the "standard" diff --git a/subxt/src/config/default_extrinsic_params.rs b/subxt/src/config/default_extrinsic_params.rs index dce83853bf..1ed66c34fb 100644 --- a/subxt/src/config/default_extrinsic_params.rs +++ b/subxt/src/config/default_extrinsic_params.rs @@ -2,6 +2,7 @@ // 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}; @@ -20,12 +21,14 @@ pub type DefaultExtrinsicParams = signed_extensions::AnyOf< ), >; -/// A builder that outputs the set of [`super::ExtrinsicParams::OtherParams`] required for +/// A builder that outputs the set of [`super::ExtrinsicParams::Params`] required for /// [`DefaultExtrinsicParams`]. This may expose methods that aren't applicable to the current /// chain; such values will simply be ignored if so. pub struct DefaultExtrinsicParamsBuilder { /// `None` means the tx will be immortal. mortality: Option>, + /// `None` means the nonce will be automatically set. + nonce: Option, /// `None` means we'll use the native token. tip_of_asset_id: Option, tip: u128, @@ -49,6 +52,7 @@ impl Default for DefaultExtrinsicParamsBuilder { tip: 0, tip_of: 0, tip_of_asset_id: None, + nonce: None, } } } @@ -72,6 +76,12 @@ impl DefaultExtrinsicParamsBuilder { self } + /// Provide a specific nonce for the submitter of the extrinsic + pub fn nonce(mut self, nonce: u64) -> Self { + self.nonce = Some(nonce); + 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. @@ -111,7 +121,7 @@ impl DefaultExtrinsicParamsBuilder { } /// Build the extrinsic parameters. - pub fn build(self) -> as ExtrinsicParams>::OtherParams { + pub fn build(self) -> as ExtrinsicParams>::Params { let check_mortality_params = if let Some(mortality) = self.mortality { signed_extensions::CheckMortalityParams::mortal( mortality.period, @@ -131,10 +141,12 @@ impl DefaultExtrinsicParamsBuilder { let charge_transaction_params = signed_extensions::ChargeTransactionPaymentParams::tip(self.tip); + let check_nonce_params = CheckNonceParams(self.nonce); + ( (), (), - (), + check_nonce_params, (), check_mortality_params, charge_asset_tx_params, diff --git a/subxt/src/config/extrinsic_params.rs b/subxt/src/config/extrinsic_params.rs index 42f3b620af..cab4583b46 100644 --- a/subxt/src/config/extrinsic_params.rs +++ b/subxt/src/config/extrinsic_params.rs @@ -10,6 +10,8 @@ use crate::{client::OfflineClientT, Config}; use core::fmt::Debug; +use super::refine_params::RefineParams; + /// An error that can be emitted when trying to construct an instance of [`ExtrinsicParams`], /// encode data from the instance, or match on signed extensions. #[derive(thiserror::Error, Debug)] @@ -53,13 +55,12 @@ pub trait ExtrinsicParams: ExtrinsicParamsEncoder + Sized + 'static { /// 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 OtherParams; + type Params: RefineParams; /// Construct a new instance of our [`ExtrinsicParams`]. fn new>( - nonce: u64, client: Client, - other_params: Self::OtherParams, + other_params: Self::Params, ) -> Result; } diff --git a/subxt/src/config/mod.rs b/subxt/src/config/mod.rs index ddfa2466c7..31833404e8 100644 --- a/subxt/src/config/mod.rs +++ b/subxt/src/config/mod.rs @@ -12,6 +12,7 @@ mod default_extrinsic_params; mod extrinsic_params; pub mod polkadot; +pub mod refine_params; pub mod signed_extensions; pub mod substrate; @@ -25,6 +26,7 @@ use serde::{de::DeserializeOwned, Serialize}; pub use default_extrinsic_params::{DefaultExtrinsicParams, DefaultExtrinsicParamsBuilder}; pub use extrinsic_params::{ExtrinsicParams, ExtrinsicParamsEncoder, ExtrinsicParamsError}; pub use polkadot::{PolkadotConfig, PolkadotExtrinsicParams, PolkadotExtrinsicParamsBuilder}; +pub use refine_params::RefineParams; pub use signed_extensions::SignedExtension; pub use substrate::{SubstrateConfig, SubstrateExtrinsicParams, SubstrateExtrinsicParamsBuilder}; @@ -60,7 +62,7 @@ pub trait Config: Sized + Send + Sync + 'static { } /// given some [`Config`], this return the other params needed for its `ExtrinsicParams`. -pub type OtherParamsFor = <::ExtrinsicParams as ExtrinsicParams>::OtherParams; +pub type ParamsFor = <::ExtrinsicParams as ExtrinsicParams>::Params; /// Block hashes must conform to a bunch of things to be used in Subxt. pub trait BlockHash: diff --git a/subxt/src/config/refine_params.rs b/subxt/src/config/refine_params.rs new file mode 100644 index 0000000000..4f2312c75a --- /dev/null +++ b/subxt/src/config/refine_params.rs @@ -0,0 +1,53 @@ +// Copyright 2019-2023 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; + +/// Types implementing [`RefineParams`] can be modified to reflect live information from the chain. +pub trait RefineParams { + /// 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, _account_nonce: u64, _block_number: u64, _block_hash: T::Hash) {} +} + +impl RefineParams for () {} + +macro_rules! impl_tuples { + ($($ident:ident $index:tt),+) => { + + impl ),+> RefineParams for ($($ident,)+){ + fn refine(&mut self, account_nonce: u64, block_number: u64, block_hash: T::Hash) { + $(self.$index.refine(account_nonce, block_number, block_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); +}; diff --git a/subxt/src/config/signed_extensions.rs b/subxt/src/config/signed_extensions.rs index f0864cee61..395515e91a 100644 --- a/subxt/src/config/signed_extensions.rs +++ b/subxt/src/config/signed_extensions.rs @@ -8,6 +8,7 @@ //! when interacting with a chain. use super::extrinsic_params::{ExtrinsicParams, ExtrinsicParamsEncoder, ExtrinsicParamsError}; +use super::RefineParams; use crate::utils::Era; use crate::{client::OfflineClientT, Config}; use codec::{Compact, Encode}; @@ -37,12 +38,11 @@ pub trait SignedExtension: ExtrinsicParams { pub struct CheckSpecVersion(u32); impl ExtrinsicParams for CheckSpecVersion { - type OtherParams = (); + type Params = (); fn new>( - _nonce: u64, client: Client, - _other_params: Self::OtherParams, + _other_params: Self::Params, ) -> Result { Ok(CheckSpecVersion(client.runtime_version().spec_version)) } @@ -65,13 +65,14 @@ impl SignedExtension for CheckSpecVersion { pub struct CheckNonce(Compact); impl ExtrinsicParams for CheckNonce { - type OtherParams = (); + type Params = CheckNonceParams; fn new>( - nonce: u64, _client: Client, - _other_params: Self::OtherParams, + params: Self::Params, ) -> Result { + // 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))) } } @@ -89,16 +90,27 @@ impl SignedExtension for CheckNonce { } } +/// Params for [`CheckNonce`] +#[derive(Debug, Clone, Default)] +pub struct CheckNonceParams(pub Option); + +impl RefineParams for CheckNonceParams { + fn refine(&mut self, account_nonce: u64, block_number: u64, block_hash: T::Hash) { + if self.0.is_none() { + self.0 = Some(account_nonce); + } + } +} + /// The [`CheckTxVersion`] signed extension. pub struct CheckTxVersion(u32); impl ExtrinsicParams for CheckTxVersion { - type OtherParams = (); + type Params = (); fn new>( - _nonce: u64, client: Client, - _other_params: Self::OtherParams, + _other_params: Self::Params, ) -> Result { Ok(CheckTxVersion(client.runtime_version().transaction_version)) } @@ -121,12 +133,11 @@ impl SignedExtension for CheckTxVersion { pub struct CheckGenesis(T::Hash); impl ExtrinsicParams for CheckGenesis { - type OtherParams = (); + type Params = (); fn new>( - _nonce: u64, client: Client, - _other_params: Self::OtherParams, + _other_params: Self::Params, ) -> Result { Ok(CheckGenesis(client.genesis_hash())) } @@ -152,16 +163,24 @@ pub struct CheckMortality { } /// Parameters to configure the [`CheckMortality`] signed extension. -pub struct CheckMortalityParams { +pub struct CheckMortalityParams(Option>); +struct CheckMortalityParamsInner { era: Era, checkpoint: Option, } impl Default for CheckMortalityParams { fn default() -> Self { - Self { - era: Default::default(), - checkpoint: Default::default(), + CheckMortalityParams(None) + } +} + +impl RefineParams for CheckMortalityParams { + fn refine(&mut self, account_nonce: u64, block_number: u64, block_hash: T::Hash) { + 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, block_number, block_hash); } } } @@ -172,32 +191,39 @@ impl CheckMortalityParams { /// `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 { - CheckMortalityParams { + Self(Some(CheckMortalityParamsInner { era: Era::mortal(period, block_number), checkpoint: Some(block_hash), - } + })) } /// An immortal transaction. pub fn immortal() -> Self { - CheckMortalityParams { + Self(Some(CheckMortalityParamsInner { era: Era::Immortal, checkpoint: None, - } + })) } } impl ExtrinsicParams for CheckMortality { - type OtherParams = CheckMortalityParams; + type Params = CheckMortalityParams; fn new>( - _nonce: u64, client: Client, - other_params: Self::OtherParams, + other_params: Self::Params, ) -> Result { - Ok(CheckMortality { - era: other_params.era, - checkpoint: other_params.checkpoint.unwrap_or(client.genesis_hash()), - }) + let check_mortality = if let Some(params) = other_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) } } @@ -278,12 +304,11 @@ impl ChargeAssetTxPaymentParams { } impl ExtrinsicParams for ChargeAssetTxPayment { - type OtherParams = ChargeAssetTxPaymentParams; + type Params = ChargeAssetTxPaymentParams; fn new>( - _nonce: u64, _client: Client, - other_params: Self::OtherParams, + other_params: Self::Params, ) -> Result { Ok(ChargeAssetTxPayment { tip: Compact(other_params.tip), @@ -292,6 +317,8 @@ impl ExtrinsicParams for ChargeAssetTxPayment { } } +impl RefineParams for ChargeAssetTxPaymentParams {} + impl ExtrinsicParamsEncoder for ChargeAssetTxPayment { fn encode_extra_to(&self, v: &mut Vec) { (self.tip, &self.asset_id).encode_to(v); @@ -336,12 +363,11 @@ impl ChargeTransactionPaymentParams { } impl ExtrinsicParams for ChargeTransactionPayment { - type OtherParams = ChargeTransactionPaymentParams; + type Params = ChargeTransactionPaymentParams; fn new>( - _nonce: u64, _client: Client, - other_params: Self::OtherParams, + other_params: Self::Params, ) -> Result { Ok(ChargeTransactionPayment { tip: Compact(other_params.tip), @@ -349,6 +375,8 @@ impl ExtrinsicParams for ChargeTransactionPayment { } } +impl RefineParams for ChargeTransactionPaymentParams {} + impl ExtrinsicParamsEncoder for ChargeTransactionPayment { fn encode_extra_to(&self, v: &mut Vec) { self.tip.encode_to(v); @@ -380,12 +408,11 @@ macro_rules! impl_tuples { T: Config, $($ident: SignedExtension,)+ { - type OtherParams = ($($ident::OtherParams,)+); + type Params = ($($ident::Params,)+); fn new>( - nonce: u64, client: Client, - other_params: Self::OtherParams, + other_params: Self::Params, ) -> Result { let metadata = client.metadata(); let types = metadata.types(); @@ -401,7 +428,7 @@ macro_rules! impl_tuples { } // Break and record as soon as we find a match: if $ident::matches(e.identifier(), e.extra_ty(), types) { - let ext = $ident::new(nonce, client.clone(), other_params.$index)?; + let ext = $ident::new(client.clone(), other_params.$index)?; let boxed_ext: Box = Box::new(ext); exts_by_index.insert(idx, boxed_ext); break diff --git a/subxt/src/tx/tx_client.rs b/subxt/src/tx/tx_client.rs index 6bf6c9ab57..251b43b119 100644 --- a/subxt/src/tx/tx_client.rs +++ b/subxt/src/tx/tx_client.rs @@ -7,8 +7,8 @@ use std::borrow::Cow; use crate::{ backend::{BackendExt, BlockRef, TransactionStatus}, client::{OfflineClientT, OnlineClientT}, - config::{Config, ExtrinsicParams, ExtrinsicParamsEncoder, Hasher}, - error::{Error, MetadataError}, + config::{Config, ExtrinsicParams, ExtrinsicParamsEncoder, Hasher, Header, RefineParams}, + error::{BlockError, Error, MetadataError}, tx::{Signer as SignerT, TxPayload, TxProgress}, utils::{Encoded, PhantomDataSendSync}, }; @@ -103,11 +103,14 @@ impl> TxClient { } /// Create a partial extrinsic. - pub fn create_partial_signed_with_nonce( + /// + /// Note: This function will **not** refine the params to use the latest account nonce and the latest block hash/number. + /// Instead your call will get default values of nonce = 0 and mortality = Immortal for the signed extensions, if the params + /// have not been 'refined' before. + pub fn create_partial_signed_offline( &self, call: &Call, - account_nonce: u64, - other_params: >::OtherParams, + params: >::Params, ) -> Result, Error> where Call: TxPayload, @@ -120,11 +123,8 @@ impl> TxClient { let call_data = self.call_data(call)?; // 3. Construct our custom additional/extra params. - let additional_and_extra_params = >::new( - account_nonce, - self.client.clone(), - other_params, - )?; + let additional_and_extra_params = + >::new(self.client.clone(), params)?; // Return these details, ready to construct a signed extrinsic from. Ok(PartialExtrinsic { @@ -135,12 +135,15 @@ impl> TxClient { } /// Creates a signed extrinsic without submitting it. - pub fn create_signed_with_nonce( + /// + /// Note: This function will **not** refine the params to use the latest account nonce and the latest block hash/number. + /// Instead your call will get default values `nonce = 0` and `mortality = Immortal` for the signed extensions, if the + /// params have not been manually set or 'refined' before. + pub fn create_signed_offline( &self, call: &Call, signer: &Signer, - account_nonce: u64, - other_params: >::OtherParams, + params: >::Params, ) -> Result, Error> where Call: TxPayload, @@ -152,8 +155,7 @@ impl> TxClient { // 2. Gather the "additional" and "extra" params along with the encoded call data, // ready to be signed. - let partial_signed = - self.create_partial_signed_with_nonce(call, account_nonce, other_params)?; + let partial_signed = self.create_partial_signed_offline(call, params)?; // 3. Sign and construct an extrinsic from these details. Ok(partial_signed.sign(signer)) @@ -165,6 +167,30 @@ where T: Config, C: OnlineClientT, { + /// Fetch the latest block header and account nonce from the backend and use them to refine [`ExtrinsicParams::Params`]. + pub async fn refine_params( + &self, + account_id: &T::AccountId, + params: &mut >::Params, + ) -> Result<(), Error> { + let block_ref = self.client.backend().latest_finalized_block_ref().await?; + let block_header = self + .client + .backend() + .block_header(block_ref.hash()) + .await? + .ok_or_else(|| Error::Block(BlockError::not_found(block_ref.hash())))?; + let account_nonce = + crate::blocks::get_account_nonce(&self.client, account_id, block_ref.hash()).await?; + + params.refine( + account_nonce, + block_header.number().into(), + block_header.hash(), + ); + Ok(()) + } + /// Get the account nonce for a given account ID. pub async fn account_nonce(&self, account_id: &T::AccountId) -> Result { let block_ref = self.client.backend().latest_finalized_block_ref().await?; @@ -176,13 +202,15 @@ where &self, call: &Call, account_id: &T::AccountId, - other_params: >::OtherParams, + mut params: >::Params, ) -> Result, Error> where Call: TxPayload, { - let account_nonce = self.account_nonce(account_id).await?; - self.create_partial_signed_with_nonce(call, account_nonce, other_params) + // Refine the params by adding account nonce and latest block information: + self.refine_params(account_id, &mut params).await?; + // Create the partial extrinsic with the refined params: + self.create_partial_signed_offline(call, params) } /// Creates a signed extrinsic, without submitting it. @@ -190,14 +218,24 @@ where &self, call: &Call, signer: &Signer, - other_params: >::OtherParams, + params: >::Params, ) -> Result, Error> where Call: TxPayload, Signer: SignerT, { - let account_nonce = self.account_nonce(&signer.account_id()).await?; - self.create_signed_with_nonce(call, signer, account_nonce, other_params) + // 1. Validate this call against the current node metadata if the call comes + // with a hash allowing us to do so. + self.validate(call)?; + + // 2. Gather the "additional" and "extra" params along with the encoded call data, + // ready to be signed. + let partial_signed = self + .create_partial_signed(call, &signer.account_id(), params) + .await?; + + // 3. Sign and construct an extrinsic from these details. + Ok(partial_signed.sign(signer)) } /// Creates and signs an extrinsic and submits it to the chain. Passes default parameters @@ -213,7 +251,7 @@ where where Call: TxPayload, Signer: SignerT, - >::OtherParams: Default, + >::Params: Default, { self.sign_and_submit_then_watch(call, signer, Default::default()) .await @@ -227,13 +265,13 @@ where &self, call: &Call, signer: &Signer, - other_params: >::OtherParams, + params: >::Params, ) -> Result, Error> where Call: TxPayload, Signer: SignerT, { - self.create_signed(call, signer, other_params) + self.create_signed(call, signer, params) .await? .submit_and_watch() .await @@ -257,7 +295,7 @@ where where Call: TxPayload, Signer: SignerT, - >::OtherParams: Default, + >::Params: Default, { self.sign_and_submit(call, signer, Default::default()).await } @@ -274,13 +312,13 @@ where &self, call: &Call, signer: &Signer, - other_params: >::OtherParams, + params: >::Params, ) -> Result where Call: TxPayload, Signer: SignerT, { - self.create_signed(call, signer, other_params) + self.create_signed(call, signer, params) .await? .submit() .await From a475a70100a932cb7d610b3b36fbc7b173f37495 Mon Sep 17 00:00:00 2001 From: Tadeo hepperle Date: Thu, 22 Feb 2024 18:12:00 +0100 Subject: [PATCH 2/3] doc and test fixes --- examples/wasm-example/Cargo.lock | 107 +++++++++--------- examples/wasm-example/src/routes/signing.rs | 4 +- subxt/src/book/usage/transactions.rs | 7 +- subxt/src/config/signed_extensions.rs | 4 +- .../src/full_client/client/unstable_rpcs.rs | 2 +- 5 files changed, 63 insertions(+), 61 deletions(-) diff --git a/examples/wasm-example/Cargo.lock b/examples/wasm-example/Cargo.lock index 0e00f041a3..f46412c7b8 100644 --- a/examples/wasm-example/Cargo.lock +++ b/examples/wasm-example/Cargo.lock @@ -476,12 +476,12 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.3" +version = "0.20.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" +checksum = "c376d08ea6aa96aafe61237c7200d1241cb177b7d3a542d791f2d118e9cbb955" dependencies = [ - "darling_core 0.20.3", - "darling_macro 0.20.3", + "darling_core 0.20.6", + "darling_macro 0.20.6", ] [[package]] @@ -500,9 +500,9 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.3" +version = "0.20.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" +checksum = "33043dcd19068b8192064c704b3f83eb464f91f1ff527b44a4e2b08d9cdb8855" dependencies = [ "fnv", "ident_case", @@ -525,11 +525,11 @@ dependencies = [ [[package]] name = "darling_macro" -version = "0.20.3" +version = "0.20.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" +checksum = "c5a91391accf613803c2a9bf9abccdbaa07c54b4244a5b64883f9c3c137c86be" dependencies = [ - "darling_core 0.20.3", + "darling_core 0.20.6", "quote", "syn 2.0.48", ] @@ -610,9 +610,9 @@ dependencies = [ [[package]] name = "either" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" [[package]] name = "equivalent" @@ -1329,18 +1329,18 @@ checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[package]] name = "js-sys" -version = "0.3.67" +version = "0.3.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a1d36f1235bc969acba30b7f5990b864423a6068a10f7c90ae8f0112e3a59d1" +checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee" dependencies = [ "wasm-bindgen", ] [[package]] name = "jsonrpsee" -version = "0.21.0" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9579d0ca9fb30da026bac2f0f7d9576ec93489aeb7cd4971dd5b4617d82c79b2" +checksum = "16fcc9dd231e72d22993f1643d5f7f0db785737dbe3c3d7ca222916ab4280795" dependencies = [ "jsonrpsee-client-transport", "jsonrpsee-core", @@ -1350,9 +1350,9 @@ dependencies = [ [[package]] name = "jsonrpsee-client-transport" -version = "0.21.0" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9f9ed46590a8d5681975f126e22531698211b926129a40a2db47cbca429220" +checksum = "0476c96eb741b40d39dcb39d0124e3b9be9840ec77653c42a0996563ae2a53f7" dependencies = [ "futures-channel", "futures-util", @@ -1373,9 +1373,9 @@ dependencies = [ [[package]] name = "jsonrpsee-core" -version = "0.21.0" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "776d009e2f591b78c038e0d053a796f94575d66ca4e77dd84bfc5e81419e436c" +checksum = "b974d8f6139efbe8425f32cb33302aba6d5e049556b5bfc067874e7a0da54a2e" dependencies = [ "anyhow", "async-lock", @@ -1398,9 +1398,9 @@ dependencies = [ [[package]] name = "jsonrpsee-http-client" -version = "0.21.0" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b7de9f3219d95985eb77fd03194d7c1b56c19bce1abfcc9d07462574b15572" +checksum = "19dc795a277cff37f27173b3ca790d042afcc0372c34a7ca068d2e76de2cb6d1" dependencies = [ "async-trait", "hyper", @@ -1418,9 +1418,9 @@ dependencies = [ [[package]] name = "jsonrpsee-types" -version = "0.21.0" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3266dfb045c9174b24c77c2dfe0084914bb23a6b2597d70c9dc6018392e1cd1b" +checksum = "b13dac43c1a9fc2648b37f306b0a5b0e29b2a6e1c36a33b95c1948da2494e9c5" dependencies = [ "anyhow", "beef", @@ -1713,18 +1713,18 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pin-project" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" +checksum = "0302c4a0442c456bd56f841aee5c3bfd17967563f6fadc9ceb9f9c23cf3807e0" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" +checksum = "266c042b60c9c76b8d53061e52b2e0d1116abc57cefc8c5cd671619a56ac3690" dependencies = [ "proc-macro2", "quote", @@ -2289,9 +2289,9 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" [[package]] name = "serde" -version = "1.0.195" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" dependencies = [ "serde_derive", ] @@ -2318,9 +2318,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.195" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", @@ -2329,9 +2329,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.111" +version = "1.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4" +checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" dependencies = [ "itoa", "ryu", @@ -2540,9 +2540,9 @@ dependencies = [ [[package]] name = "sp-core-hashing" -version = "13.0.0" +version = "15.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb8524f01591ee58b46cd83c9dbc0fcffd2fd730dabec4f59326cd58a00f17e2" +checksum = "1e0f4990add7b2cefdeca883c0efa99bb4d912cb2196120e1500c0cc099553b0" dependencies = [ "blake2b_simd", "byteorder", @@ -2659,7 +2659,7 @@ dependencies = [ name = "subxt-macro" version = "0.34.0" dependencies = [ - "darling 0.20.3", + "darling 0.20.6", "parity-scale-codec", "proc-macro-error", "quote", @@ -2672,11 +2672,12 @@ dependencies = [ name = "subxt-metadata" version = "0.34.0" dependencies = [ + "derive_more", "frame-metadata 16.0.0", + "hashbrown 0.14.3", "parity-scale-codec", "scale-info", "sp-core-hashing", - "thiserror", ] [[package]] @@ -2709,18 +2710,18 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "thiserror" -version = "1.0.56" +version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" +checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.56" +version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" +checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" dependencies = [ "proc-macro2", "quote", @@ -2744,9 +2745,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.35.1" +version = "1.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" +checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" dependencies = [ "backtrace", "bytes", @@ -3010,9 +3011,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.90" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406" +checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -3020,9 +3021,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.90" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd" +checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b" dependencies = [ "bumpalo", "log", @@ -3047,9 +3048,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.90" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999" +checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3057,9 +3058,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.90" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7" +checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66" dependencies = [ "proc-macro2", "quote", @@ -3070,9 +3071,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.90" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b" +checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838" [[package]] name = "wasm-example" diff --git a/examples/wasm-example/src/routes/signing.rs b/examples/wasm-example/src/routes/signing.rs index db6d059ada..7891ef92f8 100644 --- a/examples/wasm-example/src/routes/signing.rs +++ b/examples/wasm-example/src/routes/signing.rs @@ -7,6 +7,7 @@ use subxt::ext::codec::{Decode, Encode}; use subxt::tx::SubmittableExtrinsic; use subxt::tx::TxPayload; use subxt::utils::{AccountId32, MultiSignature}; +use subxt::config::DefaultExtrinsicParamsBuilder; use crate::services::{extension_signature_for_extrinsic, get_accounts, polkadot, Account}; use web_sys::HtmlInputElement; @@ -155,7 +156,8 @@ impl Component for SigningExamplesComponent { return Message::Error(anyhow!("MultiSignature Decoding")); }; - let Ok(partial_signed) = api.tx().create_partial_signed_with_nonce(&remark_call, account_nonce, Default::default()) else { + let params = DefaultExtrinsicParamsBuilder::new().nonce(account_nonce).build(); + let Ok(partial_signed) = api.tx().create_partial_signed_offline(&remark_call, params) else { return Message::Error(anyhow!("PartialExtrinsic creation failed")); }; diff --git a/subxt/src/book/usage/transactions.rs b/subxt/src/book/usage/transactions.rs index 6cbae2fa4c..46fbf85c5e 100644 --- a/subxt/src/book/usage/transactions.rs +++ b/subxt/src/book/usage/transactions.rs @@ -137,11 +137,10 @@ //! Value::from_bytes("Hello there") //! ]); //! -//! // Construct the tx but don't sign it. You need to provide the nonce -//! // here, or can use `create_partial_signed` to fetch the correct nonce. -//! let partial_tx = client.tx().create_partial_signed_with_nonce( +//! // Construct the tx but don't sign it. The account nonce here defaults to 0. +//! // You Can use `create_partial_signed` to fetch the correct nonce. +//! let partial_tx = client.tx().create_partial_signed_offline( //! &payload, -//! 0u64, //! Default::default() //! )?; //! diff --git a/subxt/src/config/signed_extensions.rs b/subxt/src/config/signed_extensions.rs index 395515e91a..779f2bab0a 100644 --- a/subxt/src/config/signed_extensions.rs +++ b/subxt/src/config/signed_extensions.rs @@ -95,7 +95,7 @@ impl SignedExtension for CheckNonce { pub struct CheckNonceParams(pub Option); impl RefineParams for CheckNonceParams { - fn refine(&mut self, account_nonce: u64, block_number: u64, block_hash: T::Hash) { + fn refine(&mut self, account_nonce: u64, _block_number: u64, _block_hash: T::Hash) { if self.0.is_none() { self.0 = Some(account_nonce); } @@ -176,7 +176,7 @@ impl Default for CheckMortalityParams { } impl RefineParams for CheckMortalityParams { - fn refine(&mut self, account_nonce: u64, block_number: u64, block_hash: T::Hash) { + fn refine(&mut self, _account_nonce: u64, block_number: u64, block_hash: T::Hash) { 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; diff --git a/testing/integration-tests/src/full_client/client/unstable_rpcs.rs b/testing/integration-tests/src/full_client/client/unstable_rpcs.rs index 7704e0d002..084c1223a8 100644 --- a/testing/integration-tests/src/full_client/client/unstable_rpcs.rs +++ b/testing/integration-tests/src/full_client/client/unstable_rpcs.rs @@ -261,7 +261,7 @@ async fn transaction_unstable_submit_and_watch() { let tx_bytes = ctx .client() .tx() - .create_signed_with_nonce(&payload, &dev::alice(), 0, Default::default()) + .create_signed_offline(&payload, &dev::alice(), Default::default()) .unwrap() .into_encoded(); From 920790877639af566eaeea01a10cd725489f9f09 Mon Sep 17 00:00:00 2001 From: Tadeo hepperle Date: Thu, 29 Feb 2024 10:56:26 +0100 Subject: [PATCH 3/3] bundle refine data into struct --- subxt/examples/setup_config_custom.rs | 6 +-- .../examples/setup_config_signed_extension.rs | 2 +- subxt/src/book/usage/transactions.rs | 2 +- subxt/src/config/extrinsic_params.rs | 2 +- subxt/src/config/mod.rs | 4 +- subxt/src/config/refine_params.rs | 38 +++++++++++++++++-- subxt/src/config/signed_extensions.rs | 34 +++++++++-------- subxt/src/tx/tx_client.rs | 21 +++++----- 8 files changed, 72 insertions(+), 37 deletions(-) diff --git a/subxt/examples/setup_config_custom.rs b/subxt/examples/setup_config_custom.rs index d50f317250..df4dec9fae 100644 --- a/subxt/examples/setup_config_custom.rs +++ b/subxt/examples/setup_config_custom.rs @@ -62,12 +62,12 @@ impl ExtrinsicParams for CustomExtrinsicParams { // Gather together all of the params we will need to encode: fn new>( client: Client, - other_params: Self::Params, + params: Self::Params, ) -> Result { Ok(Self { genesis_hash: client.genesis_hash(), - tip: other_params.tip, - foo: other_params.foo, + tip: params.tip, + foo: params.foo, }) } } diff --git a/subxt/examples/setup_config_signed_extension.rs b/subxt/examples/setup_config_signed_extension.rs index b3b756f119..e4a2733be5 100644 --- a/subxt/examples/setup_config_signed_extension.rs +++ b/subxt/examples/setup_config_signed_extension.rs @@ -62,7 +62,7 @@ impl ExtrinsicParams for CustomSignedExtension { fn new>( _client: Client, - _other_params: Self::Params, + _params: Self::Params, ) -> Result { Ok(CustomSignedExtension) } diff --git a/subxt/src/book/usage/transactions.rs b/subxt/src/book/usage/transactions.rs index 46fbf85c5e..e900846af2 100644 --- a/subxt/src/book/usage/transactions.rs +++ b/subxt/src/book/usage/transactions.rs @@ -138,7 +138,7 @@ //! ]); //! //! // Construct the tx but don't sign it. The account nonce here defaults to 0. -//! // You Can use `create_partial_signed` to fetch the correct nonce. +//! // You can use `create_partial_signed` to fetch the correct nonce. //! let partial_tx = client.tx().create_partial_signed_offline( //! &payload, //! Default::default() diff --git a/subxt/src/config/extrinsic_params.rs b/subxt/src/config/extrinsic_params.rs index cab4583b46..5b3148c269 100644 --- a/subxt/src/config/extrinsic_params.rs +++ b/subxt/src/config/extrinsic_params.rs @@ -60,7 +60,7 @@ pub trait ExtrinsicParams: ExtrinsicParamsEncoder + Sized + 'static { /// Construct a new instance of our [`ExtrinsicParams`]. fn new>( client: Client, - other_params: Self::Params, + params: Self::Params, ) -> Result; } diff --git a/subxt/src/config/mod.rs b/subxt/src/config/mod.rs index 31833404e8..ca84a4e9bb 100644 --- a/subxt/src/config/mod.rs +++ b/subxt/src/config/mod.rs @@ -10,9 +10,9 @@ mod default_extrinsic_params; mod extrinsic_params; +mod refine_params; pub mod polkadot; -pub mod refine_params; pub mod signed_extensions; pub mod substrate; @@ -26,7 +26,7 @@ use serde::{de::DeserializeOwned, Serialize}; pub use default_extrinsic_params::{DefaultExtrinsicParams, DefaultExtrinsicParamsBuilder}; pub use extrinsic_params::{ExtrinsicParams, ExtrinsicParamsEncoder, ExtrinsicParamsError}; pub use polkadot::{PolkadotConfig, PolkadotExtrinsicParams, PolkadotExtrinsicParamsBuilder}; -pub use refine_params::RefineParams; +pub use refine_params::{RefineParams, RefineParamsData}; pub use signed_extensions::SignedExtension; pub use substrate::{SubstrateConfig, SubstrateExtrinsicParams, SubstrateExtrinsicParamsBuilder}; diff --git a/subxt/src/config/refine_params.rs b/subxt/src/config/refine_params.rs index 4f2312c75a..3a5c3a2087 100644 --- a/subxt/src/config/refine_params.rs +++ b/subxt/src/config/refine_params.rs @@ -6,11 +6,43 @@ use crate::Config; +/// Data that can be used to refine the params of signed extensions. +pub struct RefineParamsData { + account_nonce: u64, + block_number: u64, + block_hash: T::Hash, +} + +impl RefineParamsData { + pub(crate) 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 { /// 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, _account_nonce: u64, _block_number: u64, _block_hash: T::Hash) {} + fn refine(&mut self, _data: &RefineParamsData) {} } impl RefineParams for () {} @@ -19,8 +51,8 @@ macro_rules! impl_tuples { ($($ident:ident $index:tt),+) => { impl ),+> RefineParams for ($($ident,)+){ - fn refine(&mut self, account_nonce: u64, block_number: u64, block_hash: T::Hash) { - $(self.$index.refine(account_nonce, block_number, block_hash);)+ + fn refine(&mut self, data: &RefineParamsData) { + $(self.$index.refine(data);)+ } } diff --git a/subxt/src/config/signed_extensions.rs b/subxt/src/config/signed_extensions.rs index 779f2bab0a..d5d2a1c66b 100644 --- a/subxt/src/config/signed_extensions.rs +++ b/subxt/src/config/signed_extensions.rs @@ -8,6 +8,7 @@ //! when interacting with a chain. use super::extrinsic_params::{ExtrinsicParams, ExtrinsicParamsEncoder, ExtrinsicParamsError}; +use super::refine_params::RefineParamsData; use super::RefineParams; use crate::utils::Era; use crate::{client::OfflineClientT, Config}; @@ -42,7 +43,7 @@ impl ExtrinsicParams for CheckSpecVersion { fn new>( client: Client, - _other_params: Self::Params, + _params: Self::Params, ) -> Result { Ok(CheckSpecVersion(client.runtime_version().spec_version)) } @@ -95,9 +96,9 @@ impl SignedExtension for CheckNonce { pub struct CheckNonceParams(pub Option); impl RefineParams for CheckNonceParams { - fn refine(&mut self, account_nonce: u64, _block_number: u64, _block_hash: T::Hash) { + fn refine(&mut self, data: &RefineParamsData) { if self.0.is_none() { - self.0 = Some(account_nonce); + self.0 = Some(data.account_nonce()); } } } @@ -110,7 +111,7 @@ impl ExtrinsicParams for CheckTxVersion { fn new>( client: Client, - _other_params: Self::Params, + _params: Self::Params, ) -> Result { Ok(CheckTxVersion(client.runtime_version().transaction_version)) } @@ -137,7 +138,7 @@ impl ExtrinsicParams for CheckGenesis { fn new>( client: Client, - _other_params: Self::Params, + _params: Self::Params, ) -> Result { Ok(CheckGenesis(client.genesis_hash())) } @@ -176,11 +177,12 @@ impl Default for CheckMortalityParams { } impl RefineParams for CheckMortalityParams { - fn refine(&mut self, _account_nonce: u64, block_number: u64, block_hash: T::Hash) { + fn refine(&mut self, data: &RefineParamsData) { 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, block_number, block_hash); + *self = + CheckMortalityParams::mortal(TX_VALID_FOR, data.block_number(), data.block_hash()); } } } @@ -210,9 +212,9 @@ impl ExtrinsicParams for CheckMortality { fn new>( client: Client, - other_params: Self::Params, + params: Self::Params, ) -> Result { - let check_mortality = if let Some(params) = other_params.0 { + let check_mortality = if let Some(params) = params.0 { CheckMortality { era: params.era, checkpoint: params.checkpoint.unwrap_or(client.genesis_hash()), @@ -308,11 +310,11 @@ impl ExtrinsicParams for ChargeAssetTxPayment { fn new>( _client: Client, - other_params: Self::Params, + params: Self::Params, ) -> Result { Ok(ChargeAssetTxPayment { - tip: Compact(other_params.tip), - asset_id: other_params.asset_id, + tip: Compact(params.tip), + asset_id: params.asset_id, }) } } @@ -367,10 +369,10 @@ impl ExtrinsicParams for ChargeTransactionPayment { fn new>( _client: Client, - other_params: Self::Params, + params: Self::Params, ) -> Result { Ok(ChargeTransactionPayment { - tip: Compact(other_params.tip), + tip: Compact(params.tip), }) } } @@ -412,7 +414,7 @@ macro_rules! impl_tuples { fn new>( client: Client, - other_params: Self::Params, + params: Self::Params, ) -> Result { let metadata = client.metadata(); let types = metadata.types(); @@ -428,7 +430,7 @@ macro_rules! impl_tuples { } // Break and record as soon as we find a match: if $ident::matches(e.identifier(), e.extra_ty(), types) { - let ext = $ident::new(client.clone(), other_params.$index)?; + let ext = $ident::new(client.clone(), params.$index)?; let boxed_ext: Box = Box::new(ext); exts_by_index.insert(idx, boxed_ext); break diff --git a/subxt/src/tx/tx_client.rs b/subxt/src/tx/tx_client.rs index 251b43b119..49b940d40c 100644 --- a/subxt/src/tx/tx_client.rs +++ b/subxt/src/tx/tx_client.rs @@ -7,7 +7,10 @@ use std::borrow::Cow; use crate::{ backend::{BackendExt, BlockRef, TransactionStatus}, client::{OfflineClientT, OnlineClientT}, - config::{Config, ExtrinsicParams, ExtrinsicParamsEncoder, Hasher, Header, RefineParams}, + config::{ + Config, ExtrinsicParams, ExtrinsicParamsEncoder, Hasher, Header, RefineParams, + RefineParamsData, + }, error::{BlockError, Error, MetadataError}, tx::{Signer as SignerT, TxPayload, TxProgress}, utils::{Encoded, PhantomDataSendSync}, @@ -104,9 +107,8 @@ impl> TxClient { /// Create a partial extrinsic. /// - /// Note: This function will **not** refine the params to use the latest account nonce and the latest block hash/number. - /// Instead your call will get default values of nonce = 0 and mortality = Immortal for the signed extensions, if the params - /// have not been 'refined' before. + /// Note: if not provided, the default account nonce will be set to 0 and the default mortality will be _immortal_. + /// This is because this method runs offline, and so is unable to fetch the data needed for more appropriate values. pub fn create_partial_signed_offline( &self, call: &Call, @@ -136,9 +138,8 @@ impl> TxClient { /// Creates a signed extrinsic without submitting it. /// - /// Note: This function will **not** refine the params to use the latest account nonce and the latest block hash/number. - /// Instead your call will get default values `nonce = 0` and `mortality = Immortal` for the signed extensions, if the - /// params have not been manually set or 'refined' before. + /// Note: if not provided, the default account nonce will be set to 0 and the default mortality will be _immortal_. + /// This is because this method runs offline, and so is unable to fetch the data needed for more appropriate values. pub fn create_signed_offline( &self, call: &Call, @@ -168,7 +169,7 @@ where C: OnlineClientT, { /// Fetch the latest block header and account nonce from the backend and use them to refine [`ExtrinsicParams::Params`]. - pub async fn refine_params( + async fn refine_params( &self, account_id: &T::AccountId, params: &mut >::Params, @@ -183,11 +184,11 @@ where let account_nonce = crate::blocks::get_account_nonce(&self.client, account_id, block_ref.hash()).await?; - params.refine( + params.refine(&RefineParamsData::new( account_nonce, block_header.number().into(), block_header.hash(), - ); + )); Ok(()) }