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