gear_subxt/rpc/
types.rs

1// Copyright 2019-2023 Parity Technologies (UK) Ltd.
2// This file is dual-licensed as Apache-2.0 or GPL-3.0.
3// see LICENSE for license details.
4
5//! Types sent to/from the Substrate RPC interface.
6
7use crate::{metadata::Metadata, Config};
8use codec::{Decode, Encode};
9use primitive_types::U256;
10use serde::{Deserialize, Serialize};
11use std::collections::HashMap;
12
13// Subscription types are returned from some calls, so expose it with the rest of the returned types.
14pub use super::rpc_client::Subscription;
15
16/// An error dry running an extrinsic.
17#[derive(Debug, PartialEq, Eq)]
18pub enum DryRunResult {
19    /// The transaction could be included in the block and executed.
20    Success,
21    /// The transaction could be included in the block, but the call failed to dispatch.
22    DispatchError(crate::error::DispatchError),
23    /// The transaction could not be included in the block.
24    TransactionValidityError,
25}
26
27/// The bytes representing an error dry running an extrinsic.
28pub struct DryRunResultBytes(pub Vec<u8>);
29
30impl DryRunResultBytes {
31    /// Attempt to decode the error bytes into a [`DryRunResult`] using the provided [`Metadata`].
32    pub fn into_dry_run_result(self, metadata: &Metadata) -> Result<DryRunResult, crate::Error> {
33        // dryRun returns an ApplyExtrinsicResult, which is basically a
34        // `Result<Result<(), DispatchError>, TransactionValidityError>`.
35        let bytes = self.0;
36        if bytes[0] == 0 && bytes[1] == 0 {
37            // Ok(Ok(())); transaction is valid and executed ok
38            Ok(DryRunResult::Success)
39        } else if bytes[0] == 0 && bytes[1] == 1 {
40            // Ok(Err(dispatch_error)); transaction is valid but execution failed
41            let dispatch_error =
42                crate::error::DispatchError::decode_from(&bytes[2..], metadata.clone())?;
43            Ok(DryRunResult::DispatchError(dispatch_error))
44        } else if bytes[0] == 1 {
45            // Err(transaction_error); some transaction validity error (we ignore the details at the moment)
46            Ok(DryRunResult::TransactionValidityError)
47        } else {
48            // unable to decode the bytes; they aren't what we expect.
49            Err(crate::Error::Unknown(bytes))
50        }
51    }
52}
53
54/// A number type that can be serialized both as a number or a string that encodes a number in a
55/// string.
56///
57/// We allow two representations of the block number as input. Either we deserialize to the type
58/// that is specified in the block type or we attempt to parse given hex value.
59///
60/// The primary motivation for having this type is to avoid overflows when using big integers in
61/// JavaScript (which we consider as an important RPC API consumer).
62#[derive(Copy, Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
63#[serde(untagged)]
64pub enum NumberOrHex {
65    /// The number represented directly.
66    Number(u64),
67    /// Hex representation of the number.
68    Hex(U256),
69}
70
71/// Hex-serialized shim for `Vec<u8>`.
72#[derive(PartialEq, Eq, Clone, Serialize, Deserialize, Hash, PartialOrd, Ord, Debug)]
73pub struct Bytes(#[serde(with = "impl_serde::serialize")] pub Vec<u8>);
74impl std::ops::Deref for Bytes {
75    type Target = [u8];
76    fn deref(&self) -> &[u8] {
77        &self.0[..]
78    }
79}
80impl From<Vec<u8>> for Bytes {
81    fn from(s: Vec<u8>) -> Self {
82        Bytes(s)
83    }
84}
85
86/// The response from `chain_getBlock`
87#[derive(Debug, Deserialize)]
88#[serde(bound = "T: Config")]
89pub struct ChainBlockResponse<T: Config> {
90    /// The block itself.
91    pub block: ChainBlock<T>,
92    /// Block justification.
93    pub justifications: Option<Vec<Justification>>,
94}
95
96/// Block details in the [`ChainBlockResponse`].
97#[derive(Debug, Deserialize)]
98pub struct ChainBlock<T: Config> {
99    /// The block header.
100    pub header: T::Header,
101    /// The accompanying extrinsics.
102    pub extrinsics: Vec<ChainBlockExtrinsic>,
103}
104
105/// An abstraction over justification for a block's validity under a consensus algorithm.
106pub type Justification = (ConsensusEngineId, EncodedJustification);
107/// Consensus engine unique ID.
108pub type ConsensusEngineId = [u8; 4];
109/// The encoded justification specific to a consensus engine.
110pub type EncodedJustification = Vec<u8>;
111
112/// Bytes representing an extrinsic in a [`ChainBlock`].
113#[derive(Clone, Debug)]
114pub struct ChainBlockExtrinsic(pub Vec<u8>);
115
116impl<'a> ::serde::Deserialize<'a> for ChainBlockExtrinsic {
117    fn deserialize<D>(de: D) -> Result<Self, D::Error>
118    where
119        D: ::serde::Deserializer<'a>,
120    {
121        let r = impl_serde::serialize::deserialize(de)?;
122        let bytes = Decode::decode(&mut &r[..])
123            .map_err(|e| ::serde::de::Error::custom(format!("Decode error: {e}")))?;
124        Ok(ChainBlockExtrinsic(bytes))
125    }
126}
127
128/// Wrapper for NumberOrHex to allow custom From impls
129#[derive(Serialize)]
130pub struct BlockNumber(NumberOrHex);
131
132impl From<NumberOrHex> for BlockNumber {
133    fn from(x: NumberOrHex) -> Self {
134        BlockNumber(x)
135    }
136}
137
138impl Default for NumberOrHex {
139    fn default() -> Self {
140        Self::Number(Default::default())
141    }
142}
143
144impl NumberOrHex {
145    /// Converts this number into an U256.
146    pub fn into_u256(self) -> U256 {
147        match self {
148            NumberOrHex::Number(n) => n.into(),
149            NumberOrHex::Hex(h) => h,
150        }
151    }
152}
153
154impl From<u32> for NumberOrHex {
155    fn from(n: u32) -> Self {
156        NumberOrHex::Number(n.into())
157    }
158}
159
160impl From<u64> for NumberOrHex {
161    fn from(n: u64) -> Self {
162        NumberOrHex::Number(n)
163    }
164}
165
166impl From<u128> for NumberOrHex {
167    fn from(n: u128) -> Self {
168        NumberOrHex::Hex(n.into())
169    }
170}
171
172impl From<U256> for NumberOrHex {
173    fn from(n: U256) -> Self {
174        NumberOrHex::Hex(n)
175    }
176}
177
178/// An error type that signals an out-of-range conversion attempt.
179#[derive(Debug, thiserror::Error)]
180#[error("Out-of-range conversion attempt")]
181pub struct TryFromIntError;
182
183impl TryFrom<NumberOrHex> for u32 {
184    type Error = TryFromIntError;
185    fn try_from(num_or_hex: NumberOrHex) -> Result<u32, Self::Error> {
186        num_or_hex
187            .into_u256()
188            .try_into()
189            .map_err(|_| TryFromIntError)
190    }
191}
192
193impl TryFrom<NumberOrHex> for u64 {
194    type Error = TryFromIntError;
195    fn try_from(num_or_hex: NumberOrHex) -> Result<u64, Self::Error> {
196        num_or_hex
197            .into_u256()
198            .try_into()
199            .map_err(|_| TryFromIntError)
200    }
201}
202
203impl TryFrom<NumberOrHex> for u128 {
204    type Error = TryFromIntError;
205    fn try_from(num_or_hex: NumberOrHex) -> Result<u128, Self::Error> {
206        num_or_hex
207            .into_u256()
208            .try_into()
209            .map_err(|_| TryFromIntError)
210    }
211}
212
213impl From<NumberOrHex> for U256 {
214    fn from(num_or_hex: NumberOrHex) -> U256 {
215        num_or_hex.into_u256()
216    }
217}
218
219// All unsigned ints can be converted into a BlockNumber:
220macro_rules! into_block_number {
221    ($($t: ty)+) => {
222        $(
223            impl From<$t> for BlockNumber {
224                fn from(x: $t) -> Self {
225                    NumberOrHex::Number(x.into()).into()
226                }
227            }
228        )+
229    }
230}
231into_block_number!(u8 u16 u32 u64);
232
233/// Arbitrary properties defined in the chain spec as a JSON object.
234pub type SystemProperties = serde_json::Map<String, serde_json::Value>;
235
236/// Possible transaction status events.
237///
238/// # Note
239///
240/// This is copied from `sp-transaction-pool` to avoid a dependency on that crate. Therefore it
241/// must be kept compatible with that type from the target substrate version.
242#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
243#[serde(rename_all = "camelCase")]
244pub enum SubstrateTxStatus<Hash, BlockHash> {
245    /// Transaction is part of the future queue.
246    Future,
247    /// Transaction is part of the ready queue.
248    Ready,
249    /// The transaction has been broadcast to the given peers.
250    Broadcast(Vec<String>),
251    /// Transaction has been included in block with given hash.
252    InBlock(BlockHash),
253    /// The block this transaction was included in has been retracted.
254    Retracted(BlockHash),
255    /// Maximum number of finality watchers has been reached,
256    /// old watchers are being removed.
257    FinalityTimeout(BlockHash),
258    /// Transaction has been finalized by a finality-gadget, e.g GRANDPA
259    Finalized(BlockHash),
260    /// Transaction has been replaced in the pool, by another transaction
261    /// that provides the same tags. (e.g. same (sender, nonce)).
262    Usurped(Hash),
263    /// Transaction has been dropped from the pool because of the limit.
264    Dropped,
265    /// Transaction is no longer valid in the current state.
266    Invalid,
267}
268
269/// This contains the runtime version information necessary to make transactions, as obtained from
270/// the RPC call `state_getRuntimeVersion`,
271#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
272#[serde(rename_all = "camelCase")]
273pub struct RuntimeVersion {
274    /// Version of the runtime specification. A full-node will not attempt to use its native
275    /// runtime in substitute for the on-chain Wasm runtime unless all of `spec_name`,
276    /// `spec_version` and `authoring_version` are the same between Wasm and native.
277    pub spec_version: u32,
278
279    /// All existing dispatches are fully compatible when this number doesn't change. If this
280    /// number changes, then `spec_version` must change, also.
281    ///
282    /// This number must change when an existing dispatchable (module ID, dispatch ID) is changed,
283    /// either through an alteration in its user-level semantics, a parameter
284    /// added/removed/changed, a dispatchable being removed, a module being removed, or a
285    /// dispatchable/module changing its index.
286    ///
287    /// It need *not* change when a new module is added or when a dispatchable is added.
288    pub transaction_version: u32,
289
290    /// The other fields present may vary and aren't necessary for `subxt`; they are preserved in
291    /// this map.
292    #[serde(flatten)]
293    pub other: HashMap<String, serde_json::Value>,
294}
295
296/// ReadProof struct returned by the RPC
297///
298/// # Note
299///
300/// This is copied from `sc-rpc-api` to avoid a dependency on that crate. Therefore it
301/// must be kept compatible with that type from the target substrate version.
302#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
303#[serde(rename_all = "camelCase")]
304pub struct ReadProof<Hash> {
305    /// Block hash used to generate the proof
306    pub at: Hash,
307    /// A proof used to prove that storage entries are included in the storage trie
308    pub proof: Vec<Bytes>,
309}
310
311/// Statistics of a block returned by the `dev_getBlockStats` RPC.
312#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
313#[serde(rename_all = "camelCase")]
314pub struct BlockStats {
315    /// The length in bytes of the storage proof produced by executing the block.
316    pub witness_len: u64,
317    /// The length in bytes of the storage proof after compaction.
318    pub witness_compact_len: u64,
319    /// Length of the block in bytes.
320    ///
321    /// This information can also be acquired by downloading the whole block. This merely
322    /// saves some complexity on the client side.
323    pub block_len: u64,
324    /// Number of extrinsics in the block.
325    ///
326    /// This information can also be acquired by downloading the whole block. This merely
327    /// saves some complexity on the client side.
328    pub num_extrinsics: u64,
329}
330
331/// Storage key.
332#[derive(
333    Serialize, Deserialize, Hash, PartialOrd, Ord, PartialEq, Eq, Clone, Encode, Decode, Debug,
334)]
335pub struct StorageKey(#[serde(with = "impl_serde::serialize")] pub Vec<u8>);
336impl AsRef<[u8]> for StorageKey {
337    fn as_ref(&self) -> &[u8] {
338        &self.0
339    }
340}
341
342/// Storage data.
343#[derive(
344    Serialize, Deserialize, Hash, PartialOrd, Ord, PartialEq, Eq, Clone, Encode, Decode, Debug,
345)]
346pub struct StorageData(#[serde(with = "impl_serde::serialize")] pub Vec<u8>);
347impl AsRef<[u8]> for StorageData {
348    fn as_ref(&self) -> &[u8] {
349        &self.0
350    }
351}
352
353/// Storage change set
354#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)]
355#[serde(rename_all = "camelCase")]
356pub struct StorageChangeSet<Hash> {
357    /// Block hash
358    pub block: Hash,
359    /// A list of changes
360    pub changes: Vec<(StorageKey, Option<StorageData>)>,
361}
362
363/// Health struct returned by the RPC
364#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
365#[serde(rename_all = "camelCase")]
366pub struct Health {
367    /// Number of connected peers
368    pub peers: usize,
369    /// Is the node syncing
370    pub is_syncing: bool,
371    /// Should this node have any peers
372    ///
373    /// Might be false for local chains or when running without discovery.
374    pub should_have_peers: bool,
375}
376
377/// The operation could not be processed due to an error.
378#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
379#[serde(rename_all = "camelCase")]
380pub struct ErrorEvent {
381    /// Reason of the error.
382    pub error: String,
383}
384
385/// The runtime specification of the current block.
386///
387/// This event is generated for:
388///   - the first announced block by the follow subscription
389///   - blocks that suffered a change in runtime compared with their parents
390#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
391#[serde(rename_all = "camelCase")]
392pub struct RuntimeVersionEvent {
393    /// The runtime version.
394    pub spec: RuntimeVersion,
395}
396
397/// The runtime event generated if the `follow` subscription
398/// has set the `runtime_updates` flag.
399#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
400#[serde(rename_all = "camelCase")]
401#[serde(tag = "type")]
402pub enum RuntimeEvent {
403    /// The runtime version of this block.
404    Valid(RuntimeVersionEvent),
405    /// The runtime could not be obtained due to an error.
406    Invalid(ErrorEvent),
407}
408
409/// Contain information about the latest finalized block.
410///
411/// # Note
412///
413/// This is the first event generated by the `follow` subscription
414/// and is submitted only once.
415///
416/// If the `runtime_updates` flag is set, then this event contains
417/// the `RuntimeEvent`, otherwise the `RuntimeEvent` is not present.
418#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
419#[serde(rename_all = "camelCase")]
420pub struct Initialized<Hash> {
421    /// The hash of the latest finalized block.
422    pub finalized_block_hash: Hash,
423    /// The runtime version of the finalized block.
424    ///
425    /// # Note
426    ///
427    /// This is present only if the `runtime_updates` flag is set for
428    /// the `follow` subscription.
429    pub finalized_block_runtime: Option<RuntimeEvent>,
430}
431
432/// Indicate a new non-finalized block.
433#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
434#[serde(rename_all = "camelCase")]
435pub struct NewBlock<Hash> {
436    /// The hash of the new block.
437    pub block_hash: Hash,
438    /// The parent hash of the new block.
439    pub parent_block_hash: Hash,
440    /// The runtime version of the new block.
441    ///
442    /// # Note
443    ///
444    /// This is present only if the `runtime_updates` flag is set for
445    /// the `follow` subscription.
446    pub new_runtime: Option<RuntimeEvent>,
447}
448
449/// Indicate the block hash of the new best block.
450#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
451#[serde(rename_all = "camelCase")]
452pub struct BestBlockChanged<Hash> {
453    /// The block hash of the new best block.
454    pub best_block_hash: Hash,
455}
456
457/// Indicate the finalized and pruned block hashes.
458#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
459#[serde(rename_all = "camelCase")]
460pub struct Finalized<Hash> {
461    /// Block hashes that are finalized.
462    pub finalized_block_hashes: Vec<Hash>,
463    /// Block hashes that are pruned (removed).
464    pub pruned_block_hashes: Vec<Hash>,
465}
466
467/// The event generated by the `chainHead_follow` method.
468///
469/// The events are generated in the following order:
470/// 1. Initialized - generated only once to signal the
471///      latest finalized block
472/// 2. NewBlock - a new block was added.
473/// 3. BestBlockChanged - indicate that the best block
474///      is now the one from this event. The block was
475///      announced priorly with the `NewBlock` event.
476/// 4. Finalized - State the finalized and pruned blocks.
477#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
478#[serde(rename_all = "camelCase")]
479#[serde(tag = "event")]
480pub enum FollowEvent<Hash> {
481    /// The latest finalized block.
482    ///
483    /// This event is generated only once.
484    Initialized(Initialized<Hash>),
485    /// A new non-finalized block was added.
486    NewBlock(NewBlock<Hash>),
487    /// The best block of the chain.
488    BestBlockChanged(BestBlockChanged<Hash>),
489    /// A list of finalized and pruned blocks.
490    Finalized(Finalized<Hash>),
491    /// The subscription is dropped and no further events
492    /// will be generated.
493    Stop,
494}
495
496/// The result of a chain head method.
497#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
498#[serde(rename_all = "camelCase")]
499pub struct ChainHeadResult<T> {
500    /// Result of the method.
501    pub result: T,
502}
503
504/// The event generated by the body / call / storage methods.
505#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
506#[serde(rename_all = "camelCase")]
507#[serde(tag = "event")]
508pub enum ChainHeadEvent<T> {
509    /// The request completed successfully.
510    Done(ChainHeadResult<T>),
511    /// The resources requested are inaccessible.
512    ///
513    /// Resubmitting the request later might succeed.
514    Inaccessible(ErrorEvent),
515    /// An error occurred. This is definitive.
516    Error(ErrorEvent),
517    /// The provided subscription ID is stale or invalid.
518    Disjoint,
519}
520
521/// The transaction was broadcasted to a number of peers.
522///
523/// # Note
524///
525/// The RPC does not guarantee that the peers have received the
526/// transaction.
527///
528/// When the number of peers is zero, the event guarantees that
529/// shutting down the local node will lead to the transaction
530/// not being included in the chain.
531#[derive(Debug, Clone, PartialEq, Deserialize)]
532#[serde(rename_all = "camelCase")]
533pub struct TransactionBroadcasted {
534    /// The number of peers the transaction was broadcasted to.
535    #[serde(with = "as_string")]
536    pub num_peers: usize,
537}
538
539/// The transaction was included in a block of the chain.
540#[derive(Debug, Clone, PartialEq, Deserialize)]
541#[serde(rename_all = "camelCase")]
542pub struct TransactionBlock<Hash> {
543    /// The hash of the block the transaction was included into.
544    pub hash: Hash,
545    /// The index (zero-based) of the transaction within the body of the block.
546    #[serde(with = "as_string")]
547    pub index: usize,
548}
549
550/// The transaction could not be processed due to an error.
551#[derive(Debug, Clone, PartialEq, Deserialize)]
552#[serde(rename_all = "camelCase")]
553pub struct TransactionError {
554    /// Reason of the error.
555    pub error: String,
556}
557
558/// The transaction was dropped because of exceeding limits.
559#[derive(Debug, Clone, PartialEq, Deserialize)]
560#[serde(rename_all = "camelCase")]
561pub struct TransactionDropped {
562    /// True if the transaction was broadcasted to other peers and
563    /// may still be included in the block.
564    pub broadcasted: bool,
565    /// Reason of the event.
566    pub error: String,
567}
568
569/// Possible transaction status events.
570///
571/// The status events can be grouped based on their kinds as:
572///
573/// 1. Runtime validated the transaction:
574///             - `Validated`
575///
576/// 2. Inside the `Ready` queue:
577///             - `Broadcast`
578///
579/// 3. Leaving the pool:
580///             - `BestChainBlockIncluded`
581///             - `Invalid`
582///
583/// 4. Block finalized:
584///             - `Finalized`
585///
586/// 5. At any time:
587///             - `Dropped`
588///             - `Error`
589///
590/// The subscription's stream is considered finished whenever the following events are
591/// received: `Finalized`, `Error`, `Invalid` or `Dropped`. However, the user is allowed
592/// to unsubscribe at any moment.
593#[derive(Debug, Clone, PartialEq, Deserialize)]
594// We need to manually specify the trait bounds for the `Hash` trait to ensure `into` and
595// `from` still work.
596#[serde(bound(deserialize = "Hash: Deserialize<'de> + Clone"))]
597#[serde(from = "TransactionEventIR<Hash>")]
598pub enum TransactionEvent<Hash> {
599    /// The transaction was validated by the runtime.
600    Validated,
601    /// The transaction was broadcasted to a number of peers.
602    Broadcasted(TransactionBroadcasted),
603    /// The transaction was included in a best block of the chain.
604    ///
605    /// # Note
606    ///
607    /// This may contain `None` if the block is no longer a best
608    /// block of the chain.
609    BestChainBlockIncluded(Option<TransactionBlock<Hash>>),
610    /// The transaction was included in a finalized block.
611    Finalized(TransactionBlock<Hash>),
612    /// The transaction could not be processed due to an error.
613    Error(TransactionError),
614    /// The transaction is marked as invalid.
615    Invalid(TransactionError),
616    /// The client was not capable of keeping track of this transaction.
617    Dropped(TransactionDropped),
618}
619
620/// Intermediate representation (IR) for the transaction events
621/// that handles block events only.
622///
623/// The block events require a JSON compatible interpretation similar to:
624///
625/// ```json
626/// { event: "EVENT", block: { hash: "0xFF", index: 0 } }
627/// ```
628///
629/// This IR is introduced to circumvent that the block events need to
630/// be serialized/deserialized with "tag" and "content", while other
631/// events only require "tag".
632#[derive(Debug, Clone, PartialEq, Deserialize)]
633#[serde(rename_all = "camelCase")]
634#[serde(tag = "event", content = "block")]
635enum TransactionEventBlockIR<Hash> {
636    /// The transaction was included in the best block of the chain.
637    BestChainBlockIncluded(Option<TransactionBlock<Hash>>),
638    /// The transaction was included in a finalized block of the chain.
639    Finalized(TransactionBlock<Hash>),
640}
641
642/// Intermediate representation (IR) for the transaction events
643/// that handles non-block events only.
644///
645/// The non-block events require a JSON compatible interpretation similar to:
646///
647/// ```json
648/// { event: "EVENT", num_peers: 0 }
649/// ```
650///
651/// This IR is introduced to circumvent that the block events need to
652/// be serialized/deserialized with "tag" and "content", while other
653/// events only require "tag".
654#[derive(Debug, Clone, PartialEq, Deserialize)]
655#[serde(rename_all = "camelCase")]
656#[serde(tag = "event")]
657enum TransactionEventNonBlockIR {
658    Validated,
659    Broadcasted(TransactionBroadcasted),
660    Error(TransactionError),
661    Invalid(TransactionError),
662    Dropped(TransactionDropped),
663}
664
665/// Intermediate representation (IR) used for serialization/deserialization of the
666/// [`TransactionEvent`] in a JSON compatible format.
667///
668/// Serde cannot mix `#[serde(tag = "event")]` with `#[serde(tag = "event", content = "block")]`
669/// for specific enum variants. Therefore, this IR is introduced to circumvent this
670/// restriction, while exposing a simplified [`TransactionEvent`] for users of the
671/// rust ecosystem.
672#[derive(Debug, Clone, PartialEq, Deserialize)]
673#[serde(bound(deserialize = "Hash: Deserialize<'de>"))]
674#[serde(rename_all = "camelCase")]
675#[serde(untagged)]
676enum TransactionEventIR<Hash> {
677    Block(TransactionEventBlockIR<Hash>),
678    NonBlock(TransactionEventNonBlockIR),
679}
680
681impl<Hash> From<TransactionEvent<Hash>> for TransactionEventIR<Hash> {
682    fn from(value: TransactionEvent<Hash>) -> Self {
683        match value {
684            TransactionEvent::Validated => {
685                TransactionEventIR::NonBlock(TransactionEventNonBlockIR::Validated)
686            }
687            TransactionEvent::Broadcasted(event) => {
688                TransactionEventIR::NonBlock(TransactionEventNonBlockIR::Broadcasted(event))
689            }
690            TransactionEvent::BestChainBlockIncluded(event) => {
691                TransactionEventIR::Block(TransactionEventBlockIR::BestChainBlockIncluded(event))
692            }
693            TransactionEvent::Finalized(event) => {
694                TransactionEventIR::Block(TransactionEventBlockIR::Finalized(event))
695            }
696            TransactionEvent::Error(event) => {
697                TransactionEventIR::NonBlock(TransactionEventNonBlockIR::Error(event))
698            }
699            TransactionEvent::Invalid(event) => {
700                TransactionEventIR::NonBlock(TransactionEventNonBlockIR::Invalid(event))
701            }
702            TransactionEvent::Dropped(event) => {
703                TransactionEventIR::NonBlock(TransactionEventNonBlockIR::Dropped(event))
704            }
705        }
706    }
707}
708
709impl<Hash> From<TransactionEventIR<Hash>> for TransactionEvent<Hash> {
710    fn from(value: TransactionEventIR<Hash>) -> Self {
711        match value {
712            TransactionEventIR::NonBlock(status) => match status {
713                TransactionEventNonBlockIR::Validated => TransactionEvent::Validated,
714                TransactionEventNonBlockIR::Broadcasted(event) => {
715                    TransactionEvent::Broadcasted(event)
716                }
717                TransactionEventNonBlockIR::Error(event) => TransactionEvent::Error(event),
718                TransactionEventNonBlockIR::Invalid(event) => TransactionEvent::Invalid(event),
719                TransactionEventNonBlockIR::Dropped(event) => TransactionEvent::Dropped(event),
720            },
721            TransactionEventIR::Block(block) => match block {
722                TransactionEventBlockIR::Finalized(event) => TransactionEvent::Finalized(event),
723                TransactionEventBlockIR::BestChainBlockIncluded(event) => {
724                    TransactionEvent::BestChainBlockIncluded(event)
725                }
726            },
727        }
728    }
729}
730
731/// Serialize and deserialize helper as string.
732mod as_string {
733    use super::*;
734    use serde::Deserializer;
735
736    pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<usize, D::Error> {
737        String::deserialize(deserializer)?
738            .parse()
739            .map_err(|e| serde::de::Error::custom(format!("Parsing failed: {e}")))
740    }
741}
742
743#[cfg(test)]
744mod test {
745    use super::*;
746
747    /// A util function to assert the result of serialization and deserialization is the same.
748    pub fn assert_deser<T>(s: &str, expected: T)
749    where
750        T: std::fmt::Debug + serde::ser::Serialize + serde::de::DeserializeOwned + PartialEq,
751    {
752        assert_eq!(serde_json::from_str::<T>(s).unwrap(), expected);
753        assert_eq!(serde_json::to_string(&expected).unwrap(), s);
754    }
755
756    // Check that some A can be serialized and then deserialized into some B.
757    pub fn assert_ser_deser<A, B>(a: &A, b: &B)
758    where
759        A: serde::Serialize,
760        B: serde::de::DeserializeOwned + PartialEq + std::fmt::Debug,
761    {
762        let json = serde_json::to_string(a).expect("serializing failed");
763        let new_b: B = serde_json::from_str(&json).expect("deserializing failed");
764
765        assert_eq!(b, &new_b);
766    }
767
768    #[test]
769    fn runtime_version_is_substrate_compatible() {
770        use sp_version::RuntimeVersion as SpRuntimeVersion;
771
772        let substrate_runtime_version = SpRuntimeVersion {
773            spec_version: 123,
774            transaction_version: 456,
775            ..Default::default()
776        };
777
778        let json = serde_json::to_string(&substrate_runtime_version).expect("serializing failed");
779        let val: RuntimeVersion = serde_json::from_str(&json).expect("deserializing failed");
780
781        // We ignore any other properties.
782        assert_eq!(val.spec_version, 123);
783        assert_eq!(val.transaction_version, 456);
784    }
785
786    #[test]
787    fn runtime_version_handles_arbitrary_params() {
788        let val: RuntimeVersion = serde_json::from_str(
789            r#"{
790                "specVersion": 123,
791                "transactionVersion": 456,
792                "foo": true,
793                "wibble": [1,2,3]
794            }"#,
795        )
796        .expect("deserializing failed");
797
798        let mut m = std::collections::HashMap::new();
799        m.insert("foo".to_owned(), serde_json::json!(true));
800        m.insert("wibble".to_owned(), serde_json::json!([1, 2, 3]));
801
802        assert_eq!(
803            val,
804            RuntimeVersion {
805                spec_version: 123,
806                transaction_version: 456,
807                other: m
808            }
809        );
810    }
811
812    #[test]
813    fn number_or_hex_deserializes_from_either_repr() {
814        assert_deser(r#""0x1234""#, NumberOrHex::Hex(0x1234.into()));
815        assert_deser(r#""0x0""#, NumberOrHex::Hex(0.into()));
816        assert_deser(r#"5"#, NumberOrHex::Number(5));
817        assert_deser(r#"10000"#, NumberOrHex::Number(10000));
818        assert_deser(r#"0"#, NumberOrHex::Number(0));
819        assert_deser(r#"1000000000000"#, NumberOrHex::Number(1000000000000));
820    }
821
822    #[test]
823    fn justification_is_substrate_compatible() {
824        use sp_runtime::Justification as SpJustification;
825
826        // As much as anything, this just checks that the Justification type
827        // is still a tuple as given.
828        assert_ser_deser::<SpJustification, Justification>(
829            &([1, 2, 3, 4], vec![5, 6, 7, 8]),
830            &([1, 2, 3, 4], vec![5, 6, 7, 8]),
831        );
832    }
833
834    #[test]
835    fn storage_types_are_substrate_compatible() {
836        use sp_core::storage::{
837            StorageChangeSet as SpStorageChangeSet, StorageData as SpStorageData,
838            StorageKey as SpStorageKey,
839        };
840
841        assert_ser_deser(
842            &SpStorageKey(vec![1, 2, 3, 4, 5]),
843            &StorageKey(vec![1, 2, 3, 4, 5]),
844        );
845        assert_ser_deser(
846            &SpStorageData(vec![1, 2, 3, 4, 5]),
847            &StorageData(vec![1, 2, 3, 4, 5]),
848        );
849        assert_ser_deser(
850            &SpStorageChangeSet {
851                block: 1u64,
852                changes: vec![(SpStorageKey(vec![1]), Some(SpStorageData(vec![2])))],
853            },
854            &StorageChangeSet {
855                block: 1u64,
856                changes: vec![(StorageKey(vec![1]), Some(StorageData(vec![2])))],
857            },
858        );
859    }
860}