gear_subxt/runtime_api/
runtime_payload.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
5use core::marker::PhantomData;
6use scale_encode::EncodeAsFields;
7use scale_value::Composite;
8use std::borrow::Cow;
9
10use crate::dynamic::DecodedValueThunk;
11use crate::error::MetadataError;
12use crate::{metadata::DecodeWithMetadata, Error, Metadata};
13
14/// This represents a runtime API payload that can call into the runtime of node.
15///
16/// # Components
17///
18/// - associated return type
19///
20/// Resulting bytes of the call are interpreted into this type.
21///
22/// - runtime function name
23///
24/// The function name of the runtime API call. This is obtained by concatenating
25/// the runtime trait name with the trait's method.
26///
27/// For example, the substrate runtime trait [Metadata](https://github.com/paritytech/substrate/blob/cb954820a8d8d765ce75021e244223a3b4d5722d/primitives/api/src/lib.rs#L745)
28/// contains the `metadata_at_version` function. The corresponding runtime function
29/// is `Metadata_metadata_at_version`.
30///
31/// - encoded arguments
32///
33/// Each argument of the runtime function must be scale-encoded.
34pub trait RuntimeApiPayload {
35    /// The return type of the function call.
36    // Note: `DecodeWithMetadata` is needed to decode the function call result
37    // with the `subxt::Metadata.
38    type ReturnType: DecodeWithMetadata;
39
40    /// The runtime API trait name.
41    fn trait_name(&self) -> &str;
42
43    /// The runtime API method name.
44    fn method_name(&self) -> &str;
45
46    /// Scale encode the arguments data.
47    fn encode_args_to(&self, metadata: &Metadata, out: &mut Vec<u8>) -> Result<(), Error>;
48
49    /// Encode arguments data and return the output. This is a convenience
50    /// wrapper around [`RuntimeApiPayload::encode_args_to`].
51    fn encode_args(&self, metadata: &Metadata) -> Result<Vec<u8>, Error> {
52        let mut v = Vec::new();
53        self.encode_args_to(metadata, &mut v)?;
54        Ok(v)
55    }
56
57    /// Returns the statically generated validation hash.
58    fn validation_hash(&self) -> Option<[u8; 32]> {
59        None
60    }
61}
62
63/// A runtime API payload containing the generic argument data
64/// and interpreting the result of the call as `ReturnTy`.
65///
66/// This can be created from static values (ie those generated
67/// via the `subxt` macro) or dynamic values via [`dynamic`].
68#[derive(Clone, Debug)]
69pub struct Payload<ArgsData, ReturnTy> {
70    trait_name: Cow<'static, str>,
71    method_name: Cow<'static, str>,
72    args_data: ArgsData,
73    validation_hash: Option<[u8; 32]>,
74    _marker: PhantomData<ReturnTy>,
75}
76
77impl<ArgsData: EncodeAsFields, ReturnTy: DecodeWithMetadata> RuntimeApiPayload
78    for Payload<ArgsData, ReturnTy>
79{
80    type ReturnType = ReturnTy;
81
82    fn trait_name(&self) -> &str {
83        &self.trait_name
84    }
85
86    fn method_name(&self) -> &str {
87        &self.method_name
88    }
89
90    fn encode_args_to(&self, metadata: &Metadata, out: &mut Vec<u8>) -> Result<(), Error> {
91        let api_method = metadata
92            .runtime_api_trait_by_name_err(&self.trait_name)?
93            .method_by_name(&self.method_name)
94            .ok_or_else(|| MetadataError::RuntimeMethodNotFound((*self.method_name).to_owned()))?;
95        let mut fields = api_method
96            .inputs()
97            .map(|input| scale_encode::Field::named(input.ty, &input.name));
98
99        self.args_data
100            .encode_as_fields_to(&mut fields, metadata.types(), out)?;
101        Ok(())
102    }
103
104    fn validation_hash(&self) -> Option<[u8; 32]> {
105        self.validation_hash
106    }
107}
108
109/// A dynamic runtime API payload.
110pub type DynamicRuntimeApiPayload = Payload<Composite<()>, DecodedValueThunk>;
111
112impl<ReturnTy, ArgsData> Payload<ArgsData, ReturnTy> {
113    /// Create a new [`Payload`].
114    pub fn new(
115        trait_name: impl Into<String>,
116        method_name: impl Into<String>,
117        args_data: ArgsData,
118    ) -> Self {
119        Payload {
120            trait_name: Cow::Owned(trait_name.into()),
121            method_name: Cow::Owned(method_name.into()),
122            args_data,
123            validation_hash: None,
124            _marker: PhantomData,
125        }
126    }
127
128    /// Create a new static [`Payload`] using static function name
129    /// and scale-encoded argument data.
130    ///
131    /// This is only expected to be used from codegen.
132    #[doc(hidden)]
133    pub fn new_static(
134        trait_name: &'static str,
135        method_name: &'static str,
136        args_data: ArgsData,
137        hash: [u8; 32],
138    ) -> Payload<ArgsData, ReturnTy> {
139        Payload {
140            trait_name: Cow::Borrowed(trait_name),
141            method_name: Cow::Borrowed(method_name),
142            args_data,
143            validation_hash: Some(hash),
144            _marker: std::marker::PhantomData,
145        }
146    }
147
148    /// Do not validate this call prior to submitting it.
149    pub fn unvalidated(self) -> Self {
150        Self {
151            validation_hash: None,
152            ..self
153        }
154    }
155
156    /// Returns the trait name.
157    pub fn trait_name(&self) -> &str {
158        &self.trait_name
159    }
160
161    /// Returns the method name.
162    pub fn method_name(&self) -> &str {
163        &self.method_name
164    }
165
166    /// Returns the arguments data.
167    pub fn args_data(&self) -> &ArgsData {
168        &self.args_data
169    }
170}
171
172/// Create a new [`DynamicRuntimeApiPayload`].
173pub fn dynamic(
174    trait_name: impl Into<String>,
175    method_name: impl Into<String>,
176    args_data: impl Into<Composite<()>>,
177) -> DynamicRuntimeApiPayload {
178    Payload::new(trait_name, method_name, args_data.into())
179}