iota_genesis_builder/
lib.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use std::{
6    collections::BTreeMap,
7    fs::{self, File},
8    io::{BufReader, BufWriter, prelude::Read},
9    path::{Path, PathBuf},
10    str::FromStr,
11    sync::Arc,
12};
13
14use anyhow::{Context, bail};
15use camino::Utf8Path;
16use fastcrypto::{hash::HashFunction, traits::KeyPair};
17use flate2::bufread::GzDecoder;
18use genesis_build_effects::GenesisBuildEffects;
19use iota_config::{
20    IOTA_GENESIS_MIGRATION_TX_DATA_FILENAME,
21    genesis::{
22        Delegations, Genesis, GenesisCeremonyParameters, GenesisChainParameters,
23        TokenDistributionSchedule, UnsignedGenesis,
24    },
25    migration_tx_data::{MigrationTxData, TransactionsData},
26};
27use iota_execution::{self, Executor};
28use iota_framework::{BuiltInFramework, SystemPackage};
29use iota_genesis_common::{execute_genesis_transaction, get_genesis_protocol_config};
30use iota_protocol_config::{Chain, ProtocolConfig, ProtocolVersion};
31use iota_sdk::Url;
32use iota_types::{
33    IOTA_FRAMEWORK_PACKAGE_ID, IOTA_SYSTEM_ADDRESS,
34    balance::{BALANCE_MODULE_NAME, Balance},
35    base_types::{
36        ExecutionDigests, IotaAddress, ObjectID, ObjectRef, SequenceNumber, TransactionDigest,
37        TxContext,
38    },
39    committee::Committee,
40    crypto::{
41        AuthorityKeyPair, AuthorityPublicKeyBytes, AuthoritySignInfo, AuthoritySignInfoTrait,
42        AuthoritySignature, DefaultHash, IotaAuthoritySignature,
43    },
44    deny_list_v1::{DENY_LIST_CREATE_FUNC, DENY_LIST_MODULE},
45    digests::ChainIdentifier,
46    effects::{TransactionEffects, TransactionEvents},
47    epoch_data::EpochData,
48    event::Event,
49    gas_coin::{GAS, GasCoin, STARDUST_TOTAL_SUPPLY_NANOS},
50    governance::StakedIota,
51    in_memory_storage::InMemoryStorage,
52    inner_temporary_store::InnerTemporaryStore,
53    iota_system_state::{IotaSystemState, IotaSystemStateTrait, get_iota_system_state},
54    is_system_package,
55    message_envelope::Message,
56    messages_checkpoint::{
57        CertifiedCheckpointSummary, CheckpointContents, CheckpointSummary,
58        CheckpointVersionSpecificData, CheckpointVersionSpecificDataV1,
59    },
60    metrics::LimitsMetrics,
61    object::{Object, Owner},
62    programmable_transaction_builder::ProgrammableTransactionBuilder,
63    randomness_state::{RANDOMNESS_MODULE_NAME, RANDOMNESS_STATE_CREATE_FUNCTION_NAME},
64    system_admin_cap::IOTA_SYSTEM_ADMIN_CAP_MODULE_NAME,
65    timelock::{
66        stardust_upgrade_label::STARDUST_UPGRADE_LABEL_VALUE,
67        timelocked_staked_iota::TimelockedStakedIota,
68    },
69    transaction::{
70        CallArg, CheckedInputObjects, Command, InputObjectKind, ObjectArg, ObjectReadResult,
71        Transaction,
72    },
73};
74use move_binary_format::CompiledModule;
75use move_core_types::ident_str;
76use serde::{Deserialize, Serialize};
77use shared_crypto::intent::{Intent, IntentMessage, IntentScope};
78use stake::GenesisStake;
79use stardust::migration::MigrationObjects;
80use tracing::trace;
81use validator_info::{GenesisValidatorInfo, GenesisValidatorMetadata, ValidatorInfo};
82
83pub mod genesis_build_effects;
84mod stake;
85pub mod stardust;
86pub mod validator_info;
87
88const GENESIS_BUILDER_COMMITTEE_DIR: &str = "committee";
89pub const GENESIS_BUILDER_PARAMETERS_FILE: &str = "parameters";
90const GENESIS_BUILDER_TOKEN_DISTRIBUTION_SCHEDULE_FILE: &str = "token-distribution-schedule";
91const GENESIS_BUILDER_SIGNATURE_DIR: &str = "signatures";
92const GENESIS_BUILDER_UNSIGNED_GENESIS_FILE: &str = "unsigned-genesis";
93const GENESIS_BUILDER_MIGRATION_SOURCES_FILE: &str = "migration-sources";
94const GENESIS_BUILDER_DELEGATOR_FILE: &str = "delegator";
95const GENESIS_BUILDER_DELEGATOR_MAP_FILE: &str = "delegator-map";
96
97pub const OBJECT_SNAPSHOT_FILE_PATH: &str = "stardust_object_snapshot.bin";
98pub const IOTA_OBJECT_SNAPSHOT_URL: &str = "https://stardust-objects.s3.eu-central-1.amazonaws.com/iota/alphanet/latest/stardust_object_snapshot.bin.gz";
99
100// THe number of maximum transactions for the genesis checkpoint in the case of
101// migration
102const MAX_AMOUNT_OF_TX_PER_CHECKPOINT: u64 = 10_000;
103
104pub struct Builder {
105    parameters: GenesisCeremonyParameters,
106    token_distribution_schedule: Option<TokenDistributionSchedule>,
107    objects: BTreeMap<ObjectID, Object>,
108    validators: BTreeMap<AuthorityPublicKeyBytes, GenesisValidatorInfo>,
109    // Validator signatures over checkpoint
110    signatures: BTreeMap<AuthorityPublicKeyBytes, AuthoritySignInfo>,
111    built_genesis: Option<UnsignedGenesis>,
112    migration_objects: MigrationObjects,
113    genesis_stake: GenesisStake,
114    migration_sources: Vec<SnapshotSource>,
115    migration_tx_data: Option<MigrationTxData>,
116    delegation: Option<GenesisDelegation>,
117}
118
119enum GenesisDelegation {
120    /// Represents a single delegator address that applies to all validators.
121    OneToAll(IotaAddress),
122    /// Represents a map of delegator addresses to validator addresses and
123    /// a specified stake and gas allocation.
124    ManyToMany(Delegations),
125}
126
127impl Default for Builder {
128    fn default() -> Self {
129        Self::new()
130    }
131}
132
133impl Builder {
134    pub fn new() -> Self {
135        Self {
136            parameters: Default::default(),
137            token_distribution_schedule: None,
138            objects: Default::default(),
139            validators: Default::default(),
140            signatures: Default::default(),
141            built_genesis: None,
142            migration_objects: Default::default(),
143            genesis_stake: Default::default(),
144            migration_sources: Default::default(),
145            migration_tx_data: Default::default(),
146            delegation: None,
147        }
148    }
149
150    /// Return an iterator of migration objects if this genesis is with
151    /// migration objects.
152    pub fn tx_migration_objects(&self) -> impl Iterator<Item = Object> + '_ {
153        self.migration_tx_data
154            .as_ref()
155            .map(|tx_data| tx_data.get_objects())
156            .into_iter()
157            .flatten()
158    }
159
160    /// Set the genesis delegation to be a `OneToAll` kind and set the
161    /// delegator address.
162    pub fn with_delegator(mut self, delegator: IotaAddress) -> Self {
163        self.delegation = Some(GenesisDelegation::OneToAll(delegator));
164        self
165    }
166
167    /// Set the genesis delegation to be a `ManyToMany` kind and set the
168    /// delegator map.
169    pub fn with_delegations(mut self, delegations: Delegations) -> Self {
170        self.delegation = Some(GenesisDelegation::ManyToMany(delegations));
171        self
172    }
173
174    /// Checks if the genesis to be built has no migration or if it includes
175    /// Stardust migration stakes
176    pub fn contains_migrations(&self) -> bool {
177        !self.genesis_stake.is_empty()
178    }
179
180    pub fn with_parameters(mut self, parameters: GenesisCeremonyParameters) -> Self {
181        self.parameters = parameters;
182        self
183    }
184
185    /// Set the [`TokenDistributionSchedule`].
186    ///
187    /// # Panic
188    ///
189    /// This method fails if the passed schedule contains timelocked stake.
190    /// This is to avoid conflicts with the genesis stake, that delegates
191    /// timelocked stake based on the migrated state.
192    pub fn with_token_distribution_schedule(
193        mut self,
194        token_distribution_schedule: TokenDistributionSchedule,
195    ) -> Self {
196        assert!(
197            !token_distribution_schedule.contains_timelocked_stake(),
198            "timelocked stake should be generated only from migrated stake"
199        );
200        self.token_distribution_schedule = Some(token_distribution_schedule);
201        self
202    }
203
204    pub fn with_protocol_version(mut self, v: ProtocolVersion) -> Self {
205        self.parameters.protocol_version = v;
206        self
207    }
208
209    pub fn add_object(mut self, object: Object) -> Self {
210        self.objects.insert(object.id(), object);
211        self
212    }
213
214    pub fn add_objects(mut self, objects: Vec<Object>) -> Self {
215        for object in objects {
216            self.objects.insert(object.id(), object);
217        }
218        self
219    }
220
221    pub fn add_validator(
222        mut self,
223        validator: ValidatorInfo,
224        proof_of_possession: AuthoritySignature,
225    ) -> Self {
226        self.validators.insert(
227            validator.authority_key(),
228            GenesisValidatorInfo {
229                info: validator,
230                proof_of_possession,
231            },
232        );
233        self
234    }
235
236    pub fn validators(&self) -> &BTreeMap<AuthorityPublicKeyBytes, GenesisValidatorInfo> {
237        &self.validators
238    }
239
240    pub fn add_validator_signature(mut self, keypair: &AuthorityKeyPair) -> Self {
241        let name = keypair.public().into();
242        assert!(
243            self.validators.contains_key(&name),
244            "provided keypair does not correspond to a validator in the validator set"
245        );
246
247        let UnsignedGenesis { checkpoint, .. } = self.get_or_build_unsigned_genesis();
248
249        let checkpoint_signature = {
250            let intent_msg = IntentMessage::new(
251                Intent::iota_app(IntentScope::CheckpointSummary),
252                checkpoint.clone(),
253            );
254            let signature = AuthoritySignature::new_secure(&intent_msg, &checkpoint.epoch, keypair);
255            AuthoritySignInfo {
256                epoch: checkpoint.epoch,
257                authority: name,
258                signature,
259            }
260        };
261
262        self.signatures.insert(name, checkpoint_signature);
263
264        self
265    }
266
267    pub fn add_migration_source(mut self, source: SnapshotSource) -> Self {
268        self.migration_sources.push(source);
269        self
270    }
271
272    pub fn unsigned_genesis_checkpoint(&self) -> Option<UnsignedGenesis> {
273        self.built_genesis.clone()
274    }
275
276    pub fn load_migration_sources(&mut self) -> anyhow::Result<()> {
277        for source in &self.migration_sources {
278            tracing::info!("Adding migration objects from {:?}", source);
279            self.migration_objects
280                .extend(bcs::from_reader::<Vec<_>>(source.to_reader()?)?);
281        }
282        Ok(())
283    }
284
285    /// Create and cache the [`GenesisStake`] if the builder
286    /// contains migrated objects.
287    ///
288    /// Two cases can happen here:
289    /// 1. if a delegator map is given as input -> then use the map input to
290    ///    create and cache the genesis stake.
291    /// 2. if a delegator map is NOT given as input -> then use one default
292    ///    delegator passed as input and delegate the minimum required stake to
293    ///    all validators to create and cache the genesis stake.
294    fn create_and_cache_genesis_stake(&mut self) -> anyhow::Result<()> {
295        if !self.migration_objects.is_empty() {
296            self.genesis_stake = GenesisStake::new_with_delegations(
297                match &self.delegation {
298                    Some(GenesisDelegation::ManyToMany(delegations)) => {
299                        // Case 1 -> use the delegations input to create and cache the genesis stake
300                        delegations.clone()
301                    }
302                    Some(GenesisDelegation::OneToAll(delegator)) => {
303                        // Case 2 -> use one default delegator passed as input and delegate the
304                        // minimum required stake to all validators to create the genesis stake
305                        Delegations::new_for_validators_with_default_allocation(
306                            self.validators.values().map(|v| v.info.iota_address()),
307                            *delegator,
308                        )
309                    }
310                    None => bail!("no delegator/s assigned with a migration"),
311                },
312                &self.migration_objects,
313            )?;
314        }
315        Ok(())
316    }
317
318    /// Evaluate the genesis [`TokenDistributionSchedule`].
319    ///
320    /// There are 6 cases for evaluating this:
321    /// 1. The genesis is built WITHOUT migration
322    ///    1. and a schedule is given as input -> then just use the input
323    ///       schedule;
324    ///    2. and the schedule is NOT given as input -> then instantiate a
325    ///       default token distribution schedule for a genesis without
326    ///       migration.
327    /// 2. The genesis is built with migration,
328    ///    1. and token distribution schedule is given as input
329    ///       1. if the token distribution schedule contains a timelocked stake
330    ///          -> then just use the input schedule, because it was initialized
331    ///          for migration before this execution (this is the case where we
332    ///          load a [`Builder`] from disk that has already built genesis
333    ///          with the migrated state.);
334    ///       2. if the token distribution schedule does NOT contain any
335    ///          timelocked stake -> then fetch the cached the genesis stake and
336    ///          merge it to the token distribution schedule;
337    ///    2. and token distribution schedule is NOT given as input -> then
338    ///       fetch the cached genesis stake and initialize a new token
339    ///       distribution schedule with it.
340    fn resolve_token_distribution_schedule(&mut self) -> TokenDistributionSchedule {
341        let is_genesis_with_migration = !self.migration_objects.is_empty();
342        let stardust_total_supply_nanos =
343            self.migration_sources.len() as u64 * STARDUST_TOTAL_SUPPLY_NANOS;
344
345        if let Some(schedule) = self.token_distribution_schedule.take() {
346            if !is_genesis_with_migration || schedule.contains_timelocked_stake() {
347                // Case 1.1 and 2.1.1
348                schedule
349            } else {
350                // Case 2.1.2
351                self.genesis_stake
352                    .extend_token_distribution_schedule_without_migration(
353                        schedule,
354                        stardust_total_supply_nanos,
355                    )
356            }
357        } else if !is_genesis_with_migration {
358            // Case 1.2
359            TokenDistributionSchedule::new_for_validators_with_default_allocation(
360                self.validators.values().map(|v| v.info.iota_address()),
361            )
362        } else {
363            // Case 2.2
364            self.genesis_stake
365                .to_token_distribution_schedule(stardust_total_supply_nanos)
366        }
367    }
368
369    fn build_and_cache_unsigned_genesis(&mut self) {
370        // Verify that all input data is valid.
371        // Check that if extra objects are present then it is allowed by the parameters
372        // to add extra objects and it also validates the validator info
373        self.validate_inputs().unwrap();
374
375        // If migration sources are present, then load them into memory.
376        // Otherwise do nothing.
377        self.load_migration_sources()
378            .expect("migration sources should be loaded without errors");
379
380        // If migration objects are present, then create and cache the genesis stake;
381        // this also prepares the data needed to resolve the token distribution
382        // schedule. Otherwise do nothing.
383        self.create_and_cache_genesis_stake()
384            .expect("genesis stake should be created without errors");
385
386        // Resolve the token distribution schedule based on inputs and a possible
387        // genesis stake
388        let token_distribution_schedule = self.resolve_token_distribution_schedule();
389
390        // Verify that token distribution schedule is valid
391        token_distribution_schedule.validate();
392        token_distribution_schedule
393            .check_minimum_stake_for_validators(
394                self.validators.values().map(|v| v.info.iota_address()),
395            )
396            .expect("all validators should have the required stake");
397
398        // Finally build the genesis and migration data
399        let (unsigned_genesis, migration_tx_data) = build_unsigned_genesis_data(
400            &self.parameters,
401            &token_distribution_schedule,
402            self.validators.values(),
403            self.objects.clone().into_values().collect::<Vec<_>>(),
404            &mut self.genesis_stake,
405            &mut self.migration_objects,
406        );
407
408        // Store built data
409        self.migration_tx_data = (!migration_tx_data.is_empty()).then_some(migration_tx_data);
410        self.built_genesis = Some(unsigned_genesis);
411        self.token_distribution_schedule = Some(token_distribution_schedule);
412    }
413
414    pub fn get_or_build_unsigned_genesis(&mut self) -> &UnsignedGenesis {
415        if self.built_genesis.is_none() {
416            self.build_and_cache_unsigned_genesis();
417        }
418        self.built_genesis
419            .as_ref()
420            .expect("genesis should have been built and cached")
421    }
422
423    fn committee(objects: &[Object]) -> Committee {
424        let iota_system_object =
425            get_iota_system_state(&objects).expect("IOTA System State object must always exist");
426        iota_system_object
427            .get_current_epoch_committee()
428            .committee()
429            .clone()
430    }
431
432    pub fn protocol_version(&self) -> ProtocolVersion {
433        self.parameters.protocol_version
434    }
435
436    pub fn build(mut self) -> GenesisBuildEffects {
437        if self.built_genesis.is_none() {
438            self.build_and_cache_unsigned_genesis();
439        }
440
441        // Verify that all on-chain state was properly created
442        self.validate().unwrap();
443
444        let UnsignedGenesis {
445            checkpoint,
446            checkpoint_contents,
447            transaction,
448            effects,
449            events,
450            objects,
451        } = self
452            .built_genesis
453            .take()
454            .expect("genesis should have been built");
455
456        let committee = Self::committee(&objects);
457
458        let checkpoint = {
459            let signatures = self.signatures.clone().into_values().collect();
460
461            CertifiedCheckpointSummary::new(checkpoint, signatures, &committee).unwrap()
462        };
463
464        GenesisBuildEffects::new(
465            Genesis::new(
466                checkpoint,
467                checkpoint_contents,
468                transaction,
469                effects,
470                events,
471                objects,
472            ),
473            self.migration_tx_data,
474        )
475    }
476
477    /// Validates the entire state of the build, no matter what the internal
478    /// state is (input collection phase or output phase)
479    pub fn validate(&self) -> anyhow::Result<(), anyhow::Error> {
480        self.validate_inputs()?;
481        self.validate_token_distribution_schedule()?;
482        self.validate_output();
483        Ok(())
484    }
485
486    /// Runs through validation checks on the input values present in the
487    /// builder
488    fn validate_inputs(&self) -> anyhow::Result<(), anyhow::Error> {
489        if !self.parameters.allow_insertion_of_extra_objects && !self.objects.is_empty() {
490            bail!("extra objects are disallowed");
491        }
492
493        for validator in self.validators.values() {
494            validator.validate().with_context(|| {
495                format!(
496                    "metadata for validator {} is invalid",
497                    validator.info.name()
498                )
499            })?;
500        }
501
502        Ok(())
503    }
504
505    /// Runs through validation checks on the input token distribution schedule
506    fn validate_token_distribution_schedule(&self) -> anyhow::Result<(), anyhow::Error> {
507        if let Some(token_distribution_schedule) = &self.token_distribution_schedule {
508            token_distribution_schedule.validate();
509            token_distribution_schedule.check_minimum_stake_for_validators(
510                self.validators.values().map(|v| v.info.iota_address()),
511            )?;
512        }
513
514        Ok(())
515    }
516
517    /// Runs through validation checks on the generated output (the initial
518    /// chain state) based on the input values present in the builder
519    fn validate_output(&self) {
520        // If genesis hasn't been built yet, just early return as there is nothing to
521        // validate yet
522        let Some(unsigned_genesis) = self.unsigned_genesis_checkpoint() else {
523            return;
524        };
525
526        let GenesisChainParameters {
527            protocol_version,
528            chain_start_timestamp_ms,
529            epoch_duration_ms,
530            max_validator_count,
531            min_validator_joining_stake,
532            validator_low_stake_threshold,
533            validator_very_low_stake_threshold,
534            validator_low_stake_grace_period,
535        } = self.parameters.to_genesis_chain_parameters();
536
537        // In non-testing code, genesis type must always be V1.
538        let system_state = match unsigned_genesis.iota_system_object() {
539            IotaSystemState::V1(inner) => inner,
540            IotaSystemState::V2(_) => unreachable!(),
541            #[cfg(msim)]
542            _ => {
543                // Types other than V1 used in simtests do not need to be validated.
544                return;
545            }
546        };
547
548        let protocol_config = get_genesis_protocol_config(ProtocolVersion::new(protocol_version));
549
550        if protocol_config.create_authenticator_state_in_genesis() {
551            let authenticator_state = unsigned_genesis.authenticator_state_object().unwrap();
552            assert!(authenticator_state.active_jwks.is_empty());
553        } else {
554            assert!(unsigned_genesis.authenticator_state_object().is_none());
555        }
556        assert!(unsigned_genesis.has_randomness_state_object());
557
558        assert!(unsigned_genesis.has_coin_deny_list_object());
559
560        assert_eq!(
561            self.validators.len(),
562            system_state.validators.active_validators.len()
563        );
564        let mut address_to_pool_id = BTreeMap::new();
565        for (validator, onchain_validator) in self
566            .validators
567            .values()
568            .zip(system_state.validators.active_validators.iter())
569        {
570            let metadata = onchain_validator.verified_metadata();
571
572            // Validators should not have duplicate addresses so the result of insertion
573            // should be None.
574            assert!(
575                address_to_pool_id
576                    .insert(metadata.iota_address, onchain_validator.staking_pool.id)
577                    .is_none()
578            );
579            assert_eq!(validator.info.iota_address(), metadata.iota_address);
580            assert_eq!(validator.info.authority_key(), metadata.iota_pubkey_bytes());
581            assert_eq!(validator.info.network_key, metadata.network_pubkey);
582            assert_eq!(validator.info.protocol_key, metadata.protocol_pubkey);
583            assert_eq!(
584                validator.proof_of_possession.as_ref().to_vec(),
585                metadata.proof_of_possession_bytes
586            );
587            assert_eq!(validator.info.name(), &metadata.name);
588            assert_eq!(validator.info.description, metadata.description);
589            assert_eq!(validator.info.image_url, metadata.image_url);
590            assert_eq!(validator.info.project_url, metadata.project_url);
591            assert_eq!(validator.info.network_address(), &metadata.net_address);
592            assert_eq!(validator.info.p2p_address, metadata.p2p_address);
593            assert_eq!(validator.info.primary_address, metadata.primary_address);
594
595            assert_eq!(validator.info.gas_price, onchain_validator.gas_price);
596            assert_eq!(
597                validator.info.commission_rate,
598                onchain_validator.commission_rate
599            );
600        }
601
602        assert_eq!(system_state.epoch, 0);
603        assert_eq!(system_state.protocol_version, protocol_version);
604        assert_eq!(system_state.storage_fund.non_refundable_balance.value(), 0);
605        assert_eq!(
606            system_state
607                .storage_fund
608                .total_object_storage_rebates
609                .value(),
610            0
611        );
612
613        assert_eq!(system_state.parameters.epoch_duration_ms, epoch_duration_ms);
614        assert_eq!(
615            system_state.parameters.max_validator_count,
616            max_validator_count,
617        );
618        assert_eq!(
619            system_state.parameters.min_validator_joining_stake,
620            min_validator_joining_stake,
621        );
622        assert_eq!(
623            system_state.parameters.validator_low_stake_threshold,
624            validator_low_stake_threshold,
625        );
626        assert_eq!(
627            system_state.parameters.validator_very_low_stake_threshold,
628            validator_very_low_stake_threshold,
629        );
630        assert_eq!(
631            system_state.parameters.validator_low_stake_grace_period,
632            validator_low_stake_grace_period,
633        );
634
635        assert!(!system_state.safe_mode);
636        assert_eq!(
637            system_state.epoch_start_timestamp_ms,
638            chain_start_timestamp_ms,
639        );
640        assert_eq!(system_state.validators.pending_removals.len(), 0);
641        assert_eq!(
642            system_state
643                .validators
644                .pending_active_validators
645                .contents
646                .size,
647            0
648        );
649        assert_eq!(system_state.validators.inactive_validators.size, 0);
650        assert_eq!(system_state.validators.validator_candidates.size, 0);
651
652        // Check distribution is correct
653        let token_distribution_schedule = self.token_distribution_schedule.clone().unwrap();
654
655        let allocations_amount: u64 = token_distribution_schedule
656            .allocations
657            .iter()
658            .map(|allocation| allocation.amount_nanos)
659            .sum();
660
661        assert_eq!(
662            system_state.iota_treasury_cap.total_supply().value,
663            token_distribution_schedule.pre_minted_supply + allocations_amount
664        );
665
666        let mut gas_objects: BTreeMap<ObjectID, (&Object, GasCoin)> = unsigned_genesis
667            .objects()
668            .iter()
669            .filter_map(|o| GasCoin::try_from(o).ok().map(|g| (o.id(), (o, g))))
670            .collect();
671        let mut staked_iota_objects: BTreeMap<ObjectID, (&Object, StakedIota)> = unsigned_genesis
672            .objects()
673            .iter()
674            .filter_map(|o| StakedIota::try_from(o).ok().map(|s| (o.id(), (o, s))))
675            .collect();
676        let mut timelock_staked_iota_objects: BTreeMap<ObjectID, (&Object, TimelockedStakedIota)> =
677            unsigned_genesis
678                .objects()
679                .iter()
680                .filter_map(|o| {
681                    TimelockedStakedIota::try_from(o)
682                        .ok()
683                        .map(|s| (o.id(), (o, s)))
684                })
685                .collect();
686
687        for allocation in token_distribution_schedule.allocations {
688            if let Some(staked_with_validator) = allocation.staked_with_validator {
689                let staking_pool_id = *address_to_pool_id
690                    .get(&staked_with_validator)
691                    .expect("staking pool should exist");
692                if let Some(expiration) = allocation.staked_with_timelock_expiration {
693                    let timelock_staked_iota_object_id = timelock_staked_iota_objects
694                        .iter()
695                        .find(|(_k, (o, s))| {
696                            let Owner::AddressOwner(owner) = &o.owner else {
697                                panic!("gas object owner must be address owner");
698                            };
699                            *owner == allocation.recipient_address
700                                && s.principal() == allocation.amount_nanos
701                                && s.pool_id() == staking_pool_id
702                                && s.expiration_timestamp_ms() == expiration
703                        })
704                        .map(|(k, _)| *k)
705                        .expect("all allocations should be present");
706                    let timelock_staked_iota_object = timelock_staked_iota_objects
707                        .remove(&timelock_staked_iota_object_id)
708                        .unwrap();
709                    assert_eq!(
710                        timelock_staked_iota_object.0.owner,
711                        Owner::AddressOwner(allocation.recipient_address)
712                    );
713                    assert_eq!(
714                        timelock_staked_iota_object.1.principal(),
715                        allocation.amount_nanos
716                    );
717                    assert_eq!(timelock_staked_iota_object.1.pool_id(), staking_pool_id);
718                    assert_eq!(timelock_staked_iota_object.1.activation_epoch(), 0);
719                } else {
720                    let staked_iota_object_id = staked_iota_objects
721                        .iter()
722                        .find(|(_k, (o, s))| {
723                            let Owner::AddressOwner(owner) = &o.owner else {
724                                panic!("gas object owner must be address owner");
725                            };
726                            *owner == allocation.recipient_address
727                                && s.principal() == allocation.amount_nanos
728                                && s.pool_id() == staking_pool_id
729                        })
730                        .map(|(k, _)| *k)
731                        .expect("all allocations should be present");
732                    let staked_iota_object =
733                        staked_iota_objects.remove(&staked_iota_object_id).unwrap();
734                    assert_eq!(
735                        staked_iota_object.0.owner,
736                        Owner::AddressOwner(allocation.recipient_address)
737                    );
738                    assert_eq!(staked_iota_object.1.principal(), allocation.amount_nanos);
739                    assert_eq!(staked_iota_object.1.pool_id(), staking_pool_id);
740                    assert_eq!(staked_iota_object.1.activation_epoch(), 0);
741                }
742            } else {
743                let gas_object_id = gas_objects
744                    .iter()
745                    .find(|(_k, (o, g))| {
746                        if let Owner::AddressOwner(owner) = &o.owner {
747                            *owner == allocation.recipient_address
748                                && g.value() == allocation.amount_nanos
749                        } else {
750                            false
751                        }
752                    })
753                    .map(|(k, _)| *k)
754                    .expect("all allocations should be present");
755                let gas_object = gas_objects.remove(&gas_object_id).unwrap();
756                assert_eq!(
757                    gas_object.0.owner,
758                    Owner::AddressOwner(allocation.recipient_address)
759                );
760                assert_eq!(gas_object.1.value(), allocation.amount_nanos,);
761            }
762        }
763
764        // All Gas and staked objects should be accounted for
765        if !self.parameters.allow_insertion_of_extra_objects {
766            assert!(gas_objects.is_empty());
767            assert!(staked_iota_objects.is_empty());
768            assert!(timelock_staked_iota_objects.is_empty());
769        }
770
771        let committee = system_state.get_current_epoch_committee();
772        for signature in self.signatures.values() {
773            if !self.validators.contains_key(&signature.authority) {
774                panic!("found signature for unknown validator: {:#?}", signature);
775            }
776
777            signature
778                .verify_secure(
779                    unsigned_genesis.checkpoint(),
780                    Intent::iota_app(IntentScope::CheckpointSummary),
781                    committee.committee(),
782                )
783                .expect("signature should be valid");
784        }
785
786        // Validate migration content in order to avoid corrupted or malicious data
787        if let Some(migration_tx_data) = &self.migration_tx_data {
788            migration_tx_data
789                .validate_total_supply(token_distribution_schedule.pre_minted_supply)
790                .expect("the migration data does not contain the expected total supply");
791            migration_tx_data
792                .validate_from_unsigned_genesis(&unsigned_genesis)
793                .expect("the migration data is corrupted");
794        } else {
795            assert!(
796                !self.contains_migrations(),
797                "genesis that contains migration should have migration data"
798            );
799        }
800    }
801
802    pub async fn load<P: AsRef<Path>>(path: P) -> anyhow::Result<Self, anyhow::Error> {
803        let path = path.as_ref();
804        let path: &Utf8Path = path.try_into()?;
805        trace!("Reading Genesis Builder from {}", path);
806
807        if !path.is_dir() {
808            bail!("path must be a directory");
809        }
810
811        // Load parameters
812        let parameters_file = path.join(GENESIS_BUILDER_PARAMETERS_FILE);
813        let parameters = serde_yaml::from_slice(&fs::read(&parameters_file).context(format!(
814            "unable to read genesis parameters file {parameters_file}"
815        ))?)
816        .context("unable to deserialize genesis parameters")?;
817
818        // Load migration objects if any
819        let migration_sources_file = path.join(GENESIS_BUILDER_MIGRATION_SOURCES_FILE);
820        let migration_sources: Vec<SnapshotSource> = if migration_sources_file.exists() {
821            serde_json::from_slice(
822                &fs::read(migration_sources_file)
823                    .context("unable to read migration sources file")?,
824            )
825            .context("unable to deserialize migration sources")?
826        } else {
827            Default::default()
828        };
829
830        let token_distribution_schedule_file =
831            path.join(GENESIS_BUILDER_TOKEN_DISTRIBUTION_SCHEDULE_FILE);
832        let token_distribution_schedule = if token_distribution_schedule_file.exists() {
833            Some(TokenDistributionSchedule::from_csv(fs::File::open(
834                token_distribution_schedule_file,
835            )?)?)
836        } else {
837            None
838        };
839
840        // Load validator infos
841        let mut committee = BTreeMap::new();
842        for entry in path.join(GENESIS_BUILDER_COMMITTEE_DIR).read_dir_utf8()? {
843            let entry = entry?;
844            if entry.file_name().starts_with('.') {
845                continue;
846            }
847
848            let path = entry.path();
849            let validator_info: GenesisValidatorInfo = serde_yaml::from_slice(&fs::read(path)?)
850                .with_context(|| format!("unable to load validator info for {path}"))?;
851            committee.insert(validator_info.info.authority_key(), validator_info);
852        }
853
854        // Load Signatures
855        let mut signatures = BTreeMap::new();
856        for entry in path.join(GENESIS_BUILDER_SIGNATURE_DIR).read_dir_utf8()? {
857            let entry = entry?;
858            if entry.file_name().starts_with('.') {
859                continue;
860            }
861
862            let path = entry.path();
863            let sigs: AuthoritySignInfo = bcs::from_bytes(&fs::read(path)?)
864                .with_context(|| format!("unable to load validator signature for {path}"))?;
865            signatures.insert(sigs.authority, sigs);
866        }
867
868        // Load migration txs data
869        let migration_tx_data: Option<MigrationTxData> = if !migration_sources.is_empty() {
870            let migration_tx_data_file = path.join(IOTA_GENESIS_MIGRATION_TX_DATA_FILENAME);
871            Some(MigrationTxData::load(migration_tx_data_file)?)
872        } else {
873            None
874        };
875
876        // Load delegator
877        let delegator_file = path.join(GENESIS_BUILDER_DELEGATOR_FILE);
878        let delegator = if delegator_file.exists() {
879            Some(serde_json::from_slice(&fs::read(delegator_file)?)?)
880        } else {
881            None
882        };
883
884        // Load delegator map
885        let delegator_map_file = path.join(GENESIS_BUILDER_DELEGATOR_MAP_FILE);
886        let delegator_map = if delegator_map_file.exists() {
887            Some(Delegations::from_csv(fs::File::open(delegator_map_file)?)?)
888        } else {
889            None
890        };
891
892        let delegation = delegator
893            .map(GenesisDelegation::OneToAll)
894            .or(delegator_map.map(GenesisDelegation::ManyToMany));
895
896        let mut builder = Self {
897            parameters,
898            token_distribution_schedule,
899            objects: Default::default(),
900            validators: committee,
901            signatures,
902            built_genesis: None, // Leave this as none, will build and compare below
903            migration_objects: Default::default(),
904            genesis_stake: Default::default(),
905            migration_sources,
906            migration_tx_data,
907            delegation,
908        };
909
910        let unsigned_genesis_file = path.join(GENESIS_BUILDER_UNSIGNED_GENESIS_FILE);
911        if unsigned_genesis_file.exists() {
912            let reader = BufReader::new(File::open(unsigned_genesis_file)?);
913            let loaded_genesis: UnsignedGenesis =
914                tokio::task::spawn_blocking(move || bcs::from_reader(reader)).await??;
915
916            // If we have a built genesis, then we must have a token_distribution_schedule
917            // present as well.
918            assert!(
919                builder.token_distribution_schedule.is_some(),
920                "If a built genesis is present, then there must also be a token-distribution-schedule present"
921            );
922
923            // Verify loaded genesis matches one build from the constituent parts
924            builder = tokio::task::spawn_blocking(move || {
925                builder.get_or_build_unsigned_genesis();
926                builder
927            })
928            .await?;
929            loaded_genesis.checkpoint_contents.digest(); // cache digest before compare
930            assert!(
931                *builder.get_or_build_unsigned_genesis() == loaded_genesis,
932                "loaded genesis does not match built genesis"
933            );
934
935            // Just to double check that its set after building above
936            assert!(builder.unsigned_genesis_checkpoint().is_some());
937        }
938
939        Ok(builder)
940    }
941
942    pub fn save<P: AsRef<Path>>(self, path: P) -> anyhow::Result<(), anyhow::Error> {
943        let path = path.as_ref();
944        trace!("Writing Genesis Builder to {}", path.display());
945
946        fs::create_dir_all(path)?;
947
948        // Write parameters
949        let parameters_file = path.join(GENESIS_BUILDER_PARAMETERS_FILE);
950        fs::write(parameters_file, serde_yaml::to_string(&self.parameters)?)?;
951
952        if let Some(token_distribution_schedule) = &self.token_distribution_schedule {
953            token_distribution_schedule.to_csv(fs::File::create(
954                path.join(GENESIS_BUILDER_TOKEN_DISTRIBUTION_SCHEDULE_FILE),
955            )?)?;
956        }
957
958        // Write Signatures
959        let signature_dir = path.join(GENESIS_BUILDER_SIGNATURE_DIR);
960        std::fs::create_dir_all(&signature_dir)?;
961        for (pubkey, sigs) in self.signatures {
962            let name = self.validators.get(&pubkey).unwrap().info.name();
963            fs::write(signature_dir.join(name), &bcs::to_bytes(&sigs)?)?;
964        }
965
966        // Write validator infos
967        let committee_dir = path.join(GENESIS_BUILDER_COMMITTEE_DIR);
968        fs::create_dir_all(&committee_dir)?;
969
970        for (_pubkey, validator) in self.validators {
971            fs::write(
972                committee_dir.join(validator.info.name()),
973                &serde_yaml::to_string(&validator)?,
974            )?;
975        }
976
977        if let Some(genesis) = &self.built_genesis {
978            let mut write = BufWriter::new(File::create(
979                path.join(GENESIS_BUILDER_UNSIGNED_GENESIS_FILE),
980            )?);
981            bcs::serialize_into(&mut write, &genesis)?;
982        }
983
984        if !self.migration_sources.is_empty() {
985            let file = path.join(GENESIS_BUILDER_MIGRATION_SOURCES_FILE);
986            fs::write(file, serde_json::to_string(&self.migration_sources)?)?;
987
988            // Write migration transactions data
989            let file = path.join(IOTA_GENESIS_MIGRATION_TX_DATA_FILENAME);
990            self.migration_tx_data
991                .expect("migration data should exist")
992                .save(file)?;
993        }
994
995        if let Some(delegation) = &self.delegation {
996            match delegation {
997                GenesisDelegation::OneToAll(delegator) => {
998                    // Write delegator to file
999                    let file = path.join(GENESIS_BUILDER_DELEGATOR_FILE);
1000                    let delegator_json = serde_json::to_string(delegator)?;
1001                    fs::write(file, delegator_json)?;
1002                }
1003                GenesisDelegation::ManyToMany(delegator_map) => {
1004                    // Write delegator map to CSV file
1005                    delegator_map.to_csv(fs::File::create(
1006                        path.join(GENESIS_BUILDER_DELEGATOR_MAP_FILE),
1007                    )?)?;
1008                }
1009            }
1010        }
1011
1012        Ok(())
1013    }
1014}
1015
1016// Create a Genesis Txn Context to be used when generating genesis objects by
1017// hashing all of the inputs into genesis ans using that as our "Txn Digest".
1018// This is done to ensure that coin objects created between chains are unique
1019fn create_genesis_context(
1020    epoch_data: &EpochData,
1021    genesis_chain_parameters: &GenesisChainParameters,
1022    genesis_validators: &[GenesisValidatorMetadata],
1023    token_distribution_schedule: &TokenDistributionSchedule,
1024    system_packages: &[SystemPackage],
1025) -> TxContext {
1026    let mut hasher = DefaultHash::default();
1027    hasher.update(b"iota-genesis");
1028    hasher.update(bcs::to_bytes(genesis_chain_parameters).unwrap());
1029    hasher.update(bcs::to_bytes(genesis_validators).unwrap());
1030    hasher.update(bcs::to_bytes(token_distribution_schedule).unwrap());
1031    for system_package in system_packages {
1032        hasher.update(bcs::to_bytes(&system_package.bytes).unwrap());
1033    }
1034
1035    let hash = hasher.finalize();
1036    let genesis_transaction_digest = TransactionDigest::new(hash.into());
1037
1038    TxContext::new(
1039        &IotaAddress::default(),
1040        &genesis_transaction_digest,
1041        epoch_data,
1042    )
1043}
1044
1045fn build_unsigned_genesis_data<'info>(
1046    parameters: &GenesisCeremonyParameters,
1047    token_distribution_schedule: &TokenDistributionSchedule,
1048    validators: impl Iterator<Item = &'info GenesisValidatorInfo>,
1049    objects: Vec<Object>,
1050    genesis_stake: &mut GenesisStake,
1051    migration_objects: &mut MigrationObjects,
1052) -> (UnsignedGenesis, MigrationTxData) {
1053    if !parameters.allow_insertion_of_extra_objects && !objects.is_empty() {
1054        panic!(
1055            "insertion of extra objects at genesis time is prohibited due to 'allow_insertion_of_extra_objects' parameter"
1056        );
1057    }
1058
1059    let genesis_chain_parameters = parameters.to_genesis_chain_parameters();
1060    let genesis_validators = validators
1061        .cloned()
1062        .map(GenesisValidatorMetadata::from)
1063        .collect::<Vec<_>>();
1064
1065    let epoch_data = EpochData::new_genesis(genesis_chain_parameters.chain_start_timestamp_ms);
1066
1067    // Get the correct system packages for our protocol version. If we cannot find
1068    // the snapshot that means that we must be at the latest version and we
1069    // should use the latest version of the framework.
1070    let mut system_packages =
1071        iota_framework_snapshot::load_bytecode_snapshot(parameters.protocol_version.as_u64())
1072            .unwrap_or_else(|_| BuiltInFramework::iter_system_packages().cloned().collect());
1073
1074    // if system packages are provided in `objects`, update them with the provided
1075    // bytes. This is a no-op under normal conditions and only an issue with
1076    // certain tests.
1077    update_system_packages_from_objects(&mut system_packages, &objects);
1078
1079    let mut genesis_ctx = create_genesis_context(
1080        &epoch_data,
1081        &genesis_chain_parameters,
1082        &genesis_validators,
1083        token_distribution_schedule,
1084        &system_packages,
1085    );
1086
1087    // Use a throwaway metrics registry for genesis transaction execution.
1088    let registry = prometheus::Registry::new();
1089    let metrics = Arc::new(LimitsMetrics::new(&registry));
1090    let mut txs_data: TransactionsData = BTreeMap::new();
1091    let protocol_config = get_genesis_protocol_config(parameters.protocol_version);
1092
1093    // In here the main genesis objects are created. This means the main system
1094    // objects and the ones that are created at genesis like the network coin.
1095    let (genesis_objects, events) = create_genesis_objects(
1096        &mut genesis_ctx,
1097        objects,
1098        &genesis_validators,
1099        &genesis_chain_parameters,
1100        token_distribution_schedule,
1101        system_packages,
1102        metrics.clone(),
1103    );
1104
1105    // If the genesis_stake is not empty, then it means we are dealing with a
1106    // genesis with migration data. Thus we create the migration transaction data.
1107    if !genesis_stake.is_empty() {
1108        // Part of the migration objects were already used above during the creation of
1109        // genesis objects. In particular, when the genesis involves a migration, the
1110        // token distribution schedule takes into account assets coming from the
1111        // migration data. These are either timelocked coins or gas coins. The token
1112        // distribution schedule logic assumes that these assets are indeed distributed
1113        // to some addresses and this happens above during the creation of the genesis
1114        // objects. Here then we need to destroy those assets from the original set of
1115        // migration objects.
1116        let migration_objects = destroy_staked_migration_objects(
1117            &mut genesis_ctx,
1118            migration_objects.take_objects(),
1119            &genesis_objects,
1120            &genesis_chain_parameters,
1121            genesis_stake,
1122            metrics.clone(),
1123        );
1124        // Finally, we can create the data structure representing migration transaction
1125        // data.
1126        txs_data = create_migration_tx_data(
1127            migration_objects,
1128            &protocol_config,
1129            metrics.clone(),
1130            &epoch_data,
1131        );
1132    }
1133
1134    // Create the main genesis transaction of kind `GenesisTransaction`
1135    let (genesis_transaction, genesis_effects, genesis_events, genesis_objects) =
1136        create_genesis_transaction(
1137            genesis_objects,
1138            events,
1139            &protocol_config,
1140            metrics,
1141            &epoch_data,
1142        );
1143
1144    // Create the genesis checkpoint including the main transaction and, if present,
1145    // the migration transactions
1146    let (checkpoint, checkpoint_contents) = create_genesis_checkpoint(
1147        &protocol_config,
1148        parameters,
1149        &genesis_transaction,
1150        &genesis_effects,
1151        &txs_data,
1152    );
1153
1154    (
1155        UnsignedGenesis {
1156            checkpoint,
1157            checkpoint_contents,
1158            transaction: genesis_transaction,
1159            effects: genesis_effects,
1160            events: genesis_events,
1161            objects: genesis_objects,
1162        },
1163        // Could be empty
1164        MigrationTxData::new(txs_data),
1165    )
1166}
1167
1168// Creates a map of transaction digest to transaction content involving data
1169// coming from a migration. Migration objects come into a vector of objects,
1170// here it splits this vector into chunks and creates a `GenesisTransaction`
1171// for each chunk.
1172fn create_migration_tx_data(
1173    migration_objects: Vec<Object>,
1174    protocol_config: &ProtocolConfig,
1175    metrics: Arc<LimitsMetrics>,
1176    epoch_data: &EpochData,
1177) -> TransactionsData {
1178    let mut txs_data = TransactionsData::new();
1179    let migration_tx_max_amount = protocol_config
1180        .max_transactions_per_checkpoint_as_option()
1181        .unwrap_or(MAX_AMOUNT_OF_TX_PER_CHECKPOINT)
1182        - 1;
1183    let chunk_size = migration_objects.len() / (migration_tx_max_amount as usize) + 1;
1184
1185    for objects_per_chunk in migration_objects.chunks(chunk_size) {
1186        let (migration_transaction, migration_effects, migration_events, _) =
1187            create_genesis_transaction(
1188                objects_per_chunk.to_vec(),
1189                vec![],
1190                protocol_config,
1191                metrics.clone(),
1192                epoch_data,
1193            );
1194
1195        txs_data.insert(
1196            *migration_transaction.digest(),
1197            (migration_transaction, migration_effects, migration_events),
1198        );
1199    }
1200
1201    txs_data
1202}
1203
1204// Some tests provide an override of the system packages via objects to the
1205// genesis builder. When that happens we need to update the system packages with
1206// the new bytes provided. Mock system packages in protocol config tests are an
1207// example of that (today the only example).
1208// The problem here arises from the fact that if regular system packages are
1209// pushed first *AND* if any of them is loaded in the loader cache, there is no
1210// way to override them with the provided object (no way to mock properly).
1211// System packages are loaded only from internal dependencies (a system package
1212// depending on some other), and in that case they would be loaded in the
1213// VM/loader cache. The Bridge is an example of that and what led to this code.
1214// The bridge depends on `iota_system` which is mocked in some tests, but would
1215// be in the loader cache courtesy of the Bridge, thus causing the problem.
1216fn update_system_packages_from_objects(
1217    system_packages: &mut Vec<SystemPackage>,
1218    objects: &[Object],
1219) {
1220    // Filter `objects` for system packages, and make `SystemPackage`s out of them.
1221    let system_package_overrides: BTreeMap<ObjectID, Vec<Vec<u8>>> = objects
1222        .iter()
1223        .filter_map(|obj| {
1224            let pkg = obj.data.try_as_package()?;
1225            is_system_package(pkg.id()).then(|| {
1226                (
1227                    pkg.id(),
1228                    pkg.serialized_module_map().values().cloned().collect(),
1229                )
1230            })
1231        })
1232        .collect();
1233
1234    // Replace packages in `system_packages` that are present in `objects` with
1235    // their counterparts from the previous step.
1236    for package in system_packages {
1237        if let Some(overrides) = system_package_overrides.get(&package.id).cloned() {
1238            package.bytes = overrides;
1239        }
1240    }
1241}
1242
1243fn create_genesis_checkpoint(
1244    protocol_config: &ProtocolConfig,
1245    parameters: &GenesisCeremonyParameters,
1246    system_genesis_transaction: &Transaction,
1247    system_genesis_tx_effects: &TransactionEffects,
1248    migration_tx_data: &TransactionsData,
1249) -> (CheckpointSummary, CheckpointContents) {
1250    let genesis_execution_digests = ExecutionDigests {
1251        transaction: *system_genesis_transaction.digest(),
1252        effects: system_genesis_tx_effects.digest(),
1253    };
1254
1255    let mut execution_digests = vec![genesis_execution_digests];
1256
1257    for (_, effects, _) in migration_tx_data.values() {
1258        execution_digests.push(effects.execution_digests());
1259    }
1260
1261    let execution_digests_len = execution_digests.len();
1262
1263    let contents = CheckpointContents::new_with_digests_and_signatures(
1264        execution_digests,
1265        vec![vec![]; execution_digests_len],
1266    );
1267    let version_specific_data =
1268        match protocol_config.checkpoint_summary_version_specific_data_as_option() {
1269            None | Some(0) => Vec::new(),
1270            Some(1) => bcs::to_bytes(&CheckpointVersionSpecificData::V1(
1271                CheckpointVersionSpecificDataV1::default(),
1272            ))
1273            .unwrap(),
1274            _ => unimplemented!("unrecognized version_specific_data version for CheckpointSummary"),
1275        };
1276    let checkpoint = CheckpointSummary {
1277        epoch: 0,
1278        sequence_number: 0,
1279        network_total_transactions: contents.size().try_into().unwrap(),
1280        content_digest: *contents.digest(),
1281        previous_digest: None,
1282        epoch_rolling_gas_cost_summary: Default::default(),
1283        end_of_epoch_data: None,
1284        timestamp_ms: parameters.chain_start_timestamp_ms,
1285        version_specific_data,
1286        checkpoint_commitments: Default::default(),
1287    };
1288
1289    (checkpoint, contents)
1290}
1291
1292fn create_genesis_transaction(
1293    objects: Vec<Object>,
1294    events: Vec<Event>,
1295    protocol_config: &ProtocolConfig,
1296    metrics: Arc<LimitsMetrics>,
1297    epoch_data: &EpochData,
1298) -> (
1299    Transaction,
1300    TransactionEffects,
1301    TransactionEvents,
1302    Vec<Object>,
1303) {
1304    let genesis_transaction = {
1305        let genesis_objects = objects
1306            .into_iter()
1307            .map(|mut object| {
1308                if let Some(o) = object.data.try_as_move_mut() {
1309                    o.decrement_version_to(SequenceNumber::MIN);
1310                }
1311
1312                if let Owner::Shared {
1313                    initial_shared_version,
1314                } = &mut object.owner
1315                {
1316                    *initial_shared_version = SequenceNumber::MIN;
1317                }
1318
1319                let object = object.into_inner();
1320                iota_types::transaction::GenesisObject::RawObject {
1321                    data: object.data,
1322                    owner: object.owner,
1323                }
1324            })
1325            .collect();
1326
1327        iota_types::transaction::VerifiedTransaction::new_genesis_transaction(
1328            genesis_objects,
1329            events,
1330        )
1331        .into_inner()
1332    };
1333
1334    // execute txn to effects
1335    let (effects, events, objects) =
1336        execute_genesis_transaction(epoch_data, protocol_config, metrics, &genesis_transaction);
1337
1338    (genesis_transaction, effects, events, objects)
1339}
1340
1341fn create_genesis_objects(
1342    genesis_ctx: &mut TxContext,
1343    input_objects: Vec<Object>,
1344    validators: &[GenesisValidatorMetadata],
1345    parameters: &GenesisChainParameters,
1346    token_distribution_schedule: &TokenDistributionSchedule,
1347    system_packages: Vec<SystemPackage>,
1348    metrics: Arc<LimitsMetrics>,
1349) -> (Vec<Object>, Vec<Event>) {
1350    let mut store = InMemoryStorage::new(Vec::new());
1351    let mut events = Vec::new();
1352    // We don't know the chain ID here since we haven't yet created the genesis
1353    // checkpoint. However since we know there are no chain specific protool
1354    // config options in genesis, we use Chain::Unknown here.
1355    let protocol_config = ProtocolConfig::get_for_version(
1356        ProtocolVersion::new(parameters.protocol_version),
1357        Chain::Unknown,
1358    );
1359
1360    let silent = true;
1361    let executor = iota_execution::executor(&protocol_config, silent, None)
1362        .expect("Creating an executor should not fail here");
1363
1364    for system_package in system_packages.into_iter() {
1365        let tx_events = process_package(
1366            &mut store,
1367            executor.as_ref(),
1368            genesis_ctx,
1369            &system_package.modules(),
1370            system_package.dependencies,
1371            &protocol_config,
1372            metrics.clone(),
1373        )
1374        .expect("Processing a package should not fail here");
1375
1376        events.extend(tx_events.data.into_iter());
1377    }
1378
1379    for object in input_objects {
1380        store.insert_object(object);
1381    }
1382
1383    generate_genesis_system_object(
1384        &mut store,
1385        executor.as_ref(),
1386        validators,
1387        genesis_ctx,
1388        parameters,
1389        token_distribution_schedule,
1390        metrics,
1391    )
1392    .expect("Genesis creation should not fail here");
1393
1394    (store.into_inner().into_values().collect(), events)
1395}
1396
1397pub(crate) fn process_package(
1398    store: &mut InMemoryStorage,
1399    executor: &dyn Executor,
1400    ctx: &mut TxContext,
1401    modules: &[CompiledModule],
1402    dependencies: Vec<ObjectID>,
1403    protocol_config: &ProtocolConfig,
1404    metrics: Arc<LimitsMetrics>,
1405) -> anyhow::Result<TransactionEvents> {
1406    let dependency_objects = store.get_objects(&dependencies);
1407    // When publishing genesis packages, since the std framework packages all have
1408    // non-zero addresses, [`Transaction::input_objects_in_compiled_modules`] will
1409    // consider them as dependencies even though they are not. Hence
1410    // input_objects contain objects that don't exist on-chain because they are
1411    // yet to be published.
1412    #[cfg(debug_assertions)]
1413    {
1414        use std::collections::HashSet;
1415
1416        use move_core_types::account_address::AccountAddress;
1417
1418        let to_be_published_addresses: HashSet<_> = modules
1419            .iter()
1420            .map(|module| *module.self_id().address())
1421            .collect();
1422        assert!(
1423            // An object either exists on-chain, or is one of the packages to be published.
1424            dependencies
1425                .iter()
1426                .zip(dependency_objects.iter())
1427                .all(|(dependency, obj_opt)| obj_opt.is_some()
1428                    || to_be_published_addresses.contains(&AccountAddress::from(*dependency)))
1429        );
1430    }
1431    let loaded_dependencies: Vec<_> = dependencies
1432        .iter()
1433        .zip(dependency_objects)
1434        .filter_map(|(dependency, object)| {
1435            Some(ObjectReadResult::new(
1436                InputObjectKind::MovePackage(*dependency),
1437                object?.clone().into(),
1438            ))
1439        })
1440        .collect();
1441
1442    let module_bytes = modules
1443        .iter()
1444        .map(|m| {
1445            let mut buf = vec![];
1446            m.serialize_with_version(m.version, &mut buf).unwrap();
1447            buf
1448        })
1449        .collect();
1450    let pt = {
1451        let mut builder = ProgrammableTransactionBuilder::new();
1452        // executing in Genesis mode does not create an `UpgradeCap`.
1453        builder.command(Command::Publish(module_bytes, dependencies));
1454        builder.finish()
1455    };
1456    let InnerTemporaryStore {
1457        written, events, ..
1458    } = executor.update_genesis_state(
1459        &*store,
1460        protocol_config,
1461        metrics,
1462        ctx,
1463        CheckedInputObjects::new_for_genesis(loaded_dependencies),
1464        pt,
1465    )?;
1466
1467    store.finish(written);
1468
1469    Ok(events)
1470}
1471
1472pub fn generate_genesis_system_object(
1473    store: &mut InMemoryStorage,
1474    executor: &dyn Executor,
1475    genesis_validators: &[GenesisValidatorMetadata],
1476    genesis_ctx: &mut TxContext,
1477    genesis_chain_parameters: &GenesisChainParameters,
1478    token_distribution_schedule: &TokenDistributionSchedule,
1479    metrics: Arc<LimitsMetrics>,
1480) -> anyhow::Result<()> {
1481    let protocol_config = ProtocolConfig::get_for_version(
1482        ProtocolVersion::new(genesis_chain_parameters.protocol_version),
1483        ChainIdentifier::default().chain(),
1484    );
1485
1486    let pt = {
1487        let mut builder = ProgrammableTransactionBuilder::new();
1488        // Step 1: Create the IotaSystemState UID
1489        let iota_system_state_uid = builder.programmable_move_call(
1490            IOTA_FRAMEWORK_PACKAGE_ID,
1491            ident_str!("object").to_owned(),
1492            ident_str!("iota_system_state").to_owned(),
1493            vec![],
1494            vec![],
1495        );
1496
1497        // Step 2: Create and share the Clock.
1498        builder.move_call(
1499            IOTA_FRAMEWORK_PACKAGE_ID,
1500            ident_str!("clock").to_owned(),
1501            ident_str!("create").to_owned(),
1502            vec![],
1503            vec![],
1504        )?;
1505
1506        // Step 3: Create ProtocolConfig-controlled system objects, unless disabled
1507        // (which only happens in tests).
1508        if protocol_config.create_authenticator_state_in_genesis() {
1509            builder.move_call(
1510                IOTA_FRAMEWORK_PACKAGE_ID,
1511                ident_str!("authenticator_state").to_owned(),
1512                ident_str!("create").to_owned(),
1513                vec![],
1514                vec![],
1515            )?;
1516        }
1517
1518        // Create the randomness state_object
1519        builder.move_call(
1520            IOTA_FRAMEWORK_PACKAGE_ID,
1521            RANDOMNESS_MODULE_NAME.to_owned(),
1522            RANDOMNESS_STATE_CREATE_FUNCTION_NAME.to_owned(),
1523            vec![],
1524            vec![],
1525        )?;
1526
1527        // Create the deny list
1528        builder.move_call(
1529            IOTA_FRAMEWORK_PACKAGE_ID,
1530            DENY_LIST_MODULE.to_owned(),
1531            DENY_LIST_CREATE_FUNC.to_owned(),
1532            vec![],
1533            vec![],
1534        )?;
1535
1536        // Step 4: Create the IOTA Coin Treasury Cap.
1537        let iota_treasury_cap = builder.programmable_move_call(
1538            IOTA_FRAMEWORK_PACKAGE_ID,
1539            ident_str!("iota").to_owned(),
1540            ident_str!("new").to_owned(),
1541            vec![],
1542            vec![],
1543        );
1544
1545        let pre_minted_supply_amount = builder
1546            .pure(token_distribution_schedule.pre_minted_supply)
1547            .expect("serialization of u64 should succeed");
1548        let pre_minted_supply = builder.programmable_move_call(
1549            IOTA_FRAMEWORK_PACKAGE_ID,
1550            ident_str!("iota").to_owned(),
1551            ident_str!("mint_balance").to_owned(),
1552            vec![],
1553            vec![iota_treasury_cap, pre_minted_supply_amount],
1554        );
1555
1556        builder.programmable_move_call(
1557            IOTA_FRAMEWORK_PACKAGE_ID,
1558            BALANCE_MODULE_NAME.to_owned(),
1559            ident_str!("destroy_genesis_supply").to_owned(),
1560            vec![GAS::type_tag()],
1561            vec![pre_minted_supply],
1562        );
1563
1564        // Step 5: Create System Admin Cap.
1565        let system_admin_cap = builder.programmable_move_call(
1566            IOTA_FRAMEWORK_PACKAGE_ID,
1567            IOTA_SYSTEM_ADMIN_CAP_MODULE_NAME.to_owned(),
1568            ident_str!("new_system_admin_cap").to_owned(),
1569            vec![],
1570            vec![],
1571        );
1572
1573        // Step 6: Run genesis.
1574        // The first argument is the system state uid we got from step 1 and the second
1575        // one is the IOTA `TreasuryCap` we got from step 4.
1576        let mut arguments = vec![iota_system_state_uid, iota_treasury_cap];
1577        let mut call_arg_arguments = vec![
1578            CallArg::Pure(bcs::to_bytes(&genesis_chain_parameters).unwrap()),
1579            CallArg::Pure(bcs::to_bytes(&genesis_validators).unwrap()),
1580            CallArg::Pure(bcs::to_bytes(&token_distribution_schedule).unwrap()),
1581            CallArg::Pure(bcs::to_bytes(&Some(STARDUST_UPGRADE_LABEL_VALUE)).unwrap()),
1582        ]
1583        .into_iter()
1584        .map(|a| builder.input(a))
1585        .collect::<anyhow::Result<_, _>>()?;
1586        arguments.append(&mut call_arg_arguments);
1587        arguments.push(system_admin_cap);
1588        builder.programmable_move_call(
1589            IOTA_SYSTEM_ADDRESS.into(),
1590            ident_str!("genesis").to_owned(),
1591            ident_str!("create").to_owned(),
1592            vec![],
1593            arguments,
1594        );
1595
1596        builder.finish()
1597    };
1598
1599    let InnerTemporaryStore { mut written, .. } = executor.update_genesis_state(
1600        &*store,
1601        &protocol_config,
1602        metrics,
1603        genesis_ctx,
1604        CheckedInputObjects::new_for_genesis(vec![]),
1605        pt,
1606    )?;
1607
1608    // update the value of the clock to match the chain start time
1609    {
1610        let object = written.get_mut(&iota_types::IOTA_CLOCK_OBJECT_ID).unwrap();
1611        object
1612            .data
1613            .try_as_move_mut()
1614            .unwrap()
1615            .set_clock_timestamp_ms_unsafe(genesis_chain_parameters.chain_start_timestamp_ms);
1616    }
1617
1618    store.finish(written);
1619
1620    Ok(())
1621}
1622
1623// Migration objects as input to this function were previously used to create a
1624// genesis stake, that in turn helps to create a token distribution schedule for
1625// the genesis. In this function the objects needed for the stake are destroyed
1626// (and, if needed, split) to provide a new set of migration object as output.
1627fn destroy_staked_migration_objects(
1628    genesis_ctx: &mut TxContext,
1629    migration_objects: Vec<Object>,
1630    genesis_objects: &[Object],
1631    parameters: &GenesisChainParameters,
1632    genesis_stake: &mut GenesisStake,
1633    metrics: Arc<LimitsMetrics>,
1634) -> Vec<Object> {
1635    // create the temporary store and the executor
1636    let mut store = InMemoryStorage::new(genesis_objects.to_owned());
1637    let protocol_config = ProtocolConfig::get_for_version(
1638        ProtocolVersion::new(parameters.protocol_version),
1639        Chain::Unknown,
1640    );
1641    let silent = true;
1642    let executor = iota_execution::executor(&protocol_config, silent, None)
1643        .expect("Creating an executor should not fail here");
1644
1645    for object in migration_objects {
1646        store.insert_object(object);
1647    }
1648
1649    // First operation: split the timelock objects that are needed to be split
1650    // because of the genesis stake
1651    split_timelocks(
1652        &mut store,
1653        executor.as_ref(),
1654        genesis_ctx,
1655        parameters,
1656        &genesis_stake.take_timelocks_to_split(),
1657        metrics.clone(),
1658    )
1659    .expect("Splitting timelocks should not fail here");
1660
1661    // Extract objects from the store
1662    let mut intermediate_store = store.into_inner();
1663
1664    // Second operation: destroy gas and timelocks objects.
1665    // If the genesis stake was created, then destroy gas and timelock objects that
1666    // were added to the token distribution schedule, because they will be
1667    // created on the Move side during genesis. That means we need to prevent
1668    // cloning value by evicting these here.
1669    for (id, _, _) in genesis_stake.take_gas_coins_to_destroy() {
1670        intermediate_store.remove(&id);
1671    }
1672    for (id, _, _) in genesis_stake.take_timelocks_to_destroy() {
1673        intermediate_store.remove(&id);
1674    }
1675
1676    // Clean the intermediate store from objects already present in genesis_objects
1677    for genesis_object in genesis_objects.iter() {
1678        intermediate_store.remove(&genesis_object.id());
1679    }
1680
1681    intermediate_store.into_values().collect()
1682}
1683
1684// Splits timelock objects given an amount to split.
1685pub fn split_timelocks(
1686    store: &mut InMemoryStorage,
1687    executor: &dyn Executor,
1688    genesis_ctx: &mut TxContext,
1689    genesis_chain_parameters: &GenesisChainParameters,
1690    timelocks_to_split: &[(ObjectRef, u64, IotaAddress)],
1691    metrics: Arc<LimitsMetrics>,
1692) -> anyhow::Result<()> {
1693    let protocol_config = ProtocolConfig::get_for_version(
1694        ProtocolVersion::new(genesis_chain_parameters.protocol_version),
1695        ChainIdentifier::default().chain(),
1696    );
1697
1698    // Timelocks split PTB
1699    // It takes a list of timelocks_to_split references; then for each timelock it
1700    // invokes "timelock::split" and then transfers the result to the indicated
1701    // recipient.
1702    let mut timelock_split_input_objects: Vec<ObjectReadResult> = vec![];
1703    let pt = {
1704        let mut builder = ProgrammableTransactionBuilder::new();
1705        for (timelock, surplus_amount, recipient) in timelocks_to_split {
1706            timelock_split_input_objects.push(ObjectReadResult::new(
1707                InputObjectKind::ImmOrOwnedMoveObject(*timelock),
1708                store.get_object(&timelock.0).unwrap().clone().into(),
1709            ));
1710            let arguments = vec![
1711                builder.obj(ObjectArg::ImmOrOwnedObject(*timelock))?,
1712                builder.pure(surplus_amount)?,
1713            ];
1714            let surplus_timelock = builder.programmable_move_call(
1715                IOTA_FRAMEWORK_PACKAGE_ID,
1716                ident_str!("timelock").to_owned(),
1717                ident_str!("split").to_owned(),
1718                vec![GAS::type_tag()],
1719                arguments,
1720            );
1721            let arguments = vec![surplus_timelock, builder.pure(*recipient)?];
1722            builder.programmable_move_call(
1723                IOTA_FRAMEWORK_PACKAGE_ID,
1724                ident_str!("timelock").to_owned(),
1725                ident_str!("transfer").to_owned(),
1726                vec![Balance::type_tag(GAS::type_tag())],
1727                arguments,
1728            );
1729        }
1730        builder.finish()
1731    };
1732
1733    // Execute the timelocks split PTB in a genesis environment; it returns a list
1734    // of written objects that includes the modified timelocks (the ones that were
1735    // split), plus the newly created timelocks
1736    let InnerTemporaryStore { written, .. } = executor.update_genesis_state(
1737        &*store,
1738        &protocol_config,
1739        metrics,
1740        genesis_ctx,
1741        CheckedInputObjects::new_for_genesis(timelock_split_input_objects),
1742        pt,
1743    )?;
1744
1745    // Insert the written objects into the store
1746    store.finish(written);
1747
1748    // Finally, we can destroy the timelocks that were split, keeping in the store
1749    // only the newly created timelocks
1750    for ((id, _, _), _, _) in timelocks_to_split {
1751        store.remove_object(*id);
1752    }
1753
1754    Ok(())
1755}
1756
1757#[derive(Clone, Debug, Deserialize, Serialize)]
1758pub enum SnapshotSource {
1759    /// Local uncompressed file.
1760    Local(PathBuf),
1761    /// Remote file (S3) with gzip compressed file
1762    S3(SnapshotUrl),
1763}
1764
1765impl SnapshotSource {
1766    /// Convert to a reader.
1767    pub fn to_reader(&self) -> anyhow::Result<Box<dyn Read>> {
1768        Ok(match self {
1769            SnapshotSource::Local(path) => Box::new(BufReader::new(File::open(path)?)),
1770            SnapshotSource::S3(snapshot_url) => Box::new(snapshot_url.to_reader()?),
1771        })
1772    }
1773}
1774
1775impl From<SnapshotUrl> for SnapshotSource {
1776    fn from(value: SnapshotUrl) -> Self {
1777        Self::S3(value)
1778    }
1779}
1780
1781/// The URLs to download IOTA object snapshot.
1782#[derive(Debug, Clone, Deserialize, Serialize)]
1783pub enum SnapshotUrl {
1784    Iota,
1785    /// Custom migration snapshot for testing purposes.
1786    Test(Url),
1787}
1788
1789impl std::fmt::Display for SnapshotUrl {
1790    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1791        match self {
1792            SnapshotUrl::Iota => "iota".fmt(f),
1793            SnapshotUrl::Test(url) => url.as_str().fmt(f),
1794        }
1795    }
1796}
1797
1798impl FromStr for SnapshotUrl {
1799    type Err = anyhow::Error;
1800
1801    fn from_str(s: &str) -> Result<Self, Self::Err> {
1802        if let Ok(url) = reqwest::Url::from_str(s) {
1803            return Ok(Self::Test(url));
1804        }
1805        Ok(match s.to_lowercase().as_str() {
1806            "iota" => Self::Iota,
1807            e => bail!("unsupported snapshot url: {e}"),
1808        })
1809    }
1810}
1811
1812impl SnapshotUrl {
1813    /// Returns the IOTA object snapshot download URL.
1814    pub fn to_url(&self) -> Url {
1815        match self {
1816            Self::Iota => Url::parse(IOTA_OBJECT_SNAPSHOT_URL).expect("should be valid URL"),
1817            Self::Test(url) => url.clone(),
1818        }
1819    }
1820
1821    /// Convert a gzip decoder to read the compressed object snapshot from S3.
1822    pub fn to_reader(&self) -> anyhow::Result<impl Read> {
1823        Ok(GzDecoder::new(BufReader::new(reqwest::blocking::get(
1824            self.to_url(),
1825        )?)))
1826    }
1827}
1828
1829#[cfg(test)]
1830mod test {
1831    use fastcrypto::traits::KeyPair;
1832    use iota_config::{
1833        genesis::*,
1834        local_ip_utils,
1835        node::{DEFAULT_COMMISSION_RATE, DEFAULT_VALIDATOR_GAS_PRICE},
1836    };
1837    use iota_types::{
1838        base_types::IotaAddress,
1839        crypto::{
1840            AccountKeyPair, AuthorityKeyPair, NetworkKeyPair, generate_proof_of_possession,
1841            get_key_pair_from_rng,
1842        },
1843    };
1844
1845    use crate::{Builder, validator_info::ValidatorInfo};
1846
1847    #[test]
1848    fn allocation_csv() {
1849        let schedule = TokenDistributionSchedule::new_for_validators_with_default_allocation([
1850            IotaAddress::random_for_testing_only(),
1851            IotaAddress::random_for_testing_only(),
1852        ]);
1853        let mut output = Vec::new();
1854
1855        schedule.to_csv(&mut output).unwrap();
1856
1857        let parsed_schedule = TokenDistributionSchedule::from_csv(output.as_slice()).unwrap();
1858
1859        assert_eq!(schedule, parsed_schedule);
1860
1861        std::io::Write::write_all(&mut std::io::stdout(), &output).unwrap();
1862    }
1863
1864    #[tokio::test]
1865    #[cfg_attr(msim, ignore)]
1866    async fn ceremony() {
1867        let dir = tempfile::TempDir::new().unwrap();
1868
1869        let authority_key: AuthorityKeyPair = get_key_pair_from_rng(&mut rand::rngs::OsRng).1;
1870        let protocol_key: NetworkKeyPair = get_key_pair_from_rng(&mut rand::rngs::OsRng).1;
1871        let account_key: AccountKeyPair = get_key_pair_from_rng(&mut rand::rngs::OsRng).1;
1872        let network_key: NetworkKeyPair = get_key_pair_from_rng(&mut rand::rngs::OsRng).1;
1873        let validator = ValidatorInfo {
1874            name: "0".into(),
1875            authority_key: authority_key.public().into(),
1876            protocol_key: protocol_key.public().clone(),
1877            account_address: IotaAddress::from(account_key.public()),
1878            network_key: network_key.public().clone(),
1879            gas_price: DEFAULT_VALIDATOR_GAS_PRICE,
1880            commission_rate: DEFAULT_COMMISSION_RATE,
1881            network_address: local_ip_utils::new_local_tcp_address_for_testing(),
1882            p2p_address: local_ip_utils::new_local_udp_address_for_testing(),
1883            primary_address: local_ip_utils::new_local_udp_address_for_testing(),
1884            description: String::new(),
1885            image_url: String::new(),
1886            project_url: String::new(),
1887        };
1888        let pop = generate_proof_of_possession(&authority_key, account_key.public().into());
1889        let mut builder = Builder::new().add_validator(validator, pop);
1890
1891        let genesis = builder.get_or_build_unsigned_genesis();
1892        for object in genesis.objects() {
1893            println!("ObjectID: {} Type: {:?}", object.id(), object.type_());
1894        }
1895        builder.save(dir.path()).unwrap();
1896        Builder::load(dir.path()).await.unwrap();
1897    }
1898}