Make sp_core and sp_runtime dependencies optional, and bump to latest (#760)

* begin porting over traits; remove Config use of Hash

* port over the Header bits that we need

* sp_core_hashing where possible, move Verify to PairSigner, remove unused errors

* tidy up Config things and move related bits into one place

* fix codegen

* copy Era over

* move AccountId, Address, Signer to Signer trait and a pass over fixing examples

* impl MultiAddress, MultiSignature, AccountId32 and add back to Config (for decoding later)

* Copy over StorageKey, StorageData, StorageChangeSet

* subxt core compiling with no sp_core or sp_runtime

* Get examples compiling

* pass over fixing tests

* cargo fmt

* clippy tweaks and update polkadot.rs

* fix codegen docs

* port over special DigestItem encoding/decoding

* clippy and doc fixes

* cargo fmt and example fix

* more cargo fmt-ing...

* substrate-extra to substrate-compat

* cargo.toml comments

* simplify PairSigner trait bounds

* move RPC types to a separate file

* fix docs

* Add some tests for things and other PR feedback

* bump to latest sp deps

* avoid needing substrate-compat feature in a test
This commit is contained in:
James Wilson
2023-01-10 12:02:41 +00:00
committed by GitHub
parent ea5daa444f
commit b316301d61
47 changed files with 2658 additions and 1736 deletions
+14 -14
View File
@@ -169,23 +169,23 @@ impl RuntimeGenerator {
),
(
"sp_core::crypto::AccountId32",
parse_quote!(#crate_path::ext::sp_core::crypto::AccountId32),
),
(
"primitive_types::H160",
parse_quote!(#crate_path::ext::sp_core::H160),
),
(
"primitive_types::H256",
parse_quote!(#crate_path::ext::sp_core::H256),
),
(
"primitive_types::H512",
parse_quote!(#crate_path::ext::sp_core::H512),
parse_quote!(#crate_path::utils::AccountId32),
),
(
"sp_runtime::multiaddress::MultiAddress",
parse_quote!(#crate_path::ext::sp_runtime::MultiAddress),
parse_quote!(#crate_path::utils::MultiAddress),
),
(
"primitive_types::H160",
parse_quote!(#crate_path::utils::H160),
),
(
"primitive_types::H256",
parse_quote!(#crate_path::utils::H256),
),
(
"primitive_types::H512",
parse_quote!(#crate_path::utils::H512),
),
(
"frame_support::traits::misc::WrapperKeepOpaque",
+3 -1
View File
@@ -14,8 +14,10 @@ description = "Subxt example usage"
[dev-dependencies]
subxt = { path = "../subxt" }
tokio = { version = "1.8", features = ["rt-multi-thread", "macros", "time"] }
sp-keyring = "7.0.0"
sp-keyring = "12.0.0"
futures = "0.3.13"
codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive", "full", "bit-vec"] }
hex = "0.4.3"
tracing-subscriber = "0.3.11"
sp-core = { version = "11.0.0", default-features = false }
sp-runtime = { version = "12.0.0" }
@@ -12,14 +12,16 @@
use sp_keyring::AccountKeyring;
use subxt::{
tx::{
Era,
PairSigner,
PlainTip,
PolkadotExtrinsicParamsBuilder as Params,
config::{
polkadot::{
Era,
PlainTip,
PolkadotExtrinsicParamsBuilder as Params,
},
PolkadotConfig,
},
tx::PairSigner,
OnlineClient,
PolkadotConfig,
};
#[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata.scale")]
@@ -24,7 +24,7 @@ pub mod polkadot {}
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let api = OnlineClient::<PolkadotConfig>::new().await?;
let addr = AccountKeyring::Bob.to_account_id();
let addr = AccountKeyring::Bob.to_account_id().into();
// Construct storage addresses to access:
let staking_bonded = polkadot::storage().staking().bonded(&addr);
+4 -6
View File
@@ -8,13 +8,11 @@
use sp_keyring::AccountKeyring;
use subxt::{
config::{
substrate::SubstrateExtrinsicParams,
Config,
SubstrateConfig,
},
tx::{
PairSigner,
SubstrateExtrinsicParams,
},
tx::PairSigner,
OnlineClient,
};
@@ -34,10 +32,10 @@ impl Config for MyConfig {
type Index = u64;
type BlockNumber = <SubstrateConfig as Config>::BlockNumber;
type Hash = <SubstrateConfig as Config>::Hash;
type Hashing = <SubstrateConfig as Config>::Hashing;
type Hasher = <SubstrateConfig as Config>::Hasher;
type Header = <SubstrateConfig as Config>::Header;
type AccountId = <SubstrateConfig as Config>::AccountId;
type Address = <SubstrateConfig as Config>::Address;
type Header = <SubstrateConfig as Config>::Header;
type Signature = <SubstrateConfig as Config>::Signature;
// ExtrinsicParams makes use of the index type, so we need to adjust it
// too to align with our modified index type, above:
+5 -7
View File
@@ -10,15 +10,13 @@
//! polkadot --dev --tmp
//! ```
use sp_core::{
sr25519,
Pair,
};
use sp_keyring::AccountKeyring;
use subxt::{
ext::{
sp_core::{
sr25519,
Pair,
},
sp_runtime::AccountId32,
},
utils::AccountId32,
OnlineClient,
PolkadotConfig,
};
+1 -1
View File
@@ -54,7 +54,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
// threshold
1,
// other signatories
vec![signer_account_id],
vec![signer_account_id.into()],
// maybe timepoint
None,
// call
@@ -11,11 +11,7 @@
//! ```
use subxt::{
ext::sp_runtime::{
generic::Header,
traits::BlakeTwo256,
},
rpc::Subscription,
config::Header,
OnlineClient,
PolkadotConfig,
};
@@ -28,8 +24,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
OnlineClient::<PolkadotConfig>::from_url("wss://rpc.polkadot.io:443").await?;
// For non-finalised blocks use `.subscribe_blocks()`
let mut blocks: Subscription<Header<u32, BlakeTwo256>> =
api.rpc().subscribe_finalized_block_headers().await?;
let mut blocks = api.rpc().subscribe_finalized_block_headers().await?;
while let Some(Ok(block)) = blocks.next().await {
println!(
+1 -1
View File
@@ -16,7 +16,7 @@ description = "Command line utilities for checking metadata compatibility betwee
codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive", "full"] }
frame-metadata = "15.0.0"
scale-info = "2.0.0"
sp-core = "7.0.0"
sp-core-hashing = "6.0.0"
[dev-dependencies]
bitvec = { version = "1.0.0", default-features = false, features = ["alloc"] }
+2 -2
View File
@@ -32,8 +32,8 @@ enum TypeBeingHashed {
}
/// Hashing function utilized internally.
fn hash(bytes: &[u8]) -> [u8; 32] {
sp_core::hashing::twox_256(bytes)
fn hash(data: &[u8]) -> [u8; 32] {
sp_core_hashing::twox_256(data)
}
/// XOR two hashes together. If we have two pseudorandom hashes, then this will
+26 -6
View File
@@ -14,7 +14,14 @@ description = "Submit extrinsics (transactions) to a substrate node via RPC"
keywords = ["parity", "substrate", "blockchain"]
[features]
default = ["jsonrpsee-ws"]
default = ["jsonrpsee-ws", "substrate-compat"]
# Activate this feature to pull in extra Substrate dependencies which make it
# possible to provide a proper extrinsic Signer implementation (PairSigner).
substrate-compat = [
"sp-core",
"sp-runtime"
]
# Activate this to expose functionality only used for integration testing.
# The exposed functionality is subject to breaking changes at any point,
@@ -32,7 +39,7 @@ scale-info = "2.0.0"
scale-value = "0.6.0"
scale-bits = "0.3"
scale-decode = "0.4.0"
futures = { version = "0.3.13", default-features = false }
futures = { version = "0.3.13", default-features = false, features = ["std"] }
hex = "0.4.3"
jsonrpsee = { version = "0.16", optional = true, features = ["jsonrpsee-types"] }
serde = { version = "1.0.124", features = ["derive"] }
@@ -40,15 +47,24 @@ serde_json = { version = "1.0.64", features = ["raw_value"] }
thiserror = "1.0.24"
tracing = "0.1.34"
parking_lot = "0.12.0"
frame-metadata = "15.0.0"
derivative = "2.2.0"
subxt-macro = { version = "0.25.0", path = "../macro" }
subxt-metadata = { version = "0.25.0", path = "../metadata" }
sp-core = { version = "7.0.0", default-features = false }
sp-runtime = "7.0.0"
# Provides some deserialization, types like U256/H256 and hashing impls like twox/blake256:
impl-serde = { version = "0.4.0" }
primitive-types = { version = "0.12.0", default-features = false, features = ["codec", "scale-info", "serde"] }
sp-core-hashing = "6.0.0"
frame-metadata = "15.0.0"
derivative = "2.2.0"
# For ss58 encoding AccountId32 to serialize them properly:
base58 = { version = "0.2.0" }
blake2 = { version = "0.10.4", default-features = false }
# These are only included is "substrate-compat" is enabled.
sp-core = { version = "11.0.0", default-features = false, optional = true }
sp-runtime = { version = "12.0.0", optional = true }
[target.wasm32-unknown-unknown.dependencies]
getrandom = { version = "0.2", features = ["js"] }
@@ -58,3 +74,7 @@ bitvec = "1"
codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive", "full", "bit-vec"] }
scale-info = { version = "2.0.0", features = ["bit-vec"] }
tokio = { version = "1.8", features = ["macros", "time", "rt-multi-thread"] }
sp-core = { version = "11.0.0", default-features = false }
sp-runtime = { version = "12.0.0" }
sp-keyring = "12.0.0"
sp-version = "10.0.0"
+8 -8
View File
@@ -7,20 +7,20 @@ use crate::{
OfflineClientT,
OnlineClientT,
},
config::{
Config,
Hasher,
Header,
},
error::{
BlockError,
Error,
},
events,
rpc::ChainBlockResponse,
Config,
rpc::types::ChainBlockResponse,
};
use derivative::Derivative;
use futures::lock::Mutex as AsyncMutex;
use sp_runtime::traits::{
Hash,
Header,
};
use std::sync::Arc;
/// A representation of a block.
@@ -56,7 +56,7 @@ where
/// Return the block number.
pub fn number(&self) -> T::BlockNumber {
*self.header().number()
self.header().number()
}
/// Return the entire block header.
@@ -170,7 +170,7 @@ where
pub async fn events(&self) -> Result<ExtrinsicEvents<T>, Error> {
let events =
get_events(&self.client, self.block_hash, &self.cached_events).await?;
let ext_hash = T::Hashing::hash_of(&self.bytes);
let ext_hash = T::Hasher::hash_of(&self.bytes);
Ok(ExtrinsicEvents::new(ext_hash, self.index, events))
}
}
+6 -4
View File
@@ -5,12 +5,15 @@
use super::Block;
use crate::{
client::OnlineClientT,
config::{
Config,
Header,
},
error::{
BlockError,
Error,
},
utils::PhantomDataSendSync,
Config,
};
use derivative::Derivative;
use futures::{
@@ -19,7 +22,6 @@ use futures::{
Stream,
StreamExt,
};
use sp_runtime::traits::Header;
use std::{
future::Future,
pin::Pin,
@@ -137,7 +139,7 @@ where
.rpc()
.header(Some(last_finalized_block_hash))
.await?
.map(|h| (*h.number()).into());
.map(|h| h.number().into());
let sub = client.rpc().subscribe_finalized_block_headers().await?;
@@ -203,7 +205,7 @@ where
};
// We want all previous details up to, but not including this current block num.
let end_block_num = (*header.number()).into();
let end_block_num = header.number().into();
// This is one after the last block we returned details for last time.
let start_block_num = last_block_num.map(|n| n + 1).unwrap_or(end_block_num);
+1 -1
View File
@@ -6,7 +6,7 @@ use crate::{
blocks::BlocksClient,
constants::ConstantsClient,
events::EventsClient,
rpc::RuntimeVersion,
rpc::types::RuntimeVersion,
storage::StorageClient,
tx::TxClient,
Config,
+4 -2
View File
@@ -12,10 +12,12 @@ use crate::{
error::Error,
events::EventsClient,
rpc::{
types::{
RuntimeVersion,
Subscription,
},
Rpc,
RpcClientT,
RuntimeVersion,
Subscription,
},
storage::StorageClient,
tx::TxClient,
@@ -2,19 +2,26 @@
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
//! This module contains a trait which controls the parameters that must
//! be provided in order to successfully construct an extrinsic. A basic
//! implementation of the trait is provided ([`BaseExtrinsicParams`]) which is
//! used by the provided Substrate and Polkadot configuration.
use crate::{
utils::Encoded,
Config,
};
use codec::{
Compact,
Decode,
Encode,
};
use core::fmt::Debug;
use derivative::Derivative;
// We require Era as a param below, so make it available from here.
pub use sp_runtime::generic::Era;
use serde::{
Deserialize,
Serialize,
};
/// This trait allows you to configure the "signed extra" and
/// "additional" parameters that are signed and used in transactions.
@@ -48,22 +55,6 @@ pub trait ExtrinsicParams<Index, Hash>: Debug + 'static {
fn encode_additional_to(&self, v: &mut Vec<u8>);
}
/// A struct representing the signed extra and additional parameters required
/// to construct a transaction for the default substrate node.
pub type SubstrateExtrinsicParams<T> = BaseExtrinsicParams<T, AssetTip>;
/// A builder which leads to [`SubstrateExtrinsicParams`] being constructed.
/// This is what you provide to methods like `sign_and_submit()`.
pub type SubstrateExtrinsicParamsBuilder<T> = BaseExtrinsicParamsBuilder<T, AssetTip>;
/// A struct representing the signed extra and additional parameters required
/// to construct a transaction for a polkadot node.
pub type PolkadotExtrinsicParams<T> = BaseExtrinsicParams<T, PlainTip>;
/// A builder which leads to [`PolkadotExtrinsicParams`] being constructed.
/// This is what you provide to methods like `sign_and_submit()`.
pub type PolkadotExtrinsicParamsBuilder<T> = BaseExtrinsicParamsBuilder<T, PlainTip>;
/// An implementation of [`ExtrinsicParams`] that is suitable for constructing
/// extrinsics that can be sent to a node with the same signed extra and additional
/// parameters as a Polkadot/Substrate node. The way that tip payments are specified
@@ -90,8 +81,9 @@ pub struct BaseExtrinsicParams<T: Config, Tip: Debug> {
/// construct a [`BaseExtrinsicParams`] value. This implements [`Default`], which allows
/// [`BaseExtrinsicParams`] to be used with convenience methods like `sign_and_submit_default()`.
///
/// Prefer to use [`SubstrateExtrinsicParamsBuilder`] for a version of this tailored towards
/// Substrate, or [`PolkadotExtrinsicParamsBuilder`] for a version tailored to Polkadot.
/// Prefer to use [`super::substrate::SubstrateExtrinsicParamsBuilder`] for a version of this
/// tailored towards Substrate, or [`super::polkadot::PolkadotExtrinsicParamsBuilder`] for a
/// version tailored to Polkadot.
#[derive(Derivative)]
#[derivative(
Debug(bound = "Tip: Debug"),
@@ -185,53 +177,94 @@ impl<T: Config, Tip: Debug + Encode + 'static> ExtrinsicParams<T::Index, T::Hash
}
}
/// A tip payment.
#[derive(Copy, Clone, Debug, Default, Encode)]
pub struct PlainTip {
#[codec(compact)]
tip: u128,
// Dev note: This and related bits taken from `sp_runtime::generic::Era`
/// An era to describe the longevity of a transaction.
#[derive(PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)]
pub enum Era {
/// The transaction is valid forever. The genesis hash must be present in the signed content.
Immortal,
/// Period and phase are encoded:
/// - The period of validity from the block hash found in the signing material.
/// - The phase in the period that this transaction's lifetime begins (and, importantly,
/// implies which block hash is included in the signature material). If the `period` is
/// greater than 1 << 12, then it will be a factor of the times greater than 1<<12 that
/// `period` is.
///
/// When used on `FRAME`-based runtimes, `period` cannot exceed `BlockHashCount` parameter
/// of `system` module.
Mortal(Period, Phase),
}
impl PlainTip {
/// Create a new tip of the amount provided.
pub fn new(amount: u128) -> Self {
PlainTip { tip: amount }
/// Era period
pub type Period = u64;
/// Era phase
pub type Phase = u64;
// E.g. with period == 4:
// 0 10 20 30 40
// 0123456789012345678901234567890123456789012
// |...|
// authored -/ \- expiry
// phase = 1
// n = Q(current - phase, period) + phase
impl Era {
/// Create a new era based on a period (which should be a power of two between 4 and 65536
/// inclusive) and a block number on which it should start (or, for long periods, be shortly
/// after the start).
///
/// If using `Era` in the context of `FRAME` runtime, make sure that `period`
/// does not exceed `BlockHashCount` parameter passed to `system` module, since that
/// prunes old blocks and renders transactions immediately invalid.
pub fn mortal(period: u64, current: u64) -> Self {
let period = period
.checked_next_power_of_two()
.unwrap_or(1 << 16)
.clamp(4, 1 << 16);
let phase = current % period;
let quantize_factor = (period >> 12).max(1);
let quantized_phase = phase / quantize_factor * quantize_factor;
Self::Mortal(period, quantized_phase)
}
/// Create an "immortal" transaction.
pub fn immortal() -> Self {
Self::Immortal
}
}
impl From<u128> for PlainTip {
fn from(n: u128) -> Self {
PlainTip::new(n)
}
}
/// A tip payment made in the form of a specific asset.
#[derive(Copy, Clone, Debug, Default, Encode)]
pub struct AssetTip {
#[codec(compact)]
tip: u128,
asset: Option<u32>,
}
impl AssetTip {
/// Create a new tip of the amount provided.
pub fn new(amount: u128) -> Self {
AssetTip {
tip: amount,
asset: None,
// Both copied from `sp_runtime::generic::Era`; this is the wire interface and so
// it's really the most important bit here.
impl Encode for Era {
fn encode_to<T: codec::Output + ?Sized>(&self, output: &mut T) {
match self {
Self::Immortal => output.push_byte(0),
Self::Mortal(period, phase) => {
let quantize_factor = (*period >> 12).max(1);
let encoded = (period.trailing_zeros() - 1).clamp(1, 15) as u16
| ((phase / quantize_factor) << 4) as u16;
encoded.encode_to(output);
}
}
}
/// Designate the tip as being of a particular asset class.
/// If this is not set, then the native currency is used.
pub fn of_asset(mut self, asset: u32) -> Self {
self.asset = Some(asset);
self
}
}
impl From<u128> for AssetTip {
fn from(n: u128) -> Self {
AssetTip::new(n)
}
impl Decode for Era {
fn decode<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> {
let first = input.read_byte()?;
if first == 0 {
Ok(Self::Immortal)
} else {
let encoded = first as u64 + ((input.read_byte()? as u64) << 8);
let period = 2 << (encoded % (1 << 4));
let quantize_factor = (period >> 12).max(1);
let phase = (encoded >> 4) * quantize_factor;
if period >= 4 && phase < period {
Ok(Self::Mortal(period, phase))
} else {
Err("Invalid period and phase".into())
}
}
}
}
+66 -53
View File
@@ -8,20 +8,21 @@
//! default Substrate node implementation, and [`PolkadotConfig`] for a
//! Polkadot node.
pub mod extrinsic_params;
pub mod polkadot;
pub mod substrate;
use codec::{
Codec,
Encode,
EncodeLike,
};
use core::fmt::Debug;
use sp_runtime::traits::{
AtLeast32Bit,
Hash,
Header,
MaybeSerializeDeserialize,
Member,
Verify,
};
use serde::Serialize;
pub use extrinsic_params::ExtrinsicParams;
pub use polkadot::PolkadotConfig;
pub use substrate::SubstrateConfig;
/// Runtime types.
// Note: the 'static bound isn't strictly required, but currently deriving TypeInfo
@@ -34,7 +35,6 @@ pub trait Config: 'static {
+ Member
+ serde::de::DeserializeOwned
+ Default
+ AtLeast32Bit
+ Copy
+ scale_info::TypeInfo
+ Into<u64>;
@@ -44,14 +44,15 @@ pub trait Config: 'static {
+ Member
+ Default
+ Copy
+ core::hash::Hash
+ core::str::FromStr
+ std::hash::Hash
+ std::str::FromStr
+ Into<u64>;
/// The output of the `Hashing` function.
type Hash: Parameter
+ Member
+ MaybeSerializeDeserialize
+ serde::Serialize
+ serde::de::DeserializeOwned
+ Ord
+ Default
+ Copy
@@ -60,85 +61,97 @@ pub trait Config: 'static {
+ AsMut<[u8]>
+ scale_info::TypeInfo;
/// The account ID type.
type AccountId: Clone + Serialize;
/// The address type.
type Address: Encode + From<Self::AccountId>;
/// The signature type.
type Signature: Encode;
/// The hashing system (algorithm) being used in the runtime (e.g. Blake2).
type Hashing: Hash<Output = Self::Hash>;
/// The user account identifier type for the runtime.
type AccountId: Parameter + Member + serde::Serialize;
/// The address type. This instead of `<frame_system::Trait::Lookup as StaticLookup>::Source`.
type Address: Codec + Clone + PartialEq;
type Hasher: Hasher<Output = Self::Hash>;
/// The block header.
type Header: Parameter
+ Header<Number = Self::BlockNumber, Hash = Self::Hash>
+ Header<Number = Self::BlockNumber, Hasher = Self::Hasher>
+ Member
+ serde::de::DeserializeOwned;
/// Signature type.
type Signature: Verify + Encode + Send + Sync + 'static;
/// This type defines the extrinsic extra and additional parameters.
type ExtrinsicParams: crate::tx::ExtrinsicParams<Self::Index, Self::Hash>;
type ExtrinsicParams: extrinsic_params::ExtrinsicParams<Self::Index, Self::Hash>;
}
/// Parameter trait copied from `substrate::frame_support`
/// Parameter trait copied from `substrate::frame_support`.
pub trait Parameter: Codec + EncodeLike + Clone + Eq + Debug {}
impl<T> Parameter for T where T: Codec + EncodeLike + Clone + Eq + Debug {}
/// Default set of commonly used types by Substrate runtimes.
// Note: We only use this at the type level, so it should be impossible to
// create an instance of it.
pub enum SubstrateConfig {}
/// A type that can be used in runtime structures. Copied from `sp_runtime::traits`.
pub trait Member: Send + Sync + Sized + Debug + Eq + PartialEq + Clone + 'static {}
impl<T: Send + Sync + Sized + Debug + Eq + PartialEq + Clone + 'static> Member for T {}
impl Config for SubstrateConfig {
type Index = u32;
type BlockNumber = u32;
type Hash = sp_core::H256;
type Hashing = sp_runtime::traits::BlakeTwo256;
type AccountId = sp_runtime::AccountId32;
type Address = sp_runtime::MultiAddress<Self::AccountId, u32>;
type Header =
sp_runtime::generic::Header<Self::BlockNumber, sp_runtime::traits::BlakeTwo256>;
type Signature = sp_runtime::MultiSignature;
type ExtrinsicParams = crate::tx::SubstrateExtrinsicParams<Self>;
/// This represents the hasher used by a node to hash things like block headers
/// and extrinsics.
pub trait Hasher {
/// The type given back from the hash operation
type Output;
/// Hash some bytes to the given output type.
fn hash(s: &[u8]) -> Self::Output;
/// Hash some SCALE encodable type to the given output type.
fn hash_of<S: Encode>(s: &S) -> Self::Output {
let out = s.encode();
Self::hash(&out)
}
}
/// Default set of commonly used types by Polkadot nodes.
pub type PolkadotConfig = WithExtrinsicParams<
SubstrateConfig,
crate::tx::PolkadotExtrinsicParams<SubstrateConfig>,
>;
/// This represents the block header type used by a node.
pub trait Header: Sized + Encode {
/// The block number type for this header.
type Number;
/// The hasher used to hash this header.
type Hasher: Hasher;
/// Return the block number of this header.
fn number(&self) -> Self::Number;
/// Hash this header.
fn hash(&self) -> <Self::Hasher as Hasher>::Output {
Self::Hasher::hash_of(self)
}
}
/// Take a type implementing [`Config`] (eg [`SubstrateConfig`]), and some type which describes the
/// additional and extra parameters to pass to an extrinsic (see [`crate::tx::ExtrinsicParams`]),
/// and returns a type implementing [`Config`] with those new `ExtrinsicParams`.
/// additional and extra parameters to pass to an extrinsic (see [`ExtrinsicParams`]),
/// and returns a type implementing [`Config`] with those new [`ExtrinsicParams`].
///
/// # Example
///
/// ```
/// use subxt::config::{ SubstrateConfig, WithExtrinsicParams };
/// use subxt::tx::PolkadotExtrinsicParams;
/// use subxt::config::{ SubstrateConfig, WithExtrinsicParams, polkadot::PolkadotExtrinsicParams };
///
/// // This is how PolkadotConfig is implemented:
/// type PolkadotConfig = WithExtrinsicParams<SubstrateConfig, PolkadotExtrinsicParams<SubstrateConfig>>;
/// ```
pub struct WithExtrinsicParams<
T: Config,
E: crate::tx::ExtrinsicParams<T::Index, T::Hash>,
E: extrinsic_params::ExtrinsicParams<T::Index, T::Hash>,
> {
_marker: std::marker::PhantomData<(T, E)>,
}
impl<T: Config, E: crate::tx::ExtrinsicParams<T::Index, T::Hash>> Config
impl<T: Config, E: extrinsic_params::ExtrinsicParams<T::Index, T::Hash>> Config
for WithExtrinsicParams<T, E>
{
type Index = T::Index;
type BlockNumber = T::BlockNumber;
type Hash = T::Hash;
type Hashing = T::Hashing;
type AccountId = T::AccountId;
type Address = T::Address;
type Header = T::Header;
type Signature = T::Signature;
type Hasher = T::Hasher;
type Header = T::Header;
type ExtrinsicParams = E;
}
+49
View File
@@ -0,0 +1,49 @@
// Copyright 2019-2022 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
//! Polkadot specific configuration
use codec::Encode;
use super::extrinsic_params::{
BaseExtrinsicParams,
BaseExtrinsicParamsBuilder,
};
/// Default set of commonly used types by Polkadot nodes.
pub type PolkadotConfig = super::WithExtrinsicParams<
super::SubstrateConfig,
PolkadotExtrinsicParams<super::SubstrateConfig>,
>;
/// A struct representing the signed extra and additional parameters required
/// to construct a transaction for a polkadot node.
pub type PolkadotExtrinsicParams<T> = BaseExtrinsicParams<T, PlainTip>;
/// A builder which leads to [`PolkadotExtrinsicParams`] being constructed.
/// This is what you provide to methods like `sign_and_submit()`.
pub type PolkadotExtrinsicParamsBuilder<T> = BaseExtrinsicParamsBuilder<T, PlainTip>;
// Because Era is one of the args to our extrinsic params.
pub use super::extrinsic_params::Era;
/// A tip payment.
#[derive(Copy, Clone, Debug, Default, Encode)]
pub struct PlainTip {
#[codec(compact)]
tip: u128,
}
impl PlainTip {
/// Create a new tip of the amount provided.
pub fn new(amount: u128) -> Self {
PlainTip { tip: amount }
}
}
impl From<u128> for PlainTip {
fn from(n: u128) -> Self {
PlainTip::new(n)
}
}
+294
View File
@@ -0,0 +1,294 @@
// Copyright 2019-2022 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
//! Substrate specific configuration
use super::{
extrinsic_params::{
BaseExtrinsicParams,
BaseExtrinsicParamsBuilder,
},
Config,
Hasher,
Header,
};
use codec::{
Decode,
Encode,
};
use serde::{
Deserialize,
Serialize,
};
pub use crate::utils::{
account_id::AccountId32,
multi_address::MultiAddress,
multi_signature::MultiSignature,
};
pub use primitive_types::{
H256,
U256,
};
/// Default set of commonly used types by Substrate runtimes.
// Note: We only use this at the type level, so it should be impossible to
// create an instance of it.
pub enum SubstrateConfig {}
impl Config for SubstrateConfig {
type Index = u32;
type BlockNumber = u32;
type Hash = H256;
type AccountId = AccountId32;
type Address = MultiAddress<Self::AccountId, u32>;
type Signature = MultiSignature;
type Hasher = BlakeTwo256;
type Header = SubstrateHeader<Self::BlockNumber, BlakeTwo256>;
type ExtrinsicParams = SubstrateExtrinsicParams<Self>;
}
/// A struct representing the signed extra and additional parameters required
/// to construct a transaction for the default substrate node.
pub type SubstrateExtrinsicParams<T> = BaseExtrinsicParams<T, AssetTip>;
/// A builder which leads to [`SubstrateExtrinsicParams`] being constructed.
/// This is what you provide to methods like `sign_and_submit()`.
pub type SubstrateExtrinsicParamsBuilder<T> = BaseExtrinsicParamsBuilder<T, AssetTip>;
// Because Era is one of the args to our extrinsic params.
pub use super::extrinsic_params::Era;
/// A tip payment made in the form of a specific asset.
#[derive(Copy, Clone, Debug, Default, Encode)]
pub struct AssetTip {
#[codec(compact)]
tip: u128,
asset: Option<u32>,
}
impl AssetTip {
/// Create a new tip of the amount provided.
pub fn new(amount: u128) -> Self {
AssetTip {
tip: amount,
asset: None,
}
}
/// Designate the tip as being of a particular asset class.
/// If this is not set, then the native currency is used.
pub fn of_asset(mut self, asset: u32) -> Self {
self.asset = Some(asset);
self
}
}
impl From<u128> for AssetTip {
fn from(n: u128) -> Self {
AssetTip::new(n)
}
}
/// A type that can hash values using the blaks2_256 algorithm.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode)]
pub struct BlakeTwo256;
impl Hasher for BlakeTwo256 {
type Output = H256;
fn hash(s: &[u8]) -> Self::Output {
sp_core_hashing::blake2_256(s).into()
}
}
/// A generic Substrate header type, adapted from `sp_runtime::generic::Header`.
/// The block number and hasher can be configured to adapt this for other nodes.
#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SubstrateHeader<N: Copy + Into<U256> + TryFrom<U256>, H: Hasher> {
/// The parent hash.
pub parent_hash: H::Output,
/// The block number.
#[serde(
serialize_with = "serialize_number",
deserialize_with = "deserialize_number"
)]
#[codec(compact)]
pub number: N,
/// The state trie merkle root
pub state_root: H::Output,
/// The merkle root of the extrinsics.
pub extrinsics_root: H::Output,
/// A chain-specific digest of data useful for light clients or referencing auxiliary data.
pub digest: Digest,
}
impl<N: Copy + Into<U256> + TryFrom<U256> + Encode, H: Hasher + Encode> Header
for SubstrateHeader<N, H>
where
N: Copy + Into<U256> + TryFrom<U256> + Encode,
H: Hasher + Encode,
SubstrateHeader<N, H>: Encode,
{
type Number = N;
type Hasher = H;
fn number(&self) -> Self::Number {
self.number
}
}
/// Generic header digest. From `sp_runtime::generic::digest`.
#[derive(
Encode, Decode, Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Default,
)]
pub struct Digest {
/// A list of digest items.
pub logs: Vec<DigestItem>,
}
/// Digest item that is able to encode/decode 'system' digest items and
/// provide opaque access to other items. From `sp_runtime::generic::digest`.
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum DigestItem {
/// A pre-runtime digest.
///
/// These are messages from the consensus engine to the runtime, although
/// the consensus engine can (and should) read them itself to avoid
/// code and state duplication. It is erroneous for a runtime to produce
/// these, but this is not (yet) checked.
///
/// NOTE: the runtime is not allowed to panic or fail in an `on_initialize`
/// call if an expected `PreRuntime` digest is not present. It is the
/// responsibility of a external block verifier to check this. Runtime API calls
/// will initialize the block without pre-runtime digests, so initialization
/// cannot fail when they are missing.
PreRuntime(ConsensusEngineId, Vec<u8>),
/// A message from the runtime to the consensus engine. This should *never*
/// be generated by the native code of any consensus engine, but this is not
/// checked (yet).
Consensus(ConsensusEngineId, Vec<u8>),
/// Put a Seal on it. This is only used by native code, and is never seen
/// by runtimes.
Seal(ConsensusEngineId, Vec<u8>),
/// Some other thing. Unsupported and experimental.
Other(Vec<u8>),
/// An indication for the light clients that the runtime execution
/// environment is updated.
///
/// Currently this is triggered when:
/// 1. Runtime code blob is changed or
/// 2. `heap_pages` value is changed.
RuntimeEnvironmentUpdated,
}
// From sp_runtime::generic, DigestItem enum indexes are encoded using this:
#[repr(u32)]
#[derive(Encode, Decode)]
enum DigestItemType {
Other = 0u32,
Consensus = 4u32,
Seal = 5u32,
PreRuntime = 6u32,
RuntimeEnvironmentUpdated = 8u32,
}
impl Encode for DigestItem {
fn encode(&self) -> Vec<u8> {
let mut v = Vec::new();
match self {
Self::Consensus(val, data) => {
DigestItemType::Consensus.encode_to(&mut v);
(val, data).encode_to(&mut v);
}
Self::Seal(val, sig) => {
DigestItemType::Seal.encode_to(&mut v);
(val, sig).encode_to(&mut v);
}
Self::PreRuntime(val, data) => {
DigestItemType::PreRuntime.encode_to(&mut v);
(val, data).encode_to(&mut v);
}
Self::Other(val) => {
DigestItemType::Other.encode_to(&mut v);
val.encode_to(&mut v);
}
Self::RuntimeEnvironmentUpdated => {
DigestItemType::RuntimeEnvironmentUpdated.encode_to(&mut v);
}
}
v
}
}
impl Decode for DigestItem {
fn decode<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> {
let item_type: DigestItemType = Decode::decode(input)?;
match item_type {
DigestItemType::PreRuntime => {
let vals: (ConsensusEngineId, Vec<u8>) = Decode::decode(input)?;
Ok(Self::PreRuntime(vals.0, vals.1))
}
DigestItemType::Consensus => {
let vals: (ConsensusEngineId, Vec<u8>) = Decode::decode(input)?;
Ok(Self::Consensus(vals.0, vals.1))
}
DigestItemType::Seal => {
let vals: (ConsensusEngineId, Vec<u8>) = Decode::decode(input)?;
Ok(Self::Seal(vals.0, vals.1))
}
DigestItemType::Other => Ok(Self::Other(Decode::decode(input)?)),
DigestItemType::RuntimeEnvironmentUpdated => {
Ok(Self::RuntimeEnvironmentUpdated)
}
}
}
}
/// Consensus engine unique ID. From `sp_runtime::ConsensusEngineId`.
pub type ConsensusEngineId = [u8; 4];
impl serde::Serialize for DigestItem {
fn serialize<S>(&self, seq: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.using_encoded(|bytes| impl_serde::serialize::serialize(bytes, seq))
}
}
impl<'a> serde::Deserialize<'a> for DigestItem {
fn deserialize<D>(de: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'a>,
{
let r = impl_serde::serialize::deserialize(de)?;
Decode::decode(&mut &r[..])
.map_err(|e| serde::de::Error::custom(format!("Decode error: {}", e)))
}
}
fn serialize_number<S, T: Copy + Into<U256> + TryFrom<U256>>(
val: &T,
s: S,
) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let u256: U256 = (*val).into();
serde::Serialize::serialize(&u256, s)
}
fn deserialize_number<'a, D, T: Copy + Into<U256> + TryFrom<U256>>(
d: D,
) -> Result<T, D::Error>
where
D: serde::Deserializer<'a>,
{
let u256: U256 = serde::Deserialize::deserialize(d)?;
TryFrom::try_from(u256).map_err(|_| serde::de::Error::custom("Try from failed"))
}
-20
View File
@@ -19,8 +19,6 @@ pub use scale_value::scale::{
DecodeError,
EncodeError,
};
pub use sp_core::crypto::SecretStringError;
pub use sp_runtime::transaction_validity::TransactionValidityError;
/// The underlying error enum, generic over the type held by the `Runtime`
/// variant. Prefer to use the [`Error<E>`] and [`Error`] aliases over
@@ -39,12 +37,6 @@ pub enum Error {
/// Serde serialization error
#[error("Serde json error: {0}")]
Serialization(#[from] serde_json::error::Error),
/// Secret string error.
#[error("Secret String Error")]
SecretString(SecretStringError),
/// Extrinsic validity error
#[error("Transaction Validity Error: {0:?}")]
Invalid(TransactionValidityError),
/// Invalid metadata error
#[error("Invalid Metadata: {0}")]
InvalidMetadata(#[from] InvalidMetadataError),
@@ -74,18 +66,6 @@ pub enum Error {
Other(String),
}
impl From<SecretStringError> for Error {
fn from(error: SecretStringError) -> Self {
Error::SecretString(error)
}
}
impl From<TransactionValidityError> for Error {
fn from(error: TransactionValidityError) -> Self {
Error::Invalid(error)
}
}
impl From<&str> for Error {
fn from(error: &str) -> Self {
Error::Other(error.into())
+3 -6
View File
@@ -6,13 +6,10 @@ use crate::{
client::OnlineClientT,
error::Error,
events::Events,
rpc::types::StorageKey,
Config,
};
use derivative::Derivative;
use sp_core::{
storage::StorageKey,
twox_128,
};
use std::future::Future;
/// A client for working with events.
@@ -74,8 +71,8 @@ where
// The storage key needed to access events.
fn system_events_key() -> StorageKey {
let mut storage_key = twox_128(b"System").to_vec();
storage_key.extend(twox_128(b"Events").to_vec());
let mut storage_key = sp_core_hashing::twox_128(b"System").to_vec();
storage_key.extend(sp_core_hashing::twox_128(b"Events").to_vec());
StorageKey(storage_key)
}
-2
View File
@@ -181,6 +181,4 @@ pub mod ext {
pub use frame_metadata;
pub use scale_bits;
pub use scale_value;
pub use sp_core;
pub use sp_runtime;
}
+4 -1
View File
@@ -59,7 +59,10 @@ mod rpc;
mod rpc_client;
mod rpc_client_t;
// Expose the `Rpc` struct and any associated types.
// Expose our RPC types here.
pub mod types;
// Expose the `Rpc` struct.
pub use rpc::*;
pub use rpc_client_t::{
+33 -368
View File
@@ -41,6 +41,7 @@
use super::{
rpc_params,
types,
RpcClient,
RpcClientT,
Subscription,
@@ -56,293 +57,8 @@ use codec::{
Encode,
};
use frame_metadata::RuntimeMetadataPrefixed;
use serde::{
Deserialize,
Serialize,
};
use sp_core::{
storage::{
StorageChangeSet,
StorageData,
StorageKey,
},
Bytes,
U256,
};
use sp_runtime::ApplyExtrinsicResult;
use std::{
collections::HashMap,
sync::Arc,
};
/// A number type that can be serialized both as a number or a string that encodes a number in a
/// string.
///
/// We allow two representations of the block number as input. Either we deserialize to the type
/// that is specified in the block type or we attempt to parse given hex value.
///
/// The primary motivation for having this type is to avoid overflows when using big integers in
/// JavaScript (which we consider as an important RPC API consumer).
#[derive(Copy, Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
#[serde(untagged)]
pub enum NumberOrHex {
/// The number represented directly.
Number(u64),
/// Hex representation of the number.
Hex(U256),
}
/// The response from `chain_getBlock`
#[derive(Debug, Deserialize)]
#[serde(bound = "T: Config")]
pub struct ChainBlockResponse<T: Config> {
/// The block itself.
pub block: ChainBlock<T>,
/// Block justification.
pub justifications: Option<sp_runtime::Justifications>,
}
/// Block details in the [`ChainBlockResponse`].
#[derive(Debug, Deserialize)]
pub struct ChainBlock<T: Config> {
/// The block header.
pub header: T::Header,
/// The accompanying extrinsics.
pub extrinsics: Vec<ChainBlockExtrinsic>,
}
/// Bytes representing an extrinsic in a [`ChainBlock`].
#[derive(Debug)]
pub struct ChainBlockExtrinsic(pub Vec<u8>);
impl<'a> ::serde::Deserialize<'a> for ChainBlockExtrinsic {
fn deserialize<D>(de: D) -> Result<Self, D::Error>
where
D: ::serde::Deserializer<'a>,
{
let r = sp_core::bytes::deserialize(de)?;
let bytes = Decode::decode(&mut &r[..])
.map_err(|e| ::serde::de::Error::custom(format!("Decode error: {}", e)))?;
Ok(ChainBlockExtrinsic(bytes))
}
}
/// Wrapper for NumberOrHex to allow custom From impls
#[derive(Serialize)]
pub struct BlockNumber(NumberOrHex);
impl From<NumberOrHex> for BlockNumber {
fn from(x: NumberOrHex) -> Self {
BlockNumber(x)
}
}
impl Default for NumberOrHex {
fn default() -> Self {
Self::Number(Default::default())
}
}
impl NumberOrHex {
/// Converts this number into an U256.
pub fn into_u256(self) -> U256 {
match self {
NumberOrHex::Number(n) => n.into(),
NumberOrHex::Hex(h) => h,
}
}
}
impl From<u32> for NumberOrHex {
fn from(n: u32) -> Self {
NumberOrHex::Number(n.into())
}
}
impl From<u64> for NumberOrHex {
fn from(n: u64) -> Self {
NumberOrHex::Number(n)
}
}
impl From<u128> for NumberOrHex {
fn from(n: u128) -> Self {
NumberOrHex::Hex(n.into())
}
}
impl From<U256> for NumberOrHex {
fn from(n: U256) -> Self {
NumberOrHex::Hex(n)
}
}
/// An error type that signals an out-of-range conversion attempt.
#[derive(Debug, thiserror::Error)]
#[error("Out-of-range conversion attempt")]
pub struct TryFromIntError;
impl TryFrom<NumberOrHex> for u32 {
type Error = TryFromIntError;
fn try_from(num_or_hex: NumberOrHex) -> Result<u32, Self::Error> {
num_or_hex
.into_u256()
.try_into()
.map_err(|_| TryFromIntError)
}
}
impl TryFrom<NumberOrHex> for u64 {
type Error = TryFromIntError;
fn try_from(num_or_hex: NumberOrHex) -> Result<u64, Self::Error> {
num_or_hex
.into_u256()
.try_into()
.map_err(|_| TryFromIntError)
}
}
impl TryFrom<NumberOrHex> for u128 {
type Error = TryFromIntError;
fn try_from(num_or_hex: NumberOrHex) -> Result<u128, Self::Error> {
num_or_hex
.into_u256()
.try_into()
.map_err(|_| TryFromIntError)
}
}
impl From<NumberOrHex> for U256 {
fn from(num_or_hex: NumberOrHex) -> U256 {
num_or_hex.into_u256()
}
}
// All unsigned ints can be converted into a BlockNumber:
macro_rules! into_block_number {
($($t: ty)+) => {
$(
impl From<$t> for BlockNumber {
fn from(x: $t) -> Self {
NumberOrHex::Number(x.into()).into()
}
}
)+
}
}
into_block_number!(u8 u16 u32 u64);
/// Arbitrary properties defined in the chain spec as a JSON object.
pub type SystemProperties = serde_json::Map<String, serde_json::Value>;
/// Possible transaction status events.
///
/// # Note
///
/// This is copied from `sp-transaction-pool` to avoid a dependency on that crate. Therefore it
/// must be kept compatible with that type from the target substrate version.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum SubstrateTxStatus<Hash, BlockHash> {
/// Transaction is part of the future queue.
Future,
/// Transaction is part of the ready queue.
Ready,
/// The transaction has been broadcast to the given peers.
Broadcast(Vec<String>),
/// Transaction has been included in block with given hash.
InBlock(BlockHash),
/// The block this transaction was included in has been retracted.
Retracted(BlockHash),
/// Maximum number of finality watchers has been reached,
/// old watchers are being removed.
FinalityTimeout(BlockHash),
/// Transaction has been finalized by a finality-gadget, e.g GRANDPA
Finalized(BlockHash),
/// Transaction has been replaced in the pool, by another transaction
/// that provides the same tags. (e.g. same (sender, nonce)).
Usurped(Hash),
/// Transaction has been dropped from the pool because of the limit.
Dropped,
/// Transaction is no longer valid in the current state.
Invalid,
}
/// This contains the runtime version information necessary to make transactions, as obtained from
/// the RPC call `state_getRuntimeVersion`,
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RuntimeVersion {
/// Version of the runtime specification. A full-node will not attempt to use its native
/// runtime in substitute for the on-chain Wasm runtime unless all of `spec_name`,
/// `spec_version` and `authoring_version` are the same between Wasm and native.
pub spec_version: u32,
/// All existing dispatches are fully compatible when this number doesn't change. If this
/// number changes, then `spec_version` must change, also.
///
/// This number must change when an existing dispatchable (module ID, dispatch ID) is changed,
/// either through an alteration in its user-level semantics, a parameter
/// added/removed/changed, a dispatchable being removed, a module being removed, or a
/// dispatchable/module changing its index.
///
/// It need *not* change when a new module is added or when a dispatchable is added.
pub transaction_version: u32,
/// The other fields present may vary and aren't necessary for `subxt`; they are preserved in
/// this map.
#[serde(flatten)]
pub other: HashMap<String, serde_json::Value>,
}
/// ReadProof struct returned by the RPC
///
/// # Note
///
/// This is copied from `sc-rpc-api` to avoid a dependency on that crate. Therefore it
/// must be kept compatible with that type from the target substrate version.
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ReadProof<Hash> {
/// Block hash used to generate the proof
pub at: Hash,
/// A proof used to prove that storage entries are included in the storage trie
pub proof: Vec<Bytes>,
}
/// Statistics of a block returned by the `dev_getBlockStats` RPC.
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BlockStats {
/// The length in bytes of the storage proof produced by executing the block.
pub witness_len: u64,
/// The length in bytes of the storage proof after compaction.
pub witness_compact_len: u64,
/// Length of the block in bytes.
///
/// This information can also be acquired by downloading the whole block. This merely
/// saves some complexity on the client side.
pub block_len: u64,
/// Number of extrinsics in the block.
///
/// This information can also be acquired by downloading the whole block. This merely
/// saves some complexity on the client side.
pub num_extrinsics: u64,
}
/// Health struct returned by the RPC
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Health {
/// Number of connected peers
pub peers: usize,
/// Is the node syncing
pub is_syncing: bool,
/// Should this node have any peers
///
/// Might be false for local chains or when running without discovery.
pub should_have_peers: bool,
}
use serde::Serialize;
use std::sync::Arc;
/// Client for substrate rpc interfaces
pub struct Rpc<T: Config> {
@@ -382,7 +98,7 @@ impl<T: Config> Rpc<T> {
&self,
key: &[u8],
hash: Option<T::Hash>,
) -> Result<Option<StorageData>, Error> {
) -> Result<Option<types::StorageData>, Error> {
let params = rpc_params![to_hex(key), hash];
let data = self.client.request("state_getStorage", params).await?;
Ok(data)
@@ -397,7 +113,7 @@ impl<T: Config> Rpc<T> {
count: u32,
start_key: Option<&[u8]>,
hash: Option<T::Hash>,
) -> Result<Vec<StorageKey>, Error> {
) -> Result<Vec<types::StorageKey>, Error> {
let start_key = start_key.map(to_hex);
let params = rpc_params![to_hex(key), count, start_key, hash];
let data = self.client.request("state_getKeysPaged", params).await?;
@@ -410,7 +126,7 @@ impl<T: Config> Rpc<T> {
keys: impl IntoIterator<Item = &[u8]>,
from: T::Hash,
to: Option<T::Hash>,
) -> Result<Vec<StorageChangeSet<T::Hash>>, Error> {
) -> Result<Vec<types::StorageChangeSet<T::Hash>>, Error> {
let keys: Vec<String> = keys.into_iter().map(to_hex).collect();
let params = rpc_params![keys, from, to];
self.client
@@ -424,7 +140,7 @@ impl<T: Config> Rpc<T> {
&self,
keys: impl IntoIterator<Item = &[u8]>,
at: Option<T::Hash>,
) -> Result<Vec<StorageChangeSet<T::Hash>>, Error> {
) -> Result<Vec<types::StorageChangeSet<T::Hash>>, Error> {
let keys: Vec<String> = keys.into_iter().map(to_hex).collect();
let params = rpc_params![keys, at];
self.client
@@ -444,7 +160,7 @@ impl<T: Config> Rpc<T> {
/// Fetch the metadata
pub async fn metadata(&self, at: Option<T::Hash>) -> Result<Metadata, Error> {
let bytes: Bytes = self
let bytes: types::Bytes = self
.client
.request("state_getMetadata", rpc_params![at])
.await?;
@@ -454,14 +170,14 @@ impl<T: Config> Rpc<T> {
}
/// Fetch system properties
pub async fn system_properties(&self) -> Result<SystemProperties, Error> {
pub async fn system_properties(&self) -> Result<types::SystemProperties, Error> {
self.client
.request("system_properties", rpc_params![])
.await
}
/// Fetch system health
pub async fn system_health(&self) -> Result<Health, Error> {
pub async fn system_health(&self) -> Result<types::Health, Error> {
self.client.request("system_health", rpc_params![]).await
}
@@ -481,9 +197,9 @@ impl<T: Config> Rpc<T> {
}
/// Fetch the current nonce for the given account ID.
pub async fn system_account_next_index(
pub async fn system_account_next_index<AccountId: Serialize>(
&self,
account: &T::AccountId,
account: &AccountId,
) -> Result<T::Index, Error> {
self.client
.request("system_accountNextIndex", rpc_params![account])
@@ -503,7 +219,7 @@ impl<T: Config> Rpc<T> {
/// Get a block hash, returns hash of latest block by default
pub async fn block_hash(
&self,
block_number: Option<BlockNumber>,
block_number: Option<types::BlockNumber>,
) -> Result<Option<T::Hash>, Error> {
let params = rpc_params![block_number];
let block_hash = self.client.request("chain_getBlockHash", params).await?;
@@ -523,7 +239,7 @@ impl<T: Config> Rpc<T> {
pub async fn block(
&self,
hash: Option<T::Hash>,
) -> Result<Option<ChainBlockResponse<T>>, Error> {
) -> Result<Option<types::ChainBlockResponse<T>>, Error> {
let params = rpc_params![hash];
let block = self.client.request("chain_getBlock", params).await?;
Ok(block)
@@ -537,7 +253,7 @@ impl<T: Config> Rpc<T> {
pub async fn block_stats(
&self,
block_hash: T::Hash,
) -> Result<Option<BlockStats>, Error> {
) -> Result<Option<types::BlockStats>, Error> {
let params = rpc_params![block_hash];
let stats = self.client.request("dev_getBlockStats", params).await?;
Ok(stats)
@@ -548,7 +264,7 @@ impl<T: Config> Rpc<T> {
&self,
keys: impl IntoIterator<Item = &[u8]>,
hash: Option<T::Hash>,
) -> Result<ReadProof<T::Hash>, Error> {
) -> Result<types::ReadProof<T::Hash>, Error> {
let keys: Vec<String> = keys.into_iter().map(to_hex).collect();
let params = rpc_params![keys, hash];
let proof = self.client.request("state_getReadProof", params).await?;
@@ -559,7 +275,7 @@ impl<T: Config> Rpc<T> {
pub async fn runtime_version(
&self,
at: Option<T::Hash>,
) -> Result<RuntimeVersion, Error> {
) -> Result<types::RuntimeVersion, Error> {
let params = rpc_params![at];
let version = self
.client
@@ -629,7 +345,7 @@ impl<T: Config> Rpc<T> {
/// Subscribe to runtime version updates that produce changes in the metadata.
pub async fn subscribe_runtime_version(
&self,
) -> Result<Subscription<RuntimeVersion>, Error> {
) -> Result<Subscription<types::RuntimeVersion>, Error> {
let subscription = self
.client
.subscribe(
@@ -646,7 +362,7 @@ impl<T: Config> Rpc<T> {
&self,
extrinsic: X,
) -> Result<T::Hash, Error> {
let bytes: Bytes = extrinsic.encode().into();
let bytes: types::Bytes = extrinsic.encode().into();
let params = rpc_params![bytes];
let xt_hash = self
.client
@@ -659,8 +375,8 @@ impl<T: Config> Rpc<T> {
pub async fn watch_extrinsic<X: Encode>(
&self,
extrinsic: X,
) -> Result<Subscription<SubstrateTxStatus<T::Hash, T::Hash>>, Error> {
let bytes: Bytes = extrinsic.encode().into();
) -> Result<Subscription<types::SubstrateTxStatus<T::Hash, T::Hash>>, Error> {
let bytes: types::Bytes = extrinsic.encode().into();
let params = rpc_params![bytes];
let subscription = self
.client
@@ -678,7 +394,7 @@ impl<T: Config> Rpc<T> {
&self,
key_type: String,
suri: String,
public: Bytes,
public: types::Bytes,
) -> Result<(), Error> {
let params = rpc_params![key_type, suri, public];
self.client.request("author_insertKey", params).await?;
@@ -686,7 +402,7 @@ impl<T: Config> Rpc<T> {
}
/// Generate new session keys and returns the corresponding public keys.
pub async fn rotate_keys(&self) -> Result<Bytes, Error> {
pub async fn rotate_keys(&self) -> Result<types::Bytes, Error> {
self.client
.request("author_rotateKeys", rpc_params![])
.await
@@ -697,7 +413,10 @@ impl<T: Config> Rpc<T> {
/// `session_keys` is the SCALE encoded session keys object from the runtime.
///
/// Returns `true` iff all private keys could be found.
pub async fn has_session_keys(&self, session_keys: Bytes) -> Result<bool, Error> {
pub async fn has_session_keys(
&self,
session_keys: types::Bytes,
) -> Result<bool, Error> {
let params = rpc_params![session_keys];
self.client.request("author_hasSessionKeys", params).await
}
@@ -707,7 +426,7 @@ impl<T: Config> Rpc<T> {
/// Returns `true` if a private key could be found.
pub async fn has_key(
&self,
public_key: Bytes,
public_key: types::Bytes,
key_type: String,
) -> Result<bool, Error> {
let params = rpc_params![public_key, key_type];
@@ -716,73 +435,19 @@ impl<T: Config> Rpc<T> {
/// Submits the extrinsic to the dry_run RPC, to test if it would succeed.
///
/// Returns `Ok` with an [`ApplyExtrinsicResult`], which is the result of applying of an extrinsic.
/// Returns a [`types::DryRunResult`], which is the result of performing the dry run.
pub async fn dry_run(
&self,
encoded_signed: &[u8],
at: Option<T::Hash>,
) -> Result<ApplyExtrinsicResult, Error> {
) -> Result<types::DryRunResult, Error> {
let params = rpc_params![to_hex(encoded_signed), at];
let result_bytes: Bytes = self.client.request("system_dryRun", params).await?;
let data: ApplyExtrinsicResult =
codec::Decode::decode(&mut result_bytes.0.as_slice())?;
Ok(data)
let result_bytes: types::Bytes =
self.client.request("system_dryRun", params).await?;
Ok(types::decode_dry_run_result(&mut &*result_bytes.0)?)
}
}
fn to_hex(bytes: impl AsRef<[u8]>) -> String {
format!("0x{}", hex::encode(bytes.as_ref()))
}
#[cfg(test)]
mod test {
use super::*;
/// A util function to assert the result of serialization and deserialization is the same.
pub(crate) fn assert_deser<T>(s: &str, expected: T)
where
T: std::fmt::Debug
+ serde::ser::Serialize
+ serde::de::DeserializeOwned
+ PartialEq,
{
assert_eq!(serde_json::from_str::<T>(s).unwrap(), expected);
assert_eq!(serde_json::to_string(&expected).unwrap(), s);
}
#[test]
fn test_deser_runtime_version() {
let val: RuntimeVersion = serde_json::from_str(
r#"{
"specVersion": 123,
"transactionVersion": 456,
"foo": true,
"wibble": [1,2,3]
}"#,
)
.expect("deserializing failed");
let mut m = std::collections::HashMap::new();
m.insert("foo".to_owned(), serde_json::json!(true));
m.insert("wibble".to_owned(), serde_json::json!([1, 2, 3]));
assert_eq!(
val,
RuntimeVersion {
spec_version: 123,
transaction_version: 456,
other: m
}
);
}
#[test]
fn should_serialize_and_deserialize() {
assert_deser(r#""0x1234""#, NumberOrHex::Hex(0x1234.into()));
assert_deser(r#""0x0""#, NumberOrHex::Hex(0.into()));
assert_deser(r#"5"#, NumberOrHex::Number(5));
assert_deser(r#"10000"#, NumberOrHex::Number(10000));
assert_deser(r#"0"#, NumberOrHex::Number(0));
assert_deser(r#"1000000000000"#, NumberOrHex::Number(1000000000000));
}
}
+558
View File
@@ -0,0 +1,558 @@
// Copyright 2019-2022 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
//! Types sent to/from the Substrate RPC interface.
use crate::Config;
use codec::{
Decode,
Encode,
};
use primitive_types::U256;
use serde::{
Deserialize,
Serialize,
};
use std::collections::HashMap;
// Subscription types are returned from some calls, so expose it with the rest of the returned types.
pub use super::rpc_client::Subscription;
/// Signal what the result of doing a dry run of an extrinsic is.
pub type DryRunResult = Result<(), DryRunError>;
/// An error dry running an extrinsic.
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum DryRunError {
/// The extrinsic will not be included in the block
TransactionValidityError,
/// The extrinsic will be included in the block, but the call failed to dispatch.
DispatchError,
}
/// dryRun returns an ApplyExtrinsicResult, which is basically a
/// `Result<Result<(), DispatchError>, TransactionValidityError>`. We want to convert this to
/// a [`DryRunResult`].
///
/// - if `Ok(inner)`, the transaction will be included in the block
/// - if `Ok(Ok(()))`, the transaction will be included and the call will be dispatched
/// successfully
/// - if `Ok(Err(e))`, the transaction will be included but there is some error dispatching
/// the call to the module.
///
/// The errors get a bit involved and have been known to change over time. At the moment
/// then, we will keep things simple here and just decode the Result portion (ie the initial bytes)
/// and ignore the rest.
pub(crate) fn decode_dry_run_result<I: codec::Input>(
input: &mut I,
) -> Result<DryRunResult, codec::Error> {
let res = match <Result<Result<(), ()>, ()>>::decode(input)? {
Ok(Ok(())) => Ok(()),
Ok(Err(())) => Err(DryRunError::DispatchError),
Err(()) => Err(DryRunError::TransactionValidityError),
};
Ok(res)
}
/// A number type that can be serialized both as a number or a string that encodes a number in a
/// string.
///
/// We allow two representations of the block number as input. Either we deserialize to the type
/// that is specified in the block type or we attempt to parse given hex value.
///
/// The primary motivation for having this type is to avoid overflows when using big integers in
/// JavaScript (which we consider as an important RPC API consumer).
#[derive(Copy, Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
#[serde(untagged)]
pub enum NumberOrHex {
/// The number represented directly.
Number(u64),
/// Hex representation of the number.
Hex(U256),
}
/// Hex-serialized shim for `Vec<u8>`.
#[derive(PartialEq, Eq, Clone, Serialize, Deserialize, Hash, PartialOrd, Ord, Debug)]
pub struct Bytes(#[serde(with = "impl_serde::serialize")] pub Vec<u8>);
impl std::ops::Deref for Bytes {
type Target = [u8];
fn deref(&self) -> &[u8] {
&self.0[..]
}
}
impl From<Vec<u8>> for Bytes {
fn from(s: Vec<u8>) -> Self {
Bytes(s)
}
}
/// The response from `chain_getBlock`
#[derive(Debug, Deserialize)]
#[serde(bound = "T: Config")]
pub struct ChainBlockResponse<T: Config> {
/// The block itself.
pub block: ChainBlock<T>,
/// Block justification.
pub justifications: Option<Vec<Justification>>,
}
/// Block details in the [`ChainBlockResponse`].
#[derive(Debug, Deserialize)]
pub struct ChainBlock<T: Config> {
/// The block header.
pub header: T::Header,
/// The accompanying extrinsics.
pub extrinsics: Vec<ChainBlockExtrinsic>,
}
/// An abstraction over justification for a block's validity under a consensus algorithm.
pub type Justification = (ConsensusEngineId, EncodedJustification);
/// Consensus engine unique ID.
pub type ConsensusEngineId = [u8; 4];
/// The encoded justification specific to a consensus engine.
pub type EncodedJustification = Vec<u8>;
/// Bytes representing an extrinsic in a [`ChainBlock`].
#[derive(Debug)]
pub struct ChainBlockExtrinsic(pub Vec<u8>);
impl<'a> ::serde::Deserialize<'a> for ChainBlockExtrinsic {
fn deserialize<D>(de: D) -> Result<Self, D::Error>
where
D: ::serde::Deserializer<'a>,
{
let r = impl_serde::serialize::deserialize(de)?;
let bytes = Decode::decode(&mut &r[..])
.map_err(|e| ::serde::de::Error::custom(format!("Decode error: {}", e)))?;
Ok(ChainBlockExtrinsic(bytes))
}
}
/// Wrapper for NumberOrHex to allow custom From impls
#[derive(Serialize)]
pub struct BlockNumber(NumberOrHex);
impl From<NumberOrHex> for BlockNumber {
fn from(x: NumberOrHex) -> Self {
BlockNumber(x)
}
}
impl Default for NumberOrHex {
fn default() -> Self {
Self::Number(Default::default())
}
}
impl NumberOrHex {
/// Converts this number into an U256.
pub fn into_u256(self) -> U256 {
match self {
NumberOrHex::Number(n) => n.into(),
NumberOrHex::Hex(h) => h,
}
}
}
impl From<u32> for NumberOrHex {
fn from(n: u32) -> Self {
NumberOrHex::Number(n.into())
}
}
impl From<u64> for NumberOrHex {
fn from(n: u64) -> Self {
NumberOrHex::Number(n)
}
}
impl From<u128> for NumberOrHex {
fn from(n: u128) -> Self {
NumberOrHex::Hex(n.into())
}
}
impl From<U256> for NumberOrHex {
fn from(n: U256) -> Self {
NumberOrHex::Hex(n)
}
}
/// An error type that signals an out-of-range conversion attempt.
#[derive(Debug, thiserror::Error)]
#[error("Out-of-range conversion attempt")]
pub struct TryFromIntError;
impl TryFrom<NumberOrHex> for u32 {
type Error = TryFromIntError;
fn try_from(num_or_hex: NumberOrHex) -> Result<u32, Self::Error> {
num_or_hex
.into_u256()
.try_into()
.map_err(|_| TryFromIntError)
}
}
impl TryFrom<NumberOrHex> for u64 {
type Error = TryFromIntError;
fn try_from(num_or_hex: NumberOrHex) -> Result<u64, Self::Error> {
num_or_hex
.into_u256()
.try_into()
.map_err(|_| TryFromIntError)
}
}
impl TryFrom<NumberOrHex> for u128 {
type Error = TryFromIntError;
fn try_from(num_or_hex: NumberOrHex) -> Result<u128, Self::Error> {
num_or_hex
.into_u256()
.try_into()
.map_err(|_| TryFromIntError)
}
}
impl From<NumberOrHex> for U256 {
fn from(num_or_hex: NumberOrHex) -> U256 {
num_or_hex.into_u256()
}
}
// All unsigned ints can be converted into a BlockNumber:
macro_rules! into_block_number {
($($t: ty)+) => {
$(
impl From<$t> for BlockNumber {
fn from(x: $t) -> Self {
NumberOrHex::Number(x.into()).into()
}
}
)+
}
}
into_block_number!(u8 u16 u32 u64);
/// Arbitrary properties defined in the chain spec as a JSON object.
pub type SystemProperties = serde_json::Map<String, serde_json::Value>;
/// Possible transaction status events.
///
/// # Note
///
/// This is copied from `sp-transaction-pool` to avoid a dependency on that crate. Therefore it
/// must be kept compatible with that type from the target substrate version.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum SubstrateTxStatus<Hash, BlockHash> {
/// Transaction is part of the future queue.
Future,
/// Transaction is part of the ready queue.
Ready,
/// The transaction has been broadcast to the given peers.
Broadcast(Vec<String>),
/// Transaction has been included in block with given hash.
InBlock(BlockHash),
/// The block this transaction was included in has been retracted.
Retracted(BlockHash),
/// Maximum number of finality watchers has been reached,
/// old watchers are being removed.
FinalityTimeout(BlockHash),
/// Transaction has been finalized by a finality-gadget, e.g GRANDPA
Finalized(BlockHash),
/// Transaction has been replaced in the pool, by another transaction
/// that provides the same tags. (e.g. same (sender, nonce)).
Usurped(Hash),
/// Transaction has been dropped from the pool because of the limit.
Dropped,
/// Transaction is no longer valid in the current state.
Invalid,
}
/// This contains the runtime version information necessary to make transactions, as obtained from
/// the RPC call `state_getRuntimeVersion`,
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RuntimeVersion {
/// Version of the runtime specification. A full-node will not attempt to use its native
/// runtime in substitute for the on-chain Wasm runtime unless all of `spec_name`,
/// `spec_version` and `authoring_version` are the same between Wasm and native.
pub spec_version: u32,
/// All existing dispatches are fully compatible when this number doesn't change. If this
/// number changes, then `spec_version` must change, also.
///
/// This number must change when an existing dispatchable (module ID, dispatch ID) is changed,
/// either through an alteration in its user-level semantics, a parameter
/// added/removed/changed, a dispatchable being removed, a module being removed, or a
/// dispatchable/module changing its index.
///
/// It need *not* change when a new module is added or when a dispatchable is added.
pub transaction_version: u32,
/// The other fields present may vary and aren't necessary for `subxt`; they are preserved in
/// this map.
#[serde(flatten)]
pub other: HashMap<String, serde_json::Value>,
}
/// ReadProof struct returned by the RPC
///
/// # Note
///
/// This is copied from `sc-rpc-api` to avoid a dependency on that crate. Therefore it
/// must be kept compatible with that type from the target substrate version.
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ReadProof<Hash> {
/// Block hash used to generate the proof
pub at: Hash,
/// A proof used to prove that storage entries are included in the storage trie
pub proof: Vec<Bytes>,
}
/// Statistics of a block returned by the `dev_getBlockStats` RPC.
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BlockStats {
/// The length in bytes of the storage proof produced by executing the block.
pub witness_len: u64,
/// The length in bytes of the storage proof after compaction.
pub witness_compact_len: u64,
/// Length of the block in bytes.
///
/// This information can also be acquired by downloading the whole block. This merely
/// saves some complexity on the client side.
pub block_len: u64,
/// Number of extrinsics in the block.
///
/// This information can also be acquired by downloading the whole block. This merely
/// saves some complexity on the client side.
pub num_extrinsics: u64,
}
/// Storage key.
#[derive(
Serialize,
Deserialize,
Hash,
PartialOrd,
Ord,
PartialEq,
Eq,
Clone,
Encode,
Decode,
Debug,
)]
pub struct StorageKey(#[serde(with = "impl_serde::serialize")] pub Vec<u8>);
impl AsRef<[u8]> for StorageKey {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
/// Storage data.
#[derive(
Serialize,
Deserialize,
Hash,
PartialOrd,
Ord,
PartialEq,
Eq,
Clone,
Encode,
Decode,
Debug,
)]
pub struct StorageData(#[serde(with = "impl_serde::serialize")] pub Vec<u8>);
impl AsRef<[u8]> for StorageData {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
/// Storage change set
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)]
#[serde(rename_all = "camelCase")]
pub struct StorageChangeSet<Hash> {
/// Block hash
pub block: Hash,
/// A list of changes
pub changes: Vec<(StorageKey, Option<StorageData>)>,
}
/// Health struct returned by the RPC
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Health {
/// Number of connected peers
pub peers: usize,
/// Is the node syncing
pub is_syncing: bool,
/// Should this node have any peers
///
/// Might be false for local chains or when running without discovery.
pub should_have_peers: bool,
}
#[cfg(test)]
mod test {
use super::*;
/// A util function to assert the result of serialization and deserialization is the same.
pub fn assert_deser<T>(s: &str, expected: T)
where
T: std::fmt::Debug
+ serde::ser::Serialize
+ serde::de::DeserializeOwned
+ PartialEq,
{
assert_eq!(serde_json::from_str::<T>(s).unwrap(), expected);
assert_eq!(serde_json::to_string(&expected).unwrap(), s);
}
// Check that some A can be serialized and then deserialized into some B.
pub fn assert_ser_deser<A, B>(a: &A, b: &B)
where
A: serde::Serialize,
B: serde::de::DeserializeOwned + PartialEq + std::fmt::Debug,
{
let json = serde_json::to_string(a).expect("serializing failed");
let new_b: B = serde_json::from_str(&json).expect("deserializing failed");
assert_eq!(b, &new_b);
}
#[test]
fn runtime_version_is_substrate_compatible() {
use sp_version::RuntimeVersion as SpRuntimeVersion;
let substrate_runtime_version = SpRuntimeVersion {
spec_version: 123,
transaction_version: 456,
..Default::default()
};
let json = serde_json::to_string(&substrate_runtime_version)
.expect("serializing failed");
let val: RuntimeVersion =
serde_json::from_str(&json).expect("deserializing failed");
// We ignore any other properties.
assert_eq!(val.spec_version, 123);
assert_eq!(val.transaction_version, 456);
}
#[test]
fn runtime_version_handles_arbitrary_params() {
let val: RuntimeVersion = serde_json::from_str(
r#"{
"specVersion": 123,
"transactionVersion": 456,
"foo": true,
"wibble": [1,2,3]
}"#,
)
.expect("deserializing failed");
let mut m = std::collections::HashMap::new();
m.insert("foo".to_owned(), serde_json::json!(true));
m.insert("wibble".to_owned(), serde_json::json!([1, 2, 3]));
assert_eq!(
val,
RuntimeVersion {
spec_version: 123,
transaction_version: 456,
other: m
}
);
}
#[test]
fn number_or_hex_deserializes_from_either_repr() {
assert_deser(r#""0x1234""#, NumberOrHex::Hex(0x1234.into()));
assert_deser(r#""0x0""#, NumberOrHex::Hex(0.into()));
assert_deser(r#"5"#, NumberOrHex::Number(5));
assert_deser(r#"10000"#, NumberOrHex::Number(10000));
assert_deser(r#"0"#, NumberOrHex::Number(0));
assert_deser(r#"1000000000000"#, NumberOrHex::Number(1000000000000));
}
#[test]
fn dry_run_result_is_substrate_compatible() {
use sp_runtime::{
transaction_validity::{
InvalidTransaction as SpInvalidTransaction,
TransactionValidityError as SpTransactionValidityError,
},
ApplyExtrinsicResult as SpApplyExtrinsicResult,
DispatchError as SpDispatchError,
};
let pairs = vec![
// All ok
(SpApplyExtrinsicResult::Ok(Ok(())), Ok(())),
// Some transaction error
(
SpApplyExtrinsicResult::Err(SpTransactionValidityError::Invalid(
SpInvalidTransaction::BadProof,
)),
Err(DryRunError::TransactionValidityError),
),
// Some dispatch error
(
SpApplyExtrinsicResult::Ok(Err(SpDispatchError::BadOrigin)),
Err(DryRunError::DispatchError),
),
];
for (actual, expected) in pairs {
let encoded = actual.encode();
assert_eq!(decode_dry_run_result(&mut &*encoded).unwrap(), expected);
}
}
#[test]
fn justification_is_substrate_compatible() {
use sp_runtime::Justification as SpJustification;
// As much as anything, this just checks that the Justification type
// is still a tuple as given.
assert_ser_deser::<SpJustification, Justification>(
&([1, 2, 3, 4], vec![5, 6, 7, 8]),
&([1, 2, 3, 4], vec![5, 6, 7, 8]),
);
}
#[test]
fn storage_types_are_substrate_compatible() {
use sp_core::storage::{
StorageChangeSet as SpStorageChangeSet,
StorageData as SpStorageData,
StorageKey as SpStorageKey,
};
assert_ser_deser(
&SpStorageKey(vec![1, 2, 3, 4, 5]),
&StorageKey(vec![1, 2, 3, 4, 5]),
);
assert_ser_deser(
&SpStorageData(vec![1, 2, 3, 4, 5]),
&StorageData(vec![1, 2, 3, 4, 5]),
);
assert_ser_deser(
&SpStorageChangeSet {
block: 1u64,
changes: vec![(SpStorageKey(vec![1]), Some(SpStorageData(vec![2])))],
},
&StorageChangeSet {
block: 1u64,
changes: vec![(StorageKey(vec![1]), Some(StorageData(vec![2])))],
},
);
}
}
+2 -2
View File
@@ -15,8 +15,8 @@ pub use storage_client::{
StorageClient,
};
// Re-export as this is used in the public API:
pub use sp_core::storage::StorageKey;
// Re-export as this is used in the public API in this module:
pub use crate::rpc::types::StorageKey;
/// Types representing an address which describes where a storage
/// entry lives and how to properly decode it.
+4 -4
View File
@@ -16,15 +16,15 @@ use crate::{
DecodeWithMetadata,
Metadata,
},
rpc::types::{
StorageData,
StorageKey,
},
Config,
};
use derivative::Derivative;
use frame_metadata::StorageEntryType;
use scale_info::form::PortableForm;
use sp_core::storage::{
StorageData,
StorageKey,
};
use std::{
future::Future,
marker::PhantomData,
+6 -7
View File
@@ -3,7 +3,6 @@
// see LICENSE for license details.
use codec::Encode;
pub use sp_runtime::traits::SignedExtension;
// We use this type a bunch, so export it from here.
pub use frame_metadata::StorageHasher;
@@ -37,16 +36,16 @@ impl StorageMapKey {
pub(super) fn hash_bytes(input: &[u8], hasher: &StorageHasher, bytes: &mut Vec<u8>) {
match hasher {
StorageHasher::Identity => bytes.extend(input),
StorageHasher::Blake2_128 => bytes.extend(sp_core::blake2_128(input)),
StorageHasher::Blake2_128 => bytes.extend(sp_core_hashing::blake2_128(input)),
StorageHasher::Blake2_128Concat => {
bytes.extend(sp_core::blake2_128(input));
bytes.extend(sp_core_hashing::blake2_128(input));
bytes.extend(input);
}
StorageHasher::Blake2_256 => bytes.extend(sp_core::blake2_256(input)),
StorageHasher::Twox128 => bytes.extend(sp_core::twox_128(input)),
StorageHasher::Twox256 => bytes.extend(sp_core::twox_256(input)),
StorageHasher::Blake2_256 => bytes.extend(sp_core_hashing::blake2_256(input)),
StorageHasher::Twox128 => bytes.extend(sp_core_hashing::twox_128(input)),
StorageHasher::Twox256 => bytes.extend(sp_core_hashing::twox_256(input)),
StorageHasher::Twox64Concat => {
bytes.extend(sp_core::twox_64(input));
bytes.extend(sp_core_hashing::twox_64(input));
bytes.extend(input);
}
}
+2 -2
View File
@@ -18,8 +18,8 @@ pub fn write_storage_address_root_bytes<Address: StorageAddress>(
addr: &Address,
out: &mut Vec<u8>,
) {
out.extend(sp_core::twox_128(addr.pallet_name().as_bytes()));
out.extend(sp_core::twox_128(addr.entry_name().as_bytes()));
out.extend(sp_core_hashing::twox_128(addr.pallet_name().as_bytes()));
out.extend(sp_core_hashing::twox_128(addr.entry_name().as_bytes()));
}
/// Outputs the [`storage_address_root_bytes`] as well as any additional bytes that represent
+10 -30
View File
@@ -2,45 +2,25 @@
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
//! Create signed or unsigned extrinsics.
//!
//! This modules exposes the extrinsic's parameters and the ability to sign an extrinsic.
//!
//! Create and submit extrinsics.
//!
//! An extrinsic is submitted with an "signed extra" and "additional" parameters, which can be
//! different for each chain. The trait [ExtrinsicParams] determines exactly which
//! additional and signed extra parameters are used when constructing an extrinsic.
//!
//!
//! The structure [BaseExtrinsicParams] is a base implementation of the trait which
//! configures most of the "signed extra" and "additional" parameters as needed for
//! Polkadot and Substrate nodes. Only the shape of the tip payments differs, leading to
//! [SubstrateExtrinsicParams] and [PolkadotExtrinsicParams] structs which pick an
//! appropriate shape for Substrate/Polkadot chains respectively.
//! different for each chain. The trait [`crate::config::ExtrinsicParams`] determines exactly which
//! additional and signed extra parameters are used when constructing an extrinsic, and is a part
//! of the chain configuration (see [`crate::config::Config`]).
mod params;
mod signer;
mod tx_client;
mod tx_payload;
mod tx_progress;
// The PairSigner impl currently relies on Substrate bits and pieces, so make it an optional
// feature if we want to avoid needing sp_core and sp_runtime.
#[cfg(feature = "substrate-compat")]
pub use self::signer::PairSigner;
pub use self::{
params::{
AssetTip,
BaseExtrinsicParams,
BaseExtrinsicParamsBuilder,
Era,
ExtrinsicParams,
PlainTip,
PolkadotExtrinsicParams,
PolkadotExtrinsicParamsBuilder,
SubstrateExtrinsicParams,
SubstrateExtrinsicParamsBuilder,
},
signer::{
PairSigner,
Signer,
},
signer::Signer,
tx_client::{
SubmittableExtrinsic,
TxClient,
+69 -50
View File
@@ -6,11 +6,6 @@
//! [substrate](https://github.com/paritytech/substrate) node via RPC.
use crate::Config;
use sp_core::Pair;
use sp_runtime::traits::{
IdentifyAccount,
Verify,
};
/// Signing transactions requires a [`Signer`]. This is responsible for
/// providing the "from" account that the transaction is being signed by,
@@ -29,55 +24,79 @@ pub trait Signer<T: Config> {
fn sign(&self, signer_payload: &[u8]) -> T::Signature;
}
/// A [`Signer`] implementation that can be constructed from an [`Pair`].
#[derive(Clone, Debug)]
pub struct PairSigner<T: Config, P: Pair> {
account_id: T::AccountId,
signer: P,
}
#[cfg(feature = "substrate-compat")]
pub use pair_signer::PairSigner;
impl<T, P> PairSigner<T, P>
where
T: Config,
T::Signature: From<P::Signature>,
<T::Signature as Verify>::Signer:
From<P::Public> + IdentifyAccount<AccountId = T::AccountId>,
P: Pair,
{
/// Creates a new [`Signer`] from a [`Pair`].
pub fn new(signer: P) -> Self {
let account_id =
<T::Signature as Verify>::Signer::from(signer.public()).into_account();
Self { account_id, signer }
// A signer suitable for substrate based chains. This provides compatibility with Substrate
// packages like sp_keyring and such, and so relies on sp_core and sp_runtime to be included.
#[cfg(feature = "substrate-compat")]
mod pair_signer {
use super::Signer;
use crate::Config;
use sp_core::Pair as PairT;
use sp_runtime::{
traits::{
IdentifyAccount,
Verify,
},
AccountId32 as SpAccountId32,
MultiSignature as SpMultiSignature,
};
/// A [`Signer`] implementation that can be constructed from an [`sp_core::Pair`].
#[derive(Clone, Debug)]
pub struct PairSigner<T: Config, Pair> {
account_id: T::AccountId,
signer: Pair,
}
/// Returns the [`Pair`] implementation used to construct this.
pub fn signer(&self) -> &P {
&self.signer
impl<T, Pair> PairSigner<T, Pair>
where
T: Config,
Pair: PairT,
// We go via an sp_runtime::MultiSignature. We can probably generalise this
// by implementing some of these traits on our built-in MultiSignature and then
// requiring them on all T::Signatures, to avoid any go-between.
<SpMultiSignature as Verify>::Signer: From<Pair::Public>,
T::AccountId: From<SpAccountId32>,
{
/// Creates a new [`Signer`] from an [`sp_core::Pair`].
pub fn new(signer: Pair) -> Self {
let account_id = <SpMultiSignature as Verify>::Signer::from(signer.public())
.into_account();
Self {
account_id: account_id.into(),
signer,
}
}
/// Returns the [`sp_core::Pair`] implementation used to construct this.
pub fn signer(&self) -> &Pair {
&self.signer
}
/// Return the account ID.
pub fn account_id(&self) -> &T::AccountId {
&self.account_id
}
}
/// Return the account ID.
pub fn account_id(&self) -> &T::AccountId {
&self.account_id
}
}
impl<T, P> Signer<T> for PairSigner<T, P>
where
T: Config,
T::AccountId: Into<T::Address> + Clone + 'static,
P: Pair + 'static,
P::Signature: Into<T::Signature> + 'static,
{
fn account_id(&self) -> &T::AccountId {
&self.account_id
}
fn address(&self) -> T::Address {
self.account_id.clone().into()
}
fn sign(&self, signer_payload: &[u8]) -> T::Signature {
self.signer.sign(signer_payload).into()
impl<T, Pair> Signer<T> for PairSigner<T, Pair>
where
T: Config,
Pair: PairT,
Pair::Signature: Into<T::Signature>,
{
fn account_id(&self) -> &T::AccountId {
&self.account_id
}
fn address(&self) -> T::Address {
self.account_id.clone().into()
}
fn sign(&self, signer_payload: &[u8]) -> T::Signature {
self.signer.sign(signer_payload).into()
}
}
}
+31 -26
View File
@@ -8,27 +8,29 @@ use crate::{
OfflineClientT,
OnlineClientT,
},
config::{
Config,
ExtrinsicParams,
Hasher,
},
error::Error,
tx::{
ExtrinsicParams,
Signer,
Signer as SignerT,
TxProgress,
},
utils::{
Encoded,
PhantomDataSendSync,
},
Config,
};
use codec::{
Compact,
Encode,
};
use derivative::Derivative;
use sp_runtime::{
traits::Hash,
ApplyExtrinsicResult,
};
// This is returned from an API below, so expose it here.
pub use crate::rpc::types::DryRunResult;
/// A client for working with transactions.
#[derive(Derivative)]
@@ -121,15 +123,16 @@ impl<T: Config, C: OfflineClientT<T>> TxClient<T, C> {
}
/// Creates a raw signed extrinsic without submitting it.
pub fn create_signed_with_nonce<Call>(
pub fn create_signed_with_nonce<Call, Signer>(
&self,
call: &Call,
signer: &(dyn Signer<T> + Send + Sync),
signer: &Signer,
account_nonce: T::Index,
other_params: <T::ExtrinsicParams as ExtrinsicParams<T::Index, T::Hash>>::OtherParams,
) -> Result<SubmittableExtrinsic<T, C>, Error>
where
Call: TxPayload,
Signer: SignerT<T>,
{
// 1. Validate this call against the current node metadata if the call comes
// with a hash allowing us to do so.
@@ -166,7 +169,7 @@ impl<T: Config, C: OfflineClientT<T>> TxClient<T, C> {
additional_and_extra_params.encode_extra_to(&mut bytes);
additional_and_extra_params.encode_additional_to(&mut bytes);
if bytes.len() > 256 {
signer.sign(&sp_core::blake2_256(&bytes))
signer.sign(T::Hasher::hash_of(&bytes).as_ref())
} else {
signer.sign(&bytes)
}
@@ -214,14 +217,15 @@ where
C: OnlineClientT<T>,
{
/// Creates a raw signed extrinsic, without submitting it.
pub async fn create_signed<Call>(
pub async fn create_signed<Call, Signer>(
&self,
call: &Call,
signer: &(dyn Signer<T> + Send + Sync),
signer: &Signer,
other_params: <T::ExtrinsicParams as ExtrinsicParams<T::Index, T::Hash>>::OtherParams,
) -> Result<SubmittableExtrinsic<T, C>, Error>
where
Call: TxPayload,
Signer: SignerT<T>,
{
// Get nonce from the node.
let account_nonce = self
@@ -238,13 +242,14 @@ where
///
/// Returns a [`TxProgress`], which can be used to track the status of the transaction
/// and obtain details about it, once it has made it into a block.
pub async fn sign_and_submit_then_watch_default<Call>(
pub async fn sign_and_submit_then_watch_default<Call, Signer>(
&self,
call: &Call,
signer: &(dyn Signer<T> + Send + Sync),
signer: &Signer,
) -> Result<TxProgress<T, C>, Error>
where
Call: TxPayload,
Signer: SignerT<T>,
<T::ExtrinsicParams as ExtrinsicParams<T::Index, T::Hash>>::OtherParams: Default,
{
self.sign_and_submit_then_watch(call, signer, Default::default())
@@ -255,14 +260,15 @@ where
///
/// Returns a [`TxProgress`], which can be used to track the status of the transaction
/// and obtain details about it, once it has made it into a block.
pub async fn sign_and_submit_then_watch<Call>(
pub async fn sign_and_submit_then_watch<Call, Signer>(
&self,
call: &Call,
signer: &(dyn Signer<T> + Send + Sync),
signer: &Signer,
other_params: <T::ExtrinsicParams as ExtrinsicParams<T::Index, T::Hash>>::OtherParams,
) -> Result<TxProgress<T, C>, Error>
where
Call: TxPayload,
Signer: SignerT<T>,
{
self.create_signed(call, signer, other_params)
.await?
@@ -280,13 +286,14 @@ where
///
/// Success does not mean the extrinsic has been included in the block, just that it is valid
/// and has been included in the transaction pool.
pub async fn sign_and_submit_default<Call>(
pub async fn sign_and_submit_default<Call, Signer>(
&self,
call: &Call,
signer: &(dyn Signer<T> + Send + Sync),
signer: &Signer,
) -> Result<T::Hash, Error>
where
Call: TxPayload,
Signer: SignerT<T>,
<T::ExtrinsicParams as ExtrinsicParams<T::Index, T::Hash>>::OtherParams: Default,
{
self.sign_and_submit(call, signer, Default::default()).await
@@ -300,14 +307,15 @@ where
///
/// Success does not mean the extrinsic has been included in the block, just that it is valid
/// and has been included in the transaction pool.
pub async fn sign_and_submit<Call>(
pub async fn sign_and_submit<Call, Signer>(
&self,
call: &Call,
signer: &(dyn Signer<T> + Send + Sync),
signer: &Signer,
other_params: <T::ExtrinsicParams as ExtrinsicParams<T::Index, T::Hash>>::OtherParams,
) -> Result<T::Hash, Error>
where
Call: TxPayload,
Signer: SignerT<T>,
{
self.create_signed(call, signer, other_params)
.await?
@@ -366,7 +374,7 @@ where
/// and obtain details about it, once it has made it into a block.
pub async fn submit_and_watch(&self) -> Result<TxProgress<T, C>, Error> {
// Get a hash of the extrinsic (we'll need this later).
let ext_hash = T::Hashing::hash_of(&self.encoded);
let ext_hash = T::Hasher::hash_of(&self.encoded);
// Submit and watch for transaction progress.
let sub = self.client.rpc().watch_extrinsic(&self.encoded).await?;
@@ -388,11 +396,8 @@ where
/// Submits the extrinsic to the dry_run RPC, to test if it would succeed.
///
/// Returns `Ok` with an [`ApplyExtrinsicResult`], which is the result of applying of an extrinsic.
pub async fn dry_run(
&self,
at: Option<T::Hash>,
) -> Result<ApplyExtrinsicResult, Error> {
/// Returns `Ok` with a [`DryRunResult`], which is the result of attempting to dry run the extrinsic.
pub async fn dry_run(&self, at: Option<T::Hash>) -> Result<DryRunResult, Error> {
self.client.rpc().dry_run(self.encoded(), at).await
}
}
+3 -5
View File
@@ -15,7 +15,7 @@ use crate::{
TransactionError,
},
events::EventsClient,
rpc::{
rpc::types::{
Subscription,
SubstrateTxStatus,
},
@@ -26,9 +26,6 @@ use futures::{
Stream,
StreamExt,
};
use sp_runtime::traits::Hash;
pub use sp_runtime::traits::SignedExtension;
/// This struct represents a subscription to the progress of some transaction.
#[derive(Derivative)]
@@ -373,7 +370,8 @@ impl<T: Config, C: OnlineClientT<T>> TxInBlock<T, C> {
let extrinsic_idx = block.block.extrinsics
.iter()
.position(|ext| {
let hash = T::Hashing::hash_of(&ext.0);
use crate::config::Hasher;
let hash = T::Hasher::hash_of(&ext.0);
hash == self.ext_hash
})
// If we successfully obtain the block hash we think contains our
+203
View File
@@ -0,0 +1,203 @@
// Copyright 2019-2022 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
//! The "default" Substrate/Polkadot AccountId. This is used in codegen, as well as signing related bits.
//! This doesn't contain much functionality itself, but is easy to convert to/from an `sp_core::AccountId32`
//! for instance, to gain functionality without forcing a dependency on Substrate crates here.
use codec::{
Decode,
Encode,
};
use serde::Serialize;
/// A 32-byte cryptographic identifier. This is a simplified version of Substrate's
/// `sp_core::crypto::AccountId32`. To obtain more functionality, convert this into
/// that type.
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug)]
pub struct AccountId32(pub [u8; 32]);
impl AsRef<[u8]> for AccountId32 {
fn as_ref(&self) -> &[u8] {
&self.0[..]
}
}
impl AsRef<[u8; 32]> for AccountId32 {
fn as_ref(&self) -> &[u8; 32] {
&self.0
}
}
impl From<[u8; 32]> for AccountId32 {
fn from(x: [u8; 32]) -> Self {
AccountId32(x)
}
}
impl AccountId32 {
// Return the ss58-check string for this key. Adapted from `sp_core::crypto`. We need this to
// serialize our account appropriately but otherwise don't care.
fn to_ss58check(&self) -> String {
// For serializing to a string to obtain the account nonce, we use the default substrate
// prefix (since we have no way to otherwise pick one). It doesn't really matter, since when
// it's deserialized back in system_accountNextIndex, we ignore this (so long as it's valid).
const SUBSTRATE_SS58_PREFIX: u8 = 42;
// prefix <= 63 just take up one byte at the start:
let mut v = vec![SUBSTRATE_SS58_PREFIX];
// then push the account ID bytes.
v.extend(self.0);
// then push a 2 byte checksum of what we have so far.
let r = ss58hash(&v);
v.extend(&r[0..2]);
// then encode to base58.
use base58::ToBase58;
v.to_base58()
}
// This isn't strictly needed, but to give our AccountId32 a little more usefulness, we also
// implement the logic needed to decode an AccountId32 from an SS58 encoded string. This is exposed
// via a `FromStr` impl.
fn from_ss58check(s: &str) -> Result<Self, FromSs58Error> {
const CHECKSUM_LEN: usize = 2;
let body_len = 32;
use base58::FromBase58;
let data = s.from_base58().map_err(|_| FromSs58Error::BadBase58)?;
if data.len() < 2 {
return Err(FromSs58Error::BadLength)
}
let prefix_len = match data[0] {
0..=63 => 1,
64..=127 => 2,
_ => return Err(FromSs58Error::InvalidPrefix),
};
if data.len() != prefix_len + body_len + CHECKSUM_LEN {
return Err(FromSs58Error::BadLength)
}
let hash = ss58hash(&data[0..body_len + prefix_len]);
let checksum = &hash[0..CHECKSUM_LEN];
if data[body_len + prefix_len..body_len + prefix_len + CHECKSUM_LEN] != *checksum
{
// Invalid checksum.
return Err(FromSs58Error::InvalidChecksum)
}
let result = data[prefix_len..body_len + prefix_len]
.try_into()
.map_err(|_| FromSs58Error::BadLength)?;
Ok(AccountId32(result))
}
}
/// An error obtained from trying to interpret an SS58 encoded string into an AccountId32
#[derive(thiserror::Error, Clone, Copy, Eq, PartialEq, Debug)]
#[allow(missing_docs)]
pub enum FromSs58Error {
#[error("Base 58 requirement is violated")]
BadBase58,
#[error("Length is bad")]
BadLength,
#[error("Invalid checksum")]
InvalidChecksum,
#[error("Invalid SS58 prefix byte.")]
InvalidPrefix,
}
// We do this just to get a checksum to help verify the validity of the address in to_ss58check
fn ss58hash(data: &[u8]) -> Vec<u8> {
use blake2::{
Blake2b512,
Digest,
};
const PREFIX: &[u8] = b"SS58PRE";
let mut ctx = Blake2b512::new();
ctx.update(PREFIX);
ctx.update(data);
ctx.finalize().to_vec()
}
impl Serialize for AccountId32 {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(&self.to_ss58check())
}
}
impl std::fmt::Display for AccountId32 {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.to_ss58check())
}
}
impl std::str::FromStr for AccountId32 {
type Err = FromSs58Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
AccountId32::from_ss58check(s)
}
}
// Improve compat with the substrate version if we're using those crates:
#[cfg(feature = "substrate-compat")]
mod substrate_impls {
use super::*;
impl From<sp_runtime::AccountId32> for AccountId32 {
fn from(value: sp_runtime::AccountId32) -> Self {
Self(value.into())
}
}
impl From<sp_core::sr25519::Public> for AccountId32 {
fn from(value: sp_core::sr25519::Public) -> Self {
let acc: sp_runtime::AccountId32 = value.into();
acc.into()
}
}
impl From<sp_core::ed25519::Public> for AccountId32 {
fn from(value: sp_core::ed25519::Public) -> Self {
let acc: sp_runtime::AccountId32 = value.into();
acc.into()
}
}
}
#[cfg(test)]
mod test {
use super::*;
use sp_core::crypto::Ss58Codec;
use sp_keyring::AccountKeyring;
#[test]
fn ss58_is_compatible_with_substrate_impl() {
let keyrings = vec![
AccountKeyring::Alice,
AccountKeyring::Bob,
AccountKeyring::Charlie,
];
for keyring in keyrings {
let substrate_account = keyring.to_account_id();
// Avoid "From" impl hidden behind "substrate-compat" feature so that this test
// can work either way:
let local_account = AccountId32(substrate_account.clone().into());
// Both should encode to ss58 the same way:
let substrate_ss58 = substrate_account.to_ss58check();
assert_eq!(substrate_ss58, local_account.to_ss58check());
// Both should decode from ss58 back to the same:
assert_eq!(
sp_core::crypto::AccountId32::from_ss58check(&substrate_ss58).unwrap(),
substrate_account
);
assert_eq!(
AccountId32::from_ss58check(&substrate_ss58).unwrap(),
local_account
);
}
}
}
+15
View File
@@ -4,7 +4,10 @@
//! Miscellaneous utility helpers.
pub mod account_id;
pub mod bits;
pub mod multi_address;
pub mod multi_signature;
use codec::{
Decode,
@@ -13,6 +16,18 @@ use codec::{
};
use derivative::Derivative;
pub use account_id::AccountId32;
pub use multi_address::MultiAddress;
pub use multi_signature::MultiSignature;
// Used in codegen
#[doc(hidden)]
pub use primitive_types::{
H160,
H256,
H512,
};
/// Wraps an already encoded byte vector, prevents being encoded as a raw byte vector as part of
/// the transaction payload
#[derive(Clone, Debug, Eq, PartialEq)]
+66
View File
@@ -0,0 +1,66 @@
// Copyright 2019-2022 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
//! The "default" Substrate/Polkadot Address type. This is used in codegen, as well as signing related bits.
//! This doesn't contain much functionality itself, but is easy to convert to/from an `sp_runtime::MultiAddress`
//! for instance, to gain functionality without forcing a dependency on Substrate crates here.
use codec::{
Decode,
Encode,
};
/// A multi-format address wrapper for on-chain accounts. This is a simplified version of Substrate's
/// `sp_runtime::MultiAddress`. To obtain more functionality, convert this into that type (this conversion
/// functionality is provided via `From` impls if the `substrate-compat` feature is enabled).
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug)]
pub enum MultiAddress<AccountId, AccountIndex> {
/// It's an account ID (pubkey).
Id(AccountId),
/// It's an account index.
Index(#[codec(compact)] AccountIndex),
/// It's some arbitrary raw bytes.
Raw(Vec<u8>),
/// It's a 32 byte representation.
Address32([u8; 32]),
/// Its a 20 byte representation.
Address20([u8; 20]),
}
impl<AccountId, AccountIndex> From<AccountId> for MultiAddress<AccountId, AccountIndex> {
fn from(a: AccountId) -> Self {
Self::Id(a)
}
}
// Improve compat with the substrate version if we're using those crates:
#[cfg(feature = "substrate-compat")]
mod substrate_impls {
use super::{
super::AccountId32,
*,
};
impl<N> From<sp_runtime::AccountId32> for MultiAddress<AccountId32, N> {
fn from(value: sp_runtime::AccountId32) -> Self {
let val: AccountId32 = value.into();
val.into()
}
}
impl<Id, N> From<sp_runtime::MultiAddress<Id, N>> for MultiAddress<AccountId32, N>
where
Id: Into<AccountId32>,
{
fn from(value: sp_runtime::MultiAddress<Id, N>) -> Self {
match value {
sp_runtime::MultiAddress::Id(v) => Self::Id(v.into()),
sp_runtime::MultiAddress::Index(v) => Self::Index(v),
sp_runtime::MultiAddress::Raw(v) => Self::Raw(v),
sp_runtime::MultiAddress::Address32(v) => Self::Address32(v),
sp_runtime::MultiAddress::Address20(v) => Self::Address20(v),
}
}
}
}
+61
View File
@@ -0,0 +1,61 @@
// Copyright 2019-2022 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
//! The "default" Substrate/Polkadot Signature type. This is used in codegen, as well as signing related bits.
//! This doesn't contain much functionality itself, but is easy to convert to/from an `sp_runtime::MultiSignature`
//! for instance, to gain functionality without forcing a dependency on Substrate crates here.
use codec::{
Decode,
Encode,
};
/// Signature container that can store known signature types. This is a simplified version of
/// `sp_runtime::MultiSignature`. To obtain more functionality, convert this into that type.
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug)]
pub enum MultiSignature {
/// An Ed25519 signature.
Ed25519([u8; 64]),
/// An Sr25519 signature.
Sr25519([u8; 64]),
/// An ECDSA/SECP256k1 signature (a 512-bit value, plus 8 bits for recovery ID).
Ecdsa([u8; 65]),
}
// Improve compat with the substrate version if we're using those crates:
#[cfg(feature = "substrate-compat")]
mod substrate_impls {
use super::*;
impl From<sp_runtime::MultiSignature> for MultiSignature {
fn from(value: sp_runtime::MultiSignature) -> Self {
match value {
sp_runtime::MultiSignature::Ed25519(s) => Self::Ed25519(s.0),
sp_runtime::MultiSignature::Sr25519(s) => Self::Sr25519(s.0),
sp_runtime::MultiSignature::Ecdsa(s) => Self::Ecdsa(s.0),
}
}
}
impl From<sp_core::ed25519::Signature> for MultiSignature {
fn from(value: sp_core::ed25519::Signature) -> Self {
let sig: sp_runtime::MultiSignature = value.into();
sig.into()
}
}
impl From<sp_core::sr25519::Signature> for MultiSignature {
fn from(value: sp_core::sr25519::Signature) -> Self {
let sig: sp_runtime::MultiSignature = value.into();
sig.into()
}
}
impl From<sp_core::ecdsa::Signature> for MultiSignature {
fn from(value: sp_core::ecdsa::Signature) -> Self {
let sig: sp_runtime::MultiSignature = value.into();
sig.into()
}
}
}
+3 -3
View File
@@ -23,9 +23,9 @@ futures = "0.3.13"
hex = "0.4.3"
regex = "1.5.0"
scale-info = { version = "2.0.0", features = ["bit-vec"] }
sp-core = { version = "7.0.0", default-features = false }
sp-keyring = "7.0.0"
sp-runtime = "7.0.0"
sp-core = { version = "11.0.0", default-features = false }
sp-keyring = "12.0.0"
sp-runtime = "12.0.0"
syn = "1.0.0"
subxt = { version = "0.25.0", path = "../../subxt" }
subxt-codegen = { version = "0.25.0", path = "../../codegen" }
+2 -2
View File
@@ -75,8 +75,8 @@ async fn missing_block_headers_will_be_filled_in() -> Result<(), subxt::Error> {
while let Some(header) = all_finalized_blocks.next().await {
let header = header?;
use sp_runtime::traits::Header;
let block_number: u128 = (*header.number()).into();
use subxt::config::Header;
let block_number: u128 = header.number().into();
if let Some(last) = last_block_number {
assert_eq!(last + 1, block_number);
+7 -11
View File
@@ -17,7 +17,10 @@ use sp_core::{
Pair,
};
use sp_keyring::AccountKeyring;
use subxt::error::DispatchError;
use subxt::{
error::DispatchError,
rpc::types::DryRunError,
};
#[tokio::test]
async fn insert_key() {
@@ -162,8 +165,7 @@ async fn dry_run_passes() {
.dry_run(None)
.await
.expect("dryrunning failed")
.expect("expected dryrunning to be successful")
.unwrap();
.expect("dry run should be successful");
signed_extrinsic
.submit_and_watch()
@@ -198,15 +200,9 @@ async fn dry_run_fails() {
let dry_run_res = signed_extrinsic
.dry_run(None)
.await
.expect("dryrunning failed")
.expect("expected dryrun transaction to be valid");
.expect("dryrunning failed");
if let Err(sp_runtime::DispatchError::Module(module_error)) = dry_run_res {
assert_eq!(module_error.index, 6);
assert_eq!(module_error.error, [2, 0, 0, 0]);
} else {
panic!("expected a module error when dryrunning");
}
assert_eq!(dry_run_res, Err(DryRunError::DispatchError));
let res = signed_extrinsic
.submit_and_watch()
+1 -1
View File
@@ -8,7 +8,7 @@
/// Generate by:
///
/// - run `polkadot --dev --tmp` node locally
/// - `cargo run --release -p subxt-cli -- codegen | rustfmt > integration-tests/src/codegen/polkadot.rs`
/// - `cargo run -p subxt-cli -- codegen | rustfmt > testing/integration-tests/src/codegen/polkadot.rs`
#[rustfmt::skip]
#[allow(clippy::all)]
mod polkadot;
File diff suppressed because one or more lines are too long
+19 -17
View File
@@ -18,13 +18,15 @@ use sp_core::{
Pair as _,
};
use sp_keyring::AccountKeyring;
use sp_runtime::{
AccountId32,
MultiAddress,
};
use subxt::error::{
DispatchError,
Error,
use subxt::{
error::{
DispatchError,
Error,
},
utils::{
AccountId32,
MultiAddress,
},
};
#[tokio::test]
@@ -250,8 +252,9 @@ async fn storage_total_issuance() {
#[tokio::test]
async fn storage_balance_lock() -> Result<(), subxt::Error> {
let bob = pair_signer(AccountKeyring::Bob.pair());
let charlie = AccountKeyring::Charlie.to_account_id();
let bob_signer = pair_signer(AccountKeyring::Bob.pair());
let bob: AccountId32 = AccountKeyring::Bob.to_account_id().into();
let charlie: AccountId32 = AccountKeyring::Charlie.to_account_id().into();
let ctx = test_context().await;
let api = ctx.client();
@@ -262,16 +265,14 @@ async fn storage_balance_lock() -> Result<(), subxt::Error> {
);
api.tx()
.sign_and_submit_then_watch_default(&tx, &bob)
.sign_and_submit_then_watch_default(&tx, &bob_signer)
.await?
.wait_for_finalized_success()
.await?
.find_first::<system::events::ExtrinsicSuccess>()?
.expect("No ExtrinsicSuccess Event found");
let locks_addr = node_runtime::storage()
.balances()
.locks(AccountKeyring::Bob.to_account_id());
let locks_addr = node_runtime::storage().balances().locks(bob);
let locks = api.storage().fetch_or_default(&locks_addr, None).await?;
@@ -330,12 +331,13 @@ async fn transfer_error() {
#[tokio::test]
async fn transfer_implicit_subscription() {
let alice = pair_signer(AccountKeyring::Alice.pair());
let bob = AccountKeyring::Bob.to_account_id();
let bob_addr = bob.clone().into();
let bob: AccountId32 = AccountKeyring::Bob.to_account_id().into();
let ctx = test_context().await;
let api = ctx.client();
let to_bob_tx = node_runtime::tx().balances().transfer(bob_addr, 10_000);
let to_bob_tx = node_runtime::tx()
.balances()
.transfer(bob.clone().into(), 10_000);
let event = api
.tx()
@@ -353,7 +355,7 @@ async fn transfer_implicit_subscription() {
event,
balances::events::Transfer {
from: alice.account_id().clone(),
to: bob.clone(),
to: bob,
amount: 10_000
}
);
@@ -18,12 +18,12 @@ use crate::{
TestContext,
};
use sp_core::sr25519::Pair;
use sp_runtime::MultiAddress;
use subxt::{
tx::{
PairSigner,
TxProgress,
},
utils::MultiAddress,
Config,
Error,
OnlineClient,
+6 -5
View File
@@ -9,6 +9,7 @@ use crate::{
utils::wait_for_blocks,
};
use sp_keyring::AccountKeyring;
use subxt::utils::AccountId32;
#[tokio::test]
async fn storage_plain_lookup() -> Result<(), subxt::Error> {
@@ -32,7 +33,7 @@ async fn storage_map_lookup() -> Result<(), subxt::Error> {
let api = ctx.client();
let signer = pair_signer(AccountKeyring::Alice.pair());
let alice = AccountKeyring::Alice.to_account_id();
let alice: AccountId32 = AccountKeyring::Alice.to_account_id().into();
// Do some transaction to bump the Alice nonce to 1:
let remark_tx = node_runtime::tx().system().remark(vec![1, 2, 3, 4, 5]);
@@ -43,7 +44,7 @@ async fn storage_map_lookup() -> Result<(), subxt::Error> {
.await?;
// Look up the nonce for the user (we expect it to be 1).
let nonce_addr = node_runtime::storage().system().account(&alice);
let nonce_addr = node_runtime::storage().system().account(alice);
let entry = api.storage().fetch_or_default(&nonce_addr, None).await?;
assert_eq!(entry.nonce, 1);
@@ -90,8 +91,8 @@ async fn storage_n_map_storage_lookup() -> Result<(), subxt::Error> {
// we "approveTransfer" of some of this asset class. This gives us an
// entry in the `Approvals` StorageNMap that we can try to look up.
let signer = pair_signer(AccountKeyring::Alice.pair());
let alice = AccountKeyring::Alice.to_account_id();
let bob = AccountKeyring::Bob.to_account_id();
let alice: AccountId32 = AccountKeyring::Alice.to_account_id().into();
let bob: AccountId32 = AccountKeyring::Bob.to_account_id().into();
let tx1 = node_runtime::tx()
.assets()
@@ -111,7 +112,7 @@ async fn storage_n_map_storage_lookup() -> Result<(), subxt::Error> {
.await?;
// The actual test; look up this approval in storage:
let addr = node_runtime::storage().assets().approvals(99, &alice, &bob);
let addr = node_runtime::storage().assets().approvals(99, alice, bob);
let entry = api.storage().fetch(&addr, None).await?;
assert_eq!(entry.map(|a| a.amount), Some(123));
Ok(())
+1 -2
View File
@@ -6,12 +6,11 @@ publish = false
[dependencies]
subxt = { path = "../../subxt" }
sp-runtime = "7.0.0"
sp-runtime = "12.0.0"
codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive", "full", "bit-vec"] }
[build-dependencies]
subxt = { path = "../../subxt" }
sp-core = { version = "7.0.0", default-features = false }
tokio = { version = "1.8", features = ["macros", "rt-multi-thread"] }
which = "4.2.2"
jsonrpsee = { version = "0.16.0", features = ["async-client", "client-ws-transport"] }
+1 -1
View File
@@ -47,7 +47,7 @@ async fn run() {
};
// Download metadata from binary; retry until successful, or a limit is hit.
let metadata_bytes: sp_core::Bytes = {
let metadata_bytes: subxt::rpc::types::Bytes = {
const MAX_RETRIES: usize = 6;
let mut retries = 0;