Skip to main content

iota_json_rpc_types/
lib.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5pub use balance_changes::*;
6use fastcrypto::{
7    encoding::{Base58, Base64},
8    traits::VerifyingKey,
9};
10pub use iota_checkpoint::*;
11pub use iota_coin::*;
12pub use iota_event::*;
13pub use iota_extended::*;
14pub use iota_gas_cost_summary::*;
15pub use iota_governance::*;
16pub use iota_indexer::*;
17pub use iota_move::*;
18pub use iota_object::*;
19pub use iota_object_response_error::*;
20pub use iota_owner::*;
21use iota_primitives::{
22    Base58 as Base58Schema, Base64 as Base64Schema, ObjectId as ObjectIdSchema, SequenceNumberU64,
23    TypeTag as TypeTagSchema,
24};
25pub use iota_protocol::*;
26use iota_sdk_types::{ObjectDigest, ObjectId, TypeTag};
27pub use iota_system_state_summary::*;
28pub use iota_transaction::*;
29use iota_types::{
30    crypto::{AuthorityPublicKey, AuthorityPublicKeyBytes},
31    dynamic_field::{DynamicFieldInfo, DynamicFieldName, DynamicFieldType},
32    iota_serde::BigInt,
33};
34pub use object_changes::*;
35use schemars::JsonSchema;
36use serde::{Deserialize, Serialize};
37use serde_with::{DeserializeAs, SerializeAs, serde_as};
38
39#[cfg(test)]
40#[path = "unit_tests/rpc_types_tests.rs"]
41mod rpc_types_tests;
42
43mod balance_changes;
44mod displays;
45mod iota_checkpoint;
46mod iota_coin;
47mod iota_event;
48mod iota_extended;
49mod iota_gas_cost_summary;
50mod iota_governance;
51mod iota_indexer;
52mod iota_move;
53mod iota_object;
54mod iota_object_response_error;
55mod iota_owner;
56pub mod iota_primitives;
57mod iota_protocol;
58mod iota_system_state_summary;
59mod iota_transaction;
60mod object_changes;
61
62pub type DynamicFieldPage = Page<IotaDynamicFieldInfo, ObjectId>;
63
64/// `next_cursor` points to the last item in the page;
65/// Reading with `next_cursor` will start from the next item after `next_cursor`
66/// if `next_cursor` is `Some`, otherwise it will start from the first item.
67#[derive(Clone, Debug, JsonSchema, Serialize, Deserialize, PartialEq, Eq)]
68#[serde(rename_all = "camelCase")]
69pub struct Page<T, C> {
70    pub data: Vec<T>,
71    pub next_cursor: Option<C>,
72    pub has_next_page: bool,
73    /// The oldest checkpoint this response can contain data from.
74    ///
75    /// Anything before it has been pruned and may be missing from the
76    /// response.
77    ///
78    /// Absent for methods that do not return historical data, and for servers
79    /// that do not report it.
80    #[serde(default, skip_serializing_if = "Option::is_none")]
81    #[schemars(with = "Option<String>")]
82    pub oldest_available_checkpoint: Option<BigInt<u64>>,
83}
84
85impl<T, C> Page<T, C> {
86    pub fn empty() -> Self {
87        Self {
88            data: vec![],
89            next_cursor: None,
90            has_next_page: false,
91            oldest_available_checkpoint: None,
92        }
93    }
94}
95
96/// Data attached to a JSON-RPC error raised for data that may have been
97/// pruned.
98#[derive(Clone, Debug, JsonSchema, Serialize, Deserialize, PartialEq, Eq)]
99#[serde(rename_all = "camelCase")]
100pub struct DataPrunedErrorData {
101    /// The oldest checkpoint the request could have found data in.
102    ///
103    /// Anything before it has been pruned and is no longer served.
104    #[schemars(with = "String")]
105    pub oldest_available_checkpoint: BigInt<u64>,
106}
107
108impl DataPrunedErrorData {
109    pub fn new(oldest_available_checkpoint: u64) -> Self {
110        Self {
111            oldest_available_checkpoint: oldest_available_checkpoint.into(),
112        }
113    }
114}
115
116#[serde_as]
117#[derive(Clone, Serialize, Deserialize, JsonSchema)]
118#[serde(rename_all = "camelCase", rename = "DynamicFieldName")]
119pub struct DynamicFieldNameSchema {
120    #[serde(rename = "type")]
121    #[schemars(with = "TypeTagSchema")]
122    #[serde_as(as = "TypeTagSchema")]
123    pub type_tag: TypeTag,
124    // Bincode does not like serde_json::Value, rocksdb will not insert the value without
125    // serializing value as string. TODO: investigate if this can be removed after switch to
126    // BCS.
127    pub value: serde_json::Value,
128}
129
130impl SerializeAs<DynamicFieldName> for DynamicFieldNameSchema {
131    fn serialize_as<S>(name: &DynamicFieldName, serializer: S) -> Result<S::Ok, S::Error>
132    where
133        S: serde::Serializer,
134    {
135        let schema = DynamicFieldNameSchema::from(name.clone());
136        schema.serialize(serializer)
137    }
138}
139
140impl<'de> DeserializeAs<'de, DynamicFieldName> for DynamicFieldNameSchema {
141    fn deserialize_as<D>(deserializer: D) -> Result<DynamicFieldName, D::Error>
142    where
143        D: serde::Deserializer<'de>,
144    {
145        let schema = DynamicFieldNameSchema::deserialize(deserializer)?;
146        Ok(DynamicFieldName::from(schema))
147    }
148}
149
150impl From<DynamicFieldName> for DynamicFieldNameSchema {
151    fn from(name: DynamicFieldName) -> Self {
152        Self {
153            type_tag: name.type_tag,
154            value: name.value,
155        }
156    }
157}
158
159impl From<DynamicFieldNameSchema> for DynamicFieldName {
160    fn from(name: DynamicFieldNameSchema) -> Self {
161        Self {
162            type_tag: name.type_tag,
163            value: name.value,
164        }
165    }
166}
167
168#[derive(Copy, Clone, Serialize, Deserialize, JsonSchema)]
169#[serde(rename = "DynamicFieldType")]
170pub enum DynamicFieldTypeSchema {
171    DynamicField,
172    DynamicObject,
173}
174
175impl SerializeAs<DynamicFieldType> for DynamicFieldTypeSchema {
176    fn serialize_as<S>(type_: &DynamicFieldType, serializer: S) -> Result<S::Ok, S::Error>
177    where
178        S: serde::Serializer,
179    {
180        let schema = DynamicFieldTypeSchema::from(*type_);
181        schema.serialize(serializer)
182    }
183}
184
185impl<'de> DeserializeAs<'de, DynamicFieldType> for DynamicFieldTypeSchema {
186    fn deserialize_as<D>(deserializer: D) -> Result<DynamicFieldType, D::Error>
187    where
188        D: serde::Deserializer<'de>,
189    {
190        let schema = DynamicFieldTypeSchema::deserialize(deserializer)?;
191        Ok(DynamicFieldType::from(schema))
192    }
193}
194
195impl From<DynamicFieldType> for DynamicFieldTypeSchema {
196    fn from(type_: DynamicFieldType) -> Self {
197        match type_ {
198            DynamicFieldType::DynamicField => Self::DynamicField,
199            DynamicFieldType::DynamicObject => Self::DynamicObject,
200        }
201    }
202}
203
204impl From<DynamicFieldTypeSchema> for DynamicFieldType {
205    fn from(type_: DynamicFieldTypeSchema) -> Self {
206        match type_ {
207            DynamicFieldTypeSchema::DynamicField => Self::DynamicField,
208            DynamicFieldTypeSchema::DynamicObject => Self::DynamicObject,
209        }
210    }
211}
212
213#[serde_as]
214#[derive(Clone, Serialize, Deserialize, JsonSchema, Debug)]
215#[serde(rename_all = "camelCase")]
216#[schemars(rename = "DynamicFieldInfo")]
217pub struct IotaDynamicFieldInfo {
218    #[schemars(with = "DynamicFieldNameSchema")]
219    #[serde_as(as = "DynamicFieldNameSchema")]
220    pub name: DynamicFieldName,
221    #[serde(flatten)]
222    pub bcs_name: BcsName,
223    #[schemars(with = "DynamicFieldTypeSchema")]
224    #[serde_as(as = "DynamicFieldTypeSchema")]
225    pub type_: DynamicFieldType,
226    pub object_type: String,
227    #[serde_as(as = "ObjectIdSchema")]
228    #[schemars(with = "ObjectIdSchema")]
229    pub object_id: ObjectId,
230    pub version: SequenceNumberU64,
231    #[serde_as(as = "Base58Schema")]
232    #[schemars(with = "Base58Schema")]
233    pub digest: ObjectDigest,
234}
235
236impl From<DynamicFieldInfo> for IotaDynamicFieldInfo {
237    fn from(
238        DynamicFieldInfo {
239            name,
240            bcs_name,
241            type_,
242            object_type,
243            object_id,
244            version,
245            digest,
246        }: DynamicFieldInfo,
247    ) -> Self {
248        Self {
249            name,
250            bcs_name: BcsName::new(bcs_name),
251            type_,
252            object_type,
253            object_id,
254            version: version.into(),
255            digest,
256        }
257    }
258}
259
260impl From<IotaDynamicFieldInfo> for DynamicFieldInfo {
261    fn from(
262        IotaDynamicFieldInfo {
263            name,
264            bcs_name,
265            type_,
266            object_type,
267            object_id,
268            version,
269            digest,
270        }: IotaDynamicFieldInfo,
271    ) -> Self {
272        Self {
273            name,
274            bcs_name: bcs_name.into_bytes(),
275            type_,
276            object_type,
277            object_id,
278            version: version.into(),
279            digest,
280        }
281    }
282}
283
284#[serde_as]
285#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, JsonSchema)]
286#[serde(rename_all = "camelCase", tag = "bcsEncoding")]
287#[serde(from = "MaybeTaggedBcsName")]
288pub enum BcsName {
289    Base64 {
290        #[serde_as(as = "Base64")]
291        #[schemars(with = "Base64Schema")]
292        #[serde(rename = "bcsName")]
293        bcs_name: Vec<u8>,
294    },
295    Base58 {
296        #[serde_as(as = "Base58")]
297        #[schemars(with = "Base58Schema")]
298        #[serde(rename = "bcsName")]
299        bcs_name: Vec<u8>,
300    },
301}
302
303impl BcsName {
304    pub fn new(bytes: Vec<u8>) -> Self {
305        Self::Base64 { bcs_name: bytes }
306    }
307
308    pub fn bytes(&self) -> &[u8] {
309        match self {
310            BcsName::Base64 { bcs_name } => bcs_name.as_ref(),
311            BcsName::Base58 { bcs_name } => bcs_name.as_ref(),
312        }
313    }
314
315    pub fn into_bytes(self) -> Vec<u8> {
316        match self {
317            BcsName::Base64 { bcs_name } => bcs_name,
318            BcsName::Base58 { bcs_name } => bcs_name,
319        }
320    }
321}
322
323#[allow(unused)]
324#[serde_as]
325#[derive(Serialize, Deserialize)]
326#[serde(rename_all = "camelCase", untagged)]
327enum MaybeTaggedBcsName {
328    Tagged(TaggedBcsName),
329    Base58 {
330        #[serde_as(as = "Base58")]
331        #[serde(rename = "bcsName")]
332        bcs_name: Vec<u8>,
333    },
334}
335
336#[serde_as]
337#[derive(Serialize, Deserialize)]
338#[serde(rename_all = "camelCase", tag = "bcsEncoding")]
339enum TaggedBcsName {
340    Base64 {
341        #[serde_as(as = "Base64")]
342        #[serde(rename = "bcsName")]
343        bcs_name: Vec<u8>,
344    },
345    Base58 {
346        #[serde_as(as = "Base58")]
347        #[serde(rename = "bcsName")]
348        bcs_name: Vec<u8>,
349    },
350}
351
352impl From<MaybeTaggedBcsName> for BcsName {
353    fn from(name: MaybeTaggedBcsName) -> BcsName {
354        let bcs_name = match name {
355            MaybeTaggedBcsName::Tagged(TaggedBcsName::Base58 { bcs_name })
356            | MaybeTaggedBcsName::Base58 { bcs_name } => bcs_name,
357            MaybeTaggedBcsName::Tagged(TaggedBcsName::Base64 { bcs_name }) => bcs_name,
358        };
359
360        // Bytes are already decoded, force into Base64 variant to avoid serializing to
361        // base58
362        Self::Base64 { bcs_name }
363    }
364}
365
366/// Defines the compressed version of the public key that we pass around
367/// in IOTA.
368#[serde_as]
369#[derive(
370    Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize, JsonSchema,
371)]
372#[schemars(rename = "AuthorityPublicKeyBytes")]
373pub struct IotaAuthorityPublicKeyBytes(
374    #[serde_as(as = "Base64")]
375    #[schemars(with = "Base64Schema")]
376    pub [u8; AuthorityPublicKey::LENGTH],
377);
378
379impl From<IotaAuthorityPublicKeyBytes> for AuthorityPublicKeyBytes {
380    fn from(value: IotaAuthorityPublicKeyBytes) -> Self {
381        Self(value.0)
382    }
383}
384
385impl From<AuthorityPublicKeyBytes> for IotaAuthorityPublicKeyBytes {
386    fn from(value: AuthorityPublicKeyBytes) -> Self {
387        Self(value.0)
388    }
389}
390
391#[cfg(test)]
392mod test {
393    use super::*;
394
395    #[test]
396    fn bcs_name_test() {
397        let bytes = vec![0, 1, 2, 3, 4];
398        let untagged_base58 = r#"{"bcsName":"12VfUX"}"#;
399        let tagged_base58 = r#"{"bcsEncoding":"base58","bcsName":"12VfUX"}"#;
400        let tagged_base64 = r#"{"bcsEncoding":"base64","bcsName":"AAECAwQ="}"#;
401
402        assert_eq!(
403            bytes,
404            serde_json::from_str::<BcsName>(untagged_base58)
405                .unwrap()
406                .into_bytes()
407        );
408        assert_eq!(
409            bytes,
410            serde_json::from_str::<BcsName>(tagged_base58)
411                .unwrap()
412                .into_bytes()
413        );
414        assert_eq!(
415            bytes,
416            serde_json::from_str::<BcsName>(tagged_base64)
417                .unwrap()
418                .into_bytes()
419        );
420
421        // Roundtrip base64
422        let name = serde_json::from_str::<BcsName>(tagged_base64).unwrap();
423        let json = serde_json::to_string(&name).unwrap();
424        let from_json = serde_json::from_str::<BcsName>(&json).unwrap();
425        assert_eq!(name, from_json);
426
427        // Roundtrip base58
428        let name = serde_json::from_str::<BcsName>(tagged_base58).unwrap();
429        let json = serde_json::to_string(&name).unwrap();
430        let from_json = serde_json::from_str::<BcsName>(&json).unwrap();
431        assert_eq!(name, from_json);
432    }
433}