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
+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])))],
},
);
}
}