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