1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
// Copyright (c) Mysten Labs, Inc.
// Modifications Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

#![warn(
    future_incompatible,
    nonstandard_style,
    rust_2018_idioms,
    rust_2021_compatibility
)]

use base_types::{IotaAddress, ObjectID, SequenceNumber};
pub use iota_network_stack::multiaddr;
use move_binary_format::{
    CompiledModule,
    file_format::{AbilitySet, SignatureToken},
};
use move_bytecode_utils::resolve_struct;
use move_core_types::{
    account_address::AccountAddress,
    language_storage::{ModuleId, StructTag},
};
pub use move_core_types::{identifier::Identifier, language_storage::TypeTag};
use object::OBJECT_START_VERSION;

use crate::{
    base_types::{RESOLVED_ASCII_STR, RESOLVED_STD_OPTION, RESOLVED_UTF8_STR},
    id::RESOLVED_IOTA_ID,
};

#[macro_use]
pub mod error;

pub mod accumulator;
pub mod authenticator_state;
pub mod balance;
pub mod base_types;
pub mod bridge;
pub mod clock;
pub mod coin;
pub mod coin_manager;
pub mod collection_types;
pub mod committee;
pub mod config;
pub mod crypto;
pub mod deny_list_v1;
pub mod digests;
pub mod display;
pub mod dynamic_field;
pub mod effects;
pub mod epoch_data;
pub mod event;
pub mod executable_transaction;
pub mod execution;
pub mod execution_config_utils;
pub mod execution_status;
pub mod full_checkpoint_content;
pub mod gas;
pub mod gas_coin;
pub mod gas_model;
pub mod governance;
pub mod id;
pub mod in_memory_storage;
pub mod inner_temporary_store;
pub mod iota_sdk2_conversions;
pub mod iota_serde;
pub mod iota_system_state;
pub mod layout_resolver;
pub mod message_envelope;
pub mod messages_checkpoint;
pub mod messages_consensus;
pub mod messages_grpc;
pub mod messages_safe_client;
pub mod metrics;
pub mod mock_checkpoint_builder;
pub mod move_package;
pub mod multisig;
pub mod object;
pub mod passkey_authenticator;
pub mod programmable_transaction_builder;
pub mod quorum_driver_types;
pub mod randomness_state;
pub mod signature;
pub mod signature_verification;
pub mod stardust;
pub mod storage;
pub mod supported_protocol_versions;
pub mod system_admin_cap;
pub mod timelock;
pub mod traffic_control;
pub mod transaction;
pub mod transaction_executor;
pub mod transfer;
pub mod versioned;
pub mod zk_login_authenticator;
pub mod zk_login_util;

#[cfg(any(test, feature = "test-utils"))]
#[path = "./unit_tests/utils.rs"]
pub mod utils;

macro_rules! built_in_ids {
    ($($addr:ident / $id:ident = $init:expr);* $(;)?) => {
        $(
            pub const $addr: AccountAddress = builtin_address($init);
            pub const $id: ObjectID = ObjectID::from_address($addr);
        )*
    }
}

macro_rules! built_in_pkgs {
    ($($addr:ident / $id:ident = $init:expr);* $(;)?) => {
        built_in_ids! { $($addr / $id = $init;)* }
        pub const SYSTEM_PACKAGE_ADDRESSES: &[AccountAddress] = &[$($addr),*];
        pub fn is_system_package(addr: impl Into<AccountAddress>) -> bool {
            matches!(addr.into(), $($addr)|*)
        }
    }
}

built_in_pkgs! {
    MOVE_STDLIB_ADDRESS / MOVE_STDLIB_PACKAGE_ID = 0x1;
    IOTA_FRAMEWORK_ADDRESS / IOTA_FRAMEWORK_PACKAGE_ID = 0x2;
    IOTA_SYSTEM_ADDRESS / IOTA_SYSTEM_PACKAGE_ID = 0x3;
    BRIDGE_ADDRESS / BRIDGE_PACKAGE_ID = 0xb;
    STARDUST_ADDRESS / STARDUST_PACKAGE_ID = 0x107a;
}

built_in_ids! {
    IOTA_SYSTEM_STATE_ADDRESS / IOTA_SYSTEM_STATE_OBJECT_ID = 0x5;
    IOTA_CLOCK_ADDRESS / IOTA_CLOCK_OBJECT_ID = 0x6;
    IOTA_AUTHENTICATOR_STATE_ADDRESS / IOTA_AUTHENTICATOR_STATE_OBJECT_ID = 0x7;
    IOTA_RANDOMNESS_STATE_ADDRESS / IOTA_RANDOMNESS_STATE_OBJECT_ID = 0x8;
    IOTA_BRIDGE_ADDRESS / IOTA_BRIDGE_OBJECT_ID = 0x9;
    IOTA_DENY_LIST_ADDRESS / IOTA_DENY_LIST_OBJECT_ID = 0x403;
}

pub const IOTA_SYSTEM_STATE_OBJECT_SHARED_VERSION: SequenceNumber = OBJECT_START_VERSION;
pub const IOTA_CLOCK_OBJECT_SHARED_VERSION: SequenceNumber = OBJECT_START_VERSION;
pub const IOTA_AUTHENTICATOR_STATE_OBJECT_SHARED_VERSION: SequenceNumber = OBJECT_START_VERSION;

const fn builtin_address(suffix: u16) -> AccountAddress {
    let mut addr = [0u8; AccountAddress::LENGTH];
    let [hi, lo] = suffix.to_be_bytes();
    addr[AccountAddress::LENGTH - 2] = hi;
    addr[AccountAddress::LENGTH - 1] = lo;
    AccountAddress::new(addr)
}

pub fn iota_framework_address_concat_string(suffix: &str) -> String {
    format!("{}{suffix}", IOTA_FRAMEWORK_ADDRESS.to_hex_literal())
}

/// Parses `s` as an address. Valid formats for addresses are:
///
/// - A 256bit number, encoded in decimal, or hexadecimal with a leading "0x"
///   prefix.
/// - One of a number of pre-defined named addresses: std, iota, iota_system,
///   stardust.
///
/// Parsing succeeds if and only if `s` matches one of these formats exactly,
/// with no remaining suffix. This function is intended for use within the
/// authority codebases.
pub fn parse_iota_address(s: &str) -> anyhow::Result<IotaAddress> {
    use move_command_line_common::address::ParsedAddress;
    Ok(ParsedAddress::parse(s)?
        .into_account_address(&resolve_address)?
        .into())
}

/// Parse `s` as a Module ID: An address (see `parse_iota_address`), followed by
/// `::`, and then a module name (an identifier). Parsing succeeds if and only
/// if `s` matches this format exactly, with no remaining input. This function
/// is intended for use within the authority codebases.
pub fn parse_iota_module_id(s: &str) -> anyhow::Result<ModuleId> {
    use move_command_line_common::types::ParsedModuleId;
    ParsedModuleId::parse(s)?.into_module_id(&resolve_address)
}

/// Parse `s` as a fully-qualified name: A Module ID (see
/// `parse_iota_module_id`), followed by `::`, and then an identifier (for the
/// module member). Parsing succeeds if and only if `s` matches this
/// format exactly, with no remaining input. This function is intended for use
/// within the authority codebases.
pub fn parse_iota_fq_name(s: &str) -> anyhow::Result<(ModuleId, String)> {
    use move_command_line_common::types::ParsedFqName;
    ParsedFqName::parse(s)?.into_fq_name(&resolve_address)
}

/// Parse `s` as a struct type: A fully-qualified name, optionally followed by a
/// list of type parameters (types -- see `parse_iota_type_tag`, separated by
/// commas, surrounded by angle brackets). Parsing succeeds if and only if `s`
/// matches this format exactly, with no remaining input. This function is
/// intended for use within the authority codebase.
pub fn parse_iota_struct_tag(s: &str) -> anyhow::Result<StructTag> {
    use move_command_line_common::types::ParsedStructType;
    ParsedStructType::parse(s)?.into_struct_tag(&resolve_address)
}

/// Parse `s` as a type: Either a struct type (see `parse_iota_struct_tag`), a
/// primitive type, or a vector with a type parameter. Parsing succeeds if and
/// only if `s` matches this format exactly, with no remaining input. This
/// function is intended for use within the authority codebase.
pub fn parse_iota_type_tag(s: &str) -> anyhow::Result<TypeTag> {
    use move_command_line_common::types::ParsedType;
    ParsedType::parse(s)?.into_type_tag(&resolve_address)
}

/// Resolve well-known named addresses into numeric addresses.
pub fn resolve_address(addr: &str) -> Option<AccountAddress> {
    match addr {
        "std" => Some(MOVE_STDLIB_ADDRESS),
        "iota" => Some(IOTA_FRAMEWORK_ADDRESS),
        "iota_system" => Some(IOTA_SYSTEM_ADDRESS),
        "stardust" => Some(STARDUST_ADDRESS),
        "bridge" => Some(BRIDGE_ADDRESS),
        _ => None,
    }
}

pub trait MoveTypeTagTrait {
    fn get_type_tag() -> TypeTag;
}

impl MoveTypeTagTrait for u8 {
    fn get_type_tag() -> TypeTag {
        TypeTag::U8
    }
}

impl MoveTypeTagTrait for u64 {
    fn get_type_tag() -> TypeTag {
        TypeTag::U64
    }
}

impl MoveTypeTagTrait for ObjectID {
    fn get_type_tag() -> TypeTag {
        TypeTag::Address
    }
}

impl MoveTypeTagTrait for IotaAddress {
    fn get_type_tag() -> TypeTag {
        TypeTag::Address
    }
}

impl<T: MoveTypeTagTrait> MoveTypeTagTrait for Vec<T> {
    fn get_type_tag() -> TypeTag {
        TypeTag::Vector(Box::new(T::get_type_tag()))
    }
}

pub fn is_primitive(
    view: &CompiledModule,
    function_type_args: &[AbilitySet],
    s: &SignatureToken,
) -> bool {
    use SignatureToken as S;
    match s {
        S::Bool | S::U8 | S::U16 | S::U32 | S::U64 | S::U128 | S::U256 | S::Address => true,
        S::Signer => false,
        // optimistic, but no primitive has key
        S::TypeParameter(idx) => !function_type_args[*idx as usize].has_key(),

        S::Datatype(idx) => [RESOLVED_IOTA_ID, RESOLVED_ASCII_STR, RESOLVED_UTF8_STR]
            .contains(&resolve_struct(view, *idx)),

        S::DatatypeInstantiation(inst) => {
            let (idx, targs) = &**inst;
            let resolved_struct = resolve_struct(view, *idx);
            // option is a primitive
            resolved_struct == RESOLVED_STD_OPTION
                && targs.len() == 1
                && is_primitive(view, function_type_args, &targs[0])
        }

        S::Vector(inner) => is_primitive(view, function_type_args, inner),
        S::Reference(_) | S::MutableReference(_) => false,
    }
}

pub fn is_object(
    view: &CompiledModule,
    function_type_args: &[AbilitySet],
    t: &SignatureToken,
) -> Result<bool, String> {
    use SignatureToken as S;
    match t {
        S::Reference(inner) | S::MutableReference(inner) => {
            is_object(view, function_type_args, inner)
        }
        _ => is_object_struct(view, function_type_args, t),
    }
}

pub fn is_object_vector(
    view: &CompiledModule,
    function_type_args: &[AbilitySet],
    t: &SignatureToken,
) -> Result<bool, String> {
    use SignatureToken as S;
    match t {
        S::Vector(inner) => is_object_struct(view, function_type_args, inner),
        _ => is_object_struct(view, function_type_args, t),
    }
}

fn is_object_struct(
    view: &CompiledModule,
    function_type_args: &[AbilitySet],
    s: &SignatureToken,
) -> Result<bool, String> {
    use SignatureToken as S;
    match s {
        S::Bool
        | S::U8
        | S::U16
        | S::U32
        | S::U64
        | S::U128
        | S::U256
        | S::Address
        | S::Signer
        | S::Vector(_)
        | S::Reference(_)
        | S::MutableReference(_) => Ok(false),
        S::TypeParameter(idx) => Ok(function_type_args
            .get(*idx as usize)
            .map(|abs| abs.has_key())
            .unwrap_or(false)),
        S::Datatype(_) | S::DatatypeInstantiation(_) => {
            let abilities = view
                .abilities(s, function_type_args)
                .map_err(|vm_err| vm_err.to_string())?;
            Ok(abilities.has_key())
        }
    }
}

#[cfg(test)]
mod tests {
    use expect_test::expect;

    use super::*;

    #[test]
    fn test_parse_iota_numeric_address() {
        let result = parse_iota_address("0x2").expect("should not error");

        let expected =
            expect!["0x0000000000000000000000000000000000000000000000000000000000000002"];
        expected.assert_eq(&result.to_string());
    }

    #[test]
    fn test_parse_iota_named_address() {
        let result = parse_iota_address("iota").expect("should not error");

        let expected =
            expect!["0x0000000000000000000000000000000000000000000000000000000000000002"];
        expected.assert_eq(&result.to_string());
    }

    #[test]
    fn test_parse_iota_module_id() {
        let result = parse_iota_module_id("0x2::iota").expect("should not error");
        let expected =
            expect!["0x0000000000000000000000000000000000000000000000000000000000000002::iota"];
        expected.assert_eq(&result.to_canonical_string(/* with_prefix */ true));
    }

    #[test]
    fn test_parse_iota_fq_name() {
        let (module, name) = parse_iota_fq_name("0x2::object::new").expect("should not error");
        let expected = expect![
            "0x0000000000000000000000000000000000000000000000000000000000000002::object::new"
        ];
        expected.assert_eq(&format!(
            "{}::{name}",
            module.to_canonical_display(/* with_prefix */ true)
        ));
    }

    #[test]
    fn test_parse_iota_struct_tag_short_account_addr() {
        let result = parse_iota_struct_tag("0x2::iota::IOTA").expect("should not error");

        let expected = expect!["0x2::iota::IOTA"];
        expected.assert_eq(&result.to_string());

        let expected = expect![
            "0x0000000000000000000000000000000000000000000000000000000000000002::iota::IOTA"
        ];
        expected.assert_eq(&result.to_canonical_string(/* with_prefix */ true));
    }

    #[test]
    fn test_parse_iota_struct_tag_long_account_addr() {
        let result = parse_iota_struct_tag(
            "0x00000000000000000000000000000000000000000000000000000000000000002::iota::IOTA",
        )
        .expect("should not error");

        let expected = expect!["0x2::iota::IOTA"];
        expected.assert_eq(&result.to_string());

        let expected = expect![
            "0x0000000000000000000000000000000000000000000000000000000000000002::iota::IOTA"
        ];
        expected.assert_eq(&result.to_canonical_string(/* with_prefix */ true));
    }

    #[test]
    fn test_parse_iota_struct_with_type_param_short_addr() {
        let result =
            parse_iota_struct_tag("0x2::coin::COIN<0x2::iota::IOTA>").expect("should not error");

        let expected = expect!["0x2::coin::COIN<0x2::iota::IOTA>"];
        expected.assert_eq(&result.to_string());

        let expected = expect![
            "0x0000000000000000000000000000000000000000000000000000000000000002::coin::COIN<0x0000000000000000000000000000000000000000000000000000000000000002::iota::IOTA>"
        ];
        expected.assert_eq(&result.to_canonical_string(/* with_prefix */ true));
    }

    #[test]
    fn test_parse_iota_struct_with_type_param_long_addr() {
        let result = parse_iota_struct_tag("0x0000000000000000000000000000000000000000000000000000000000000002::coin::COIN<0x0000000000000000000000000000000000000000000000000000000000000002::iota::IOTA>")
            .expect("should not error");

        let expected = expect!["0x2::coin::COIN<0x2::iota::IOTA>"];
        expected.assert_eq(&result.to_string());

        let expected = expect![
            "0x0000000000000000000000000000000000000000000000000000000000000002::coin::COIN<0x0000000000000000000000000000000000000000000000000000000000000002::iota::IOTA>"
        ];
        expected.assert_eq(&result.to_canonical_string(/* with_prefix */ true));
    }

    #[test]
    fn test_complex_struct_tag_with_short_addr() {
        let result = parse_iota_struct_tag(
            "0xe7::vec_coin::VecCoin<vector<0x2::coin::Coin<0x2::iota::IOTA>>>",
        )
        .expect("should not error");

        let expected = expect!["0xe7::vec_coin::VecCoin<vector<0x2::coin::Coin<0x2::iota::IOTA>>>"];
        expected.assert_eq(&result.to_string());

        let expected = expect![
            "0x00000000000000000000000000000000000000000000000000000000000000e7::vec_coin::VecCoin<vector<0x0000000000000000000000000000000000000000000000000000000000000002::coin::Coin<0x0000000000000000000000000000000000000000000000000000000000000002::iota::IOTA>>>"
        ];
        expected.assert_eq(&result.to_canonical_string(/* with_prefix */ true));
    }

    #[test]
    fn test_complex_struct_tag_with_long_addr() {
        let result = parse_iota_struct_tag("0x00000000000000000000000000000000000000000000000000000000000000e7::vec_coin::VecCoin<vector<0x0000000000000000000000000000000000000000000000000000000000000002::coin::Coin<0x0000000000000000000000000000000000000000000000000000000000000002::iota::IOTA>>>")
            .expect("should not error");

        let expected = expect!["0xe7::vec_coin::VecCoin<vector<0x2::coin::Coin<0x2::iota::IOTA>>>"];
        expected.assert_eq(&result.to_string());

        let expected = expect![
            "0x00000000000000000000000000000000000000000000000000000000000000e7::vec_coin::VecCoin<vector<0x0000000000000000000000000000000000000000000000000000000000000002::coin::Coin<0x0000000000000000000000000000000000000000000000000000000000000002::iota::IOTA>>>"
        ];
        expected.assert_eq(&result.to_canonical_string(/* with_prefix */ true));
    }

    #[test]
    fn test_dynamic_field_short_addr() {
        let result = parse_iota_struct_tag(
            "0x2::dynamic_field::Field<address, 0x2::balance::Balance<0x234::coin::COIN>>",
        )
        .expect("should not error");

        let expected =
            expect!["0x2::dynamic_field::Field<address, 0x2::balance::Balance<0x234::coin::COIN>>"];
        expected.assert_eq(&result.to_string());

        let expected = expect![
            "0x0000000000000000000000000000000000000000000000000000000000000002::dynamic_field::Field<address,0x0000000000000000000000000000000000000000000000000000000000000002::balance::Balance<0x0000000000000000000000000000000000000000000000000000000000000234::coin::COIN>>"
        ];
        expected.assert_eq(&result.to_canonical_string(/* with_prefix */ true));
    }

    #[test]
    fn test_dynamic_field_long_addr() {
        let result = parse_iota_struct_tag(
            "0x2::dynamic_field::Field<address, 0x2::balance::Balance<0x234::coin::COIN>>",
        )
        .expect("should not error");

        let expected =
            expect!["0x2::dynamic_field::Field<address, 0x2::balance::Balance<0x234::coin::COIN>>"];
        expected.assert_eq(&result.to_string());

        let expected = expect![
            "0x0000000000000000000000000000000000000000000000000000000000000002::dynamic_field::Field<address,0x0000000000000000000000000000000000000000000000000000000000000002::balance::Balance<0x0000000000000000000000000000000000000000000000000000000000000234::coin::COIN>>"
        ];
        expected.assert_eq(&result.to_canonical_string(/* with_prefix */ true));
    }
}