use crate::{metadata::Metadata, Config};
use codec::{Decode, Encode};
use primitive_types::U256;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
pub use super::rpc_client::Subscription;
#[derive(Debug, PartialEq, Eq)]
pub enum DryRunResult {
Success,
DispatchError(crate::error::DispatchError),
TransactionValidityError,
}
pub struct DryRunResultBytes(pub Vec<u8>);
impl DryRunResultBytes {
pub fn into_dry_run_result(self, metadata: &Metadata) -> Result<DryRunResult, crate::Error> {
let bytes = self.0;
if bytes[0] == 0 && bytes[1] == 0 {
Ok(DryRunResult::Success)
} else if bytes[0] == 0 && bytes[1] == 1 {
let dispatch_error =
crate::error::DispatchError::decode_from(&bytes[2..], metadata.clone())?;
Ok(DryRunResult::DispatchError(dispatch_error))
} else if bytes[0] == 1 {
Ok(DryRunResult::TransactionValidityError)
} else {
Err(crate::Error::Unknown(bytes))
}
}
}
#[derive(Copy, Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
#[serde(untagged)]
pub enum NumberOrHex {
Number(u64),
Hex(U256),
}
#[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)
}
}
#[derive(Debug, Deserialize)]
#[serde(bound = "T: Config")]
pub struct ChainBlockResponse<T: Config> {
pub block: ChainBlock<T>,
pub justifications: Option<Vec<Justification>>,
}
#[derive(Debug, Deserialize)]
pub struct ChainBlock<T: Config> {
pub header: T::Header,
pub extrinsics: Vec<ChainBlockExtrinsic>,
}
pub type Justification = (ConsensusEngineId, EncodedJustification);
pub type ConsensusEngineId = [u8; 4];
pub type EncodedJustification = Vec<u8>;
#[derive(Clone, 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))
}
}
#[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 {
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)
}
}
#[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()
}
}
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);
pub type SystemProperties = serde_json::Map<String, serde_json::Value>;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum SubstrateTxStatus<Hash, BlockHash> {
Future,
Ready,
Broadcast(Vec<String>),
InBlock(BlockHash),
Retracted(BlockHash),
FinalityTimeout(BlockHash),
Finalized(BlockHash),
Usurped(Hash),
Dropped,
Invalid,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RuntimeVersion {
pub spec_version: u32,
pub transaction_version: u32,
#[serde(flatten)]
pub other: HashMap<String, serde_json::Value>,
}
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ReadProof<Hash> {
pub at: Hash,
pub proof: Vec<Bytes>,
}
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BlockStats {
pub witness_len: u64,
pub witness_compact_len: u64,
pub block_len: u64,
pub num_extrinsics: u64,
}
#[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
}
}
#[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
}
}
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)]
#[serde(rename_all = "camelCase")]
pub struct StorageChangeSet<Hash> {
pub block: Hash,
pub changes: Vec<(StorageKey, Option<StorageData>)>,
}
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Health {
pub peers: usize,
pub is_syncing: bool,
pub should_have_peers: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ErrorEvent {
pub error: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RuntimeVersionEvent {
pub spec: RuntimeVersion,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "camelCase")]
#[serde(tag = "type")]
pub enum RuntimeEvent {
Valid(RuntimeVersionEvent),
Invalid(ErrorEvent),
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Initialized<Hash> {
pub finalized_block_hash: Hash,
pub finalized_block_runtime: Option<RuntimeEvent>,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct NewBlock<Hash> {
pub block_hash: Hash,
pub parent_block_hash: Hash,
pub new_runtime: Option<RuntimeEvent>,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BestBlockChanged<Hash> {
pub best_block_hash: Hash,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Finalized<Hash> {
pub finalized_block_hashes: Vec<Hash>,
pub pruned_block_hashes: Vec<Hash>,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "camelCase")]
#[serde(tag = "event")]
pub enum FollowEvent<Hash> {
Initialized(Initialized<Hash>),
NewBlock(NewBlock<Hash>),
BestBlockChanged(BestBlockChanged<Hash>),
Finalized(Finalized<Hash>),
Stop,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ChainHeadResult<T> {
pub result: T,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "camelCase")]
#[serde(tag = "event")]
pub enum ChainHeadEvent<T> {
Done(ChainHeadResult<T>),
Inaccessible(ErrorEvent),
Error(ErrorEvent),
Disjoint,
}
#[derive(Debug, Clone, PartialEq, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TransactionBroadcasted {
#[serde(with = "as_string")]
pub num_peers: usize,
}
#[derive(Debug, Clone, PartialEq, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TransactionBlock<Hash> {
pub hash: Hash,
#[serde(with = "as_string")]
pub index: usize,
}
#[derive(Debug, Clone, PartialEq, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TransactionError {
pub error: String,
}
#[derive(Debug, Clone, PartialEq, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TransactionDropped {
pub broadcasted: bool,
pub error: String,
}
#[derive(Debug, Clone, PartialEq, Deserialize)]
#[serde(bound(deserialize = "Hash: Deserialize<'de> + Clone"))]
#[serde(from = "TransactionEventIR<Hash>")]
pub enum TransactionEvent<Hash> {
Validated,
Broadcasted(TransactionBroadcasted),
BestChainBlockIncluded(Option<TransactionBlock<Hash>>),
Finalized(TransactionBlock<Hash>),
Error(TransactionError),
Invalid(TransactionError),
Dropped(TransactionDropped),
}
#[derive(Debug, Clone, PartialEq, Deserialize)]
#[serde(rename_all = "camelCase")]
#[serde(tag = "event", content = "block")]
enum TransactionEventBlockIR<Hash> {
BestChainBlockIncluded(Option<TransactionBlock<Hash>>),
Finalized(TransactionBlock<Hash>),
}
#[derive(Debug, Clone, PartialEq, Deserialize)]
#[serde(rename_all = "camelCase")]
#[serde(tag = "event")]
enum TransactionEventNonBlockIR {
Validated,
Broadcasted(TransactionBroadcasted),
Error(TransactionError),
Invalid(TransactionError),
Dropped(TransactionDropped),
}
#[derive(Debug, Clone, PartialEq, Deserialize)]
#[serde(bound(deserialize = "Hash: Deserialize<'de>"))]
#[serde(rename_all = "camelCase")]
#[serde(untagged)]
enum TransactionEventIR<Hash> {
Block(TransactionEventBlockIR<Hash>),
NonBlock(TransactionEventNonBlockIR),
}
impl<Hash> From<TransactionEvent<Hash>> for TransactionEventIR<Hash> {
fn from(value: TransactionEvent<Hash>) -> Self {
match value {
TransactionEvent::Validated => {
TransactionEventIR::NonBlock(TransactionEventNonBlockIR::Validated)
}
TransactionEvent::Broadcasted(event) => {
TransactionEventIR::NonBlock(TransactionEventNonBlockIR::Broadcasted(event))
}
TransactionEvent::BestChainBlockIncluded(event) => {
TransactionEventIR::Block(TransactionEventBlockIR::BestChainBlockIncluded(event))
}
TransactionEvent::Finalized(event) => {
TransactionEventIR::Block(TransactionEventBlockIR::Finalized(event))
}
TransactionEvent::Error(event) => {
TransactionEventIR::NonBlock(TransactionEventNonBlockIR::Error(event))
}
TransactionEvent::Invalid(event) => {
TransactionEventIR::NonBlock(TransactionEventNonBlockIR::Invalid(event))
}
TransactionEvent::Dropped(event) => {
TransactionEventIR::NonBlock(TransactionEventNonBlockIR::Dropped(event))
}
}
}
}
impl<Hash> From<TransactionEventIR<Hash>> for TransactionEvent<Hash> {
fn from(value: TransactionEventIR<Hash>) -> Self {
match value {
TransactionEventIR::NonBlock(status) => match status {
TransactionEventNonBlockIR::Validated => TransactionEvent::Validated,
TransactionEventNonBlockIR::Broadcasted(event) => {
TransactionEvent::Broadcasted(event)
}
TransactionEventNonBlockIR::Error(event) => TransactionEvent::Error(event),
TransactionEventNonBlockIR::Invalid(event) => TransactionEvent::Invalid(event),
TransactionEventNonBlockIR::Dropped(event) => TransactionEvent::Dropped(event),
},
TransactionEventIR::Block(block) => match block {
TransactionEventBlockIR::Finalized(event) => TransactionEvent::Finalized(event),
TransactionEventBlockIR::BestChainBlockIncluded(event) => {
TransactionEvent::BestChainBlockIncluded(event)
}
},
}
}
}
mod as_string {
use super::*;
use serde::Deserializer;
pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<usize, D::Error> {
String::deserialize(deserializer)?
.parse()
.map_err(|e| serde::de::Error::custom(format!("Parsing failed: {e}")))
}
}
#[cfg(test)]
mod test {
use super::*;
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);
}
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");
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 justification_is_substrate_compatible() {
use sp_runtime::Justification as SpJustification;
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])))],
},
);
}
}