gear_subxt/config/
substrate.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//! Substrate specific configuration
6
7use super::{
8    extrinsic_params::{BaseExtrinsicParams, BaseExtrinsicParamsBuilder},
9    Config, Hasher, Header,
10};
11use codec::{Decode, Encode};
12use serde::{Deserialize, Serialize};
13
14pub use crate::utils::{AccountId32, MultiAddress, MultiSignature};
15pub use primitive_types::{H256, U256};
16
17/// Default set of commonly used types by Substrate runtimes.
18// Note: We only use this at the type level, so it should be impossible to
19// create an instance of it.
20pub enum SubstrateConfig {}
21
22impl Config for SubstrateConfig {
23    type Index = u32;
24    type Hash = H256;
25    type AccountId = AccountId32;
26    type Address = MultiAddress<Self::AccountId, u32>;
27    type Signature = MultiSignature;
28    type Hasher = BlakeTwo256;
29    type Header = SubstrateHeader<u32, BlakeTwo256>;
30    type ExtrinsicParams = SubstrateExtrinsicParams<Self>;
31}
32
33/// A struct representing the signed extra and additional parameters required
34/// to construct a transaction for the default substrate node.
35pub type SubstrateExtrinsicParams<T> = BaseExtrinsicParams<T, AssetTip>;
36
37/// A builder which leads to [`SubstrateExtrinsicParams`] being constructed.
38/// This is what you provide to methods like `sign_and_submit()`.
39pub type SubstrateExtrinsicParamsBuilder<T> = BaseExtrinsicParamsBuilder<T, AssetTip>;
40
41// Because Era is one of the args to our extrinsic params.
42pub use super::extrinsic_params::Era;
43
44/// A tip payment made in the form of a specific asset.
45#[derive(Copy, Clone, Debug, Default, Encode)]
46pub struct AssetTip {
47    #[codec(compact)]
48    tip: u128,
49    asset: Option<u32>,
50}
51
52impl AssetTip {
53    /// Create a new tip of the amount provided.
54    pub fn new(amount: u128) -> Self {
55        AssetTip {
56            tip: amount,
57            asset: None,
58        }
59    }
60
61    /// Designate the tip as being of a particular asset class.
62    /// If this is not set, then the native currency is used.
63    pub fn of_asset(mut self, asset: u32) -> Self {
64        self.asset = Some(asset);
65        self
66    }
67}
68
69impl From<u128> for AssetTip {
70    fn from(n: u128) -> Self {
71        AssetTip::new(n)
72    }
73}
74
75/// A type that can hash values using the blaks2_256 algorithm.
76#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode)]
77pub struct BlakeTwo256;
78
79impl Hasher for BlakeTwo256 {
80    type Output = H256;
81    fn hash(s: &[u8]) -> Self::Output {
82        sp_core_hashing::blake2_256(s).into()
83    }
84}
85
86/// A generic Substrate header type, adapted from `sp_runtime::generic::Header`.
87/// The block number and hasher can be configured to adapt this for other nodes.
88#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
89#[serde(rename_all = "camelCase")]
90pub struct SubstrateHeader<N: Copy + Into<U256> + TryFrom<U256>, H: Hasher> {
91    /// The parent hash.
92    pub parent_hash: H::Output,
93    /// The block number.
94    #[serde(
95        serialize_with = "serialize_number",
96        deserialize_with = "deserialize_number"
97    )]
98    #[codec(compact)]
99    pub number: N,
100    /// The state trie merkle root
101    pub state_root: H::Output,
102    /// The merkle root of the extrinsics.
103    pub extrinsics_root: H::Output,
104    /// A chain-specific digest of data useful for light clients or referencing auxiliary data.
105    pub digest: Digest,
106}
107
108impl<N, H> Header for SubstrateHeader<N, H>
109where
110    N: Copy + Into<u64> + Into<U256> + TryFrom<U256> + Encode,
111    H: Hasher + Encode,
112    SubstrateHeader<N, H>: Encode,
113{
114    type Number = N;
115    type Hasher = H;
116    fn number(&self) -> Self::Number {
117        self.number
118    }
119}
120
121/// Generic header digest. From `sp_runtime::generic::digest`.
122#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Default)]
123pub struct Digest {
124    /// A list of digest items.
125    pub logs: Vec<DigestItem>,
126}
127
128/// Digest item that is able to encode/decode 'system' digest items and
129/// provide opaque access to other items. From `sp_runtime::generic::digest`.
130#[derive(Debug, PartialEq, Eq, Clone)]
131pub enum DigestItem {
132    /// A pre-runtime digest.
133    ///
134    /// These are messages from the consensus engine to the runtime, although
135    /// the consensus engine can (and should) read them itself to avoid
136    /// code and state duplication. It is erroneous for a runtime to produce
137    /// these, but this is not (yet) checked.
138    ///
139    /// NOTE: the runtime is not allowed to panic or fail in an `on_initialize`
140    /// call if an expected `PreRuntime` digest is not present. It is the
141    /// responsibility of a external block verifier to check this. Runtime API calls
142    /// will initialize the block without pre-runtime digests, so initialization
143    /// cannot fail when they are missing.
144    PreRuntime(ConsensusEngineId, Vec<u8>),
145
146    /// A message from the runtime to the consensus engine. This should *never*
147    /// be generated by the native code of any consensus engine, but this is not
148    /// checked (yet).
149    Consensus(ConsensusEngineId, Vec<u8>),
150
151    /// Put a Seal on it. This is only used by native code, and is never seen
152    /// by runtimes.
153    Seal(ConsensusEngineId, Vec<u8>),
154
155    /// Some other thing. Unsupported and experimental.
156    Other(Vec<u8>),
157
158    /// An indication for the light clients that the runtime execution
159    /// environment is updated.
160    ///
161    /// Currently this is triggered when:
162    /// 1. Runtime code blob is changed or
163    /// 2. `heap_pages` value is changed.
164    RuntimeEnvironmentUpdated,
165}
166
167// From sp_runtime::generic, DigestItem enum indexes are encoded using this:
168#[repr(u32)]
169#[derive(Encode, Decode)]
170enum DigestItemType {
171    Other = 0u32,
172    Consensus = 4u32,
173    Seal = 5u32,
174    PreRuntime = 6u32,
175    RuntimeEnvironmentUpdated = 8u32,
176}
177impl Encode for DigestItem {
178    fn encode(&self) -> Vec<u8> {
179        let mut v = Vec::new();
180
181        match self {
182            Self::Consensus(val, data) => {
183                DigestItemType::Consensus.encode_to(&mut v);
184                (val, data).encode_to(&mut v);
185            }
186            Self::Seal(val, sig) => {
187                DigestItemType::Seal.encode_to(&mut v);
188                (val, sig).encode_to(&mut v);
189            }
190            Self::PreRuntime(val, data) => {
191                DigestItemType::PreRuntime.encode_to(&mut v);
192                (val, data).encode_to(&mut v);
193            }
194            Self::Other(val) => {
195                DigestItemType::Other.encode_to(&mut v);
196                val.encode_to(&mut v);
197            }
198            Self::RuntimeEnvironmentUpdated => {
199                DigestItemType::RuntimeEnvironmentUpdated.encode_to(&mut v);
200            }
201        }
202
203        v
204    }
205}
206impl Decode for DigestItem {
207    fn decode<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> {
208        let item_type: DigestItemType = Decode::decode(input)?;
209        match item_type {
210            DigestItemType::PreRuntime => {
211                let vals: (ConsensusEngineId, Vec<u8>) = Decode::decode(input)?;
212                Ok(Self::PreRuntime(vals.0, vals.1))
213            }
214            DigestItemType::Consensus => {
215                let vals: (ConsensusEngineId, Vec<u8>) = Decode::decode(input)?;
216                Ok(Self::Consensus(vals.0, vals.1))
217            }
218            DigestItemType::Seal => {
219                let vals: (ConsensusEngineId, Vec<u8>) = Decode::decode(input)?;
220                Ok(Self::Seal(vals.0, vals.1))
221            }
222            DigestItemType::Other => Ok(Self::Other(Decode::decode(input)?)),
223            DigestItemType::RuntimeEnvironmentUpdated => Ok(Self::RuntimeEnvironmentUpdated),
224        }
225    }
226}
227
228/// Consensus engine unique ID. From `sp_runtime::ConsensusEngineId`.
229pub type ConsensusEngineId = [u8; 4];
230
231impl serde::Serialize for DigestItem {
232    fn serialize<S>(&self, seq: S) -> Result<S::Ok, S::Error>
233    where
234        S: serde::Serializer,
235    {
236        self.using_encoded(|bytes| impl_serde::serialize::serialize(bytes, seq))
237    }
238}
239
240impl<'a> serde::Deserialize<'a> for DigestItem {
241    fn deserialize<D>(de: D) -> Result<Self, D::Error>
242    where
243        D: serde::Deserializer<'a>,
244    {
245        let r = impl_serde::serialize::deserialize(de)?;
246        Decode::decode(&mut &r[..])
247            .map_err(|e| serde::de::Error::custom(format!("Decode error: {e}")))
248    }
249}
250
251fn serialize_number<S, T: Copy + Into<U256>>(val: &T, s: S) -> Result<S::Ok, S::Error>
252where
253    S: serde::Serializer,
254{
255    let u256: U256 = (*val).into();
256    serde::Serialize::serialize(&u256, s)
257}
258
259fn deserialize_number<'a, D, T: TryFrom<U256>>(d: D) -> Result<T, D::Error>
260where
261    D: serde::Deserializer<'a>,
262{
263    // At the time of writing, Smoldot gives back block numbers in numeric rather
264    // than hex format. So let's support deserializing from both here:
265    use crate::rpc::types::NumberOrHex;
266    let number_or_hex = NumberOrHex::deserialize(d)?;
267    let u256 = number_or_hex.into_u256();
268    TryFrom::try_from(u256).map_err(|_| serde::de::Error::custom("Try from failed"))
269}
270
271#[cfg(test)]
272mod test {
273    use super::*;
274
275    // Smoldot returns numeric block numbers in the header at the time of writing;
276    // ensure we can deserialize them properly.
277    #[test]
278    fn can_deserialize_numeric_block_number() {
279        let numeric_block_number_json = r#"
280            {
281                "digest": {
282                    "logs": []
283                },
284                "extrinsicsRoot": "0x0000000000000000000000000000000000000000000000000000000000000000",
285                "number": 4,
286                "parentHash": "0xcb2690b2c85ceab55be03fc7f7f5f3857e7efeb7a020600ebd4331e10be2f7a5",
287                "stateRoot": "0x0000000000000000000000000000000000000000000000000000000000000000"
288            }
289        "#;
290
291        let header: SubstrateHeader<u32, BlakeTwo256> =
292            serde_json::from_str(numeric_block_number_json).expect("valid block header");
293        assert_eq!(header.number(), 4);
294    }
295
296    // Substrate returns hex block numbers; ensure we can also deserialize those OK.
297    #[test]
298    fn can_deserialize_hex_block_number() {
299        let numeric_block_number_json = r#"
300            {
301                "digest": {
302                    "logs": []
303                },
304                "extrinsicsRoot": "0x0000000000000000000000000000000000000000000000000000000000000000",
305                "number": "0x04",
306                "parentHash": "0xcb2690b2c85ceab55be03fc7f7f5f3857e7efeb7a020600ebd4331e10be2f7a5",
307                "stateRoot": "0x0000000000000000000000000000000000000000000000000000000000000000"
308            }
309        "#;
310
311        let header: SubstrateHeader<u32, BlakeTwo256> =
312            serde_json::from_str(numeric_block_number_json).expect("valid block header");
313        assert_eq!(header.number(), 4);
314    }
315}