Skip to main content

iota_types/
lib.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5#![warn(
6    future_incompatible,
7    nonstandard_style,
8    rust_2018_idioms,
9    rust_2021_compatibility
10)]
11
12use base_types::{IotaAddress, SequenceNumber};
13#[cfg(not(target_arch = "wasm32"))]
14pub use iota_network_stack::multiaddr;
15#[cfg(target_arch = "wasm32")]
16#[path = "wasm_multiaddr.rs"]
17pub mod multiaddr;
18pub use iota_sdk_types as sdk_types;
19use iota_sdk_types::{ObjectId, StructTag, TypeTag};
20use move_binary_format::{
21    CompiledModule,
22    file_format::{AbilitySet, SignatureToken},
23};
24use move_bytecode_utils::resolve_struct;
25use move_core_types::{account_address::AccountAddress, language_storage::ModuleId};
26use object::OBJECT_START_VERSION;
27
28use crate::{
29    base_types::{RESOLVED_ASCII_STR, RESOLVED_STD_OPTION, RESOLVED_UTF8_STR},
30    id::RESOLVED_IOTA_ID,
31    iota_sdk_types_conversions::{struct_tag_core_to_sdk, type_tag_core_to_sdk},
32};
33
34#[macro_use]
35pub mod error;
36
37pub mod account_abstraction;
38pub mod auth_context;
39pub mod balance;
40pub mod base_types;
41pub mod clock;
42pub mod coin;
43pub mod coin_manager;
44pub mod collection_types;
45pub mod committee;
46pub mod config;
47pub mod crypto;
48pub mod deny_list_v1;
49pub mod derived_object;
50pub mod digests;
51pub mod display;
52pub mod dynamic_field;
53pub mod effects;
54pub mod epoch_data;
55pub mod event;
56pub mod executable_transaction;
57pub mod execution;
58pub mod execution_config_utils;
59pub mod full_checkpoint_content;
60pub mod gas;
61pub mod gas_coin;
62pub mod gas_model;
63pub mod global_state_hash;
64pub mod governance;
65pub mod id;
66pub mod in_memory_storage;
67pub mod inner_temporary_store;
68pub mod iota_sdk_types_conversions;
69pub mod iota_serde;
70pub mod iota_system_state;
71pub mod layout_resolver;
72pub mod message_envelope;
73pub mod messages_checkpoint;
74// Consensus message types (and the gRPC API types that carry them) are
75// node-only and pull in fastcrypto-tbls / tonic, which don't build on wasm32.
76#[cfg(not(target_arch = "wasm32"))]
77pub mod messages_consensus;
78#[cfg(not(target_arch = "wasm32"))]
79pub mod messages_grpc;
80pub mod messages_safe_client;
81pub mod metrics;
82pub mod mock_checkpoint_builder;
83pub mod move_authenticator;
84pub mod move_package;
85pub mod multisig;
86pub mod object;
87pub mod passkey_authenticator;
88pub mod programmable_transaction_builder;
89pub mod proto_value;
90pub mod quorum_driver_types;
91pub mod randomness_state;
92pub mod signature;
93pub mod signature_verification;
94pub mod stardust;
95pub mod storage;
96pub mod supported_protocol_versions;
97pub mod system_admin_cap;
98pub mod test_checkpoint_data_builder;
99pub mod timelock;
100pub mod traffic_control;
101pub mod transaction;
102pub mod transaction_executor;
103pub mod transfer;
104pub mod versioned;
105
106#[path = "./unit_tests/utils.rs"]
107pub mod utils;
108
109macro_rules! built_in_ids {
110    ($($addr:ident / $id:ident = $init:expr);* $(;)?) => {
111        $(
112            pub const $addr: AccountAddress = builtin_address($init);
113            pub const $id: ObjectId = ObjectId::new($addr.into_bytes());
114        )*
115    }
116}
117
118macro_rules! built_in_pkgs {
119    ($($addr:ident / $id:ident = $init:expr);* $(;)?) => {
120        built_in_ids! { $($addr / $id = $init;)* }
121    }
122}
123
124built_in_pkgs! {
125    MOVE_STDLIB_ADDRESS / MOVE_STDLIB_PACKAGE_ID = 0x1;
126    IOTA_FRAMEWORK_ADDRESS / IOTA_FRAMEWORK_PACKAGE_ID = 0x2;
127    IOTA_SYSTEM_ADDRESS / IOTA_SYSTEM_PACKAGE_ID = 0x3;
128    GENESIS_BRIDGE_ADDRESS / GENESIS_BRIDGE_PACKAGE_ID = 0xb;
129    STARDUST_ADDRESS / STARDUST_PACKAGE_ID = 0x107a;
130}
131
132built_in_ids! {
133    IOTA_SYSTEM_STATE_ADDRESS / IOTA_SYSTEM_STATE_OBJECT_ID = 0x5;
134    IOTA_CLOCK_ADDRESS / IOTA_CLOCK_OBJECT_ID = 0x6;
135    IOTA_AUTHENTICATOR_STATE_ADDRESS / IOTA_AUTHENTICATOR_STATE_OBJECT_ID = 0x7;
136    IOTA_RANDOMNESS_STATE_ADDRESS / IOTA_RANDOMNESS_STATE_OBJECT_ID = 0x8;
137    GENESIS_IOTA_BRIDGE_ADDRESS / GENESIS_IOTA_BRIDGE_OBJECT_ID = 0x9;
138    IOTA_DENY_LIST_ADDRESS / IOTA_DENY_LIST_OBJECT_ID = 0x403;
139}
140
141pub const SYSTEM_PACKAGE_ADDRESSES: [IotaAddress; 5] = [
142    IotaAddress::STD,
143    IotaAddress::FRAMEWORK,
144    IotaAddress::SYSTEM,
145    IotaAddress::GENESIS_BRIDGE,
146    IotaAddress::STARDUST,
147];
148
149pub const IOTA_SYSTEM_STATE_OBJECT_SHARED_VERSION: SequenceNumber = OBJECT_START_VERSION;
150pub const IOTA_CLOCK_OBJECT_SHARED_VERSION: SequenceNumber = OBJECT_START_VERSION;
151
152const fn builtin_address(suffix: u16) -> AccountAddress {
153    let mut addr = [0u8; AccountAddress::LENGTH];
154    let [hi, lo] = suffix.to_be_bytes();
155    addr[AccountAddress::LENGTH - 2] = hi;
156    addr[AccountAddress::LENGTH - 1] = lo;
157    AccountAddress::new(addr)
158}
159
160pub fn iota_framework_address_concat_string(suffix: &str) -> String {
161    format!("{}{suffix}", IotaAddress::FRAMEWORK.to_short_hex())
162}
163
164/// Parses `s` as an address. Valid formats for addresses are:
165///
166/// - A 256bit number, encoded in decimal, or hexadecimal with a leading "0x"
167///   prefix.
168/// - One of a number of pre-defined named addresses: std, iota, iota_system,
169///   stardust.
170///
171/// Parsing succeeds if and only if `s` matches one of these formats exactly,
172/// with no remaining suffix. This function is intended for use within the
173/// authority codebases.
174pub fn parse_iota_address(s: &str) -> anyhow::Result<IotaAddress> {
175    use move_core_types::parsing::address::ParsedAddress;
176    Ok(IotaAddress::new(
177        ParsedAddress::parse(s)?
178            .into_account_address(&resolve_address)?
179            .into_bytes(),
180    ))
181}
182
183/// Parse `s` as a Module ID: An address (see `parse_iota_address`), followed by
184/// `::`, and then a module name (an identifier). Parsing succeeds if and only
185/// if `s` matches this format exactly, with no remaining input. This function
186/// is intended for use within the authority codebases.
187pub fn parse_iota_module_id(s: &str) -> anyhow::Result<ModuleId> {
188    use move_core_types::parsing::types::ParsedModuleId;
189    ParsedModuleId::parse(s)?.into_module_id(&resolve_address)
190}
191
192/// Parse `s` as a fully-qualified name: A Module ID (see
193/// `parse_iota_module_id`), followed by `::`, and then an identifier (for the
194/// module member). Parsing succeeds if and only if `s` matches this
195/// format exactly, with no remaining input. This function is intended for use
196/// within the authority codebases.
197pub fn parse_iota_fq_name(s: &str) -> anyhow::Result<(ModuleId, String)> {
198    use move_core_types::parsing::types::ParsedFqName;
199    ParsedFqName::parse(s)?.into_fq_name(&resolve_address)
200}
201
202/// Parse `s` as a struct type: A fully-qualified name, optionally followed by a
203/// list of type parameters (types -- see `parse_iota_type_tag`, separated by
204/// commas, surrounded by angle brackets). Parsing succeeds if and only if `s`
205/// matches this format exactly, with no remaining input. This function is
206/// intended for use within the authority codebase.
207pub fn parse_iota_struct_tag(s: &str) -> anyhow::Result<StructTag> {
208    use move_core_types::parsing::types::ParsedStructType;
209    ParsedStructType::parse(s)?
210        .into_struct_tag(&resolve_address)
211        .map(|s| struct_tag_core_to_sdk(&s))
212}
213
214/// Parse `s` as a type: Either a struct type (see `parse_iota_struct_tag`), a
215/// primitive type, or a vector with a type parameter. Parsing succeeds if and
216/// only if `s` matches this format exactly, with no remaining input. This
217/// function is intended for use within the authority codebase.
218pub fn parse_iota_type_tag(s: &str) -> anyhow::Result<TypeTag> {
219    use move_core_types::parsing::types::ParsedType;
220    ParsedType::parse(s)?
221        .into_type_tag(&resolve_address)
222        .map(|s| type_tag_core_to_sdk(&s))
223}
224
225/// Resolve well-known named addresses into numeric addresses.
226pub fn resolve_address(addr: &str) -> Option<AccountAddress> {
227    match addr {
228        "std" => Some(IotaAddress::STD),
229        "iota" => Some(IotaAddress::FRAMEWORK),
230        "iota_system" => Some(IotaAddress::SYSTEM),
231        "stardust" => Some(IotaAddress::STARDUST),
232        _ => None,
233    }
234    .map(|addr| AccountAddress::new(addr.into_bytes()))
235}
236
237pub trait MoveTypeTagTrait {
238    fn get_type_tag() -> TypeTag;
239}
240
241impl MoveTypeTagTrait for u8 {
242    fn get_type_tag() -> TypeTag {
243        TypeTag::U8
244    }
245}
246
247impl MoveTypeTagTrait for u64 {
248    fn get_type_tag() -> TypeTag {
249        TypeTag::U64
250    }
251}
252
253impl MoveTypeTagTrait for ObjectId {
254    fn get_type_tag() -> TypeTag {
255        TypeTag::Address
256    }
257}
258
259impl MoveTypeTagTrait for IotaAddress {
260    fn get_type_tag() -> TypeTag {
261        TypeTag::Address
262    }
263}
264
265impl<T: MoveTypeTagTrait> MoveTypeTagTrait for Vec<T> {
266    fn get_type_tag() -> TypeTag {
267        TypeTag::Vector(Box::new(T::get_type_tag()))
268    }
269}
270
271/// Check if a type is a primitive type in optimistic mode. It invokes the inner
272/// function with is_strict = false.
273pub fn is_primitive(
274    view: &CompiledModule,
275    function_type_args: &[AbilitySet],
276    s: &SignatureToken,
277) -> bool {
278    is_primitive_inner(view, function_type_args, s, false)
279}
280
281/// Check if a type is a primitive type in strict mode. It invokes the inner
282/// function with is_strict = true.
283pub fn is_primitive_strict(
284    view: &CompiledModule,
285    function_type_args: &[AbilitySet],
286    s: &SignatureToken,
287) -> bool {
288    is_primitive_inner(view, function_type_args, s, true)
289}
290
291/// Check if a type is a primitive type.
292/// In optimistic mode (is_strict = false), a type parameter is considered
293/// primitive if it has no key ability. In strict mode (is_strict = true), a
294/// type parameter is considered primitive if it has at least copy or drop
295/// ability.
296pub fn is_primitive_inner(
297    view: &CompiledModule,
298    function_type_args: &[AbilitySet],
299    s: &SignatureToken,
300    is_strict: bool,
301) -> bool {
302    use SignatureToken as S;
303    match s {
304        S::Bool | S::U8 | S::U16 | S::U32 | S::U64 | S::U128 | S::U256 | S::Address => true,
305        S::Signer => false,
306        // optimistic -> no primitive has key
307        // strict -> all primitives have at least copy or drop
308        S::TypeParameter(idx) => {
309            if !is_strict {
310                // optimistic: has no key
311                !function_type_args[*idx as usize].has_key()
312            } else {
313                // strict: has at least one of: copy or drop (or store and one of the others).
314                // copy or drop abilities always imply having no key, but here we double check
315                let abilities = function_type_args[*idx as usize];
316                !abilities.has_key() && (abilities.has_copy() || abilities.has_drop())
317            }
318        }
319
320        S::Datatype(idx) => [RESOLVED_IOTA_ID, RESOLVED_ASCII_STR, RESOLVED_UTF8_STR]
321            .contains(&resolve_struct(view, *idx)),
322
323        S::DatatypeInstantiation(inst) => {
324            let (idx, targs) = &**inst;
325            let resolved_struct = resolve_struct(view, *idx);
326            // option is a primitive
327            resolved_struct == RESOLVED_STD_OPTION
328                && targs.len() == 1
329                && is_primitive_inner(view, function_type_args, &targs[0], is_strict)
330        }
331
332        S::Vector(inner) => is_primitive_inner(view, function_type_args, inner, is_strict),
333        S::Reference(_) | S::MutableReference(_) => false,
334    }
335}
336
337pub fn is_object(
338    view: &CompiledModule,
339    function_type_args: &[AbilitySet],
340    t: &SignatureToken,
341) -> Result<bool, String> {
342    use SignatureToken as S;
343    match t {
344        S::Reference(inner) | S::MutableReference(inner) => {
345            is_object(view, function_type_args, inner)
346        }
347        _ => is_object_struct(view, function_type_args, t),
348    }
349}
350
351pub fn is_object_vector(
352    view: &CompiledModule,
353    function_type_args: &[AbilitySet],
354    t: &SignatureToken,
355) -> Result<bool, String> {
356    use SignatureToken as S;
357    match t {
358        S::Vector(inner) => is_object_struct(view, function_type_args, inner),
359        _ => is_object_struct(view, function_type_args, t),
360    }
361}
362
363pub fn is_object_struct(
364    view: &CompiledModule,
365    function_type_args: &[AbilitySet],
366    s: &SignatureToken,
367) -> Result<bool, String> {
368    use SignatureToken as S;
369    match s {
370        S::Bool
371        | S::U8
372        | S::U16
373        | S::U32
374        | S::U64
375        | S::U128
376        | S::U256
377        | S::Address
378        | S::Signer
379        | S::Vector(_)
380        | S::Reference(_)
381        | S::MutableReference(_) => Ok(false),
382        S::TypeParameter(idx) => Ok(function_type_args
383            .get(*idx as usize)
384            .map(|abs| abs.has_key())
385            .unwrap_or(false)),
386        S::Datatype(_) | S::DatatypeInstantiation(_) => {
387            let abilities = view
388                .abilities(s, function_type_args)
389                .map_err(|vm_err| vm_err.to_string())?;
390            Ok(abilities.has_key())
391        }
392    }
393}
394
395#[cfg(test)]
396mod tests {
397    use expect_test::expect;
398
399    use super::*;
400
401    #[test]
402    fn test_parse_iota_numeric_address() {
403        let result = parse_iota_address("0x2").expect("should not error");
404
405        let expected =
406            expect!["0x0000000000000000000000000000000000000000000000000000000000000002"];
407        expected.assert_eq(&result.to_string());
408    }
409
410    #[test]
411    fn test_parse_iota_named_address() {
412        let result = parse_iota_address("iota").expect("should not error");
413
414        let expected =
415            expect!["0x0000000000000000000000000000000000000000000000000000000000000002"];
416        expected.assert_eq(&result.to_string());
417    }
418
419    #[test]
420    fn test_parse_iota_module_id() {
421        let result = parse_iota_module_id("0x2::iota").expect("should not error");
422        let expected =
423            expect!["0x0000000000000000000000000000000000000000000000000000000000000002::iota"];
424        expected.assert_eq(&result.to_canonical_string(/* with_prefix */ true));
425    }
426
427    #[test]
428    fn test_parse_iota_fq_name() {
429        let (module, name) = parse_iota_fq_name("0x2::object::new").expect("should not error");
430        let expected = expect![
431            "0x0000000000000000000000000000000000000000000000000000000000000002::object::new"
432        ];
433        expected.assert_eq(&format!(
434            "{}::{name}",
435            module.to_canonical_display(/* with_prefix */ true)
436        ));
437    }
438
439    #[test]
440    fn test_parse_iota_struct_tag_short_account_addr() {
441        let result = parse_iota_struct_tag("0x2::iota::IOTA").expect("should not error");
442
443        let expected = expect!["0x2::iota::IOTA"];
444        expected.assert_eq(&result.to_string());
445
446        let expected = expect![
447            "0x0000000000000000000000000000000000000000000000000000000000000002::iota::IOTA"
448        ];
449        expected.assert_eq(&result.to_canonical_string(/* with_prefix */ true));
450    }
451
452    #[test]
453    fn test_parse_iota_struct_tag_long_account_addr() {
454        let result = parse_iota_struct_tag(
455            "0x0000000000000000000000000000000000000000000000000000000000000002::iota::IOTA",
456        )
457        .expect("should not error");
458
459        let expected = expect!["0x2::iota::IOTA"];
460        expected.assert_eq(&result.to_string());
461
462        let expected = expect![
463            "0x0000000000000000000000000000000000000000000000000000000000000002::iota::IOTA"
464        ];
465        expected.assert_eq(&result.to_canonical_string(/* with_prefix */ true));
466    }
467
468    #[test]
469    fn test_parse_iota_struct_with_type_param_short_addr() {
470        let result =
471            parse_iota_struct_tag("0x2::coin::COIN<0x2::iota::IOTA>").expect("should not error");
472
473        let expected = expect!["0x2::coin::COIN<0x2::iota::IOTA>"];
474        expected.assert_eq(&result.to_string());
475
476        let expected = expect![
477            "0x0000000000000000000000000000000000000000000000000000000000000002::coin::COIN<0x0000000000000000000000000000000000000000000000000000000000000002::iota::IOTA>"
478        ];
479        expected.assert_eq(&result.to_canonical_string(/* with_prefix */ true));
480    }
481
482    #[test]
483    fn test_parse_iota_struct_with_type_param_long_addr() {
484        let result = parse_iota_struct_tag("0x0000000000000000000000000000000000000000000000000000000000000002::coin::COIN<0x0000000000000000000000000000000000000000000000000000000000000002::iota::IOTA>")
485            .expect("should not error");
486
487        let expected = expect!["0x2::coin::COIN<0x2::iota::IOTA>"];
488        expected.assert_eq(&result.to_string());
489
490        let expected = expect![
491            "0x0000000000000000000000000000000000000000000000000000000000000002::coin::COIN<0x0000000000000000000000000000000000000000000000000000000000000002::iota::IOTA>"
492        ];
493        expected.assert_eq(&result.to_canonical_string(/* with_prefix */ true));
494    }
495
496    #[test]
497    fn test_complex_struct_tag_with_short_addr() {
498        let result = parse_iota_struct_tag(
499            "0xe7::vec_coin::VecCoin<vector<0x2::coin::Coin<0x2::iota::IOTA>>>",
500        )
501        .expect("should not error");
502
503        let expected = expect!["0xe7::vec_coin::VecCoin<vector<0x2::coin::Coin<0x2::iota::IOTA>>>"];
504        expected.assert_eq(&result.to_string());
505
506        let expected = expect![
507            "0x00000000000000000000000000000000000000000000000000000000000000e7::vec_coin::VecCoin<vector<0x0000000000000000000000000000000000000000000000000000000000000002::coin::Coin<0x0000000000000000000000000000000000000000000000000000000000000002::iota::IOTA>>>"
508        ];
509        expected.assert_eq(&result.to_canonical_string(/* with_prefix */ true));
510    }
511
512    #[test]
513    fn test_complex_struct_tag_with_long_addr() {
514        let result = parse_iota_struct_tag("0x00000000000000000000000000000000000000000000000000000000000000e7::vec_coin::VecCoin<vector<0x0000000000000000000000000000000000000000000000000000000000000002::coin::Coin<0x0000000000000000000000000000000000000000000000000000000000000002::iota::IOTA>>>")
515            .expect("should not error");
516
517        let expected = expect!["0xe7::vec_coin::VecCoin<vector<0x2::coin::Coin<0x2::iota::IOTA>>>"];
518        expected.assert_eq(&result.to_string());
519
520        let expected = expect![
521            "0x00000000000000000000000000000000000000000000000000000000000000e7::vec_coin::VecCoin<vector<0x0000000000000000000000000000000000000000000000000000000000000002::coin::Coin<0x0000000000000000000000000000000000000000000000000000000000000002::iota::IOTA>>>"
522        ];
523        expected.assert_eq(&result.to_canonical_string(/* with_prefix */ true));
524    }
525
526    #[test]
527    fn test_dynamic_field_short_addr() {
528        let result = parse_iota_struct_tag(
529            "0x2::dynamic_field::Field<address, 0x2::balance::Balance<0x234::coin::COIN>>",
530        )
531        .expect("should not error");
532
533        let expected =
534            expect!["0x2::dynamic_field::Field<address, 0x2::balance::Balance<0x234::coin::COIN>>"];
535        expected.assert_eq(&result.to_string());
536
537        let expected = expect![
538            "0x0000000000000000000000000000000000000000000000000000000000000002::dynamic_field::Field<address,0x0000000000000000000000000000000000000000000000000000000000000002::balance::Balance<0x0000000000000000000000000000000000000000000000000000000000000234::coin::COIN>>"
539        ];
540        expected.assert_eq(&result.to_canonical_string(/* with_prefix */ true));
541    }
542
543    #[test]
544    fn test_dynamic_field_long_addr() {
545        let result = parse_iota_struct_tag(
546            "0x2::dynamic_field::Field<address, 0x2::balance::Balance<0x234::coin::COIN>>",
547        )
548        .expect("should not error");
549
550        let expected =
551            expect!["0x2::dynamic_field::Field<address, 0x2::balance::Balance<0x234::coin::COIN>>"];
552        expected.assert_eq(&result.to_string());
553
554        let expected = expect![
555            "0x0000000000000000000000000000000000000000000000000000000000000002::dynamic_field::Field<address,0x0000000000000000000000000000000000000000000000000000000000000002::balance::Balance<0x0000000000000000000000000000000000000000000000000000000000000234::coin::COIN>>"
556        ];
557        expected.assert_eq(&result.to_canonical_string(/* with_prefix */ true));
558    }
559}