iota_genesis_builder/stardust/migration/verification/
alias.rs

1// Copyright (c) 2024 IOTA Stiftung
2// SPDX-License-Identifier: Apache-2.0
3
4use std::collections::HashMap;
5
6use anyhow::{anyhow, bail, ensure};
7use iota_sdk::types::block::output as stardust;
8use iota_types::{
9    TypeTag,
10    balance::Balance,
11    base_types::{IotaAddress, ObjectID},
12    dynamic_field::{DynamicFieldInfo, Field, derive_dynamic_field_id},
13    in_memory_storage::InMemoryStorage,
14    object::Owner,
15    stardust::output::{
16        ALIAS_DYNAMIC_OBJECT_FIELD_KEY, ALIAS_DYNAMIC_OBJECT_FIELD_KEY_TYPE, Alias, AliasOutput,
17    },
18};
19
20use crate::stardust::{
21    migration::{
22        executor::FoundryLedgerData,
23        verification::{
24            created_objects::CreatedObjects,
25            util::{
26                TokensAmountCounter, verify_address_owner, verify_issuer_feature,
27                verify_metadata_feature, verify_native_tokens, verify_parent,
28                verify_sender_feature,
29            },
30        },
31    },
32    types::address_swap_map::AddressSwapMap,
33};
34
35pub(super) fn verify_alias_output(
36    output_id: stardust::OutputId,
37    output: &stardust::AliasOutput,
38    created_objects: &CreatedObjects,
39    foundry_data: &HashMap<stardust::TokenId, FoundryLedgerData>,
40    storage: &InMemoryStorage,
41    tokens_counter: &mut TokensAmountCounter,
42    address_swap_map: &AddressSwapMap,
43) -> anyhow::Result<()> {
44    let alias_id = ObjectID::new(*output.alias_id_non_null(&output_id));
45
46    let created_output_obj = created_objects.output().and_then(|id| {
47        storage
48            .get_object(id)
49            .ok_or_else(|| anyhow!("missing alias output object"))
50    })?;
51
52    let created_alias_obj = storage
53        .get_object(&alias_id)
54        .ok_or_else(|| anyhow!("missing alias object"))?;
55
56    // Alias Output Owner
57    verify_address_owner(
58        output.governor_address(),
59        created_output_obj,
60        "alias output",
61        address_swap_map,
62    )?;
63
64    // Alias Owner
65    let expected_alias_owner = Owner::ObjectOwner(
66        derive_dynamic_field_id(
67            created_output_obj.id(),
68            &DynamicFieldInfo::dynamic_object_field_wrapper(
69                ALIAS_DYNAMIC_OBJECT_FIELD_KEY_TYPE.parse::<TypeTag>()?,
70            )
71            .into(),
72            &bcs::to_bytes(ALIAS_DYNAMIC_OBJECT_FIELD_KEY)?,
73        )?
74        .into(),
75    );
76
77    ensure!(
78        created_alias_obj.owner == expected_alias_owner,
79        "alias owner mismatch: found {}, expected {}",
80        created_alias_obj.owner,
81        expected_alias_owner
82    );
83
84    let created_alias = created_alias_obj
85        .to_rust::<Alias>()
86        .ok_or_else(|| anyhow!("invalid alias object"))?;
87
88    let created_output = created_output_obj
89        .to_rust::<AliasOutput>()
90        .ok_or_else(|| anyhow!("invalid alias output object"))?;
91
92    // Amount
93    ensure!(
94        created_output.balance.value() == output.amount(),
95        "amount mismatch: found {}, expected {}",
96        created_output.balance.value(),
97        output.amount()
98    );
99    tokens_counter.update_total_value_for_iota(created_output.balance.value());
100
101    // Native Tokens
102    verify_native_tokens::<Field<String, Balance>>(
103        output.native_tokens(),
104        foundry_data,
105        created_output.native_tokens,
106        created_objects.native_tokens().ok(),
107        storage,
108        tokens_counter,
109    )?;
110
111    // Legacy State Controller
112    let expected_state_controller = output
113        .state_controller_address()
114        .to_string()
115        .parse::<IotaAddress>()?;
116    ensure!(
117        created_alias.legacy_state_controller == expected_state_controller,
118        "legacy state controller mismatch: found {}, expected {}",
119        created_alias.legacy_state_controller,
120        expected_state_controller
121    );
122
123    // State Index
124    ensure!(
125        created_alias.state_index == output.state_index(),
126        "state index mismatch: found {}, expected {}",
127        created_alias.state_index,
128        output.state_index()
129    );
130
131    // State Metadata
132    if output.state_metadata().is_empty() {
133        ensure!(
134            created_alias.state_metadata.is_none(),
135            "unexpected state metadata found"
136        );
137    } else {
138        let Some(state_metadata) = created_alias.state_metadata.as_ref() else {
139            bail!("missing state metadata")
140        };
141
142        ensure!(
143            state_metadata.as_slice() == output.state_metadata(),
144            "state metadata mismatch: found {:?}, expected {:?}",
145            state_metadata,
146            output.state_metadata()
147        );
148    }
149
150    // Sender Feature
151    verify_sender_feature(output.features().sender(), created_alias.sender)?;
152
153    // Metadata Feature
154    verify_metadata_feature(
155        output.features().metadata(),
156        created_alias.metadata.as_ref(),
157    )?;
158
159    // Immutable Issuer Feature
160    verify_issuer_feature(
161        output.immutable_features().issuer(),
162        created_alias.immutable_issuer,
163    )?;
164
165    // Immutable Metadata Feature
166    verify_metadata_feature(
167        output.immutable_features().metadata(),
168        created_alias.immutable_metadata.as_ref(),
169    )?;
170
171    verify_parent(&output_id, output.governor_address(), storage)?;
172
173    ensure!(created_objects.coin().is_err(), "unexpected coin found");
174
175    ensure!(
176        created_objects.native_token_coin().is_err(),
177        "unexpected native token coin found"
178    );
179
180    ensure!(
181        created_objects.coin_manager().is_err(),
182        "unexpected coin manager found"
183    );
184
185    ensure!(
186        created_objects.coin_manager_treasury_cap().is_err(),
187        "unexpected coin manager treasury cap found"
188    );
189
190    ensure!(
191        created_objects.package().is_err(),
192        "unexpected package found"
193    );
194
195    Ok(())
196}