subxt_core/runtime_api/
mod.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//! Encode runtime API payloads, decode the associated values returned from them, and validate
6//! static runtime API payloads.
7//!
8//! # Example
9//!
10//! ```rust
11//! use subxt_macro::subxt;
12//! use subxt_core::runtime_api;
13//! use subxt_core::metadata;
14//!
15//! // If we generate types without `subxt`, we need to point to `::subxt_core`:
16//! #[subxt(
17//!     crate = "::subxt_core",
18//!     runtime_metadata_path = "../artifacts/polkadot_metadata_small.scale",
19//! )]
20//! pub mod polkadot {}
21//!
22//! // Some metadata we'll use to work with storage entries:
23//! let metadata_bytes = include_bytes!("../../../artifacts/polkadot_metadata_small.scale");
24//! let metadata = metadata::decode_from(&metadata_bytes[..]).unwrap();
25//!
26//! // Build a storage query to access account information.
27//! let payload = polkadot::apis().metadata().metadata_versions();
28//!
29//! // We can validate that the payload is compatible with the given metadata.
30//! runtime_api::validate(&payload, &metadata).unwrap();
31//!
32//! // Encode the payload name and arguments to hand to a node:
33//! let _call_name = runtime_api::call_name(&payload);
34//! let _call_args = runtime_api::call_args(&payload, &metadata).unwrap();
35//!
36//! // If we were to obtain a value back from the node, we could
37//! // then decode it using the same payload and metadata like so:
38//! let value_bytes = hex::decode("080e0000000f000000").unwrap();
39//! let value = runtime_api::decode_value(&mut &*value_bytes, &payload, &metadata).unwrap();
40//!
41//! println!("Available metadata versions: {value:?}");
42//! ```
43
44pub mod payload;
45
46use crate::error::{Error, MetadataError};
47use crate::metadata::{DecodeWithMetadata, Metadata};
48use alloc::borrow::ToOwned;
49use alloc::format;
50use alloc::string::String;
51use alloc::vec::Vec;
52use payload::Payload;
53
54/// Run the validation logic against some runtime API payload you'd like to use. Returns `Ok(())`
55/// if the payload is valid (or if it's not possible to check since the payload has no validation hash).
56/// Return an error if the payload was not valid or something went wrong trying to validate it (ie
57/// the runtime API in question do not exist at all)
58pub fn validate<P: Payload>(payload: &P, metadata: &Metadata) -> Result<(), Error> {
59    let Some(static_hash) = payload.validation_hash() else {
60        return Ok(());
61    };
62
63    let api_trait = metadata.runtime_api_trait_by_name_err(payload.trait_name())?;
64
65    let Some(runtime_hash) = api_trait.method_hash(payload.method_name()) else {
66        return Err(MetadataError::IncompatibleCodegen.into());
67    };
68    if static_hash != runtime_hash {
69        return Err(MetadataError::IncompatibleCodegen.into());
70    }
71    Ok(())
72}
73
74/// Return the name of the runtime API call from the payload.
75pub fn call_name<P: Payload>(payload: &P) -> String {
76    format!("{}_{}", payload.trait_name(), payload.method_name())
77}
78
79/// Return the encoded call args given a runtime API payload.
80pub fn call_args<P: Payload>(payload: &P, metadata: &Metadata) -> Result<Vec<u8>, Error> {
81    payload.encode_args(metadata)
82}
83
84/// Decode the value bytes at the location given by the provided runtime API payload.
85pub fn decode_value<P: Payload>(
86    bytes: &mut &[u8],
87    payload: &P,
88    metadata: &Metadata,
89) -> Result<P::ReturnType, Error> {
90    let api_method = metadata
91        .runtime_api_trait_by_name_err(payload.trait_name())?
92        .method_by_name(payload.method_name())
93        .ok_or_else(|| MetadataError::RuntimeMethodNotFound(payload.method_name().to_owned()))?;
94
95    let val = <P::ReturnType as DecodeWithMetadata>::decode_with_metadata(
96        &mut &bytes[..],
97        api_method.output_ty(),
98        metadata,
99    )?;
100
101    Ok(val)
102}