iota_sdk_types/
lib.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2025 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5//! Core type definitions for the IOTA blockchain.
6//!
7//! [IOTA] is a next-generation smart contract platform with high throughput,
8//! low latency, and an asset-oriented programming model powered by the Move
9//! programming language. This crate provides type definitions for working with
10//! the data that makes up the IOTA blockchain.
11//!
12//! [IOTA]: https://iota.org
13//!
14//! # Feature flags
15//!
16//! This library uses a set of [feature flags] to reduce the number of
17//! dependencies and amount of compiled code. By default, no features are
18//! enabled which allows one to enable a subset specifically for their use case.
19//! Below is a list of the available feature flags.
20//!
21//! - `schemars`: Enables JSON schema generation using the [schemars] library.
22//! - `serde`: Enables support for serializing and deserializing types to/from
23//!   BCS utilizing [serde] library.
24//! - `rand`: Enables support for generating random instances of a number of
25//!   types via the [rand] library.
26//! - `hash`: Enables support for hashing, which is required for deriving
27//!   addresses and calculating digests for various types.
28//! - `proptest`: Enables support for the [proptest] library by providing
29//!   implementations of [proptest::arbitrary::Arbitrary] for many types.
30//!
31//! [feature flags]: https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section
32//! [serde]: https://docs.rs/serde
33//! [rand]: https://docs.rs/rand
34//! [proptest]: https://docs.rs/proptest
35//! [schemars]: https://docs.rs/schemars
36//! [proptest::arbitrary::Arbitrary]: https://docs.rs/proptest/latest/proptest/arbitrary/trait.Arbitrary.html
37//!
38//! # BCS
39//!
40//! [BCS] is the serialization format used to represent the state of the
41//! blockchain and is used extensively throughout the IOTA ecosystem. In
42//! particular the BCS format is leveraged because it _"guarantees canonical
43//! serialization, meaning that for any given data type, there is a one-to-one
44//! correspondence between in-memory values and valid byte representations."_
45//! One benefit of this property of having a canonical serialized representation
46//! is to allow different entities in the ecosystem to all agree on how a
47//! particular type should be interpreted and more importantly define a
48//! deterministic representation for hashing and signing.
49//!
50//! This library strives to guarantee that the types defined are fully
51//! BCS-compatible with the data that the network produces. The one caveat to
52//! this would be that as the IOTA protocol evolves, new type variants are added
53//! and older versions of this library may not support those newly
54//! added variants. The expectation is that the most recent release of this
55//! library will support new variants and types as they are released to IOTA's
56//! `testnet` network.
57//!
58//! See the documentation for the various types defined by this crate for a
59//! specification of their BCS serialized representation which will be defined
60//! using ABNF notation as described by [RFC-5234]. In addition to the format
61//! itself, some types have an extra layer of verification and may impose
62//! additional restrictions on valid byte representations above and beyond those
63//! already provided by BCS. In these instances the documentation for those
64//! types will clearly specify these additional restrictions.
65//!
66//! Here are some common rules:
67//!
68//! ```text
69//! ; --- BCS Value ---
70//! bcs-value           = bcs-struct / bcs-enum / bcs-length-prefixed / bcs-fixed-length
71//! bcs-length-prefixed = bytes / string / vector / option
72//! bcs-fixed-length    = u8 / u16 / u32 / u64 / u128 /
73//!                       i8 / i16 / i32 / i64 / i128 /
74//!                       boolean
75//! bcs-struct          = *bcs-value                ; Sequence of serialized fields
76//! bcs-enum            = uleb128-index bcs-value   ; Enum index and associated value
77//!
78//! ; --- Length-prefixed types ---
79//! bytes           = uleb128 *OCTET          ; Raw bytes of the specified length
80//! string          = uleb128 *OCTET          ; valid utf8 string of the specified length
81//! vector          = uleb128 *bcs-value      ; Length-prefixed list of values
82//! option          = %x00 / (%x01 bcs-value) ; optional value
83//!
84//! ; --- Fixed-length types ---
85//! u8          = OCTET                     ; 1-byte unsigned integer
86//! u16         = 2OCTET                    ; 2-byte unsigned integer, little-endian
87//! u32         = 4OCTET                    ; 4-byte unsigned integer, little-endian
88//! u64         = 8OCTET                    ; 8-byte unsigned integer, little-endian
89//! u128        = 16OCTET                   ; 16-byte unsigned integer, little-endian
90//! i8          = OCTET                     ; 1-byte signed integer
91//! i16         = 2OCTET                    ; 2-byte signed integer, little-endian
92//! i32         = 4OCTET                    ; 4-byte signed integer, little-endian
93//! i64         = 8OCTET                    ; 8-byte signed integer, little-endian
94//! i128        = 16OCTET                   ; 16-byte signed integer, little-endian
95//! boolean     = %x00 / %x01               ; Boolean: 0 = false, 1 = true
96//! array       = *(bcs-value)              ; Fixed-length array
97//!
98//! ; --- ULEB128 definition ---
99//! uleb128         = 1*5uleb128-byte       ; Variable-length ULEB128 encoding
100//! uleb128-byte    = %x00-7F / %x80-FF     ; ULEB128 continuation rules
101//! uleb128-index   = uleb128               ; ULEB128-encoded variant index
102//! ```
103//!
104//! [BCS]: https://docs.rs/bcs
105//! [RFC-5234]: https://datatracker.ietf.org/doc/html/rfc5234
106
107#![cfg_attr(doc_cfg, feature(doc_cfg))]
108
109#[cfg(feature = "hash")]
110#[cfg_attr(doc_cfg, doc(cfg(feature = "hash")))]
111pub mod hash;
112
113mod address;
114mod checkpoint;
115mod crypto;
116mod digest;
117mod effects;
118mod events;
119mod execution_status;
120pub mod framework;
121mod gas;
122pub mod iota_names;
123mod move_package;
124mod object;
125mod object_id;
126mod transaction;
127mod type_tag;
128mod u256;
129mod validator;
130
131pub use address::{Address, AddressParseError};
132pub use checkpoint::{
133    CheckpointCommitment, CheckpointContents, CheckpointData, CheckpointSequenceNumber,
134    CheckpointSummary, CheckpointTimestamp, CheckpointTransaction, CheckpointTransactionInfo,
135    EndOfEpochData, EpochId, ProtocolVersion, SignedCheckpointSummary, StakeUnit,
136};
137pub use crypto::{
138    Bls12381PublicKey, Bls12381Signature, Bn254FieldElement, CircomG1, CircomG2, Ed25519PublicKey,
139    Ed25519Signature, Intent, IntentAppId, IntentScope, IntentVersion, InvalidSignatureScheme,
140    InvalidZkLoginAuthenticatorError, Jwk, JwkId, MultisigAggregatedSignature, MultisigCommittee,
141    MultisigMember, MultisigMemberPublicKey, MultisigMemberSignature, PasskeyAuthenticator,
142    PasskeyPublicKey, PublicKeyExt, Secp256k1PublicKey, Secp256k1Signature, Secp256r1PublicKey,
143    Secp256r1Signature, SignatureScheme, SimpleSignature, UserSignature, ZkLoginAuthenticator,
144    ZkLoginClaim, ZkLoginInputs, ZkLoginProof, ZkLoginPublicIdentifier,
145};
146pub use digest::{Digest, DigestParseError, SigningDigest};
147pub use effects::{
148    ChangedObject, DryRunEffect, DryRunMutation, DryRunResult, DryRunReturn, IdOperation, ObjectIn,
149    ObjectOut, TransactionArgument, TransactionEffects, TransactionEffectsV1, UnchangedSharedKind,
150    UnchangedSharedObject,
151};
152pub use events::{BalanceChange, Event, TransactionEvents};
153pub use execution_status::{
154    CommandArgumentError, ExecutionError, ExecutionStatus, MoveLocation, PackageUpgradeError,
155    TypeArgumentError,
156};
157pub use framework::Coin;
158pub use gas::GasCostSummary;
159pub use move_package::{MovePackageData, UpgradePolicy};
160pub use object::{
161    GenesisObject, MovePackage, MoveStruct, Object, ObjectData, ObjectReference, ObjectType, Owner,
162    TypeOrigin, UpgradeInfo, Version,
163};
164pub use object_id::ObjectId;
165#[cfg(feature = "serde")]
166#[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))]
167pub(crate) use transaction::SignedTransactionWithIntentMessage;
168pub use transaction::{
169    ActiveJwk, Argument, AuthenticatorStateExpire, AuthenticatorStateUpdateV1,
170    CancelledTransaction, ChangeEpoch, ChangeEpochV2, ChangeEpochV3, Command,
171    ConsensusCommitPrologueV1, ConsensusDeterminedVersionAssignments, EndOfEpochTransactionKind,
172    ExecutionTimeObservation, ExecutionTimeObservationKey, ExecutionTimeObservations, GasPayment,
173    GenesisTransaction, Input, MakeMoveVector, MergeCoins, MoveCall, ProgrammableTransaction,
174    Publish, RandomnessStateUpdate, SenderSignedTransaction, SignedTransaction, SplitCoins,
175    SystemPackage, Transaction, TransactionExpiration, TransactionKind, TransactionV1,
176    TransferObjects, Upgrade, ValidatorExecutionTimeObservation, VersionAssignment,
177};
178pub use type_tag::{Identifier, IdentifierRef, StructTag, TypeParseError, TypeTag};
179pub use validator::{
180    ValidatorAggregatedSignature, ValidatorCommittee, ValidatorCommitteeMember, ValidatorSignature,
181};
182
183#[cfg(all(test, feature = "serde", feature = "proptest"))]
184mod serialization_proptests;
185
186#[derive(Clone, Debug, PartialEq, Eq)]
187pub struct PersonalMessage<'a>(pub std::borrow::Cow<'a, [u8]>);
188
189#[macro_export]
190macro_rules! def_is {
191    ($($variant:ident),* $(,)?) => {
192        paste::paste! {$(
193        #[inline]
194        pub fn [< is_ $variant:snake >](&self) -> bool {
195            matches!(self, Self::$variant { .. })
196        }
197        )*}
198    };
199}
200
201#[macro_export]
202macro_rules! def_is_as_into_opt {
203    (@into $variant:ident ($rename:ident) [Box<$inner:ty>]) => {
204        paste::paste! {
205        #[inline]
206        pub fn [< into_ $rename _opt >](self) -> Option<$inner> {
207            #[allow(irrefutable_let_patterns)]
208            if let Self::$variant(inner) = self {
209                Some(*inner)
210            } else {
211                None
212            }
213        }
214
215        #[inline]
216        pub fn [< into_ $rename >](self) -> $inner {
217            self.[< into_ $rename _opt >]().expect(&format!("not a {}", stringify!($rename)))
218        }
219        }
220    };
221    (@into $variant:ident ($rename:ident) [$inner:ty]) => {
222        paste::paste! {
223        #[inline]
224        pub fn [< into_ $rename _opt >](self) -> Option<$inner> {
225            #[allow(irrefutable_let_patterns)]
226            if let Self::$variant(inner) = self {
227                Some(inner)
228            } else {
229                None
230            }
231        }
232
233        #[inline]
234        pub fn [< into_ $rename >](self) -> $inner {
235            self.[< into_ $rename _opt >]().expect(&format!("not a {}", stringify!($variant)))
236        }
237        }
238    };
239    (@impl $variant:ident ($rename:ident) [Box<$inner:ty>]) => {
240        paste::paste! {
241        #[inline]
242        pub fn [< is_ $rename >](&self) -> bool {
243            matches!(self, Self::$variant(_))
244        }
245
246        #[inline]
247        pub fn [< as_ $rename >](&self) -> &$inner {
248            self.[< as_ $rename _opt >]().expect(&format!("not a {}", stringify!($variant)))
249        }
250
251        #[inline]
252        pub fn [< as_ $rename _opt >](&self) -> Option<&$inner> {
253            #[allow(irrefutable_let_patterns)]
254            if let Self::$variant(inner) = self {
255                Some(inner)
256            } else {
257                None
258            }
259        }
260        }
261
262        $crate::def_is_as_into_opt!{@into $variant($rename) [Box<$inner>]}
263    };
264    (@impl $variant:ident ($rename:ident) [$inner:ty]) => {
265        paste::paste! {
266        #[inline]
267        pub fn [< is_ $rename >](&self) -> bool {
268            matches!(self, Self::$variant(_))
269        }
270
271        #[inline]
272        pub fn [< as_ $rename >](&self) -> &$inner {
273            self.[< as_ $rename _opt >]().expect(&format!("not a {}", stringify!($variant)))
274        }
275
276        #[inline]
277        pub fn [< as_ $rename _opt >](&self) -> Option<&$inner> {
278            #[allow(irrefutable_let_patterns)]
279            if let Self::$variant(inner) = self {
280                Some(inner)
281            } else {
282                None
283            }
284        }
285        }
286
287        $crate::def_is_as_into_opt!{@into $variant($rename) [$inner]}
288    };
289    (@parse $variant:ident ($rename:ident) [$($inner:tt)*]) => {
290        $crate::def_is_as_into_opt!{@impl $variant($rename) [$($inner)*]}
291    };
292    (@parse $variant:ident ($rename:ident)) => {
293        $crate::def_is_as_into_opt!{@impl $variant($rename) [$variant]}
294    };
295    (@parse $variant:ident [$($inner:tt)*]) => {
296        paste::paste! { $crate::def_is_as_into_opt!{@impl $variant ([< $variant:snake >]) [$($inner)*]} }
297    };
298    (@parse $variant:ident) => {
299        paste::paste! { $crate::def_is_as_into_opt!{@impl $variant ([< $variant:snake >]) [$variant]} }
300    };
301    ($($variant:ident $( as $rename:ident)? $(($($inner:tt)*))?),* $(,)?) => {
302        $(
303        $crate::def_is_as_into_opt!{@parse $variant $(($rename))? $([$($inner)*])?}
304        )*
305    };
306}
307
308#[cfg(feature = "serde")]
309mod _serde {
310    use std::borrow::Cow;
311
312    use base64ct::{Base64, Encoding};
313    use serde::{Deserialize, Deserializer, Serialize, Serializer};
314    use serde_with::{Bytes, DeserializeAs, SerializeAs};
315
316    pub(crate) type ReadableDisplay =
317        ::serde_with::As<::serde_with::IfIsHumanReadable<::serde_with::DisplayFromStr>>;
318
319    pub(crate) type OptionReadableDisplay =
320        ::serde_with::As<Option<::serde_with::IfIsHumanReadable<::serde_with::DisplayFromStr>>>;
321
322    pub(crate) type ReadableBase64Encoded =
323        ::serde_with::As<::serde_with::IfIsHumanReadable<Base64Encoded, ::serde_with::Bytes>>;
324
325    pub(crate) struct Base64Encoded;
326
327    impl<T: AsRef<[u8]>> SerializeAs<T> for Base64Encoded {
328        fn serialize_as<S>(source: &T, serializer: S) -> Result<S::Ok, S::Error>
329        where
330            S: Serializer,
331        {
332            let bytes = source.as_ref();
333            let b64 = Base64::encode_string(bytes);
334            b64.serialize(serializer)
335        }
336    }
337
338    impl<'de, T: TryFrom<Vec<u8>>> DeserializeAs<'de, T> for Base64Encoded {
339        fn deserialize_as<D>(deserializer: D) -> Result<T, D::Error>
340        where
341            D: Deserializer<'de>,
342        {
343            let b64: Cow<'de, str> = Deserialize::deserialize(deserializer)?;
344            let bytes = Base64::decode_vec(&b64).map_err(serde::de::Error::custom)?;
345            let length = bytes.len();
346            T::try_from(bytes).map_err(|_| {
347                serde::de::Error::custom(format_args!(
348                    "Can't convert a Byte Vector of length {length} to the output type."
349                ))
350            })
351        }
352    }
353
354    /// Serializes a bitmap according to the roaring bitmap on-disk standard.
355    /// <https://github.com/RoaringBitmap/RoaringFormatSpec>
356    pub(crate) struct BinaryRoaringBitmap;
357
358    impl SerializeAs<roaring::RoaringBitmap> for BinaryRoaringBitmap {
359        fn serialize_as<S>(
360            source: &roaring::RoaringBitmap,
361            serializer: S,
362        ) -> Result<S::Ok, S::Error>
363        where
364            S: Serializer,
365        {
366            let mut bytes = vec![];
367
368            source
369                .serialize_into(&mut bytes)
370                .map_err(serde::ser::Error::custom)?;
371            Bytes::serialize_as(&bytes, serializer)
372        }
373    }
374
375    impl<'de> DeserializeAs<'de, roaring::RoaringBitmap> for BinaryRoaringBitmap {
376        fn deserialize_as<D>(deserializer: D) -> Result<roaring::RoaringBitmap, D::Error>
377        where
378            D: Deserializer<'de>,
379        {
380            let bytes: Cow<'de, [u8]> = Bytes::deserialize_as(deserializer)?;
381            roaring::RoaringBitmap::deserialize_from(&bytes[..]).map_err(serde::de::Error::custom)
382        }
383    }
384
385    pub(crate) struct Base64RoaringBitmap;
386
387    impl SerializeAs<roaring::RoaringBitmap> for Base64RoaringBitmap {
388        fn serialize_as<S>(
389            source: &roaring::RoaringBitmap,
390            serializer: S,
391        ) -> Result<S::Ok, S::Error>
392        where
393            S: Serializer,
394        {
395            let mut bytes = vec![];
396
397            source
398                .serialize_into(&mut bytes)
399                .map_err(serde::ser::Error::custom)?;
400            let b64 = Base64::encode_string(&bytes);
401            b64.serialize(serializer)
402        }
403    }
404
405    impl<'de> DeserializeAs<'de, roaring::RoaringBitmap> for Base64RoaringBitmap {
406        fn deserialize_as<D>(deserializer: D) -> Result<roaring::RoaringBitmap, D::Error>
407        where
408            D: Deserializer<'de>,
409        {
410            let b64: Cow<'de, str> = Deserialize::deserialize(deserializer)?;
411            let bytes = Base64::decode_vec(&b64).map_err(serde::de::Error::custom)?;
412            roaring::RoaringBitmap::deserialize_from(&bytes[..]).map_err(serde::de::Error::custom)
413        }
414    }
415
416    pub(crate) use super::SignedTransactionWithIntentMessage;
417}
418
419#[cfg(feature = "schemars")]
420mod _schemars {
421    use schemars::{
422        JsonSchema,
423        schema::{InstanceType, Metadata, SchemaObject},
424    };
425
426    pub(crate) struct U64;
427
428    impl JsonSchema for U64 {
429        fn schema_name() -> String {
430            "u64".to_owned()
431        }
432
433        fn json_schema(_: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
434            SchemaObject {
435                metadata: Some(Box::new(Metadata {
436                    description: Some("Radix-10 encoded 64-bit unsigned integer".to_owned()),
437                    ..Default::default()
438                })),
439                instance_type: Some(InstanceType::String.into()),
440                format: Some("u64".to_owned()),
441                ..Default::default()
442            }
443            .into()
444        }
445
446        fn is_referenceable() -> bool {
447            false
448        }
449    }
450
451    pub(crate) struct I128;
452
453    impl JsonSchema for I128 {
454        fn schema_name() -> String {
455            "i128".to_owned()
456        }
457
458        fn json_schema(_: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
459            SchemaObject {
460                metadata: Some(Box::new(Metadata {
461                    description: Some("Radix-10 encoded 128-bit signed integer".to_owned()),
462                    ..Default::default()
463                })),
464                instance_type: Some(InstanceType::String.into()),
465                format: Some("i128".to_owned()),
466                ..Default::default()
467            }
468            .into()
469        }
470
471        fn is_referenceable() -> bool {
472            false
473        }
474    }
475
476    pub(crate) struct U256;
477
478    impl JsonSchema for U256 {
479        fn schema_name() -> String {
480            "u256".to_owned()
481        }
482
483        fn json_schema(_: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
484            SchemaObject {
485                metadata: Some(Box::new(Metadata {
486                    description: Some("Radix-10 encoded 256-bit unsigned integer".to_owned()),
487                    ..Default::default()
488                })),
489                instance_type: Some(InstanceType::String.into()),
490                format: Some("u256".to_owned()),
491                ..Default::default()
492            }
493            .into()
494        }
495
496        fn is_referenceable() -> bool {
497            false
498        }
499    }
500
501    pub(crate) struct Base64;
502
503    impl JsonSchema for Base64 {
504        fn schema_name() -> String {
505            "Base64".to_owned()
506        }
507
508        fn json_schema(_: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
509            SchemaObject {
510                metadata: Some(Box::new(Metadata {
511                    description: Some("Base64 encoded data".to_owned()),
512                    ..Default::default()
513                })),
514                instance_type: Some(InstanceType::String.into()),
515                format: Some("base64".to_owned()),
516                ..Default::default()
517            }
518            .into()
519        }
520
521        fn is_referenceable() -> bool {
522            false
523        }
524    }
525
526    pub(crate) struct Base58;
527
528    impl JsonSchema for Base58 {
529        fn schema_name() -> String {
530            "Base58".to_owned()
531        }
532
533        fn json_schema(_: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
534            SchemaObject {
535                metadata: Some(Box::new(Metadata {
536                    description: Some("Base58 encoded data".to_owned()),
537                    ..Default::default()
538                })),
539                instance_type: Some(InstanceType::String.into()),
540                format: Some("base58".to_owned()),
541                ..Default::default()
542            }
543            .into()
544        }
545
546        fn is_referenceable() -> bool {
547            false
548        }
549    }
550}