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