frame_decode/
lib.rs

1// Copyright (C) 2022-2025 Parity Technologies (UK) Ltd. (admin@parity.io)
2// This file is a part of the frame-decode crate.
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8//         http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16//! Decode extrinsics and storage values from substrate based networks which expose `frame-metadata::RuntimeMetadata`
17//! like Polkadot.
18//!
19//! - See [`extrinsics`] for decoding Extrinsics.
20//! - See [`storage`] for encoding/decoding storage keys and decoding values.
21//! - See [`runtime_apis`] for encoding Runtime API inputs and decoding Runtime API responses
22//! - See [`legacy_types`] to access historic type information for certain chains.
23//!
24#![deny(missing_docs)]
25#![cfg_attr(not(feature = "std"), no_std)]
26
27extern crate alloc;
28
29mod methods;
30mod utils;
31
32pub mod extrinsics {
33    //! This module contains functions for decoding extrinsics.
34    //!
35    //! - See [`decode_extrinsic`] for a general function to decode modern or historic extrinsics.
36    //! - See [`ExtrinsicTypeInfo`] for the underlying trait which extracts the relevant information.
37
38    pub use crate::methods::extrinsic_decoder::{
39        Extrinsic, ExtrinsicDecodeError, ExtrinsicExtensions, ExtrinsicOwned, ExtrinsicSignature,
40        ExtrinsicType, NamedArg, decode_extrinsic,
41    };
42    pub use crate::methods::extrinsic_type_info::{
43        ExtrinsicCallInfo, ExtrinsicExtensionInfo, ExtrinsicInfoArg, ExtrinsicInfoError,
44        ExtrinsicSignatureInfo, ExtrinsicTypeInfo,
45    };
46}
47
48pub mod storage {
49    //! This module contains functions for decoding storage keys and values.
50    //!
51    //! - See [`decode_storage_key`] and [`decode_storage_value`] to decode storage keys or values
52    //!   from modern or historic runtimes.
53    //! - See [`encode_storage_key_prefix`] to encode storage prefixes, and [`encode_storage_key`] to encode
54    //!   storage keys.
55    //! - See [`StorageTypeInfo`] for the underlying trait which provides storage entry information.
56    //! - See [`StorageEntryInfo`] for a underlying trait which provides information about the available
57    //!   storage entries.
58
59    pub use crate::methods::storage_decoder::{
60        StorageKey, StorageKeyDecodeError, StorageKeyPart, StorageKeyPartValue,
61        StorageKeyValueDecodeError, StorageValueDecodeError,
62        decode_default_storage_value_with_info, decode_storage_key, decode_storage_key_values,
63        decode_storage_key_with_info, decode_storage_value, decode_storage_value_with_info,
64    };
65    pub use crate::methods::storage_encoder::{
66        StorageKeyEncodeError, encode_storage_key, encode_storage_key_prefix,
67        encode_storage_key_suffix, encode_storage_key_suffix_to,
68        encode_storage_key_suffix_with_info_to, encode_storage_key_to,
69        encode_storage_key_with_info, encode_storage_key_with_info_to,
70    };
71    pub use crate::methods::storage_type_info::{
72        StorageEntry, StorageEntryInfo, StorageHasher, StorageInfo, StorageInfoError,
73        StorageKeyInfo, StorageTypeInfo,
74    };
75    pub use crate::utils::{
76        DecodableValues, EncodableValues, IntoDecodableValues, IntoEncodableValues,
77    };
78}
79
80pub mod constants {
81    //! This module contains types and functions for working with constants.
82    //!
83    //! - See [`decode_constant`] and [`decode_constant_with_info`] to decode constants
84    //! - See [`ConstantTypeInfo`] for the underlying trait which extracts constant
85    //!   information from metadata.
86    //! - See [`ConstantEntryInfo`] for a underlying trait which provides information about the available
87    //!   constants.
88
89    pub use crate::methods::constant_decoder::{
90        ConstantDecodeError, decode_constant, decode_constant_with_info,
91    };
92    pub use crate::methods::constant_type_info::{
93        ConstantEntry, ConstantEntryInfo, ConstantInfo, ConstantInfoError, ConstantTypeInfo,
94    };
95}
96
97pub mod runtime_apis {
98    //! This module contains types and functions for working with Runtime APIs.
99    //!
100    //! - See [`encode_runtime_api_name`] and [`encode_runtime_api_inputs`] to encode
101    //!   the name and inputs to make a Runtime API call.
102    //! - See [`decode_runtime_api_response`] to decode Runtime API responses.
103    //! - See [`RuntimeApiTypeInfo`] for the underlying trait which extracts the relevant information.
104    //! - See [`RuntimeApiEntryInfo`] for a underlying trait which provides information about the available
105    //!   Runtime APIs.
106
107    pub use crate::methods::runtime_api_decoder::{
108        RuntimeApiDecodeError, decode_runtime_api_response, decode_runtime_api_response_with_info,
109    };
110    pub use crate::methods::runtime_api_encoder::{
111        RuntimeApiInputsEncodeError, encode_runtime_api_inputs, encode_runtime_api_inputs_to,
112        encode_runtime_api_inputs_with_info_to, encode_runtime_api_name,
113    };
114    pub use crate::methods::runtime_api_type_info::{
115        RuntimeApiEntry, RuntimeApiEntryInfo, RuntimeApiInfo, RuntimeApiInfoError, RuntimeApiInput,
116        RuntimeApiTypeInfo,
117    };
118    pub use crate::utils::{EncodableValues, IntoEncodableValues};
119}
120
121pub mod view_functions {
122    //! This module contains types and functions for working with View Functions.
123    //!
124    //! - See [`RUNTIME_API_NAME`] and [`encode_view_function_inputs`] to obtain the Runtime API name
125    //!   and the encoded input data required to call a given View Function.
126    //! - See [`decode_view_function_response`] to decode View Function responses.
127    //! - See [`ViewFunctionTypeInfo`] for the underlying trait which extracts the relevant information.
128    //! - See [`ViewFunctionEntryInfo`] for a underlying trait which provides information about the available
129    //!   View Functions.
130
131    pub use crate::methods::view_function_decoder::{
132        ViewFunctionDecodeError, decode_view_function_response,
133        decode_view_function_response_with_info,
134    };
135    pub use crate::methods::view_function_encoder::{
136        RUNTIME_API_NAME, ViewFunctionInputsEncodeError, encode_view_function_inputs,
137        encode_view_function_inputs_to, encode_view_function_inputs_with_info_to,
138    };
139    pub use crate::methods::view_function_type_info::{
140        ViewFunctionEntry, ViewFunctionEntryInfo, ViewFunctionInfo, ViewFunctionInfoError,
141        ViewFunctionInput, ViewFunctionTypeInfo,
142    };
143    pub use crate::utils::{EncodableValues, IntoEncodableValues};
144}
145
146pub mod custom_values {
147    //! This module contains types and functions for working with custom values.
148    //!
149    //! - See [`decode_custom_value`] and [`decode_custom_value_with_info`] to decode custom values
150    //! - See [`CustomValueTypeInfo`] for the underlying trait which extracts custom value
151    //!   information from metadata.
152    //! - See [`CustomValueEntryInfo`] for a underlying trait which provides information about the available
153    //!   custom values.
154
155    pub use crate::methods::custom_value_decoder::{
156        CustomValueDecodeError, decode_custom_value, decode_custom_value_with_info,
157    };
158    pub use crate::methods::custom_value_type_info::{
159        CustomValue, CustomValueEntryInfo, CustomValueInfo, CustomValueInfoError,
160        CustomValueTypeInfo,
161    };
162}
163
164#[cfg(feature = "legacy-types")]
165pub mod legacy_types {
166    //! This module contains legacy types that can be used to decode pre-V14 blocks and storage.
167
168    use alloc::boxed::Box;
169    type GenericError = Box<dyn core::error::Error + Send + Sync + 'static>;
170
171    /// Deserialize bytes into a [`scale_info_legacy::ChainTypeRegistry`].
172    pub fn from_bytes(bytes: &[u8]) -> Result<scale_info_legacy::ChainTypeRegistry, GenericError> {
173        let types = serde_yaml::from_slice(bytes)?;
174        Ok(types)
175    }
176
177    pub mod polkadot {
178        //! Legacy types for Polkadot chains.
179
180        /// Legacy types for the Polkadot Relay Chain.
181        pub fn relay_chain() -> scale_info_legacy::ChainTypeRegistry {
182            // This is a convenience function to load the Polkadot relay chain types.
183            // It is used in the examples in this crate.
184            let bytes = include_bytes!("../types/polkadot_relay_types.yaml");
185            serde_yaml::from_slice(bytes).expect("Polkadot RC types are valid YAML")
186        }
187    }
188
189    // Hidden until the types are ready.
190    #[doc(hidden)]
191    pub mod kusama {
192        //! Legacy types for Kusama chains.
193
194        /// Legacy types for the Kusama Relay Chain.
195        pub fn relay_chain() -> scale_info_legacy::ChainTypeRegistry {
196            // This is a convenience function to load the Polkadot relay chain types.
197            // It is used in the examples in this crate.
198            let bytes = include_bytes!("../types/kusama_relay_types.yaml");
199            serde_yaml::from_slice(bytes).expect("Kusama RC types are valid YAML")
200        }
201
202        /// Legacy types for the Kusama Asset Hub.
203        pub fn asset_hub() -> scale_info_legacy::ChainTypeRegistry {
204            // This is a convenience function to load the Polkadot relay chain types.
205            // It is used in the examples in this crate.
206            let bytes = include_bytes!("../types/kusama_assethub_types.yaml");
207            serde_yaml::from_slice(bytes).expect("Kusama AssetHub types are valid YAML")
208        }
209    }
210}
211
212pub mod helpers {
213    //! Helper functions and types to assist with decoding.
214    //!
215    //! - [`type_registry_from_metadata`] is expected to be used when decoding things from historic
216    //!   runtimes, adding the ability to decode some types from information in the metadata.
217    //! - [`decode_with_error_tracing`] is like [`decode_with_visitor`], but
218    //!   will use a tracing visitor (if the `error-tracing` feature is enabled) to provide more
219    //!   information in the event that decoding fails.
220
221    pub use crate::methods::Entry;
222
223    pub use crate::utils::{
224        DecodableValues, DecodeErrorTrace, EncodableValues, IntoDecodableValues,
225        IntoEncodableValues, decode_with_error_tracing,
226    };
227    #[cfg(feature = "legacy")]
228    pub use crate::utils::{type_registry_from_metadata, type_registry_from_metadata_any};
229
230    /// An alias to [`scale_decode::visitor::decode_with_visitor`]. This can be used to decode the byte ranges
231    /// given back from functions like [`crate::extrinsics::decode_extrinsic`] or
232    /// [`crate::storage::decode_storage_key`].
233    pub use scale_decode::visitor::decode_with_visitor;
234
235    /// An alias to the underlying [`scale_decode`] crate.
236    pub use scale_decode;
237}
238
239#[cfg(test)]
240mod test {
241    use crate::methods::extrinsic_type_info::ExtrinsicTypeInfo;
242    use crate::methods::runtime_api_type_info::RuntimeApiTypeInfo;
243    use crate::methods::storage_type_info::StorageTypeInfo;
244    use crate::methods::view_function_type_info::ViewFunctionTypeInfo;
245    use crate::utils::ToTypeRegistry;
246    use scale_info_legacy::type_registry::TypeRegistryResolveError;
247    use scale_info_legacy::{ChainTypeRegistry, LookupName};
248    use scale_type_resolver::Field;
249
250    // This will panic if there is any issue decoding the legacy types we provide.
251    #[test]
252    fn test_deserializing_legacy_types() {
253        let _ = crate::legacy_types::polkadot::relay_chain();
254        let _ = crate::legacy_types::kusama::relay_chain();
255        let _ = crate::legacy_types::kusama::asset_hub();
256    }
257
258    fn legacy_types() -> [(&'static str, ChainTypeRegistry); 3] {
259        [
260            ("Polkadot RC", crate::legacy_types::polkadot::relay_chain()),
261            ("Kusama RC", crate::legacy_types::kusama::relay_chain()),
262            ("Kusama AH", crate::legacy_types::kusama::asset_hub()),
263        ]
264    }
265
266    fn all_type_registry_sets(
267        registry: &scale_info_legacy::ChainTypeRegistry,
268    ) -> impl Iterator<Item = scale_info_legacy::TypeRegistrySet<'_>> {
269        let all_spec_versions = core::iter::once(u64::MAX)
270            .chain(registry.spec_version_ranges().map(|(low, _high)| low));
271        all_spec_versions.map(|version| registry.for_spec_version(version))
272    }
273
274    #[test]
275    fn test_legacy_types_have_sane_type_names() {
276        // We ignore these ones:
277        let builtins = &[
278            "u8", "u16", "u32", "u64", "u128", "u256", "i8", "i16", "i32", "i64", "i128", "i256",
279            "char", "bool", "str",
280        ];
281
282        for (chain, types) in legacy_types() {
283            for types in all_type_registry_sets(&types) {
284                for ty in types.keys() {
285                    if let Some(path_and_name) = ty.name() {
286                        let name = path_and_name.split("::").last().unwrap();
287
288                        if builtins.contains(&name) {
289                            continue;
290                        }
291
292                        if name.starts_with(|c: char| !c.is_uppercase() || !c.is_ascii_alphabetic())
293                        {
294                            panic!("{chain}: {ty} does not begin with an uppercase letter");
295                        }
296                        if name.contains(|c: char| !c.is_ascii_alphanumeric()) {
297                            panic!("{chain}: {ty} contains a non-ascii-alphanumeric character");
298                        }
299                    }
300                }
301            }
302        }
303    }
304
305    #[test]
306    fn test_legacy_types_have_sane_field_names() {
307        fn assert_sane<'a>(
308            chain: &str,
309            ty: &LookupName,
310            fields: impl Iterator<Item = Field<'a, LookupName>>,
311        ) {
312            let field_names: Vec<_> = fields.map(|f| f.name).collect();
313
314            let all_fields_named = field_names.iter().all(|n| n.is_some());
315            let all_fields_unnamed = field_names.iter().all(|n| n.is_none());
316
317            if !(all_fields_named || all_fields_unnamed) {
318                panic!("{chain}: All fields must be named or unnamed, but aren't in '{ty}'");
319            }
320            if all_fields_named {
321                for name in field_names.into_iter().map(|n| n.unwrap()) {
322                    let Some(fst) = name.chars().next() else {
323                        panic!("{chain}: {ty} has a present but empty field name");
324                    };
325                    if !fst.is_ascii_alphabetic() {
326                        panic!(
327                            "{chain}: {ty} field name '{name}' is invalid (does not start with ascii letter)"
328                        );
329                    }
330                    if !name.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') {
331                        panic!(
332                            "{chain}: {ty} field name '{name}' is invalid (non-ascii-letter-or-number-or-underscore present in the name"
333                        );
334                    }
335                    if name.contains(|c: char| c.is_uppercase()) {
336                        panic!(
337                            "{chain}: {ty} field name '{name}' contains uppercase. Field names should be lowercase only"
338                        );
339                    }
340                }
341            }
342        }
343
344        for (chain, types) in legacy_types() {
345            for types in all_type_registry_sets(&types) {
346                for ty in types.keys() {
347                    let visitor = scale_type_resolver::visitor::new((), |_, _| ())
348                        .visit_variant(|_ctx, _path, vars| {
349                            for variant in vars {
350                                assert_sane(chain, &ty, variant.fields);
351                            }
352                        })
353                        .visit_composite(|_ctx, _path, fields| {
354                            assert_sane(chain, &ty, fields);
355                        });
356
357                    if let Err(e) = types.resolve_type(ty.clone(), visitor) {
358                        match e {
359                            TypeRegistryResolveError::UnexpectedBitOrderType
360                            | TypeRegistryResolveError::UnexpectedBitStoreType => {
361                                /* Ignore these */
362                            }
363                            e => panic!("{chain}: Cannot resolve type '{ty}': {e}"),
364                        }
365                    }
366                }
367            }
368        }
369    }
370
371    #[test]
372    fn test_legacy_types_have_sane_variant_names() {
373        fn assert_sane(chain: &str, ty: &LookupName, variant_name: &str) {
374            let Some(fst) = variant_name.chars().next() else {
375                panic!("{chain}: Enum {ty} has an empty variant");
376            };
377
378            if !fst.is_uppercase() {
379                panic!(
380                    "{chain}: Enum {ty} variant name '{variant_name}' should start with an uppercase letter"
381                );
382            }
383            if !fst.is_ascii_alphabetic() {
384                panic!(
385                    "{chain}: Enum {ty} variant name '{variant_name}' should start with an ASCII letter"
386                );
387            }
388            if !variant_name.chars().all(|c| c.is_ascii_alphanumeric()) {
389                panic!(
390                    "{chain}: Enum {ty} variant name '{variant_name}' is invalid (non-ascii-letter-or-number present in the name"
391                );
392            }
393        }
394
395        for (chain, types) in legacy_types() {
396            for types in all_type_registry_sets(&types) {
397                for ty in types.keys() {
398                    let visitor = scale_type_resolver::visitor::new((), |_, _| ()).visit_variant(
399                        |_ctx, _path, vars| {
400                            for variant in vars {
401                                assert_sane(chain, &ty, variant.name);
402                            }
403                        },
404                    );
405
406                    if let Err(e) = types.resolve_type(ty.clone(), visitor) {
407                        match e {
408                            TypeRegistryResolveError::UnexpectedBitOrderType
409                            | TypeRegistryResolveError::UnexpectedBitStoreType => {
410                                /* Ignore these */
411                            }
412                            e => panic!("{chain}: Cannot resolve type '{ty}': {e}"),
413                        }
414                    }
415                }
416            }
417        }
418    }
419
420    macro_rules! impls_trait {
421        ($type:ty, $trait:path) => {
422            const _: () = {
423                const fn assert_impl<T: $trait>() {}
424                assert_impl::<$type>();
425            };
426        };
427    }
428
429    // Just a sanity check that all of the metadata versions we expect implement
430    // all of the key traits. Makes it harder to miss something when adding a new metadata
431    // version; just add it below and implement the traits until everything compiles.
432    #[rustfmt::skip]
433    const _: () = {
434        impls_trait!(frame_metadata::v8::RuntimeMetadataV8, ExtrinsicTypeInfo);
435        impls_trait!(frame_metadata::v9::RuntimeMetadataV9, ExtrinsicTypeInfo);
436        impls_trait!(frame_metadata::v10::RuntimeMetadataV10, ExtrinsicTypeInfo);
437        impls_trait!(frame_metadata::v11::RuntimeMetadataV11, ExtrinsicTypeInfo);
438        impls_trait!(frame_metadata::v12::RuntimeMetadataV12, ExtrinsicTypeInfo);
439        impls_trait!(frame_metadata::v13::RuntimeMetadataV13, ExtrinsicTypeInfo);
440        impls_trait!(frame_metadata::v14::RuntimeMetadataV14, ExtrinsicTypeInfo);
441        impls_trait!(frame_metadata::v15::RuntimeMetadataV15, ExtrinsicTypeInfo);
442        impls_trait!(frame_metadata::v16::RuntimeMetadataV16, ExtrinsicTypeInfo);
443
444        impls_trait!(frame_metadata::v8::RuntimeMetadataV8, StorageTypeInfo);
445        impls_trait!(frame_metadata::v9::RuntimeMetadataV9, StorageTypeInfo);
446        impls_trait!(frame_metadata::v10::RuntimeMetadataV10, StorageTypeInfo);
447        impls_trait!(frame_metadata::v11::RuntimeMetadataV11, StorageTypeInfo);
448        impls_trait!(frame_metadata::v12::RuntimeMetadataV12, StorageTypeInfo);
449        impls_trait!(frame_metadata::v13::RuntimeMetadataV13, StorageTypeInfo);
450        impls_trait!(frame_metadata::v14::RuntimeMetadataV14, StorageTypeInfo);
451        impls_trait!(frame_metadata::v15::RuntimeMetadataV15, StorageTypeInfo);
452        impls_trait!(frame_metadata::v16::RuntimeMetadataV16, StorageTypeInfo);
453
454        // Only V16+ metadata contains any view function information. Prior to this,
455        // hardly any view functions existed. We _could_ extend our legacy type information
456        // to support them if necessary, but it's unlikely it will be.
457        impls_trait!(frame_metadata::v16::RuntimeMetadataV16, ViewFunctionTypeInfo);
458
459        // Only V15+ metadata has Runtime API info in. For earlier, we lean on
460        // our scale-Info-legacy type registry to provide the information.
461        impls_trait!(scale_info_legacy::TypeRegistry, RuntimeApiTypeInfo);
462        impls_trait!(scale_info_legacy::TypeRegistrySet, RuntimeApiTypeInfo);
463        impls_trait!(frame_metadata::v15::RuntimeMetadataV15, RuntimeApiTypeInfo);
464        impls_trait!(frame_metadata::v16::RuntimeMetadataV16, RuntimeApiTypeInfo);
465
466        // This is a legacy trait and so only legacy metadata versions implement it:
467        impls_trait!(frame_metadata::v8::RuntimeMetadataV8, ToTypeRegistry);
468        impls_trait!(frame_metadata::v9::RuntimeMetadataV9, ToTypeRegistry);
469        impls_trait!(frame_metadata::v10::RuntimeMetadataV10, ToTypeRegistry);
470        impls_trait!(frame_metadata::v11::RuntimeMetadataV11, ToTypeRegistry);
471        impls_trait!(frame_metadata::v12::RuntimeMetadataV12, ToTypeRegistry);
472        impls_trait!(frame_metadata::v13::RuntimeMetadataV13, ToTypeRegistry);
473    };
474}