mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-15 19:41:05 +00:00
Add subxt_signer crate for native & WASM compatible signing (#1016)
* Add and use subxt-signer crate for WASM compatible signing * cargo fmt * dev keypairs already references * WIP fix various breakages * re-jig features to be simpler and various test fixes etc * doc and web fix * fix various bits and pieces * fix a test I broke * dev-deps can't be linked to in docs, hrmph * cargo fmt * another doc link * document the subxt_signer crate more thoroughly * move feature flag for consistency * more docs, no default subxt feature flag on signer, update release instrs * Add missing license header * unwrap_inner => into_inner * extend a test a little to better check derive junctions * note more clearly that the crypto bits come from sp_core::crypto
This commit is contained in:
@@ -16,8 +16,6 @@
|
||||
//! We can use the statically generated interface to build constant queries:
|
||||
//!
|
||||
//! ```rust,no_run
|
||||
//! use sp_keyring::AccountKeyring;
|
||||
//!
|
||||
//! #[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata_full.scale")]
|
||||
//! pub mod polkadot {}
|
||||
//!
|
||||
@@ -27,10 +25,8 @@
|
||||
//! Alternately, we can dynamically construct a constant query:
|
||||
//!
|
||||
//! ```rust,no_run
|
||||
//! use sp_keyring::AccountKeyring;
|
||||
//! use subxt::dynamic::Value;
|
||||
//!
|
||||
//! let account = AccountKeyring::Alice.to_account_id();
|
||||
//! let storage_query = subxt::dynamic::constant("System", "BlockLength");
|
||||
//! ```
|
||||
//!
|
||||
|
||||
@@ -22,8 +22,6 @@
|
||||
//! We can use the statically generated interface to build runtime calls:
|
||||
//!
|
||||
//! ```rust,no_run
|
||||
//! use sp_keyring::AccountKeyring;
|
||||
//!
|
||||
//! #[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata_small.scale")]
|
||||
//! pub mod polkadot {}
|
||||
//!
|
||||
@@ -33,10 +31,8 @@
|
||||
//! Alternately, we can dynamically construct a runtime call:
|
||||
//!
|
||||
//! ```rust,no_run
|
||||
//! use sp_keyring::AccountKeyring;
|
||||
//! use subxt::dynamic::Value;
|
||||
//!
|
||||
//! let account = AccountKeyring::Alice.to_account_id();
|
||||
//! let runtime_call = subxt::dynamic::runtime_api_call(
|
||||
//! "Metadata",
|
||||
//! "metadata_versions",
|
||||
|
||||
@@ -16,12 +16,12 @@
|
||||
//! We can use the statically generated interface to build storage queries:
|
||||
//!
|
||||
//! ```rust,no_run
|
||||
//! use sp_keyring::AccountKeyring;
|
||||
//! use subxt_signer::sr25519::dev;
|
||||
//!
|
||||
//! #[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata_small.scale")]
|
||||
//! pub mod polkadot {}
|
||||
//!
|
||||
//! let account = AccountKeyring::Alice.to_account_id().into();
|
||||
//! let account = dev::alice().public_key().into();
|
||||
//! let storage_query = polkadot::storage().system().account(&account);
|
||||
//! ```
|
||||
//!
|
||||
@@ -29,10 +29,10 @@
|
||||
//! validated until it's submitted:
|
||||
//!
|
||||
//! ```rust,no_run
|
||||
//! use sp_keyring::AccountKeyring;
|
||||
//! use subxt_signer::sr25519::dev;
|
||||
//! use subxt::dynamic::Value;
|
||||
//!
|
||||
//! let account = AccountKeyring::Alice.to_account_id();
|
||||
//! let account = dev::alice().public_key();
|
||||
//! let storage_query = subxt::dynamic::storage("System", "Account", vec![
|
||||
//! Value::from_bytes(account)
|
||||
//! ]);
|
||||
@@ -43,8 +43,6 @@
|
||||
//! will only be available on static constructors when iteration is actually possible):
|
||||
//!
|
||||
//! ```rust,no_run
|
||||
//! use sp_keyring::AccountKeyring;
|
||||
//!
|
||||
//! #[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata_small.scale")]
|
||||
//! pub mod polkadot {}
|
||||
//!
|
||||
|
||||
@@ -60,28 +60,54 @@
|
||||
//! ## Signing it
|
||||
//!
|
||||
//! You'll normally need to sign an extrinsic to prove that it originated from an account that you
|
||||
//! control. To do this, you will typically first create an [`crate::tx::Signer`], which tells Subxt
|
||||
//! who the extrinsic is from, and takes care of signing the relevant details to prove this.
|
||||
//! control. To do this, you will typically first create a [`crate::tx::Signer`] instance, which tells
|
||||
//! Subxt who the extrinsic is from, and takes care of signing the relevant details to prove this.
|
||||
//!
|
||||
//! Subxt provides a [`crate::tx::PairSigner`] which implements this trait (if the
|
||||
//! `substrate-compat` feature is enabled) which accepts any valid [`sp_core::Pair`] and uses that
|
||||
//! to sign transactions:
|
||||
//! There are two main ways to create a compatible signer instance:
|
||||
//! 1. The `subxt_signer` crate provides a WASM compatible implementation of [`crate::tx::Signer`]
|
||||
//! for chains which require sr25519 signatures (requires the `subxt` feature to be enabled).
|
||||
//! 2. Alternately, Subxt can use instances of Substrate's [`sp_core::Pair`] to sign things by wrapping
|
||||
//! them in a [`crate::tx::PairSigner`] (requires the `substrate-compat` feature to be enabled).
|
||||
//!
|
||||
//! Going for 1 leads to fewer dependencies being imported and WASM compatibility out of the box via
|
||||
//! the `web` feature flag. Going for 2 is useful if you're already using the Substrate dependencies or
|
||||
//! need additional signing algorithms that `subxt_signer` doesn't support, and don't care about WASM
|
||||
//! compatibility.
|
||||
//!
|
||||
//! Let's see how to use each of these approaches:
|
||||
//!
|
||||
//! ```rust
|
||||
//! use subxt::config::PolkadotConfig;
|
||||
//! use std::str::FromStr;
|
||||
//!
|
||||
//! //// 1. Use a `subxt_signer` impl:
|
||||
//! use subxt_signer::{ SecretUri, sr25519 };
|
||||
//!
|
||||
//! // Get hold of a `Signer` for a test account:
|
||||
//! let alice = sr25519::dev::alice();
|
||||
//!
|
||||
//! // Or generate a keypair, here from an SURI:
|
||||
//! let uri = SecretUri::from_str("vessel ladder alter error federal sibling chat ability sun glass valve picture/0/1///Password")
|
||||
//! .expect("valid URI");
|
||||
//! let keypair = sr25519::Keypair::from_uri(&uri)
|
||||
//! .expect("valid keypair");
|
||||
//!
|
||||
//! //// 2. Use the corresponding `sp_core::Pair` impl:
|
||||
//! use subxt::tx::PairSigner;
|
||||
//! use sp_core::Pair;
|
||||
//! use subxt::config::PolkadotConfig;
|
||||
//!
|
||||
//! // Get hold of a `Signer` given a test account:
|
||||
//! let pair = sp_keyring::AccountKeyring::Alice.pair();
|
||||
//! let signer = PairSigner::<PolkadotConfig,_>::new(pair);
|
||||
//! // Get hold of a `Signer` for a test account:
|
||||
//! let alice = sp_keyring::AccountKeyring::Alice.pair();
|
||||
//! let alice = PairSigner::<PolkadotConfig,_>::new(alice);
|
||||
//!
|
||||
//! // Or generate an `sr25519` keypair to use:
|
||||
//! let (pair, _, _) = sp_core::sr25519::Pair::generate_with_phrase(Some("password"));
|
||||
//! let signer = PairSigner::<PolkadotConfig,_>::new(pair);
|
||||
//! // Or generate a keypair, here from an SURI:
|
||||
//! let keypair = sp_core::sr25519::Pair::from_string("vessel ladder alter error federal sibling chat ability sun glass valve picture/0/1///Password", None)
|
||||
//! .expect("valid URI");
|
||||
//! let keypair = PairSigner::<PolkadotConfig,_>::new(keypair);
|
||||
//! ```
|
||||
//!
|
||||
//! See the [`sp_core::Pair`] docs for more ways to generate them.
|
||||
//! See the `subxt_signer::sr25519::Keypair` or the [`sp_core::Pair`] docs for more ways to construct
|
||||
//! and work with key pairs.
|
||||
//!
|
||||
//! If this isn't suitable/available, you can either implement [`crate::tx::Signer`] yourself to use
|
||||
//! custom signing logic, or you can use some external signing logic, like so:
|
||||
@@ -118,10 +144,9 @@
|
||||
//! let signature;
|
||||
//! let address;
|
||||
//! # use subxt::tx::Signer;
|
||||
//! # let pair = sp_keyring::AccountKeyring::Alice.pair();
|
||||
//! # let signer = subxt::tx::PairSigner::<PolkadotConfig,_>::new(pair);
|
||||
//! # signature = signer.sign(&signer_payload);
|
||||
//! # address = signer.address();
|
||||
//! # let signer = subxt_signer::sr25519::dev::alice();
|
||||
//! # signature = signer.sign(&signer_payload).into();
|
||||
//! # address = signer.public_key().to_address();
|
||||
//!
|
||||
//! // Now we can build an tx, which one can call `submit` or `submit_and_watch`
|
||||
//! // on to submit to a node and optionally watch the status.
|
||||
|
||||
@@ -16,8 +16,5 @@ pub use online_client::{
|
||||
ClientRuntimeUpdater, OnlineClient, OnlineClientT, RuntimeUpdaterStream, Update, UpgradeError,
|
||||
};
|
||||
|
||||
#[cfg(any(
|
||||
feature = "jsonrpsee-ws",
|
||||
all(feature = "jsonrpsee-web", target_arch = "wasm32")
|
||||
))]
|
||||
#[cfg(feature = "jsonrpsee")]
|
||||
pub use online_client::default_rpc_client;
|
||||
|
||||
@@ -55,10 +55,7 @@ impl<T: Config> std::fmt::Debug for OnlineClient<T> {
|
||||
}
|
||||
|
||||
/// The default RPC client that's used (based on [`jsonrpsee`]).
|
||||
#[cfg(any(
|
||||
feature = "jsonrpsee-ws",
|
||||
all(feature = "jsonrpsee-web", target_arch = "wasm32")
|
||||
))]
|
||||
#[cfg(feature = "jsonrpsee")]
|
||||
pub async fn default_rpc_client<U: AsRef<str>>(url: U) -> Result<impl RpcClientT, Error> {
|
||||
let client = jsonrpsee_helpers::client(url.as_ref())
|
||||
.await
|
||||
@@ -67,10 +64,7 @@ pub async fn default_rpc_client<U: AsRef<str>>(url: U) -> Result<impl RpcClientT
|
||||
}
|
||||
|
||||
// The default constructors assume Jsonrpsee.
|
||||
#[cfg(any(
|
||||
feature = "jsonrpsee-ws",
|
||||
all(feature = "jsonrpsee-web", target_arch = "wasm32")
|
||||
))]
|
||||
#[cfg(feature = "jsonrpsee")]
|
||||
impl<T: Config> OnlineClient<T> {
|
||||
/// Construct a new [`OnlineClient`] using default settings which
|
||||
/// point to a locally running node on `ws://127.0.0.1:9944`.
|
||||
@@ -428,7 +422,7 @@ impl Update {
|
||||
}
|
||||
|
||||
// helpers for a jsonrpsee specific OnlineClient.
|
||||
#[cfg(feature = "jsonrpsee-ws")]
|
||||
#[cfg(all(feature = "jsonrpsee", feature = "native"))]
|
||||
mod jsonrpsee_helpers {
|
||||
pub use jsonrpsee::{
|
||||
client_transport::ws::{InvalidUri, Receiver, Sender, Uri, WsTransportClientBuilder},
|
||||
@@ -458,7 +452,7 @@ mod jsonrpsee_helpers {
|
||||
}
|
||||
|
||||
// helpers for a jsonrpsee specific OnlineClient.
|
||||
#[cfg(all(feature = "jsonrpsee-web", target_arch = "wasm32"))]
|
||||
#[cfg(all(feature = "jsonrpsee", feature = "web"))]
|
||||
mod jsonrpsee_helpers {
|
||||
pub use jsonrpsee::{
|
||||
client_transport::web,
|
||||
|
||||
@@ -19,7 +19,7 @@ use std::sync::Arc;
|
||||
/// A collection of events obtained from a block, bundled with the necessary
|
||||
/// information needed to decode and iterate over them.
|
||||
#[derive(Derivative)]
|
||||
#[derivative(Debug(bound = ""), Clone(bound = ""))]
|
||||
#[derivative(Clone(bound = ""))]
|
||||
pub struct Events<T: Config> {
|
||||
metadata: Metadata,
|
||||
block_hash: T::Hash,
|
||||
@@ -31,6 +31,18 @@ pub struct Events<T: Config> {
|
||||
num_events: u32,
|
||||
}
|
||||
|
||||
// Ignore the Metadata when debug-logging events; it's big and distracting.
|
||||
impl<T: Config> std::fmt::Debug for Events<T> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("Events")
|
||||
.field("block_hash", &self.block_hash)
|
||||
.field("event_bytes", &self.event_bytes)
|
||||
.field("start_idx", &self.start_idx)
|
||||
.field("num_events", &self.num_events)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> Events<T> {
|
||||
pub(crate) fn new(metadata: Metadata, block_hash: T::Hash, event_bytes: Vec<u8>) -> Self {
|
||||
// event_bytes is a SCALE encoded vector of events. So, pluck the
|
||||
|
||||
+15
-7
@@ -33,23 +33,31 @@
|
||||
)]
|
||||
#![allow(clippy::type_complexity)]
|
||||
|
||||
#[cfg(any(
|
||||
all(feature = "web", feature = "native"),
|
||||
not(any(feature = "web", feature = "native"))
|
||||
))]
|
||||
compile_error!("subxt: exactly one of the 'web' and 'native' features should be used.");
|
||||
|
||||
#[cfg(all(feature = "web", feature = "substrate-compat"))]
|
||||
compile_error!("subxt: the 'substrate-compat' feature is not compatible with the 'web' feature.");
|
||||
|
||||
// The guide is here.
|
||||
pub mod book;
|
||||
|
||||
// Suppress an unused dependency warning because tokio is
|
||||
// only used in example code snippets at the time of writing.
|
||||
#[cfg(test)]
|
||||
use tokio as _;
|
||||
mod only_used_in_docs_or_tests {
|
||||
use subxt_signer as _;
|
||||
use tokio as _;
|
||||
}
|
||||
|
||||
// Used to enable the js feature for wasm.
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[cfg(feature = "web")]
|
||||
#[allow(unused_imports)]
|
||||
pub use getrandom as _;
|
||||
|
||||
#[cfg(all(feature = "jsonrpsee-ws", feature = "jsonrpsee-web"))]
|
||||
std::compile_error!(
|
||||
"Both the features `jsonrpsee-ws` and `jsonrpsee-web` are enabled which are mutually exclusive"
|
||||
);
|
||||
|
||||
pub mod blocks;
|
||||
pub mod client;
|
||||
pub mod config;
|
||||
|
||||
@@ -12,7 +12,7 @@ use crate::Config;
|
||||
/// as well as actually signing a SCALE encoded payload.
|
||||
pub trait Signer<T: Config> {
|
||||
/// Return the "from" account ID.
|
||||
fn account_id(&self) -> &T::AccountId;
|
||||
fn account_id(&self) -> T::AccountId;
|
||||
|
||||
/// Return the "from" address.
|
||||
fn address(&self) -> T::Address;
|
||||
@@ -83,8 +83,8 @@ mod pair_signer {
|
||||
Pair: PairT,
|
||||
Pair::Signature: Into<T::Signature>,
|
||||
{
|
||||
fn account_id(&self) -> &T::AccountId {
|
||||
&self.account_id
|
||||
fn account_id(&self) -> T::AccountId {
|
||||
self.account_id.clone()
|
||||
}
|
||||
|
||||
fn address(&self) -> T::Address {
|
||||
|
||||
@@ -211,7 +211,7 @@ where
|
||||
Call: TxPayload,
|
||||
Signer: SignerT<T>,
|
||||
{
|
||||
let account_nonce = self.account_nonce(signer.account_id()).await?;
|
||||
let account_nonce = self.account_nonce(&signer.account_id()).await?;
|
||||
self.create_signed_with_nonce(call, signer, account_nonce, other_params)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user