mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-21 01:41:03 +00:00
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).
This commit is contained in:
+1
-1
@@ -19,7 +19,7 @@ include = ["Cargo.toml", "src/**/*.rs", "README.md", "LICENSE"]
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
futures = "0.3"
|
futures = "0.3.5"
|
||||||
jsonrpsee = { version = "0.1", features = ["ws"] }
|
jsonrpsee = { version = "0.1", features = ["ws"] }
|
||||||
num-traits = { version = "0.2", default-features = false }
|
num-traits = { version = "0.2", default-features = false }
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
|||||||
@@ -82,6 +82,7 @@ pub fn call(s: Structure) -> TokenStream {
|
|||||||
T: #module + #subxt::system::System + Send + Sync + 'static,
|
T: #module + #subxt::system::System + Send + Sync + 'static,
|
||||||
S: #codec::Encode + Send + Sync + 'static,
|
S: #codec::Encode + Send + Sync + 'static,
|
||||||
E: #subxt::SignedExtra<T> + #subxt::sp_runtime::traits::SignedExtension + 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>(
|
fn #call<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
@@ -154,6 +155,7 @@ mod tests {
|
|||||||
T: Balances + substrate_subxt::system::System + Send + Sync + 'static,
|
T: Balances + substrate_subxt::system::System + Send + Sync + 'static,
|
||||||
S: codec::Encode + Send + Sync + 'static,
|
S: codec::Encode + Send + Sync + 'static,
|
||||||
E: substrate_subxt::SignedExtra<T> + substrate_subxt::sp_runtime::traits::SignedExtension + 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>(
|
fn transfer<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
|
|||||||
+4
-17
@@ -16,21 +16,11 @@
|
|||||||
|
|
||||||
//! Implements support for the pallet_balances module.
|
//! Implements support for the pallet_balances module.
|
||||||
|
|
||||||
use crate::frame::system::{
|
use crate::frame::system::{System, SystemEventsDecoder};
|
||||||
System,
|
use codec::{Decode, Encode};
|
||||||
SystemEventsDecoder,
|
|
||||||
};
|
|
||||||
use codec::{
|
|
||||||
Decode,
|
|
||||||
Encode,
|
|
||||||
};
|
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
use frame_support::Parameter;
|
use frame_support::Parameter;
|
||||||
use sp_runtime::traits::{
|
use sp_runtime::traits::{AtLeast32Bit, MaybeSerialize, Member};
|
||||||
AtLeast32Bit,
|
|
||||||
MaybeSerialize,
|
|
||||||
Member,
|
|
||||||
};
|
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
/// The subset of the `pallet_balances::Trait` that a client must implement.
|
/// The subset of the `pallet_balances::Trait` that a client must implement.
|
||||||
@@ -110,10 +100,7 @@ pub struct TransferEvent<T: Balances> {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{
|
use crate::{
|
||||||
system::{
|
system::{AccountStore, AccountStoreExt},
|
||||||
AccountStore,
|
|
||||||
AccountStoreExt,
|
|
||||||
},
|
|
||||||
tests::test_client,
|
tests::test_client,
|
||||||
};
|
};
|
||||||
use sp_keyring::AccountKeyring;
|
use sp_keyring::AccountKeyring;
|
||||||
|
|||||||
+21
-9
@@ -46,7 +46,10 @@ pub use sp_core;
|
|||||||
pub use sp_runtime;
|
pub use sp_runtime;
|
||||||
|
|
||||||
use codec::Encode;
|
use codec::Encode;
|
||||||
use futures::future;
|
use futures::{
|
||||||
|
future,
|
||||||
|
future::TryFutureExt as _,
|
||||||
|
};
|
||||||
use jsonrpsee::client::Subscription;
|
use jsonrpsee::client::Subscription;
|
||||||
use sc_rpc_api::state::ReadProof;
|
use sc_rpc_api::state::ReadProof;
|
||||||
use sp_core::storage::{
|
use sp_core::storage::{
|
||||||
@@ -118,7 +121,7 @@ pub struct ClientBuilder<T: System, S = MultiSignature, E = DefaultExtra<T>> {
|
|||||||
client: Option<jsonrpsee::Client>,
|
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.
|
/// Creates a new ClientBuilder.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
@@ -327,18 +330,21 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a signed extrinsic.
|
/// Creates a signed extrinsic.
|
||||||
pub async fn create_signed<C: Call<T>>(
|
pub async fn create_signed<C: Call<T> + Send + Sync>(
|
||||||
&self,
|
&self,
|
||||||
call: C,
|
call: C,
|
||||||
signer: &(dyn Signer<T, S, E> + Send + Sync),
|
signer: &(dyn Signer<T, S, E> + Send + Sync),
|
||||||
) -> Result<
|
) -> Result<
|
||||||
UncheckedExtrinsic<T::Address, Encoded, S, <E as SignedExtra<T>>::Extra>,
|
UncheckedExtrinsic<T::Address, Encoded, S, <E as SignedExtra<T>>::Extra>,
|
||||||
Error,
|
Error,
|
||||||
> {
|
>
|
||||||
|
where
|
||||||
|
<<E as SignedExtra<T>>::Extra as SignedExtension>::AdditionalSigned: Send + Sync,
|
||||||
|
{
|
||||||
let unsigned = self
|
let unsigned = self
|
||||||
.create_unsigned(call, signer.account_id(), signer.nonce())
|
.create_unsigned(call, signer.account_id(), signer.nonce())
|
||||||
.await?;
|
.await?;
|
||||||
Ok(signer.sign(unsigned))
|
signer.sign(unsigned).map_err(From::from).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an events decoder for a call.
|
/// Returns an events decoder for a call.
|
||||||
@@ -379,21 +385,27 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Submits a transaction to the chain.
|
/// Submits a transaction to the chain.
|
||||||
pub async fn submit<C: Call<T>>(
|
pub async fn submit<C: Call<T> + Send + Sync>(
|
||||||
&self,
|
&self,
|
||||||
call: C,
|
call: C,
|
||||||
signer: &(dyn Signer<T, S, E> + Send + Sync),
|
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?;
|
let extrinsic = self.create_signed(call, signer).await?;
|
||||||
self.submit_extrinsic(extrinsic).await
|
self.submit_extrinsic(extrinsic).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Submits transaction to the chain and watch for events.
|
/// 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,
|
&self,
|
||||||
call: C,
|
call: C,
|
||||||
signer: &(dyn Signer<T, S, E> + Send + Sync),
|
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 extrinsic = self.create_signed(call, signer).await?;
|
||||||
let decoder = self.events_decoder::<C>()?;
|
let decoder = self.events_decoder::<C>()?;
|
||||||
self.submit_and_watch_extrinsic(extrinsic, decoder).await
|
self.submit_and_watch_extrinsic(extrinsic, decoder).await
|
||||||
|
|||||||
+41
-25
@@ -17,27 +17,19 @@
|
|||||||
//! A library to **sub**mit e**xt**rinsics to a
|
//! A library to **sub**mit e**xt**rinsics to a
|
||||||
//! [substrate](https://github.com/paritytech/substrate) node via RPC.
|
//! [substrate](https://github.com/paritytech/substrate) node via RPC.
|
||||||
|
|
||||||
use crate::{
|
use crate::{extra::SignedExtra, frame::system::System, Encoded};
|
||||||
extra::SignedExtra,
|
|
||||||
frame::system::System,
|
|
||||||
Encoded,
|
|
||||||
};
|
|
||||||
use codec::Encode;
|
use codec::Encode;
|
||||||
use sp_core::Pair;
|
use sp_core::Pair;
|
||||||
use sp_runtime::{
|
use sp_runtime::{
|
||||||
generic::{
|
generic::{SignedPayload, UncheckedExtrinsic},
|
||||||
SignedPayload,
|
traits::{IdentifyAccount, Verify, SignedExtension},
|
||||||
UncheckedExtrinsic,
|
|
||||||
},
|
|
||||||
traits::{
|
|
||||||
IdentifyAccount,
|
|
||||||
Verify,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
use std::marker::PhantomData;
|
use std::{future::Future, marker::PhantomData, pin::Pin};
|
||||||
|
|
||||||
/// Extrinsic signer.
|
/// 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.
|
/// Returns the account id.
|
||||||
fn account_id(&self) -> &T::AccountId;
|
fn account_id(&self) -> &T::AccountId;
|
||||||
|
|
||||||
@@ -45,10 +37,23 @@ pub trait Signer<T: System, S: Encode, E: SignedExtra<T>> {
|
|||||||
fn nonce(&self) -> Option<T::Index>;
|
fn nonce(&self) -> Option<T::Index>;
|
||||||
|
|
||||||
/// Takes an unsigned extrinsic and returns a signed extrinsic.
|
/// 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(
|
fn sign(
|
||||||
&self,
|
&self,
|
||||||
extrinsic: SignedPayload<Encoded, E::Extra>,
|
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.
|
/// Extrinsic signer using a private key.
|
||||||
@@ -91,12 +96,13 @@ where
|
|||||||
|
|
||||||
impl<T, S, E, P> Signer<T, S, E> for PairSigner<T, S, E, P>
|
impl<T, S, E, P> Signer<T, S, E> for PairSigner<T, S, E, P>
|
||||||
where
|
where
|
||||||
T: System,
|
T: System + 'static,
|
||||||
T::AccountId: Into<T::Address>,
|
T::AccountId: Into<T::Address> + 'static,
|
||||||
S: Encode,
|
S: Encode + 'static + Send + Sync,
|
||||||
E: SignedExtra<T>,
|
E: SignedExtra<T> + 'static,
|
||||||
P: Pair,
|
P: Pair + 'static,
|
||||||
P::Signature: Into<S>,
|
P::Signature: Into<S> + 'static,
|
||||||
|
<<E as SignedExtra<T>>::Extra as SignedExtension>::AdditionalSigned: Send + Sync,
|
||||||
{
|
{
|
||||||
fn account_id(&self) -> &T::AccountId {
|
fn account_id(&self) -> &T::AccountId {
|
||||||
&self.account_id
|
&self.account_id
|
||||||
@@ -109,14 +115,24 @@ where
|
|||||||
fn sign(
|
fn sign(
|
||||||
&self,
|
&self,
|
||||||
extrinsic: SignedPayload<Encoded, E::Extra>,
|
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 signature = extrinsic.using_encoded(|payload| self.signer.sign(payload));
|
||||||
let (call, extra, _) = extrinsic.deconstruct();
|
let (call, extra, _) = extrinsic.deconstruct();
|
||||||
UncheckedExtrinsic::new_signed(
|
Box::pin(futures::future::ready(Ok(UncheckedExtrinsic::new_signed(
|
||||||
call,
|
call,
|
||||||
self.account_id.clone().into(),
|
self.account_id.clone().into(),
|
||||||
signature.into(),
|
signature.into(),
|
||||||
extra,
|
extra,
|
||||||
)
|
))))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user