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 let Some(api_method) = api_trait.method_by_name(payload.method_name()) else {
65 return Err(MetadataError::IncompatibleCodegen.into());
66 };
67
68 let runtime_hash = api_method.hash();
69 if static_hash != runtime_hash {
70 return Err(MetadataError::IncompatibleCodegen.into());
71 }
72 Ok(())
73}
74
75/// Return the name of the runtime API call from the payload.
76pub fn call_name<P: Payload>(payload: &P) -> String {
77 format!("{}_{}", payload.trait_name(), payload.method_name())
78}
79
80/// Return the encoded call args given a runtime API payload.
81pub fn call_args<P: Payload>(payload: &P, metadata: &Metadata) -> Result<Vec<u8>, Error> {
82 payload.encode_args(metadata)
83}
84
85/// Decode the value bytes at the location given by the provided runtime API payload.
86pub fn decode_value<P: Payload>(
87 bytes: &mut &[u8],
88 payload: &P,
89 metadata: &Metadata,
90) -> Result<P::ReturnType, Error> {
91 let api_method = metadata
92 .runtime_api_trait_by_name_err(payload.trait_name())?
93 .method_by_name(payload.method_name())
94 .ok_or_else(|| MetadataError::RuntimeMethodNotFound(payload.method_name().to_owned()))?;
95
96 let val = <P::ReturnType as DecodeWithMetadata>::decode_with_metadata(
97 &mut &bytes[..],
98 api_method.output_ty(),
99 metadata,
100 )?;
101
102 Ok(val)
103}