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

Allow PartialExtrinsic to be held across await points #1658

Merged
merged 3 commits into from
Jun 26, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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
2 changes: 1 addition & 1 deletion core/src/config/extrinsic_params.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion core/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Expand Down
4 changes: 2 additions & 2 deletions core/src/config/signed_extensions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)>,
}

Expand Down Expand Up @@ -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
}
Expand Down
53 changes: 53 additions & 0 deletions subxt/examples/tx_partial.rs
Original file line number Diff line number Diff line change
@@ -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(())
}
7 changes: 7 additions & 0 deletions subxt/src/book/usage/transactions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,13 @@
#![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:
//!
#![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.
//!
Loading