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::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::{Address, 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_driver_types;
103pub mod transaction_executor;
104pub mod transfer;
105pub mod versioned;
106
107#[path = "./unit_tests/utils.rs"]
108pub mod utils;
109
110macro_rules! built_in_ids {
111    ($($addr:ident / $id:ident = $init:expr);* $(;)?) => {
112        $(
113            pub const $addr: AccountAddress = builtin_address($init);
114            pub const $id: ObjectId = ObjectId::new($addr.into_bytes());
115        )*
116    }
117}
118
119macro_rules! built_in_pkgs {
120    ($($addr:ident / $id:ident = $init:expr);* $(;)?) => {
121        built_in_ids! { $($addr / $id = $init;)* }
122    }
123}
124
125built_in_pkgs! {
126    MOVE_STDLIB_ADDRESS / MOVE_STDLIB_PACKAGE_ID = 0x1;
127    IOTA_FRAMEWORK_ADDRESS / IOTA_FRAMEWORK_PACKAGE_ID = 0x2;
128    IOTA_SYSTEM_ADDRESS / IOTA_SYSTEM_PACKAGE_ID = 0x3;
129    GENESIS_BRIDGE_ADDRESS / GENESIS_BRIDGE_PACKAGE_ID = 0xb;
130    STARDUST_ADDRESS / STARDUST_PACKAGE_ID = 0x107a;
131}
132
133built_in_ids! {
134    IOTA_SYSTEM_STATE_ADDRESS / IOTA_SYSTEM_STATE_OBJECT_ID = 0x5;
135    IOTA_CLOCK_ADDRESS / IOTA_CLOCK_OBJECT_ID = 0x6;
136    IOTA_AUTHENTICATOR_STATE_ADDRESS / IOTA_AUTHENTICATOR_STATE_OBJECT_ID = 0x7;
137    IOTA_RANDOMNESS_STATE_ADDRESS / IOTA_RANDOMNESS_STATE_OBJECT_ID = 0x8;
138    GENESIS_IOTA_BRIDGE_ADDRESS / GENESIS_IOTA_BRIDGE_OBJECT_ID = 0x9;
139    IOTA_DENY_LIST_ADDRESS / IOTA_DENY_LIST_OBJECT_ID = 0x403;
140}
141
142pub const SYSTEM_PACKAGE_ADDRESSES: [Address; 5] = [
143    Address::STD,
144    Address::FRAMEWORK,
145    Address::SYSTEM,
146    Address::GENESIS_BRIDGE,
147    Address::STARDUST,
148];
149
150pub const IOTA_SYSTEM_STATE_OBJECT_SHARED_VERSION: SequenceNumber = OBJECT_START_VERSION;
151pub const IOTA_CLOCK_OBJECT_SHARED_VERSION: SequenceNumber = OBJECT_START_VERSION;
152
153const fn builtin_address(suffix: u16) -> AccountAddress {
154    let mut addr = [0u8; AccountAddress::LENGTH];
155    let [hi, lo] = suffix.to_be_bytes();
156    addr[AccountAddress::LENGTH - 2] = hi;
157    addr[AccountAddress::LENGTH - 1] = lo;
158    AccountAddress::new(addr)
159}
160
161pub fn iota_framework_address_concat_string(suffix: &str) -> String {
162    format!("{}{suffix}", Address::FRAMEWORK.to_short_hex())
163}
164
165/// Parses `s` as an address. Valid formats for addresses are:
166///
167/// - A 256bit number, encoded in decimal, or hexadecimal with a leading "0x"
168///   prefix.
169/// - One of a number of pre-defined named addresses: std, iota, iota_system,
170///   stardust.
171///
172/// Parsing succeeds if and only if `s` matches one of these formats exactly,
173/// with no remaining suffix. This function is intended for use within the
174/// authority codebases.
175pub fn parse_iota_address(s: &str) -> anyhow::Result<Address> {
176    use move_core_types::parsing::address::ParsedAddress;
177    Ok(Address::new(
178        ParsedAddress::parse(s)?
179            .into_account_address(&resolve_address)?
180            .into_bytes(),
181    ))
182}
183
184/// Parse `s` as a Module ID: An address (see `parse_iota_address`), followed by
185/// `::`, and then a module name (an identifier). Parsing succeeds if and only
186/// if `s` matches this format exactly, with no remaining input. This function
187/// is intended for use within the authority codebases.
188pub fn parse_iota_module_id(s: &str) -> anyhow::Result<ModuleId> {
189    use move_core_types::parsing::types::ParsedModuleId;
190    ParsedModuleId::parse(s)?.into_module_id(&resolve_address)
191}
192
193/// Parse `s` as a fully-qualified name: A Module ID (see
194/// `parse_iota_module_id`), followed by `::`, and then an identifier (for the
195/// module member). Parsing succeeds if and only if `s` matches this
196/// format exactly, with no remaining input. This function is intended for use
197/// within the authority codebases.
198pub fn parse_iota_fq_name(s: &str) -> anyhow::Result<(ModuleId, String)> {
199    use move_core_types::parsing::types::ParsedFqName;
200    ParsedFqName::parse(s)?.into_fq_name(&resolve_address)
201}
202
203/// Parse `s` as a struct type: A fully-qualified name, optionally followed by a
204/// list of type parameters (types -- see `parse_iota_type_tag`, separated by
205/// commas, surrounded by angle brackets). Parsing succeeds if and only if `s`
206/// matches this format exactly, with no remaining input. This function is
207/// intended for use within the authority codebase.
208pub fn parse_iota_struct_tag(s: &str) -> anyhow::Result<StructTag> {
209    use move_core_types::parsing::types::ParsedStructType;
210    ParsedStructType::parse(s)?
211        .into_struct_tag(&resolve_address)
212        .map(|s| struct_tag_core_to_sdk(&s))
213}
214
215/// Parse `s` as a type: Either a struct type (see `parse_iota_struct_tag`), a
216/// primitive type, or a vector with a type parameter. Parsing succeeds if and
217/// only if `s` matches this format exactly, with no remaining input. This
218/// function is intended for use within the authority codebase.
219pub fn parse_iota_type_tag(s: &str) -> anyhow::Result<TypeTag> {
220    use move_core_types::parsing::types::ParsedType;
221    ParsedType::parse(s)?
222        .into_type_tag(&resolve_address)
223        .map(|s| type_tag_core_to_sdk(&s))
224}
225
226/// Resolve well-known named addresses into numeric addresses.
227pub fn resolve_address(addr: &str) -> Option<AccountAddress> {
228    match addr {
229        "std" => Some(Address::STD),
230        "iota" => Some(Address::FRAMEWORK),
231        "iota_system" => Some(Address::SYSTEM),
232        "stardust" => Some(Address::STARDUST),
233        _ => None,
234    }
235    .map(|addr| AccountAddress::new(addr.into_bytes()))
236}
237
238pub trait MoveTypeTagTrait {
239    fn get_type_tag() -> TypeTag;
240}
241
242impl MoveTypeTagTrait for u8 {
243    fn get_type_tag() -> TypeTag {
244        TypeTag::U8
245    }
246}
247
248impl MoveTypeTagTrait for u64 {
249    fn get_type_tag() -> TypeTag {
250        TypeTag::U64
251    }
252}
253
254impl MoveTypeTagTrait for ObjectId {
255    fn get_type_tag() -> TypeTag {
256        TypeTag::Address
257    }
258}
259
260impl MoveTypeTagTrait for Address {
261    fn get_type_tag() -> TypeTag {
262        TypeTag::Address
263    }
264}
265
266impl<T: MoveTypeTagTrait> MoveTypeTagTrait for Vec<T> {
267    fn get_type_tag() -> TypeTag {
268        TypeTag::Vector(Box::new(T::get_type_tag()))
269    }
270}
271
272/// Check if a type is a primitive type in optimistic mode. It invokes the inner
273/// function with is_strict = false.
274pub fn is_primitive(
275    view: &CompiledModule,
276    function_type_args: &[AbilitySet],
277    s: &SignatureToken,
278) -> bool {
279    is_primitive_inner(view, function_type_args, s, false)
280}
281
282/// Check if a type is a primitive type in strict mode. It invokes the inner
283/// function with is_strict = true.
284pub fn is_primitive_strict(
285    view: &CompiledModule,
286    function_type_args: &[AbilitySet],
287    s: &SignatureToken,
288) -> bool {
289    is_primitive_inner(view, function_type_args, s, true)
290}
291
292/// Check if a type is a primitive type.
293/// In optimistic mode (is_strict = false), a type parameter is considered
294/// primitive if it has no key ability. In strict mode (is_strict = true), a
295/// type parameter is considered primitive if it has at least copy or drop
296/// ability.
297pub fn is_primitive_inner(
298    view: &CompiledModule,
299    function_type_args: &[AbilitySet],
300    s: &SignatureToken,
301    is_strict: bool,
302) -> bool {
303    use SignatureToken as S;
304    match s {
305        S::Bool | S::U8 | S::U16 | S::U32 | S::U64 | S::U128 | S::U256 | S::Address => true,
306        S::Signer => false,
307        // optimistic -> no primitive has key
308        // strict -> all primitives have at least copy or drop
309        S::TypeParameter(idx) => {
310            if !is_strict {
311                // optimistic: has no key
312                !function_type_args[*idx as usize].has_key()
313            } else {
314                // strict: has at least one of: copy or drop (or store and one of the others).
315                // copy or drop abilities always imply having no key, but here we double check
316                let abilities = function_type_args[*idx as usize];
317                !abilities.has_key() && (abilities.has_copy() || abilities.has_drop())
318            }
319        }
320
321        S::Datatype(idx) => [RESOLVED_IOTA_ID, RESOLVED_ASCII_STR, RESOLVED_UTF8_STR]
322            .contains(&resolve_struct(view, *idx)),
323
324        S::DatatypeInstantiation(inst) => {
325            let (idx, targs) = &**inst;
326            let resolved_struct = resolve_struct(view, *idx);
327            // option is a primitive
328            resolved_struct == RESOLVED_STD_OPTION
329                && targs.len() == 1
330                && is_primitive_inner(view, function_type_args, &targs[0], is_strict)
331        }
332
333        S::Vector(inner) => is_primitive_inner(view, function_type_args, inner, is_strict),
334        S::Reference(_) | S::MutableReference(_) => false,
335    }
336}
337
338pub fn is_object(
339    view: &CompiledModule,
340    function_type_args: &[AbilitySet],
341    t: &SignatureToken,
342) -> Result<bool, String> {
343    use SignatureToken as S;
344    match t {
345        S::Reference(inner) | S::MutableReference(inner) => {
346            is_object(view, function_type_args, inner)
347        }
348        _ => is_object_struct(view, function_type_args, t),
349    }
350}
351
352pub fn is_object_vector(
353    view: &CompiledModule,
354    function_type_args: &[AbilitySet],
355    t: &SignatureToken,
356) -> Result<bool, String> {
357    use SignatureToken as S;
358    match t {
359        S::Vector(inner) => is_object_struct(view, function_type_args, inner),
360        _ => is_object_struct(view, function_type_args, t),
361    }
362}
363
364pub fn is_object_struct(
365    view: &CompiledModule,
366    function_type_args: &[AbilitySet],
367    s: &SignatureToken,
368) -> Result<bool, String> {
369    use SignatureToken as S;
370    match s {
371        S::Bool
372        | S::U8
373        | S::U16
374        | S::U32
375        | S::U64
376        | S::U128
377        | S::U256
378        | S::Address
379        | S::Signer
380        | S::Vector(_)
381        | S::Reference(_)
382        | S::MutableReference(_) => Ok(false),
383        S::TypeParameter(idx) => Ok(function_type_args
384            .get(*idx as usize)
385            .map(|abs| abs.has_key())
386            .unwrap_or(false)),
387        S::Datatype(_) | S::DatatypeInstantiation(_) => {
388            let abilities = view
389                .abilities(s, function_type_args)
390                .map_err(|vm_err| vm_err.to_string())?;
391            Ok(abilities.has_key())
392        }
393    }
394}
395
396#[cfg(test)]
397mod tests {
398    use expect_test::expect;
399
400    use super::*;
401
402    #[test]
403    fn test_parse_iota_numeric_address() {
404        let result = parse_iota_address("0x2").expect("should not error");
405
406        let expected =
407            expect!["0x0000000000000000000000000000000000000000000000000000000000000002"];
408        expected.assert_eq(&result.to_string());
409    }
410
411    #[test]
412    fn test_parse_iota_named_address() {
413        let result = parse_iota_address("iota").expect("should not error");
414
415        let expected =
416            expect!["0x0000000000000000000000000000000000000000000000000000000000000002"];
417        expected.assert_eq(&result.to_string());
418    }
419
420    #[test]
421    fn test_parse_iota_module_id() {
422        let result = parse_iota_module_id("0x2::iota").expect("should not error");
423        let expected =
424            expect!["0x0000000000000000000000000000000000000000000000000000000000000002::iota"];
425        expected.assert_eq(&result.to_canonical_string(/* with_prefix */ true));
426    }
427
428    #[test]
429    fn test_parse_iota_fq_name() {
430        let (module, name) = parse_iota_fq_name("0x2::object::new").expect("should not error");
431        let expected = expect![
432            "0x0000000000000000000000000000000000000000000000000000000000000002::object::new"
433        ];
434        expected.assert_eq(&format!(
435            "{}::{name}",
436            module.to_canonical_display(/* with_prefix */ true)
437        ));
438    }
439
440    #[test]
441    fn test_parse_iota_struct_tag_short_account_addr() {
442        let result = parse_iota_struct_tag("0x2::iota::IOTA").expect("should not error");
443
444        let expected = expect!["0x2::iota::IOTA"];
445        expected.assert_eq(&result.to_string());
446
447        let expected = expect![
448            "0x0000000000000000000000000000000000000000000000000000000000000002::iota::IOTA"
449        ];
450        expected.assert_eq(&result.to_canonical_string(/* with_prefix */ true));
451    }
452
453    #[test]
454    fn test_parse_iota_struct_tag_long_account_addr() {
455        let result = parse_iota_struct_tag(
456            "0x0000000000000000000000000000000000000000000000000000000000000002::iota::IOTA",
457        )
458        .expect("should not error");
459
460        let expected = expect!["0x2::iota::IOTA"];
461        expected.assert_eq(&result.to_string());
462
463        let expected = expect![
464            "0x0000000000000000000000000000000000000000000000000000000000000002::iota::IOTA"
465        ];
466        expected.assert_eq(&result.to_canonical_string(/* with_prefix */ true));
467    }
468
469    #[test]
470    fn test_parse_iota_struct_with_type_param_short_addr() {
471        let result =
472            parse_iota_struct_tag("0x2::coin::COIN<0x2::iota::IOTA>").expect("should not error");
473
474        let expected = expect!["0x2::coin::COIN<0x2::iota::IOTA>"];
475        expected.assert_eq(&result.to_string());
476
477        let expected = expect![
478            "0x0000000000000000000000000000000000000000000000000000000000000002::coin::COIN<0x0000000000000000000000000000000000000000000000000000000000000002::iota::IOTA>"
479        ];
480        expected.assert_eq(&result.to_canonical_string(/* with_prefix */ true));
481    }
482
483    #[test]
484    fn test_parse_iota_struct_with_type_param_long_addr() {
485        let result = parse_iota_struct_tag("0x0000000000000000000000000000000000000000000000000000000000000002::coin::COIN<0x0000000000000000000000000000000000000000000000000000000000000002::iota::IOTA>")
486            .expect("should not error");
487
488        let expected = expect!["0x2::coin::COIN<0x2::iota::IOTA>"];
489        expected.assert_eq(&result.to_string());
490
491        let expected = expect![
492            "0x0000000000000000000000000000000000000000000000000000000000000002::coin::COIN<0x0000000000000000000000000000000000000000000000000000000000000002::iota::IOTA>"
493        ];
494        expected.assert_eq(&result.to_canonical_string(/* with_prefix */ true));
495    }
496
497    #[test]
498    fn test_complex_struct_tag_with_short_addr() {
499        let result = parse_iota_struct_tag(
500            "0xe7::vec_coin::VecCoin<vector<0x2::coin::Coin<0x2::iota::IOTA>>>",
501        )
502        .expect("should not error");
503
504        let expected = expect!["0xe7::vec_coin::VecCoin<vector<0x2::coin::Coin<0x2::iota::IOTA>>>"];
505        expected.assert_eq(&result.to_string());
506
507        let expected = expect![
508            "0x00000000000000000000000000000000000000000000000000000000000000e7::vec_coin::VecCoin<vector<0x0000000000000000000000000000000000000000000000000000000000000002::coin::Coin<0x0000000000000000000000000000000000000000000000000000000000000002::iota::IOTA>>>"
509        ];
510        expected.assert_eq(&result.to_canonical_string(/* with_prefix */ true));
511    }
512
513    #[test]
514    fn test_complex_struct_tag_with_long_addr() {
515        let result = parse_iota_struct_tag("0x00000000000000000000000000000000000000000000000000000000000000e7::vec_coin::VecCoin<vector<0x0000000000000000000000000000000000000000000000000000000000000002::coin::Coin<0x0000000000000000000000000000000000000000000000000000000000000002::iota::IOTA>>>")
516            .expect("should not error");
517
518        let expected = expect!["0xe7::vec_coin::VecCoin<vector<0x2::coin::Coin<0x2::iota::IOTA>>>"];
519        expected.assert_eq(&result.to_string());
520
521        let expected = expect![
522            "0x00000000000000000000000000000000000000000000000000000000000000e7::vec_coin::VecCoin<vector<0x0000000000000000000000000000000000000000000000000000000000000002::coin::Coin<0x0000000000000000000000000000000000000000000000000000000000000002::iota::IOTA>>>"
523        ];
524        expected.assert_eq(&result.to_canonical_string(/* with_prefix */ true));
525    }
526
527    #[test]
528    fn test_dynamic_field_short_addr() {
529        let result = parse_iota_struct_tag(
530            "0x2::dynamic_field::Field<address, 0x2::balance::Balance<0x234::coin::COIN>>",
531        )
532        .expect("should not error");
533
534        let expected =
535            expect!["0x2::dynamic_field::Field<address, 0x2::balance::Balance<0x234::coin::COIN>>"];
536        expected.assert_eq(&result.to_string());
537
538        let expected = expect![
539            "0x0000000000000000000000000000000000000000000000000000000000000002::dynamic_field::Field<address,0x0000000000000000000000000000000000000000000000000000000000000002::balance::Balance<0x0000000000000000000000000000000000000000000000000000000000000234::coin::COIN>>"
540        ];
541        expected.assert_eq(&result.to_canonical_string(/* with_prefix */ true));
542    }
543
544    #[test]
545    fn test_dynamic_field_long_addr() {
546        let result = parse_iota_struct_tag(
547            "0x2::dynamic_field::Field<address, 0x2::balance::Balance<0x234::coin::COIN>>",
548        )
549        .expect("should not error");
550
551        let expected =
552            expect!["0x2::dynamic_field::Field<address, 0x2::balance::Balance<0x234::coin::COIN>>"];
553        expected.assert_eq(&result.to_string());
554
555        let expected = expect![
556            "0x0000000000000000000000000000000000000000000000000000000000000002::dynamic_field::Field<address,0x0000000000000000000000000000000000000000000000000000000000000002::balance::Balance<0x0000000000000000000000000000000000000000000000000000000000000234::coin::COIN>>"
557        ];
558        expected.assert_eq(&result.to_canonical_string(/* with_prefix */ true));
559    }
560}