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