Allow PartialExtrinsic to be held across await points (#1658)

* Allow PartialTransaction to be held across await points, and example to prove it

* Add comment to tx_parital example

* Fix book link
This commit is contained in:
James Wilson
2024-06-26 13:23:26 +01:00
committed by GitHub
parent 4fcabe211d
commit 75bb9b8354
5 changed files with 66 additions and 4 deletions
+1 -1
View File
@@ -14,7 +14,7 @@ use alloc::vec::Vec;
/// This trait allows you to configure the "signed extra" and
/// "additional" parameters that are a part of the transaction payload
/// or the signer payload respectively.
pub trait ExtrinsicParams<T: Config>: ExtrinsicParamsEncoder + Sized + 'static {
pub trait ExtrinsicParams<T: Config>: ExtrinsicParamsEncoder + Sized + Send + '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.
+1 -1
View File
@@ -58,7 +58,7 @@ pub trait Config: Sized + Send + Sync + 'static {
type ExtrinsicParams: ExtrinsicParams<Self>;
/// This is used to identify an asset in the `ChargeAssetTxPayment` signed extension.
type AssetId: Debug + Clone + Encode + DecodeAsType + EncodeAsType;
type AssetId: Debug + Clone + Encode + DecodeAsType + EncodeAsType + Send;
}
/// given some [`Config`], this return the other params needed for its `ExtrinsicParams`.
+2 -2
View File
@@ -435,7 +435,7 @@ impl<T: Config> SignedExtension<T> for ChargeTransactionPayment {
/// 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> {
params: Vec<Box<dyn ExtrinsicParamsEncoder>>,
params: Vec<Box<dyn ExtrinsicParamsEncoder + Send + 'static>>,
_marker: core::marker::PhantomData<(T, Params)>,
}
@@ -470,7 +470,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, params.$index)?;
let boxed_ext: Box<dyn ExtrinsicParamsEncoder> = Box::new(ext);
let boxed_ext: Box<dyn ExtrinsicParamsEncoder + Send + 'static> = Box::new(ext);
exts_by_index.insert(idx, boxed_ext);
break
}
+53
View File
@@ -0,0 +1,53 @@
#![allow(missing_docs)]
use subxt::{OnlineClient, PolkadotConfig};
use subxt_signer::sr25519::dev;
type BoxedError = Box<dyn std::error::Error + Send + Sync + 'static>;
#[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata_small.scale")]
pub mod polkadot {}
#[tokio::main]
async fn main() -> Result<(), BoxedError> {
// Spawned tasks require things held across await points to impl Send,
// so we use one to demonstrate that this is possible with `PartialExtrinsic`
tokio::spawn(signing_example()).await??;
Ok(())
}
async fn signing_example() -> Result<(), BoxedError> {
let api = OnlineClient::<PolkadotConfig>::new().await?;
// Build a balance transfer extrinsic.
let dest = dev::bob().public_key().into();
let balance_transfer_tx = polkadot::tx().balances().transfer_allow_death(dest, 10_000);
let alice = dev::alice();
// Create partial tx, ready to be signed.
let partial_tx = api
.tx()
.create_partial_signed(
&balance_transfer_tx,
&alice.public_key().to_account_id(),
Default::default(),
)
.await?;
// Simulate taking some time to get a signature back, in part to
// show that the `PartialExtrinsic` can be held across await points.
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
let signature = alice.sign(&partial_tx.signer_payload());
// Sign the transaction.
let tx = partial_tx
.sign_with_address_and_signature(&alice.public_key().to_address(), &signature.into());
// Submit it.
tx.submit_and_watch()
.await?
.wait_for_finalized_success()
.await?;
Ok(())
}
+9
View File
@@ -205,6 +205,15 @@
#![doc = include_str!("../../../examples/tx_status_stream.rs")]
//! ```
//!
//! ### Signing transactions externally
//!
//! Subxt also allows you to get hold of the signer payload and hand that off to something else to be
//! signed. The signature can then be provided back to Subxt to build the final transaction to submit:
//!
//! ```rust,ignore
#![doc = include_str!("../../../examples/tx_partial.rs")]
//! ```
//!
//! Take a look at the API docs for [`crate::tx::TxProgress`], [`crate::tx::TxStatus`] and
//! [`crate::tx::TxInBlock`] for more options.
//!