subxt_core/config/
substrate.rs

1// Copyright 2019-2024 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::{Config, DefaultExtrinsicParams, DefaultExtrinsicParamsBuilder, Hasher, Header};
8pub use crate::utils::{AccountId32, MultiAddress, MultiSignature};
9use alloc::format;
10use alloc::vec::Vec;
11use codec::{Decode, Encode};
12pub use primitive_types::{H256, U256};
13use serde::{Deserialize, Serialize};
14use subxt_metadata::Metadata;
15
16/// Default set of commonly used types by Substrate runtimes.
17// Note: We only use this at the type level, so it should be impossible to
18// create an instance of it.
19// The trait implementations exist just to make life easier,
20// but shouldn't strictly be necessary since users can't instantiate this type.
21#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
22pub enum SubstrateConfig {}
23
24impl Config for SubstrateConfig {
25    type AccountId = AccountId32;
26    type Address = MultiAddress<Self::AccountId, u32>;
27    type Signature = MultiSignature;
28    type Hasher = DynamicHasher256;
29    type Header = SubstrateHeader<u32, DynamicHasher256>;
30    type ExtrinsicParams = SubstrateExtrinsicParams<Self>;
31    type AssetId = u32;
32}
33
34/// A struct representing the signed extra and additional parameters required
35/// to construct a transaction for the default substrate node.
36pub type SubstrateExtrinsicParams<T> = DefaultExtrinsicParams<T>;
37
38/// A builder which leads to [`SubstrateExtrinsicParams`] being constructed.
39/// This is what you provide to methods like `sign_and_submit()`.
40pub type SubstrateExtrinsicParamsBuilder<T> = DefaultExtrinsicParamsBuilder<T>;
41
42/// A hasher (ie implements [`Hasher`]) which hashes values using the blaks2_256 algorithm.
43#[derive(Debug, Clone, Copy, PartialEq, Eq)]
44pub struct BlakeTwo256;
45
46impl Hasher for BlakeTwo256 {
47    type Output = H256;
48
49    fn new(_metadata: &Metadata) -> Self {
50        Self
51    }
52
53    fn hash(&self, s: &[u8]) -> Self::Output {
54        sp_crypto_hashing::blake2_256(s).into()
55    }
56}
57
58/// A hasher (ie implements [`Hasher`]) which inspects the runtime metadata to decide how to
59/// hash types, falling back to blake2_256 if the hasher information is not available.
60///
61/// Currently this hasher supports only `BlakeTwo256` and `Keccak256` hashing methods.
62#[derive(Debug, Clone, Copy, PartialEq, Eq)]
63pub struct DynamicHasher256(HashType);
64
65#[derive(Debug, Clone, Copy, PartialEq, Eq)]
66enum HashType {
67    // Most chains use this:
68    BlakeTwo256,
69    // Chains like Hyperbridge use this (tends to be eth compatible chains)
70    Keccak256,
71    // If we don't have V16 metadata, we'll emit this and default to BlakeTwo256.
72    Unknown,
73}
74
75impl Hasher for DynamicHasher256 {
76    type Output = H256;
77
78    fn new(metadata: &Metadata) -> Self {
79        // Determine the Hash associated type used for the current chain, if possible.
80        let Some(system_pallet) = metadata.pallet_by_name("System") else {
81            return Self(HashType::Unknown);
82        };
83        let Some(hash_ty_id) = system_pallet.associated_type_id("Hashing") else {
84            return Self(HashType::Unknown);
85        };
86
87        let ty = metadata
88            .types()
89            .resolve(hash_ty_id)
90            .expect("Type information for 'Hashing' associated type should be in metadata");
91
92        let hash_type = match ty.path.ident().as_deref().unwrap_or("") {
93            "BlakeTwo256" => HashType::BlakeTwo256,
94            "Keccak256" => HashType::Keccak256,
95            _ => HashType::Unknown,
96        };
97
98        Self(hash_type)
99    }
100
101    fn hash(&self, s: &[u8]) -> Self::Output {
102        match self.0 {
103            HashType::BlakeTwo256 | HashType::Unknown => sp_crypto_hashing::blake2_256(s).into(),
104            HashType::Keccak256 => sp_crypto_hashing::keccak_256(s).into(),
105        }
106    }
107}
108
109/// A generic Substrate header type, adapted from `sp_runtime::generic::Header`.
110/// The block number and hasher can be configured to adapt this for other nodes.
111#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
112#[serde(rename_all = "camelCase")]
113pub struct SubstrateHeader<N: Copy + Into<U256> + TryFrom<U256>, H: Hasher> {
114    /// The parent hash.
115    pub parent_hash: H::Output,
116    /// The block number.
117    #[serde(
118        serialize_with = "serialize_number",
119        deserialize_with = "deserialize_number"
120    )]
121    #[codec(compact)]
122    pub number: N,
123    /// The state trie merkle root
124    pub state_root: H::Output,
125    /// The merkle root of the extrinsics.
126    pub extrinsics_root: H::Output,
127    /// A chain-specific digest of data useful for light clients or referencing auxiliary data.
128    pub digest: Digest,
129}
130
131impl<N, H> Header for SubstrateHeader<N, H>
132where
133    N: Copy + Into<u64> + Into<U256> + TryFrom<U256> + Encode,
134    H: Hasher,
135    SubstrateHeader<N, H>: Encode + Decode,
136{
137    type Number = N;
138    type Hasher = H;
139
140    fn number(&self) -> Self::Number {
141        self.number
142    }
143}
144
145/// Generic header digest. From `sp_runtime::generic::digest`.
146#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Default)]
147pub struct Digest {
148    /// A list of digest items.
149    pub logs: Vec<DigestItem>,
150}
151
152/// Digest item that is able to encode/decode 'system' digest items and
153/// provide opaque access to other items. From `sp_runtime::generic::digest`.
154#[derive(Debug, PartialEq, Eq, Clone)]
155pub enum DigestItem {
156    /// A pre-runtime digest.
157    ///
158    /// These are messages from the consensus engine to the runtime, although
159    /// the consensus engine can (and should) read them itself to avoid
160    /// code and state duplication. It is erroneous for a runtime to produce
161    /// these, but this is not (yet) checked.
162    ///
163    /// NOTE: the runtime is not allowed to panic or fail in an `on_initialize`
164    /// call if an expected `PreRuntime` digest is not present. It is the
165    /// responsibility of a external block verifier to check this. Runtime API calls
166    /// will initialize the block without pre-runtime digests, so initialization
167    /// cannot fail when they are missing.
168    PreRuntime(ConsensusEngineId, Vec<u8>),
169
170    /// A message from the runtime to the consensus engine. This should *never*
171    /// be generated by the native code of any consensus engine, but this is not
172    /// checked (yet).
173    Consensus(ConsensusEngineId, Vec<u8>),
174
175    /// Put a Seal on it. This is only used by native code, and is never seen
176    /// by runtimes.
177    Seal(ConsensusEngineId, Vec<u8>),
178
179    /// Some other thing. Unsupported and experimental.
180    Other(Vec<u8>),
181
182    /// An indication for the light clients that the runtime execution
183    /// environment is updated.
184    ///
185    /// Currently this is triggered when:
186    /// 1. Runtime code blob is changed or
187    /// 2. `heap_pages` value is changed.
188    RuntimeEnvironmentUpdated,
189}
190
191// From sp_runtime::generic, DigestItem enum indexes are encoded using this:
192#[repr(u32)]
193#[derive(Encode, Decode)]
194enum DigestItemType {
195    Other = 0u32,
196    Consensus = 4u32,
197    Seal = 5u32,
198    PreRuntime = 6u32,
199    RuntimeEnvironmentUpdated = 8u32,
200}
201impl Encode for DigestItem {
202    fn encode(&self) -> Vec<u8> {
203        let mut v = Vec::new();
204
205        match self {
206            Self::Consensus(val, data) => {
207                DigestItemType::Consensus.encode_to(&mut v);
208                (val, data).encode_to(&mut v);
209            }
210            Self::Seal(val, sig) => {
211                DigestItemType::Seal.encode_to(&mut v);
212                (val, sig).encode_to(&mut v);
213            }
214            Self::PreRuntime(val, data) => {
215                DigestItemType::PreRuntime.encode_to(&mut v);
216                (val, data).encode_to(&mut v);
217            }
218            Self::Other(val) => {
219                DigestItemType::Other.encode_to(&mut v);
220                val.encode_to(&mut v);
221            }
222            Self::RuntimeEnvironmentUpdated => {
223                DigestItemType::RuntimeEnvironmentUpdated.encode_to(&mut v);
224            }
225        }
226
227        v
228    }
229}
230impl Decode for DigestItem {
231    fn decode<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> {
232        let item_type: DigestItemType = Decode::decode(input)?;
233        match item_type {
234            DigestItemType::PreRuntime => {
235                let vals: (ConsensusEngineId, Vec<u8>) = Decode::decode(input)?;
236                Ok(Self::PreRuntime(vals.0, vals.1))
237            }
238            DigestItemType::Consensus => {
239                let vals: (ConsensusEngineId, Vec<u8>) = Decode::decode(input)?;
240                Ok(Self::Consensus(vals.0, vals.1))
241            }
242            DigestItemType::Seal => {
243                let vals: (ConsensusEngineId, Vec<u8>) = Decode::decode(input)?;
244                Ok(Self::Seal(vals.0, vals.1))
245            }
246            DigestItemType::Other => Ok(Self::Other(Decode::decode(input)?)),
247            DigestItemType::RuntimeEnvironmentUpdated => Ok(Self::RuntimeEnvironmentUpdated),
248        }
249    }
250}
251
252/// Consensus engine unique ID. From `sp_runtime::ConsensusEngineId`.
253pub type ConsensusEngineId = [u8; 4];
254
255impl serde::Serialize for DigestItem {
256    fn serialize<S>(&self, seq: S) -> Result<S::Ok, S::Error>
257    where
258        S: serde::Serializer,
259    {
260        self.using_encoded(|bytes| impl_serde::serialize::serialize(bytes, seq))
261    }
262}
263
264impl<'a> serde::Deserialize<'a> for DigestItem {
265    fn deserialize<D>(de: D) -> Result<Self, D::Error>
266    where
267        D: serde::Deserializer<'a>,
268    {
269        let r = impl_serde::serialize::deserialize(de)?;
270        Decode::decode(&mut &r[..])
271            .map_err(|e| serde::de::Error::custom(format!("Decode error: {e}")))
272    }
273}
274
275fn serialize_number<S, T: Copy + Into<U256>>(val: &T, s: S) -> Result<S::Ok, S::Error>
276where
277    S: serde::Serializer,
278{
279    let u256: U256 = (*val).into();
280    serde::Serialize::serialize(&u256, s)
281}
282
283fn deserialize_number<'a, D, T: TryFrom<U256>>(d: D) -> Result<T, D::Error>
284where
285    D: serde::Deserializer<'a>,
286{
287    // At the time of writing, Smoldot gives back block numbers in numeric rather
288    // than hex format. So let's support deserializing from both here:
289    let number_or_hex = NumberOrHex::deserialize(d)?;
290    let u256 = number_or_hex.into_u256();
291    TryFrom::try_from(u256).map_err(|_| serde::de::Error::custom("Try from failed"))
292}
293
294/// A number type that can be serialized both as a number or a string that encodes a number in a
295/// string.
296///
297/// We allow two representations of the block number as input. Either we deserialize to the type
298/// that is specified in the block type or we attempt to parse given hex value.
299///
300/// The primary motivation for having this type is to avoid overflows when using big integers in
301/// JavaScript (which we consider as an important RPC API consumer).
302#[derive(Copy, Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
303#[serde(untagged)]
304pub enum NumberOrHex {
305    /// The number represented directly.
306    Number(u64),
307    /// Hex representation of the number.
308    Hex(U256),
309}
310
311impl NumberOrHex {
312    /// Converts this number into an U256.
313    pub fn into_u256(self) -> U256 {
314        match self {
315            NumberOrHex::Number(n) => n.into(),
316            NumberOrHex::Hex(h) => h,
317        }
318    }
319}
320
321impl From<NumberOrHex> for U256 {
322    fn from(num_or_hex: NumberOrHex) -> U256 {
323        num_or_hex.into_u256()
324    }
325}
326
327macro_rules! into_number_or_hex {
328    ($($t: ty)+) => {
329        $(
330            impl From<$t> for NumberOrHex {
331                fn from(x: $t) -> Self {
332                    NumberOrHex::Number(x.into())
333                }
334            }
335        )+
336    }
337}
338into_number_or_hex!(u8 u16 u32 u64);
339
340impl From<u128> for NumberOrHex {
341    fn from(n: u128) -> Self {
342        NumberOrHex::Hex(n.into())
343    }
344}
345
346impl From<U256> for NumberOrHex {
347    fn from(n: U256) -> Self {
348        NumberOrHex::Hex(n)
349    }
350}
351
352#[cfg(test)]
353mod test {
354    use super::*;
355
356    // Smoldot returns numeric block numbers in the header at the time of writing;
357    // ensure we can deserialize them properly.
358    #[test]
359    fn can_deserialize_numeric_block_number() {
360        let numeric_block_number_json = r#"
361            {
362                "digest": {
363                    "logs": []
364                },
365                "extrinsicsRoot": "0x0000000000000000000000000000000000000000000000000000000000000000",
366                "number": 4,
367                "parentHash": "0xcb2690b2c85ceab55be03fc7f7f5f3857e7efeb7a020600ebd4331e10be2f7a5",
368                "stateRoot": "0x0000000000000000000000000000000000000000000000000000000000000000"
369            }
370        "#;
371
372        let header: SubstrateHeader<u32, BlakeTwo256> =
373            serde_json::from_str(numeric_block_number_json).expect("valid block header");
374        assert_eq!(header.number(), 4);
375    }
376
377    // Substrate returns hex block numbers; ensure we can also deserialize those OK.
378    #[test]
379    fn can_deserialize_hex_block_number() {
380        let numeric_block_number_json = r#"
381            {
382                "digest": {
383                    "logs": []
384                },
385                "extrinsicsRoot": "0x0000000000000000000000000000000000000000000000000000000000000000",
386                "number": "0x04",
387                "parentHash": "0xcb2690b2c85ceab55be03fc7f7f5f3857e7efeb7a020600ebd4331e10be2f7a5",
388                "stateRoot": "0x0000000000000000000000000000000000000000000000000000000000000000"
389            }
390        "#;
391
392        let header: SubstrateHeader<u32, BlakeTwo256> =
393            serde_json::from_str(numeric_block_number_json).expect("valid block header");
394        assert_eq!(header.number(), 4);
395    }
396}