mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-28 06:17:56 +00:00
Make signing fallable and asynchronous (#119)
* Make signing fallable and asynchronous This is needed for hardware wallets, which require human confirmation to sign transactions. Blocking on a human to sign transactions is not a good idea, and the signing might fail for many reasons (device unplugged, authorization not granted, etc). * Reformat * Refactor as suggested by Andrew Jones (@ascjones). * Use future::ok Co-authored-by: Andrew Jones <ascjones@gmail.com>
This commit is contained in:
+1
-1
@@ -19,7 +19,7 @@ include = ["Cargo.toml", "src/**/*.rs", "README.md", "LICENSE"]
|
||||
[dependencies]
|
||||
log = "0.4"
|
||||
thiserror = "1.0"
|
||||
futures = "0.3"
|
||||
futures = "0.3.5"
|
||||
jsonrpsee = { version = "0.1", features = ["ws"] }
|
||||
num-traits = { version = "0.2", default-features = false }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
|
||||
@@ -82,6 +82,7 @@ pub fn call(s: Structure) -> TokenStream {
|
||||
T: #module + #subxt::system::System + Send + Sync + 'static,
|
||||
S: #codec::Encode + Send + Sync + 'static,
|
||||
E: #subxt::SignedExtra<T> + #subxt::sp_runtime::traits::SignedExtension + Send + Sync + 'static,
|
||||
<<E as #subxt::SignedExtra<T>>::Extra as #subxt::sp_runtime::traits::SignedExtension>::AdditionalSigned: Send + Sync,
|
||||
{
|
||||
fn #call<'a>(
|
||||
&'a self,
|
||||
@@ -154,6 +155,7 @@ mod tests {
|
||||
T: Balances + substrate_subxt::system::System + Send + Sync + 'static,
|
||||
S: codec::Encode + Send + Sync + 'static,
|
||||
E: substrate_subxt::SignedExtra<T> + substrate_subxt::sp_runtime::traits::SignedExtension + Send + Sync + 'static,
|
||||
<<E as substrate_subxt::SignedExtra<T>>::Extra as substrate_subxt::sp_runtime::traits::SignedExtension>::AdditionalSigned: Send + Sync,
|
||||
{
|
||||
fn transfer<'a>(
|
||||
&'a self,
|
||||
|
||||
+18
-8
@@ -118,7 +118,7 @@ pub struct ClientBuilder<T: System, S = MultiSignature, E = DefaultExtra<T>> {
|
||||
client: Option<jsonrpsee::Client>,
|
||||
}
|
||||
|
||||
impl<T: System, S, E> ClientBuilder<T, S, E> {
|
||||
impl<T: System + Send + Sync, S, E> ClientBuilder<T, S, E> {
|
||||
/// Creates a new ClientBuilder.
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
@@ -327,18 +327,22 @@ where
|
||||
}
|
||||
|
||||
/// Creates a signed extrinsic.
|
||||
pub async fn create_signed<C: Call<T>>(
|
||||
pub async fn create_signed<C: Call<T> + Send + Sync>(
|
||||
&self,
|
||||
call: C,
|
||||
signer: &(dyn Signer<T, S, E> + Send + Sync),
|
||||
) -> Result<
|
||||
UncheckedExtrinsic<T::Address, Encoded, S, <E as SignedExtra<T>>::Extra>,
|
||||
Error,
|
||||
> {
|
||||
>
|
||||
where
|
||||
<<E as SignedExtra<T>>::Extra as SignedExtension>::AdditionalSigned: Send + Sync,
|
||||
{
|
||||
let unsigned = self
|
||||
.create_unsigned(call, signer.account_id(), signer.nonce())
|
||||
.await?;
|
||||
Ok(signer.sign(unsigned))
|
||||
let signed = signer.sign(unsigned).await?;
|
||||
Ok(signed)
|
||||
}
|
||||
|
||||
/// Returns an events decoder for a call.
|
||||
@@ -379,21 +383,27 @@ where
|
||||
}
|
||||
|
||||
/// Submits a transaction to the chain.
|
||||
pub async fn submit<C: Call<T>>(
|
||||
pub async fn submit<C: Call<T> + Send + Sync>(
|
||||
&self,
|
||||
call: C,
|
||||
signer: &(dyn Signer<T, S, E> + Send + Sync),
|
||||
) -> Result<T::Hash, Error> {
|
||||
) -> Result<T::Hash, Error>
|
||||
where
|
||||
<<E as SignedExtra<T>>::Extra as SignedExtension>::AdditionalSigned: Send + Sync,
|
||||
{
|
||||
let extrinsic = self.create_signed(call, signer).await?;
|
||||
self.submit_extrinsic(extrinsic).await
|
||||
}
|
||||
|
||||
/// Submits transaction to the chain and watch for events.
|
||||
pub async fn watch<C: Call<T>>(
|
||||
pub async fn watch<C: Call<T> + Send + Sync>(
|
||||
&self,
|
||||
call: C,
|
||||
signer: &(dyn Signer<T, S, E> + Send + Sync),
|
||||
) -> Result<ExtrinsicSuccess<T>, Error> {
|
||||
) -> Result<ExtrinsicSuccess<T>, Error>
|
||||
where
|
||||
<<E as SignedExtra<T>>::Extra as SignedExtension>::AdditionalSigned: Send + Sync,
|
||||
{
|
||||
let extrinsic = self.create_signed(call, signer).await?;
|
||||
let decoder = self.events_decoder::<C>()?;
|
||||
self.submit_and_watch_extrinsic(extrinsic, decoder).await
|
||||
|
||||
+44
-12
@@ -31,13 +31,21 @@ use sp_runtime::{
|
||||
},
|
||||
traits::{
|
||||
IdentifyAccount,
|
||||
SignedExtension,
|
||||
Verify,
|
||||
},
|
||||
};
|
||||
use std::marker::PhantomData;
|
||||
use std::{
|
||||
future::Future,
|
||||
marker::PhantomData,
|
||||
pin::Pin,
|
||||
};
|
||||
|
||||
/// Extrinsic signer.
|
||||
pub trait Signer<T: System, S: Encode, E: SignedExtra<T>> {
|
||||
pub trait Signer<T: System, S: Encode, E: SignedExtra<T>>
|
||||
where
|
||||
<<E as SignedExtra<T>>::Extra as SignedExtension>::AdditionalSigned: Send + Sync,
|
||||
{
|
||||
/// Returns the account id.
|
||||
fn account_id(&self) -> &T::AccountId;
|
||||
|
||||
@@ -45,10 +53,23 @@ pub trait Signer<T: System, S: Encode, E: SignedExtra<T>> {
|
||||
fn nonce(&self) -> Option<T::Index>;
|
||||
|
||||
/// Takes an unsigned extrinsic and returns a signed extrinsic.
|
||||
///
|
||||
/// Some signers may fail, for instance because the hardware on which the keys are located has
|
||||
/// refused the operation.
|
||||
fn sign(
|
||||
&self,
|
||||
extrinsic: SignedPayload<Encoded, E::Extra>,
|
||||
) -> UncheckedExtrinsic<T::Address, Encoded, S, E::Extra>;
|
||||
) -> Pin<
|
||||
Box<
|
||||
dyn Future<
|
||||
Output = Result<
|
||||
UncheckedExtrinsic<T::Address, Encoded, S, E::Extra>,
|
||||
String,
|
||||
>,
|
||||
> + Send
|
||||
+ Sync,
|
||||
>,
|
||||
>;
|
||||
}
|
||||
|
||||
/// Extrinsic signer using a private key.
|
||||
@@ -91,12 +112,13 @@ where
|
||||
|
||||
impl<T, S, E, P> Signer<T, S, E> for PairSigner<T, S, E, P>
|
||||
where
|
||||
T: System,
|
||||
T::AccountId: Into<T::Address>,
|
||||
S: Encode,
|
||||
E: SignedExtra<T>,
|
||||
P: Pair,
|
||||
P::Signature: Into<S>,
|
||||
T: System + 'static,
|
||||
T::AccountId: Into<T::Address> + 'static,
|
||||
S: Encode + 'static + Send + Sync,
|
||||
E: SignedExtra<T> + 'static,
|
||||
P: Pair + 'static,
|
||||
P::Signature: Into<S> + 'static,
|
||||
<<E as SignedExtra<T>>::Extra as SignedExtension>::AdditionalSigned: Send + Sync,
|
||||
{
|
||||
fn account_id(&self) -> &T::AccountId {
|
||||
&self.account_id
|
||||
@@ -109,14 +131,24 @@ where
|
||||
fn sign(
|
||||
&self,
|
||||
extrinsic: SignedPayload<Encoded, E::Extra>,
|
||||
) -> UncheckedExtrinsic<T::Address, Encoded, S, E::Extra> {
|
||||
) -> Pin<
|
||||
Box<
|
||||
dyn Future<
|
||||
Output = Result<
|
||||
UncheckedExtrinsic<T::Address, Encoded, S, E::Extra>,
|
||||
String,
|
||||
>,
|
||||
> + Send
|
||||
+ Sync,
|
||||
>,
|
||||
> {
|
||||
let signature = extrinsic.using_encoded(|payload| self.signer.sign(payload));
|
||||
let (call, extra, _) = extrinsic.deconstruct();
|
||||
UncheckedExtrinsic::new_signed(
|
||||
Box::pin(futures::future::ok(UncheckedExtrinsic::new_signed(
|
||||
call,
|
||||
self.account_id.clone().into(),
|
||||
signature.into(),
|
||||
extra,
|
||||
)
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user