Skip to main content

gear_core/
rpc.rs

1// Copyright (C) Gear Technologies Inc.
2// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
3
4//! This module contains types commonly used as output in RPC calls.
5
6use alloc::vec::Vec;
7use gear_core_errors::ReplyCode;
8use gprimitives::H256;
9use parity_scale_codec::{Decode, Encode};
10use scale_decode::DecodeAsType;
11use scale_encode::EncodeAsType;
12use scale_info::TypeInfo;
13
14use crate::message::UserMessage;
15
16/// Pre-calculated gas consumption estimate for a message.
17///
18/// Intended to be used as a result in `calculateGasFor*` RPC calls.
19#[derive(
20    Clone, Debug, Default, PartialEq, Eq, Encode, EncodeAsType, Decode, DecodeAsType, TypeInfo,
21)]
22#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
23pub struct GasInfo {
24    /// The minimum amount of gas required for successful execution.
25    pub min_limit: u64,
26    /// The amount of gas that would be reserved.
27    pub reserved: u64,
28    /// The amount of gas that would be burned.
29    pub burned: u64,
30    /// The amount of gas that may be returned.
31    pub may_be_returned: u64,
32    /// Indicates whether the message was placed into the waitlist.
33    ///
34    /// This flag signifies that `min_limit` guarantees apply only to the first execution attempt.
35    pub waited: bool,
36}
37
38/// Pre-calculated reply information.
39///
40/// Intended to be used as a result in `calculateReplyFor*` RPC calls.
41#[derive(
42    Clone, Debug, PartialEq, Eq, Encode, EncodeAsType, Decode, DecodeAsType, TypeInfo, Hash,
43)]
44#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
45pub struct ReplyInfo {
46    /// Payload of the reply.
47    #[cfg_attr(feature = "std", serde(with = "impl_serde::serialize"))]
48    pub payload: Vec<u8>,
49    /// Value attached to the reply.
50    pub value: u128,
51    /// Reply code of the reply.
52    #[cfg_attr(feature = "std", serde(with = "serialize_reply_code"))]
53    pub code: ReplyCode,
54}
55
56/// Pre-calculated reply information with user messages produced during calculation.
57#[derive(
58    Clone, Debug, PartialEq, Eq, Encode, EncodeAsType, Decode, DecodeAsType, TypeInfo, Hash,
59)]
60#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
61pub struct CalculateReplyForHandleResult {
62    /// Reply to the calculated handle message.
63    pub reply: ReplyInfo,
64    /// User messages sent during the calculated execution.
65    pub messages: Vec<UserMessage>,
66}
67
68impl ReplyInfo {
69    /// Calculates `blake2b` hash from [`ReplyInfo`].
70    pub fn to_hash(&self) -> H256 {
71        let ReplyInfo {
72            payload,
73            value,
74            code,
75        } = self;
76
77        let bytes = [
78            payload.as_ref(),
79            value.to_be_bytes().as_ref(),
80            code.to_bytes().as_ref(),
81        ]
82        .concat();
83        super::utils::hash(&bytes).into()
84    }
85}
86
87/// Serializer and deserializer for ReplyCode as 0x-prefixed hex string.
88#[cfg(feature = "std")]
89pub(crate) mod serialize_reply_code {
90    use super::ReplyCode;
91    use core::fmt::Write;
92    use serde::de;
93
94    pub fn serialize<S>(code: &ReplyCode, serializer: S) -> Result<S::Ok, S::Error>
95    where
96        S: serde::Serializer,
97    {
98        let mut s = alloc::string::String::with_capacity(10);
99        s.push_str("0x");
100        for byte in code.to_bytes() {
101            write!(&mut s, "{:02x}", byte).unwrap();
102        }
103        serializer.serialize_str(&s)
104    }
105
106    pub fn deserialize<'de, D>(deserializer: D) -> Result<ReplyCode, D::Error>
107    where
108        D: serde::Deserializer<'de>,
109    {
110        struct Visitor;
111
112        impl<'b> de::Visitor<'b> for Visitor {
113            type Value = ReplyCode;
114
115            fn expecting(&self, formatter: &mut alloc::fmt::Formatter) -> alloc::fmt::Result {
116                formatter.write_str("a 0x-prefixed hex string representing a 4-byte ReplyCode")
117            }
118
119            fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
120            where
121                E: de::Error,
122            {
123                let v = v.strip_prefix("0x").ok_or_else(|| {
124                    E::custom("invalid format: expected a 0x-prefixed hex string")
125                })?;
126                let mut bytes = [0u8; 4];
127                hex::decode_to_slice(v, &mut bytes)
128                    .map_err(|e| E::custom(alloc::format!("invalid hex string: {e}")))?;
129                Ok(ReplyCode::from_bytes(bytes))
130            }
131        }
132        deserializer.deserialize_str(Visitor)
133    }
134}
135
136/// `u128` value wrapper intended for usage in RPC calls due to serialization specifications.
137#[derive(
138    Clone,
139    Copy,
140    Debug,
141    Default,
142    PartialEq,
143    Eq,
144    Encode,
145    EncodeAsType,
146    Decode,
147    DecodeAsType,
148    TypeInfo,
149    derive_more::From,
150    derive_more::Into,
151)]
152pub struct RpcValue(pub u128);
153
154#[cfg(feature = "std")]
155impl<'de> serde::Deserialize<'de> for RpcValue {
156    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
157    where
158        D: serde::Deserializer<'de>,
159    {
160        use alloc::format;
161        use core::fmt;
162        use serde::de::{self, Visitor};
163
164        struct RpcValueVisitor;
165
166        impl<'de> Visitor<'de> for RpcValueVisitor {
167            type Value = RpcValue;
168
169            fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
170                f.write_str("a numeric literal, a 0x-prefixed string with big-endian bytes, or a numeric string representing a u128; for large integer literals, consider using string options for clarity and to avoid potential parsing issues")
171            }
172
173            fn visit_u128<E>(self, v: u128) -> Result<Self::Value, E> {
174                Ok(RpcValue(v))
175            }
176
177            fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E> {
178                Ok(RpcValue(v as u128))
179            }
180
181            fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
182            where
183                E: de::Error,
184            {
185                if let Some(hex) = v.strip_prefix("0x") {
186                    let bytes = hex::decode(hex)
187                        .map_err(|e| E::custom(format!("invalid hex string: {e}")))?;
188
189                    if bytes.len() > 16 {
190                        return Err(E::custom("invalid hex string: too long for u128"));
191                    }
192
193                    // left pad to 16 bytes (big-endian)
194                    let mut padded = [0u8; 16];
195                    padded[16 - bytes.len()..].copy_from_slice(&bytes);
196
197                    Ok(RpcValue(u128::from_be_bytes(padded)))
198                } else {
199                    v.parse::<u128>()
200                        .map(RpcValue)
201                        .map_err(|e| E::custom(format!("invalid numeric string: {e}")))
202                }
203            }
204        }
205
206        deserializer.deserialize_any(RpcValueVisitor)
207    }
208}