frame_decode/methods/
runtime_api_encoder.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
16use super::runtime_api_type_info::{RuntimeApiInfo, RuntimeApiInfoError, RuntimeApiTypeInfo};
17use crate::utils::{EncodableValues, IntoEncodableValues};
18use alloc::format;
19use alloc::string::String;
20use alloc::vec::Vec;
21use scale_type_resolver::TypeResolver;
22
23/// An error returned trying to encode Runtime API inputs.
24#[non_exhaustive]
25#[allow(missing_docs)]
26#[derive(Debug, thiserror::Error)]
27pub enum RuntimeApiInputsEncodeError {
28    #[error("Cannot get Runtime API info: {0}")]
29    CannotGetInfo(RuntimeApiInfoError<'static>),
30    #[error("Failed to encode Runtime API info: {0}")]
31    EncodeError(#[from] scale_encode::Error),
32    #[error("Wrong number of inputs provided: expected {num_inputs_expected}")]
33    WrongNumberOfInputsProvided {
34        /// The number of input parameters that were expected.
35        num_inputs_expected: usize,
36    },
37}
38
39/// Encode the name/ID of a Runtime API used in RPC methods given the trait name and method name.
40pub fn encode_runtime_api_name(trait_name: &str, method_name: &str) -> String {
41    format!("{trait_name}_{method_name}")
42}
43
44/// Encode the inputs to a Runtime API.
45pub fn encode_runtime_api_inputs<Info, Resolver, Inputs>(
46    trait_name: &str,
47    method_name: &str,
48    keys: Inputs,
49    info: &Info,
50    type_resolver: &Resolver,
51) -> Result<Vec<u8>, RuntimeApiInputsEncodeError>
52where
53    Inputs: IntoEncodableValues,
54    Info: RuntimeApiTypeInfo,
55    Info::TypeId: Clone + core::fmt::Debug,
56    Resolver: TypeResolver<TypeId = Info::TypeId>,
57{
58    let mut out = Vec::new();
59    encode_runtime_api_inputs_to(trait_name, method_name, keys, info, type_resolver, &mut out)?;
60    Ok(out)
61}
62
63/// Encode the inputs to a Runtime API to a provided output `Vec`.
64pub fn encode_runtime_api_inputs_to<Info, Resolver, Inputs>(
65    trait_name: &str,
66    method_name: &str,
67    keys: Inputs,
68    info: &Info,
69    type_resolver: &Resolver,
70    out: &mut Vec<u8>,
71) -> Result<(), RuntimeApiInputsEncodeError>
72where
73    Inputs: IntoEncodableValues,
74    Info: RuntimeApiTypeInfo,
75    Info::TypeId: Clone + core::fmt::Debug,
76    Resolver: TypeResolver<TypeId = Info::TypeId>,
77{
78    let runtime_api_info = info
79        .runtime_api_info(trait_name, method_name)
80        .map_err(|e| RuntimeApiInputsEncodeError::CannotGetInfo(e.into_owned()))?;
81
82    encode_runtime_api_inputs_with_info_to(keys, &runtime_api_info, type_resolver, out)
83}
84
85/// Encode the inputs to a Runtime API to a provided output `Vec`.
86///
87/// Unlike [`encode_runtime_api_inputs_to`], which obtains the Runtime API info internally given trait and method names,
88/// this function takes the Runtime API info as an argument. This is useful if you already have the info available,
89/// for example if you are encoding multiple inputs for a given Runtime API.
90pub fn encode_runtime_api_inputs_with_info_to<Resolver, Inputs>(
91    inputs: Inputs,
92    runtime_api_info: &RuntimeApiInfo<<Resolver as TypeResolver>::TypeId>,
93    type_resolver: &Resolver,
94    out: &mut Vec<u8>,
95) -> Result<(), RuntimeApiInputsEncodeError>
96where
97    Inputs: IntoEncodableValues,
98    Resolver: TypeResolver,
99    <Resolver as TypeResolver>::TypeId: Clone + core::fmt::Debug,
100{
101    // If wrong number of inputs provided, bail early.
102    if runtime_api_info.inputs.len() != inputs.num_encodable_values() {
103        return Err(RuntimeApiInputsEncodeError::WrongNumberOfInputsProvided {
104            num_inputs_expected: runtime_api_info.inputs.len(),
105        });
106    }
107
108    // Encode the inputs to our out bytes.
109    let mut inputs = inputs.into_encodable_values();
110    for input in &*runtime_api_info.inputs {
111        inputs
112            .encode_next_value_to(input.id.clone(), type_resolver, out)
113            .map_err(RuntimeApiInputsEncodeError::EncodeError)?;
114    }
115
116    Ok(())
117}