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    cell::RefCell,
7    collections::BTreeMap,
8    fs::{self, File},
9    io::{BufReader, BufWriter, prelude::Read},
10    path::{Path, PathBuf},
11    rc::Rc,
12    str::FromStr,
13    sync::Arc,
14};
15
16use anyhow::{Context, bail};
17use camino::Utf8Path;
18use fastcrypto::{hash::HashFunction, traits::KeyPair};
19use flate2::bufread::GzDecoder;
20use genesis_build_effects::GenesisBuildEffects;
21use iota_config::{
22    IOTA_GENESIS_MIGRATION_TX_DATA_FILENAME,
23    genesis::{
24        Delegations, Genesis, GenesisCeremonyParameters, GenesisChainParameters,
25        TokenDistributionSchedule, UnsignedGenesis,
26    },
27    migration_tx_data::{MigrationTxData, TransactionsData},
28};
29use iota_execution::{self, Executor};
30use iota_framework::{BuiltInFramework, SystemPackage};
31use iota_genesis_common::{execute_genesis_transaction, get_genesis_protocol_config};
32use iota_protocol_config::{Chain, ProtocolConfig, ProtocolVersion};
33use iota_sdk_types::crypto::{Intent, IntentMessage, IntentScope};
34use iota_types::{
35    IOTA_FRAMEWORK_PACKAGE_ID, IOTA_SYSTEM_ADDRESS,
36    balance::{BALANCE_MODULE_NAME, Balance},
37    base_types::{
38        ExecutionDigests, IotaAddress, ObjectID, ObjectRef, SequenceNumber, TransactionDigest,
39        TxContext,
40    },
41    committee::Committee,
42    crypto::{
43        AuthorityKeyPair, AuthorityPublicKeyBytes, AuthoritySignInfo, AuthoritySignInfoTrait,
44        AuthoritySignature, DefaultHash, IotaAuthoritySignature,
45    },
46    deny_list_v1::{DENY_LIST_CREATE_FUNC, DENY_LIST_MODULE},
47    digests::ChainIdentifier,
48    effects::{TransactionEffects, TransactionEvents},
49    epoch_data::EpochData,
50    event::Event,
51    gas_coin::{GAS, GasCoin, STARDUST_TOTAL_SUPPLY_NANOS},
52    governance::StakedIota,
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 stake::GenesisStake;
80use stardust::migration::MigrationObjects;
81use tracing::trace;
82use url::Url;
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!(unsigned_genesis.has_coin_deny_list_object());
561
562        assert_eq!(
563            self.validators.len(),
564            system_state.validators.active_validators.len()
565        );
566        let mut address_to_pool_id = BTreeMap::new();
567        for (validator, onchain_validator) in self
568            .validators
569            .values()
570            .zip(system_state.validators.active_validators.iter())
571        {
572            let metadata = onchain_validator.verified_metadata();
573
574            // Validators should not have duplicate addresses so the result of insertion
575            // should be None.
576            assert!(
577                address_to_pool_id
578                    .insert(metadata.iota_address, onchain_validator.staking_pool.id)
579                    .is_none()
580            );
581            assert_eq!(validator.info.iota_address(), metadata.iota_address);
582            assert_eq!(validator.info.authority_key(), metadata.iota_pubkey_bytes());
583            assert_eq!(validator.info.network_key, metadata.network_pubkey);
584            assert_eq!(validator.info.protocol_key, metadata.protocol_pubkey);
585            assert_eq!(
586                validator.proof_of_possession.as_ref().to_vec(),
587                metadata.proof_of_possession_bytes
588            );
589            assert_eq!(validator.info.name(), &metadata.name);
590            assert_eq!(validator.info.description, metadata.description);
591            assert_eq!(validator.info.image_url, metadata.image_url);
592            assert_eq!(validator.info.project_url, metadata.project_url);
593            assert_eq!(validator.info.network_address(), &metadata.net_address);
594            assert_eq!(validator.info.p2p_address, metadata.p2p_address);
595            assert_eq!(validator.info.primary_address, metadata.primary_address);
596
597            assert_eq!(validator.info.gas_price, onchain_validator.gas_price);
598            assert_eq!(
599                validator.info.commission_rate,
600                onchain_validator.commission_rate
601            );
602        }
603
604        assert_eq!(system_state.epoch, 0);
605        assert_eq!(system_state.protocol_version, protocol_version);
606        assert_eq!(system_state.storage_fund.non_refundable_balance.value(), 0);
607        assert_eq!(
608            system_state
609                .storage_fund
610                .total_object_storage_rebates
611                .value(),
612            0
613        );
614
615        assert_eq!(system_state.parameters.epoch_duration_ms, epoch_duration_ms);
616        assert_eq!(
617            system_state.parameters.max_validator_count,
618            max_validator_count,
619        );
620        assert_eq!(
621            system_state.parameters.min_validator_joining_stake,
622            min_validator_joining_stake,
623        );
624        assert_eq!(
625            system_state.parameters.validator_low_stake_threshold,
626            validator_low_stake_threshold,
627        );
628        assert_eq!(
629            system_state.parameters.validator_very_low_stake_threshold,
630            validator_very_low_stake_threshold,
631        );
632        assert_eq!(
633            system_state.parameters.validator_low_stake_grace_period,
634            validator_low_stake_grace_period,
635        );
636
637        assert!(!system_state.safe_mode);
638        assert_eq!(
639            system_state.epoch_start_timestamp_ms,
640            chain_start_timestamp_ms,
641        );
642        assert_eq!(system_state.validators.pending_removals.len(), 0);
643        assert_eq!(
644            system_state
645                .validators
646                .pending_active_validators
647                .contents
648                .size,
649            0
650        );
651        assert_eq!(system_state.validators.inactive_validators.size, 0);
652        assert_eq!(system_state.validators.validator_candidates.size, 0);
653
654        // Check distribution is correct
655        let token_distribution_schedule = self.token_distribution_schedule.clone().unwrap();
656
657        let allocations_amount: u64 = token_distribution_schedule
658            .allocations
659            .iter()
660            .map(|allocation| allocation.amount_nanos)
661            .sum();
662
663        assert_eq!(
664            system_state.iota_treasury_cap.total_supply().value,
665            token_distribution_schedule.pre_minted_supply + allocations_amount
666        );
667
668        let mut gas_objects: BTreeMap<ObjectID, (&Object, GasCoin)> = unsigned_genesis
669            .objects()
670            .iter()
671            .filter_map(|o| GasCoin::try_from(o).ok().map(|g| (o.id(), (o, g))))
672            .collect();
673        let mut staked_iota_objects: BTreeMap<ObjectID, (&Object, StakedIota)> = unsigned_genesis
674            .objects()
675            .iter()
676            .filter_map(|o| StakedIota::try_from(o).ok().map(|s| (o.id(), (o, s))))
677            .collect();
678        let mut timelock_staked_iota_objects: BTreeMap<ObjectID, (&Object, TimelockedStakedIota)> =
679            unsigned_genesis
680                .objects()
681                .iter()
682                .filter_map(|o| {
683                    TimelockedStakedIota::try_from(o)
684                        .ok()
685                        .map(|s| (o.id(), (o, s)))
686                })
687                .collect();
688
689        for allocation in token_distribution_schedule.allocations {
690            if let Some(staked_with_validator) = allocation.staked_with_validator {
691                let staking_pool_id = *address_to_pool_id
692                    .get(&staked_with_validator)
693                    .expect("staking pool should exist");
694                if let Some(expiration) = allocation.staked_with_timelock_expiration {
695                    let timelock_staked_iota_object_id = timelock_staked_iota_objects
696                        .iter()
697                        .find(|(_k, (o, s))| {
698                            let Owner::AddressOwner(owner) = &o.owner else {
699                                panic!("gas object owner must be address owner");
700                            };
701                            *owner == allocation.recipient_address
702                                && s.principal() == allocation.amount_nanos
703                                && s.pool_id() == staking_pool_id
704                                && s.expiration_timestamp_ms() == expiration
705                        })
706                        .map(|(k, _)| *k)
707                        .expect("all allocations should be present");
708                    let timelock_staked_iota_object = timelock_staked_iota_objects
709                        .remove(&timelock_staked_iota_object_id)
710                        .unwrap();
711                    assert_eq!(
712                        timelock_staked_iota_object.0.owner,
713                        Owner::AddressOwner(allocation.recipient_address)
714                    );
715                    assert_eq!(
716                        timelock_staked_iota_object.1.principal(),
717                        allocation.amount_nanos
718                    );
719                    assert_eq!(timelock_staked_iota_object.1.pool_id(), staking_pool_id);
720                    assert_eq!(timelock_staked_iota_object.1.activation_epoch(), 0);
721                } else {
722                    let staked_iota_object_id = staked_iota_objects
723                        .iter()
724                        .find(|(_k, (o, s))| {
725                            let Owner::AddressOwner(owner) = &o.owner else {
726                                panic!("gas object owner must be address owner");
727                            };
728                            *owner == allocation.recipient_address
729                                && s.principal() == allocation.amount_nanos
730                                && s.pool_id() == staking_pool_id
731                        })
732                        .map(|(k, _)| *k)
733                        .expect("all allocations should be present");
734                    let staked_iota_object =
735                        staked_iota_objects.remove(&staked_iota_object_id).unwrap();
736                    assert_eq!(
737                        staked_iota_object.0.owner,
738                        Owner::AddressOwner(allocation.recipient_address)
739                    );
740                    assert_eq!(staked_iota_object.1.principal(), allocation.amount_nanos);
741                    assert_eq!(staked_iota_object.1.pool_id(), staking_pool_id);
742                    assert_eq!(staked_iota_object.1.activation_epoch(), 0);
743                }
744            } else {
745                let gas_object_id = gas_objects
746                    .iter()
747                    .find(|(_k, (o, g))| {
748                        if let Owner::AddressOwner(owner) = &o.owner {
749                            *owner == allocation.recipient_address
750                                && g.value() == allocation.amount_nanos
751                        } else {
752                            false
753                        }
754                    })
755                    .map(|(k, _)| *k)
756                    .expect("all allocations should be present");
757                let gas_object = gas_objects.remove(&gas_object_id).unwrap();
758                assert_eq!(
759                    gas_object.0.owner,
760                    Owner::AddressOwner(allocation.recipient_address)
761                );
762                assert_eq!(gas_object.1.value(), allocation.amount_nanos,);
763            }
764        }
765
766        // All Gas and staked objects should be accounted for
767        if !self.parameters.allow_insertion_of_extra_objects {
768            assert!(gas_objects.is_empty());
769            assert!(staked_iota_objects.is_empty());
770            assert!(timelock_staked_iota_objects.is_empty());
771        }
772
773        let committee = system_state.get_current_epoch_committee();
774        for signature in self.signatures.values() {
775            if !self.validators.contains_key(&signature.authority) {
776                panic!("found signature for unknown validator: {signature:#?}");
777            }
778
779            signature
780                .verify_secure(
781                    unsigned_genesis.checkpoint(),
782                    Intent::iota_app(IntentScope::CheckpointSummary),
783                    committee.committee(),
784                )
785                .expect("signature should be valid");
786        }
787
788        // Validate migration content in order to avoid corrupted or malicious data
789        if let Some(migration_tx_data) = &self.migration_tx_data {
790            migration_tx_data
791                .validate_total_supply(token_distribution_schedule.pre_minted_supply)
792                .expect("the migration data does not contain the expected total supply");
793            migration_tx_data
794                .validate_from_unsigned_genesis(&unsigned_genesis)
795                .expect("the migration data is corrupted");
796        } else {
797            assert!(
798                !self.contains_migrations(),
799                "genesis that contains migration should have migration data"
800            );
801        }
802    }
803
804    pub async fn load<P: AsRef<Path>>(path: P) -> anyhow::Result<Self, anyhow::Error> {
805        let path = path.as_ref();
806        let path: &Utf8Path = path.try_into()?;
807        trace!("Reading Genesis Builder from {}", path);
808
809        if !path.is_dir() {
810            bail!("path must be a directory");
811        }
812
813        // Load parameters
814        let parameters_file = path.join(GENESIS_BUILDER_PARAMETERS_FILE);
815        let parameters = serde_yaml::from_slice(&fs::read(&parameters_file).context(format!(
816            "unable to read genesis parameters file {parameters_file}"
817        ))?)
818        .context("unable to deserialize genesis parameters")?;
819
820        // Load migration objects if any
821        let migration_sources_file = path.join(GENESIS_BUILDER_MIGRATION_SOURCES_FILE);
822        let migration_sources: Vec<SnapshotSource> = if migration_sources_file.exists() {
823            serde_json::from_slice(
824                &fs::read(migration_sources_file)
825                    .context("unable to read migration sources file")?,
826            )
827            .context("unable to deserialize migration sources")?
828        } else {
829            Default::default()
830        };
831
832        let token_distribution_schedule_file =
833            path.join(GENESIS_BUILDER_TOKEN_DISTRIBUTION_SCHEDULE_FILE);
834        let token_distribution_schedule = if token_distribution_schedule_file.exists() {
835            Some(TokenDistributionSchedule::from_csv(fs::File::open(
836                token_distribution_schedule_file,
837            )?)?)
838        } else {
839            None
840        };
841
842        // Load validator infos
843        let mut committee = BTreeMap::new();
844        for entry in path.join(GENESIS_BUILDER_COMMITTEE_DIR).read_dir_utf8()? {
845            let entry = entry?;
846            if entry.file_name().starts_with('.') {
847                continue;
848            }
849
850            let path = entry.path();
851            let validator_info: GenesisValidatorInfo = serde_yaml::from_slice(&fs::read(path)?)
852                .with_context(|| format!("unable to load validator info for {path}"))?;
853            committee.insert(validator_info.info.authority_key(), validator_info);
854        }
855
856        // Load Signatures
857        let mut signatures = BTreeMap::new();
858        for entry in path.join(GENESIS_BUILDER_SIGNATURE_DIR).read_dir_utf8()? {
859            let entry = entry?;
860            if entry.file_name().starts_with('.') {
861                continue;
862            }
863
864            let path = entry.path();
865            let sigs: AuthoritySignInfo = bcs::from_bytes(&fs::read(path)?)
866                .with_context(|| format!("unable to load validator signature for {path}"))?;
867            signatures.insert(sigs.authority, sigs);
868        }
869
870        // Load migration txs data
871        let migration_tx_data: Option<MigrationTxData> = if !migration_sources.is_empty() {
872            let migration_tx_data_file = path.join(IOTA_GENESIS_MIGRATION_TX_DATA_FILENAME);
873            Some(MigrationTxData::load(migration_tx_data_file)?)
874        } else {
875            None
876        };
877
878        // Load delegator
879        let delegator_file = path.join(GENESIS_BUILDER_DELEGATOR_FILE);
880        let delegator = if delegator_file.exists() {
881            Some(serde_json::from_slice(&fs::read(delegator_file)?)?)
882        } else {
883            None
884        };
885
886        // Load delegator map
887        let delegator_map_file = path.join(GENESIS_BUILDER_DELEGATOR_MAP_FILE);
888        let delegator_map = if delegator_map_file.exists() {
889            Some(Delegations::from_csv(fs::File::open(delegator_map_file)?)?)
890        } else {
891            None
892        };
893
894        let delegation = delegator
895            .map(GenesisDelegation::OneToAll)
896            .or(delegator_map.map(GenesisDelegation::ManyToMany));
897
898        let mut builder = Self {
899            parameters,
900            token_distribution_schedule,
901            objects: Default::default(),
902            validators: committee,
903            signatures,
904            built_genesis: None, // Leave this as none, will build and compare below
905            migration_objects: Default::default(),
906            genesis_stake: Default::default(),
907            migration_sources,
908            migration_tx_data,
909            delegation,
910        };
911
912        let unsigned_genesis_file = path.join(GENESIS_BUILDER_UNSIGNED_GENESIS_FILE);
913        if unsigned_genesis_file.exists() {
914            let reader = BufReader::new(File::open(unsigned_genesis_file)?);
915            let loaded_genesis: UnsignedGenesis =
916                tokio::task::spawn_blocking(move || bcs::from_reader(reader)).await??;
917
918            // If we have a built genesis, then we must have a token_distribution_schedule
919            // present as well.
920            assert!(
921                builder.token_distribution_schedule.is_some(),
922                "If a built genesis is present, then there must also be a token-distribution-schedule present"
923            );
924
925            // Verify loaded genesis matches one build from the constituent parts
926            builder = tokio::task::spawn_blocking(move || {
927                builder.get_or_build_unsigned_genesis();
928                builder
929            })
930            .await?;
931            loaded_genesis.checkpoint_contents.digest(); // cache digest before compare
932            assert!(
933                *builder.get_or_build_unsigned_genesis() == loaded_genesis,
934                "loaded genesis does not match built genesis"
935            );
936
937            // Just to double check that its set after building above
938            assert!(builder.unsigned_genesis_checkpoint().is_some());
939        }
940
941        Ok(builder)
942    }
943
944    pub fn save<P: AsRef<Path>>(self, path: P) -> anyhow::Result<(), anyhow::Error> {
945        let path = path.as_ref();
946        trace!("Writing Genesis Builder to {}", path.display());
947
948        fs::create_dir_all(path)?;
949
950        // Write parameters
951        let parameters_file = path.join(GENESIS_BUILDER_PARAMETERS_FILE);
952        fs::write(parameters_file, serde_yaml::to_string(&self.parameters)?)?;
953
954        if let Some(token_distribution_schedule) = &self.token_distribution_schedule {
955            token_distribution_schedule.to_csv(fs::File::create(
956                path.join(GENESIS_BUILDER_TOKEN_DISTRIBUTION_SCHEDULE_FILE),
957            )?)?;
958        }
959
960        // Write Signatures
961        let signature_dir = path.join(GENESIS_BUILDER_SIGNATURE_DIR);
962        std::fs::create_dir_all(&signature_dir)?;
963        for (pubkey, sigs) in self.signatures {
964            let name = self.validators.get(&pubkey).unwrap().info.name();
965            fs::write(signature_dir.join(name), &bcs::to_bytes(&sigs)?)?;
966        }
967
968        // Write validator infos
969        let committee_dir = path.join(GENESIS_BUILDER_COMMITTEE_DIR);
970        fs::create_dir_all(&committee_dir)?;
971
972        for (_pubkey, validator) in self.validators {
973            fs::write(
974                committee_dir.join(validator.info.name()),
975                &serde_yaml::to_string(&validator)?,
976            )?;
977        }
978
979        if let Some(genesis) = &self.built_genesis {
980            let mut write = BufWriter::new(File::create(
981                path.join(GENESIS_BUILDER_UNSIGNED_GENESIS_FILE),
982            )?);
983            bcs::serialize_into(&mut write, &genesis)?;
984        }
985
986        if !self.migration_sources.is_empty() {
987            let file = path.join(GENESIS_BUILDER_MIGRATION_SOURCES_FILE);
988            fs::write(file, serde_json::to_string(&self.migration_sources)?)?;
989
990            // Write migration transactions data
991            let file = path.join(IOTA_GENESIS_MIGRATION_TX_DATA_FILENAME);
992            self.migration_tx_data
993                .expect("migration data should exist")
994                .save(file)?;
995        }
996
997        if let Some(delegation) = &self.delegation {
998            match delegation {
999                GenesisDelegation::OneToAll(delegator) => {
1000                    // Write delegator to file
1001                    let file = path.join(GENESIS_BUILDER_DELEGATOR_FILE);
1002                    let delegator_json = serde_json::to_string(delegator)?;
1003                    fs::write(file, delegator_json)?;
1004                }
1005                GenesisDelegation::ManyToMany(delegator_map) => {
1006                    // Write delegator map to CSV file
1007                    delegator_map.to_csv(fs::File::create(
1008                        path.join(GENESIS_BUILDER_DELEGATOR_MAP_FILE),
1009                    )?)?;
1010                }
1011            }
1012        }
1013
1014        Ok(())
1015    }
1016}
1017
1018// Create a Genesis Txn Context to be used when generating genesis objects by
1019// hashing all of the inputs into genesis ans using that as our "Txn Digest".
1020// This is done to ensure that coin objects created between chains are unique
1021fn create_genesis_context(
1022    epoch_data: &EpochData,
1023    genesis_chain_parameters: &GenesisChainParameters,
1024    genesis_validators: &[GenesisValidatorMetadata],
1025    token_distribution_schedule: &TokenDistributionSchedule,
1026    system_packages: &[SystemPackage],
1027    protocol_config: &ProtocolConfig,
1028) -> Rc<RefCell<TxContext>> {
1029    let mut hasher = DefaultHash::default();
1030    hasher.update(b"iota-genesis");
1031    hasher.update(bcs::to_bytes(genesis_chain_parameters).unwrap());
1032    hasher.update(bcs::to_bytes(genesis_validators).unwrap());
1033    hasher.update(bcs::to_bytes(token_distribution_schedule).unwrap());
1034    for system_package in system_packages {
1035        hasher.update(bcs::to_bytes(&system_package.bytes).unwrap());
1036    }
1037
1038    let hash = hasher.finalize();
1039    let genesis_transaction_digest = TransactionDigest::new(hash.into());
1040
1041    let tx_context = TxContext::new(
1042        &IotaAddress::default(),
1043        &genesis_transaction_digest,
1044        epoch_data,
1045        0,
1046        0,
1047        0,
1048        None,
1049        protocol_config,
1050    );
1051
1052    Rc::new(RefCell::new(tx_context))
1053}
1054
1055fn build_unsigned_genesis_data<'info>(
1056    parameters: &GenesisCeremonyParameters,
1057    token_distribution_schedule: &TokenDistributionSchedule,
1058    validators: impl Iterator<Item = &'info GenesisValidatorInfo>,
1059    objects: Vec<Object>,
1060    genesis_stake: &mut GenesisStake,
1061    migration_objects: &mut MigrationObjects,
1062) -> (UnsignedGenesis, MigrationTxData) {
1063    if !parameters.allow_insertion_of_extra_objects && !objects.is_empty() {
1064        panic!(
1065            "insertion of extra objects at genesis time is prohibited due to 'allow_insertion_of_extra_objects' parameter"
1066        );
1067    }
1068
1069    let genesis_chain_parameters = parameters.to_genesis_chain_parameters();
1070    let genesis_validators = validators
1071        .cloned()
1072        .map(GenesisValidatorMetadata::from)
1073        .collect::<Vec<_>>();
1074
1075    let epoch_data = EpochData::new_genesis(genesis_chain_parameters.chain_start_timestamp_ms);
1076
1077    // Get the correct system packages for our protocol version. If we cannot find
1078    // the snapshot that means that we must be at the latest version and we
1079    // should use the latest version of the framework.
1080    let mut system_packages =
1081        iota_framework_snapshot::load_bytecode_snapshot(parameters.protocol_version.as_u64())
1082            .unwrap_or_else(|_| BuiltInFramework::iter_system_packages().cloned().collect());
1083
1084    // if system packages are provided in `objects`, update them with the provided
1085    // bytes. This is a no-op under normal conditions and only an issue with
1086    // certain tests.
1087    update_system_packages_from_objects(&mut system_packages, &objects);
1088
1089    let protocol_config = get_genesis_protocol_config(parameters.protocol_version);
1090
1091    let genesis_ctx = create_genesis_context(
1092        &epoch_data,
1093        &genesis_chain_parameters,
1094        &genesis_validators,
1095        token_distribution_schedule,
1096        &system_packages,
1097        &protocol_config,
1098    );
1099
1100    // Use a throwaway metrics registry for genesis transaction execution.
1101    let registry = prometheus::Registry::new();
1102    let metrics = Arc::new(LimitsMetrics::new(&registry));
1103    let mut txs_data: TransactionsData = BTreeMap::new();
1104
1105    // In here the main genesis objects are created. This means the main system
1106    // objects and the ones that are created at genesis like the network coin.
1107    let (genesis_objects, events) = create_genesis_objects(
1108        genesis_ctx.clone(),
1109        objects,
1110        &genesis_validators,
1111        &genesis_chain_parameters,
1112        token_distribution_schedule,
1113        system_packages,
1114        metrics.clone(),
1115    );
1116
1117    // If the genesis_stake is not empty, then it means we are dealing with a
1118    // genesis with migration data. Thus we create the migration transaction data.
1119    if !genesis_stake.is_empty() {
1120        // Part of the migration objects were already used above during the creation of
1121        // genesis objects. In particular, when the genesis involves a migration, the
1122        // token distribution schedule takes into account assets coming from the
1123        // migration data. These are either timelocked coins or gas coins. The token
1124        // distribution schedule logic assumes that these assets are indeed distributed
1125        // to some addresses and this happens above during the creation of the genesis
1126        // objects. Here then we need to destroy those assets from the original set of
1127        // migration objects.
1128        let migration_objects = destroy_staked_migration_objects(
1129            genesis_ctx,
1130            migration_objects.take_objects(),
1131            &genesis_objects,
1132            &genesis_chain_parameters,
1133            genesis_stake,
1134            metrics.clone(),
1135        );
1136        // Finally, we can create the data structure representing migration transaction
1137        // data.
1138        txs_data = create_migration_tx_data(
1139            migration_objects,
1140            &protocol_config,
1141            metrics.clone(),
1142            &epoch_data,
1143        );
1144    }
1145
1146    // Create the main genesis transaction of kind `GenesisTransaction`
1147    let (genesis_transaction, genesis_effects, genesis_events, genesis_objects) =
1148        create_genesis_transaction(
1149            genesis_objects,
1150            events,
1151            &protocol_config,
1152            metrics,
1153            &epoch_data,
1154        );
1155
1156    // Create the genesis checkpoint including the main transaction and, if present,
1157    // the migration transactions
1158    let (checkpoint, checkpoint_contents) = create_genesis_checkpoint(
1159        &protocol_config,
1160        parameters,
1161        &genesis_transaction,
1162        &genesis_effects,
1163        &txs_data,
1164    );
1165
1166    (
1167        UnsignedGenesis {
1168            checkpoint,
1169            checkpoint_contents,
1170            transaction: genesis_transaction,
1171            effects: genesis_effects,
1172            events: genesis_events,
1173            objects: genesis_objects,
1174        },
1175        // Could be empty
1176        MigrationTxData::new(txs_data),
1177    )
1178}
1179
1180// Creates a map of transaction digest to transaction content involving data
1181// coming from a migration. Migration objects come into a vector of objects,
1182// here it splits this vector into chunks and creates a `GenesisTransaction`
1183// for each chunk.
1184fn create_migration_tx_data(
1185    migration_objects: Vec<Object>,
1186    protocol_config: &ProtocolConfig,
1187    metrics: Arc<LimitsMetrics>,
1188    epoch_data: &EpochData,
1189) -> TransactionsData {
1190    let mut txs_data = TransactionsData::new();
1191    let migration_tx_max_amount = protocol_config
1192        .max_transactions_per_checkpoint_as_option()
1193        .unwrap_or(MAX_AMOUNT_OF_TX_PER_CHECKPOINT)
1194        - 1;
1195    let chunk_size = migration_objects.len() / (migration_tx_max_amount as usize) + 1;
1196
1197    for objects_per_chunk in migration_objects.chunks(chunk_size) {
1198        let (migration_transaction, migration_effects, migration_events, _) =
1199            create_genesis_transaction(
1200                objects_per_chunk.to_vec(),
1201                vec![],
1202                protocol_config,
1203                metrics.clone(),
1204                epoch_data,
1205            );
1206
1207        txs_data.insert(
1208            *migration_transaction.digest(),
1209            (migration_transaction, migration_effects, migration_events),
1210        );
1211    }
1212
1213    txs_data
1214}
1215
1216// Some tests provide an override of the system packages via objects to the
1217// genesis builder. When that happens we need to update the system packages with
1218// the new bytes provided. Mock system packages in protocol config tests are an
1219// example of that (today the only example).
1220// The problem here arises from the fact that if regular system packages are
1221// pushed first *AND* if any of them is loaded in the loader cache, there is no
1222// way to override them with the provided object (no way to mock properly).
1223// System packages are loaded only from internal dependencies (a system package
1224// depending on some other), and in that case they would be loaded in the
1225// VM/loader cache. The Bridge is an example of that and what led to this code.
1226// The bridge depends on `iota_system` which is mocked in some tests, but would
1227// be in the loader cache courtesy of the Bridge, thus causing the problem.
1228fn update_system_packages_from_objects(
1229    system_packages: &mut Vec<SystemPackage>,
1230    objects: &[Object],
1231) {
1232    // Filter `objects` for system packages, and make `SystemPackage`s out of them.
1233    let system_package_overrides: BTreeMap<ObjectID, Vec<Vec<u8>>> = objects
1234        .iter()
1235        .filter_map(|obj| {
1236            let pkg = obj.data.try_as_package()?;
1237            is_system_package(pkg.id()).then(|| {
1238                (
1239                    pkg.id(),
1240                    pkg.serialized_module_map().values().cloned().collect(),
1241                )
1242            })
1243        })
1244        .collect();
1245
1246    // Replace packages in `system_packages` that are present in `objects` with
1247    // their counterparts from the previous step.
1248    for package in system_packages {
1249        if let Some(overrides) = system_package_overrides.get(&package.id).cloned() {
1250            package.bytes = overrides;
1251        }
1252    }
1253}
1254
1255fn create_genesis_checkpoint(
1256    protocol_config: &ProtocolConfig,
1257    parameters: &GenesisCeremonyParameters,
1258    system_genesis_transaction: &Transaction,
1259    system_genesis_tx_effects: &TransactionEffects,
1260    migration_tx_data: &TransactionsData,
1261) -> (CheckpointSummary, CheckpointContents) {
1262    let genesis_execution_digests = ExecutionDigests {
1263        transaction: *system_genesis_transaction.digest(),
1264        effects: system_genesis_tx_effects.digest(),
1265    };
1266
1267    let mut execution_digests = vec![genesis_execution_digests];
1268
1269    for (_, effects, _) in migration_tx_data.values() {
1270        execution_digests.push(effects.execution_digests());
1271    }
1272
1273    let execution_digests_len = execution_digests.len();
1274
1275    let contents = CheckpointContents::new_with_digests_and_signatures(
1276        execution_digests,
1277        vec![vec![]; execution_digests_len],
1278    );
1279    let version_specific_data =
1280        match protocol_config.checkpoint_summary_version_specific_data_as_option() {
1281            None | Some(0) => Vec::new(),
1282            Some(1) => bcs::to_bytes(&CheckpointVersionSpecificData::V1(
1283                CheckpointVersionSpecificDataV1::default(),
1284            ))
1285            .unwrap(),
1286            _ => unimplemented!("unrecognized version_specific_data version for CheckpointSummary"),
1287        };
1288    let checkpoint = CheckpointSummary {
1289        epoch: 0,
1290        sequence_number: 0,
1291        network_total_transactions: contents.size().try_into().unwrap(),
1292        content_digest: *contents.digest(),
1293        previous_digest: None,
1294        epoch_rolling_gas_cost_summary: Default::default(),
1295        end_of_epoch_data: None,
1296        timestamp_ms: parameters.chain_start_timestamp_ms,
1297        version_specific_data,
1298        checkpoint_commitments: Default::default(),
1299    };
1300
1301    (checkpoint, contents)
1302}
1303
1304fn create_genesis_transaction(
1305    objects: Vec<Object>,
1306    events: Vec<Event>,
1307    protocol_config: &ProtocolConfig,
1308    metrics: Arc<LimitsMetrics>,
1309    epoch_data: &EpochData,
1310) -> (
1311    Transaction,
1312    TransactionEffects,
1313    TransactionEvents,
1314    Vec<Object>,
1315) {
1316    let genesis_transaction = {
1317        let genesis_objects = objects
1318            .into_iter()
1319            .map(|mut object| {
1320                if let Some(o) = object.data.try_as_move_mut() {
1321                    o.decrement_version_to(SequenceNumber::MIN_VALID_INCL);
1322                }
1323
1324                if let Owner::Shared {
1325                    initial_shared_version,
1326                } = &mut object.owner
1327                {
1328                    *initial_shared_version = SequenceNumber::MIN_VALID_INCL;
1329                }
1330
1331                let object = object.into_inner();
1332                iota_types::transaction::GenesisObject::RawObject {
1333                    data: object.data,
1334                    owner: object.owner,
1335                }
1336            })
1337            .collect();
1338
1339        iota_types::transaction::VerifiedTransaction::new_genesis_transaction(
1340            genesis_objects,
1341            events,
1342        )
1343        .into_inner()
1344    };
1345
1346    // execute txn to effects
1347    let (effects, events, objects) =
1348        execute_genesis_transaction(epoch_data, protocol_config, metrics, &genesis_transaction);
1349
1350    (genesis_transaction, effects, events, objects)
1351}
1352
1353fn create_genesis_objects(
1354    genesis_ctx: Rc<RefCell<TxContext>>,
1355    input_objects: Vec<Object>,
1356    validators: &[GenesisValidatorMetadata],
1357    parameters: &GenesisChainParameters,
1358    token_distribution_schedule: &TokenDistributionSchedule,
1359    system_packages: Vec<SystemPackage>,
1360    metrics: Arc<LimitsMetrics>,
1361) -> (Vec<Object>, Vec<Event>) {
1362    let mut store = InMemoryStorage::new(Vec::new());
1363    let mut events = Vec::new();
1364    // We don't know the chain ID here since we haven't yet created the genesis
1365    // checkpoint. However since we know there are no chain specific protocol
1366    // config options in genesis, we use Chain::Unknown here.
1367    let protocol_config = ProtocolConfig::get_for_version(
1368        ProtocolVersion::new(parameters.protocol_version),
1369        Chain::Unknown,
1370    );
1371
1372    let silent = true;
1373    let executor = iota_execution::executor(&protocol_config, silent, None)
1374        .expect("Creating an executor should not fail here");
1375
1376    for system_package in system_packages.into_iter() {
1377        let tx_events = process_package(
1378            &mut store,
1379            executor.as_ref(),
1380            genesis_ctx.clone(),
1381            &system_package.modules(),
1382            system_package.dependencies,
1383            &protocol_config,
1384            metrics.clone(),
1385        )
1386        .expect("Processing a package should not fail here");
1387
1388        events.extend(tx_events.data.into_iter());
1389    }
1390
1391    for object in input_objects {
1392        store.insert_object(object);
1393    }
1394
1395    generate_genesis_system_object(
1396        &mut store,
1397        executor.as_ref(),
1398        validators,
1399        genesis_ctx,
1400        parameters,
1401        token_distribution_schedule,
1402        metrics,
1403    )
1404    .expect("Genesis creation should not fail here");
1405
1406    (store.into_inner().into_values().collect(), events)
1407}
1408
1409pub(crate) fn process_package(
1410    store: &mut InMemoryStorage,
1411    executor: &dyn Executor,
1412    ctx: Rc<RefCell<TxContext>>,
1413    modules: &[CompiledModule],
1414    dependencies: Vec<ObjectID>,
1415    protocol_config: &ProtocolConfig,
1416    metrics: Arc<LimitsMetrics>,
1417) -> anyhow::Result<TransactionEvents> {
1418    let dependency_objects = store.get_objects(&dependencies);
1419    // When publishing genesis packages, since the std framework packages all have
1420    // non-zero addresses, [`Transaction::input_objects_in_compiled_modules`] will
1421    // consider them as dependencies even though they are not. Hence
1422    // input_objects contain objects that don't exist on-chain because they are
1423    // yet to be published.
1424    #[cfg(debug_assertions)]
1425    {
1426        use std::collections::HashSet;
1427
1428        use move_core_types::account_address::AccountAddress;
1429
1430        let to_be_published_addresses: HashSet<_> = modules
1431            .iter()
1432            .map(|module| *module.self_id().address())
1433            .collect();
1434        assert!(
1435            // An object either exists on-chain, or is one of the packages to be published.
1436            dependencies
1437                .iter()
1438                .zip(dependency_objects.iter())
1439                .all(|(dependency, obj_opt)| obj_opt.is_some()
1440                    || to_be_published_addresses.contains(&AccountAddress::from(*dependency)))
1441        );
1442    }
1443    let loaded_dependencies: Vec<_> = dependencies
1444        .iter()
1445        .zip(dependency_objects)
1446        .filter_map(|(dependency, object)| {
1447            Some(ObjectReadResult::new(
1448                InputObjectKind::MovePackage(*dependency),
1449                object?.clone().into(),
1450            ))
1451        })
1452        .collect();
1453
1454    let module_bytes = modules
1455        .iter()
1456        .map(|m| {
1457            let mut buf = vec![];
1458            m.serialize_with_version(m.version, &mut buf).unwrap();
1459            buf
1460        })
1461        .collect();
1462    let pt = {
1463        let mut builder = ProgrammableTransactionBuilder::new();
1464        // executing in Genesis mode does not create an `UpgradeCap`.
1465        builder.command(Command::Publish(module_bytes, dependencies));
1466        builder.finish()
1467    };
1468    let InnerTemporaryStore {
1469        written, events, ..
1470    } = executor.update_genesis_state(
1471        &*store,
1472        protocol_config,
1473        metrics,
1474        ctx,
1475        CheckedInputObjects::new_for_genesis(loaded_dependencies),
1476        pt,
1477    )?;
1478
1479    store.finish(written);
1480
1481    Ok(events)
1482}
1483
1484pub fn generate_genesis_system_object(
1485    store: &mut InMemoryStorage,
1486    executor: &dyn Executor,
1487    genesis_validators: &[GenesisValidatorMetadata],
1488    genesis_ctx: Rc<RefCell<TxContext>>,
1489    genesis_chain_parameters: &GenesisChainParameters,
1490    token_distribution_schedule: &TokenDistributionSchedule,
1491    metrics: Arc<LimitsMetrics>,
1492) -> anyhow::Result<()> {
1493    let protocol_config = ProtocolConfig::get_for_version(
1494        ProtocolVersion::new(genesis_chain_parameters.protocol_version),
1495        ChainIdentifier::default().chain(),
1496    );
1497
1498    let pt = {
1499        let mut builder = ProgrammableTransactionBuilder::new();
1500        // Step 1: Create the IotaSystemState UID
1501        let iota_system_state_uid = builder.programmable_move_call(
1502            IOTA_FRAMEWORK_PACKAGE_ID,
1503            ident_str!("object").to_owned(),
1504            ident_str!("iota_system_state").to_owned(),
1505            vec![],
1506            vec![],
1507        );
1508
1509        // Step 2: Create and share the Clock.
1510        builder.move_call(
1511            IOTA_FRAMEWORK_PACKAGE_ID,
1512            ident_str!("clock").to_owned(),
1513            ident_str!("create").to_owned(),
1514            vec![],
1515            vec![],
1516        )?;
1517
1518        // Step 3: Create ProtocolConfig-controlled system objects, unless disabled
1519        // (which only happens in tests).
1520        if protocol_config.create_authenticator_state_in_genesis() {
1521            builder.move_call(
1522                IOTA_FRAMEWORK_PACKAGE_ID,
1523                ident_str!("authenticator_state").to_owned(),
1524                ident_str!("create").to_owned(),
1525                vec![],
1526                vec![],
1527            )?;
1528        }
1529
1530        // Create the randomness state_object
1531        builder.move_call(
1532            IOTA_FRAMEWORK_PACKAGE_ID,
1533            RANDOMNESS_MODULE_NAME.to_owned(),
1534            RANDOMNESS_STATE_CREATE_FUNCTION_NAME.to_owned(),
1535            vec![],
1536            vec![],
1537        )?;
1538
1539        // Create the deny list
1540        builder.move_call(
1541            IOTA_FRAMEWORK_PACKAGE_ID,
1542            DENY_LIST_MODULE.to_owned(),
1543            DENY_LIST_CREATE_FUNC.to_owned(),
1544            vec![],
1545            vec![],
1546        )?;
1547
1548        // Step 4: Create the IOTA Coin Treasury Cap.
1549        let iota_treasury_cap = builder.programmable_move_call(
1550            IOTA_FRAMEWORK_PACKAGE_ID,
1551            ident_str!("iota").to_owned(),
1552            ident_str!("new").to_owned(),
1553            vec![],
1554            vec![],
1555        );
1556
1557        let pre_minted_supply_amount = builder
1558            .pure(token_distribution_schedule.pre_minted_supply)
1559            .expect("serialization of u64 should succeed");
1560        let pre_minted_supply = builder.programmable_move_call(
1561            IOTA_FRAMEWORK_PACKAGE_ID,
1562            ident_str!("iota").to_owned(),
1563            ident_str!("mint_balance").to_owned(),
1564            vec![],
1565            vec![iota_treasury_cap, pre_minted_supply_amount],
1566        );
1567
1568        builder.programmable_move_call(
1569            IOTA_FRAMEWORK_PACKAGE_ID,
1570            BALANCE_MODULE_NAME.to_owned(),
1571            ident_str!("destroy_genesis_supply").to_owned(),
1572            vec![GAS::type_tag()],
1573            vec![pre_minted_supply],
1574        );
1575
1576        // Step 5: Create System Admin Cap.
1577        let system_admin_cap = builder.programmable_move_call(
1578            IOTA_FRAMEWORK_PACKAGE_ID,
1579            IOTA_SYSTEM_ADMIN_CAP_MODULE_NAME.to_owned(),
1580            ident_str!("new_system_admin_cap").to_owned(),
1581            vec![],
1582            vec![],
1583        );
1584
1585        // Step 6: Run genesis.
1586        // The first argument is the system state uid we got from step 1 and the second
1587        // one is the IOTA `TreasuryCap` we got from step 4.
1588        let mut arguments = vec![iota_system_state_uid, iota_treasury_cap];
1589        let mut call_arg_arguments = vec![
1590            CallArg::Pure(bcs::to_bytes(&genesis_chain_parameters).unwrap()),
1591            CallArg::Pure(bcs::to_bytes(&genesis_validators).unwrap()),
1592            CallArg::Pure(bcs::to_bytes(&token_distribution_schedule).unwrap()),
1593            CallArg::Pure(bcs::to_bytes(&Some(STARDUST_UPGRADE_LABEL_VALUE)).unwrap()),
1594        ]
1595        .into_iter()
1596        .map(|a| builder.input(a))
1597        .collect::<anyhow::Result<_, _>>()?;
1598        arguments.append(&mut call_arg_arguments);
1599        arguments.push(system_admin_cap);
1600        builder.programmable_move_call(
1601            IOTA_SYSTEM_ADDRESS.into(),
1602            ident_str!("genesis").to_owned(),
1603            ident_str!("create").to_owned(),
1604            vec![],
1605            arguments,
1606        );
1607
1608        builder.finish()
1609    };
1610
1611    let InnerTemporaryStore { mut written, .. } = executor.update_genesis_state(
1612        &*store,
1613        &protocol_config,
1614        metrics,
1615        genesis_ctx,
1616        CheckedInputObjects::new_for_genesis(vec![]),
1617        pt,
1618    )?;
1619
1620    // update the value of the clock to match the chain start time
1621    {
1622        let object = written.get_mut(&iota_types::IOTA_CLOCK_OBJECT_ID).unwrap();
1623        object
1624            .data
1625            .try_as_move_mut()
1626            .unwrap()
1627            .set_clock_timestamp_ms_unsafe(genesis_chain_parameters.chain_start_timestamp_ms);
1628    }
1629
1630    store.finish(written);
1631
1632    Ok(())
1633}
1634
1635// Migration objects as input to this function were previously used to create a
1636// genesis stake, that in turn helps to create a token distribution schedule for
1637// the genesis. In this function the objects needed for the stake are destroyed
1638// (and, if needed, split) to provide a new set of migration object as output.
1639fn destroy_staked_migration_objects(
1640    genesis_ctx: Rc<RefCell<TxContext>>,
1641    migration_objects: Vec<Object>,
1642    genesis_objects: &[Object],
1643    parameters: &GenesisChainParameters,
1644    genesis_stake: &mut GenesisStake,
1645    metrics: Arc<LimitsMetrics>,
1646) -> Vec<Object> {
1647    // create the temporary store and the executor
1648    let mut store = InMemoryStorage::new(genesis_objects.to_owned());
1649    let protocol_config = ProtocolConfig::get_for_version(
1650        ProtocolVersion::new(parameters.protocol_version),
1651        Chain::Unknown,
1652    );
1653    let silent = true;
1654    let executor = iota_execution::executor(&protocol_config, silent, None)
1655        .expect("Creating an executor should not fail here");
1656
1657    for object in migration_objects {
1658        store.insert_object(object);
1659    }
1660
1661    // First operation: split the timelock objects that are needed to be split
1662    // because of the genesis stake
1663    split_timelocks(
1664        &mut store,
1665        executor.as_ref(),
1666        genesis_ctx,
1667        parameters,
1668        &genesis_stake.take_timelocks_to_split(),
1669        metrics,
1670    )
1671    .expect("Splitting timelocks should not fail here");
1672
1673    // Extract objects from the store
1674    let mut intermediate_store = store.into_inner();
1675
1676    // Second operation: destroy gas and timelocks objects.
1677    // If the genesis stake was created, then destroy gas and timelock objects that
1678    // were added to the token distribution schedule, because they will be
1679    // created on the Move side during genesis. That means we need to prevent
1680    // cloning value by evicting these here.
1681    for (id, _, _) in genesis_stake.take_gas_coins_to_destroy() {
1682        intermediate_store.remove(&id);
1683    }
1684    for (id, _, _) in genesis_stake.take_timelocks_to_destroy() {
1685        intermediate_store.remove(&id);
1686    }
1687
1688    // Clean the intermediate store from objects already present in genesis_objects
1689    for genesis_object in genesis_objects.iter() {
1690        intermediate_store.remove(&genesis_object.id());
1691    }
1692
1693    intermediate_store.into_values().collect()
1694}
1695
1696// Splits timelock objects given an amount to split.
1697pub fn split_timelocks(
1698    store: &mut InMemoryStorage,
1699    executor: &dyn Executor,
1700    genesis_ctx: Rc<RefCell<TxContext>>,
1701    genesis_chain_parameters: &GenesisChainParameters,
1702    timelocks_to_split: &[(ObjectRef, u64, IotaAddress)],
1703    metrics: Arc<LimitsMetrics>,
1704) -> anyhow::Result<()> {
1705    let protocol_config = ProtocolConfig::get_for_version(
1706        ProtocolVersion::new(genesis_chain_parameters.protocol_version),
1707        ChainIdentifier::default().chain(),
1708    );
1709
1710    // Timelocks split PTB
1711    // It takes a list of timelocks_to_split references; then for each timelock it
1712    // invokes "timelock::split" and then transfers the result to the indicated
1713    // recipient.
1714    let mut timelock_split_input_objects: Vec<ObjectReadResult> = vec![];
1715    let pt = {
1716        let mut builder = ProgrammableTransactionBuilder::new();
1717        for (timelock, surplus_amount, recipient) in timelocks_to_split {
1718            timelock_split_input_objects.push(ObjectReadResult::new(
1719                InputObjectKind::ImmOrOwnedMoveObject(*timelock),
1720                store.get_object(&timelock.0).unwrap().clone().into(),
1721            ));
1722            let arguments = vec![
1723                builder.obj(ObjectArg::ImmOrOwnedObject(*timelock))?,
1724                builder.pure(surplus_amount)?,
1725            ];
1726            let surplus_timelock = builder.programmable_move_call(
1727                IOTA_FRAMEWORK_PACKAGE_ID,
1728                ident_str!("timelock").to_owned(),
1729                ident_str!("split").to_owned(),
1730                vec![GAS::type_tag()],
1731                arguments,
1732            );
1733            let arguments = vec![surplus_timelock, builder.pure(*recipient)?];
1734            builder.programmable_move_call(
1735                IOTA_FRAMEWORK_PACKAGE_ID,
1736                ident_str!("timelock").to_owned(),
1737                ident_str!("transfer").to_owned(),
1738                vec![Balance::type_tag(GAS::type_tag())],
1739                arguments,
1740            );
1741        }
1742        builder.finish()
1743    };
1744
1745    // Execute the timelocks split PTB in a genesis environment; it returns a list
1746    // of written objects that includes the modified timelocks (the ones that were
1747    // split), plus the newly created timelocks
1748    let InnerTemporaryStore { written, .. } = executor.update_genesis_state(
1749        &*store,
1750        &protocol_config,
1751        metrics,
1752        genesis_ctx,
1753        CheckedInputObjects::new_for_genesis(timelock_split_input_objects),
1754        pt,
1755    )?;
1756
1757    // Insert the written objects into the store
1758    store.finish(written);
1759
1760    // Finally, we can destroy the timelocks that were split, keeping in the store
1761    // only the newly created timelocks
1762    for ((id, _, _), _, _) in timelocks_to_split {
1763        store.remove_object(*id);
1764    }
1765
1766    Ok(())
1767}
1768
1769#[derive(Clone, Debug, Deserialize, Serialize)]
1770pub enum SnapshotSource {
1771    /// Local uncompressed file.
1772    Local(PathBuf),
1773    /// Remote file (S3) with gzip compressed file
1774    S3(SnapshotUrl),
1775}
1776
1777impl SnapshotSource {
1778    /// Convert to a reader.
1779    pub fn to_reader(&self) -> anyhow::Result<Box<dyn Read>> {
1780        Ok(match self {
1781            SnapshotSource::Local(path) => Box::new(BufReader::new(File::open(path)?)),
1782            SnapshotSource::S3(snapshot_url) => Box::new(snapshot_url.to_reader()?),
1783        })
1784    }
1785}
1786
1787impl From<SnapshotUrl> for SnapshotSource {
1788    fn from(value: SnapshotUrl) -> Self {
1789        Self::S3(value)
1790    }
1791}
1792
1793/// The URLs to download IOTA object snapshot.
1794#[derive(Debug, Clone, Deserialize, Serialize)]
1795pub enum SnapshotUrl {
1796    Iota,
1797    /// Custom migration snapshot for testing purposes.
1798    Test(Url),
1799}
1800
1801impl std::fmt::Display for SnapshotUrl {
1802    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1803        match self {
1804            SnapshotUrl::Iota => "iota".fmt(f),
1805            SnapshotUrl::Test(url) => url.as_str().fmt(f),
1806        }
1807    }
1808}
1809
1810impl FromStr for SnapshotUrl {
1811    type Err = anyhow::Error;
1812
1813    fn from_str(s: &str) -> Result<Self, Self::Err> {
1814        if let Ok(url) = reqwest::Url::from_str(s) {
1815            return Ok(Self::Test(url));
1816        }
1817        Ok(match s.to_lowercase().as_str() {
1818            "iota" => Self::Iota,
1819            e => bail!("unsupported snapshot url: {e}"),
1820        })
1821    }
1822}
1823
1824impl SnapshotUrl {
1825    /// Returns the IOTA object snapshot download URL.
1826    pub fn to_url(&self) -> Url {
1827        match self {
1828            Self::Iota => Url::parse(IOTA_OBJECT_SNAPSHOT_URL).expect("should be valid URL"),
1829            Self::Test(url) => url.clone(),
1830        }
1831    }
1832
1833    /// Convert a gzip decoder to read the compressed object snapshot from S3.
1834    pub fn to_reader(&self) -> anyhow::Result<impl Read> {
1835        Ok(GzDecoder::new(BufReader::new(reqwest::blocking::get(
1836            self.to_url(),
1837        )?)))
1838    }
1839}
1840
1841#[cfg(test)]
1842mod test {
1843    use fastcrypto::traits::KeyPair;
1844    use iota_config::{
1845        genesis::*,
1846        local_ip_utils,
1847        node::{DEFAULT_COMMISSION_RATE, DEFAULT_VALIDATOR_GAS_PRICE},
1848    };
1849    use iota_types::{
1850        base_types::IotaAddress,
1851        crypto::{
1852            AccountKeyPair, AuthorityKeyPair, NetworkKeyPair, generate_proof_of_possession,
1853            get_key_pair_from_rng,
1854        },
1855    };
1856
1857    use crate::{Builder, validator_info::ValidatorInfo};
1858
1859    #[test]
1860    fn allocation_csv() {
1861        let schedule = TokenDistributionSchedule::new_for_validators_with_default_allocation([
1862            IotaAddress::random_for_testing_only(),
1863            IotaAddress::random_for_testing_only(),
1864        ]);
1865        let mut output = Vec::new();
1866
1867        schedule.to_csv(&mut output).unwrap();
1868
1869        let parsed_schedule = TokenDistributionSchedule::from_csv(output.as_slice()).unwrap();
1870
1871        assert_eq!(schedule, parsed_schedule);
1872
1873        std::io::Write::write_all(&mut std::io::stdout(), &output).unwrap();
1874    }
1875
1876    #[tokio::test]
1877    #[cfg_attr(msim, ignore)]
1878    async fn ceremony() {
1879        let dir = tempfile::TempDir::new().unwrap();
1880
1881        let authority_key: AuthorityKeyPair = get_key_pair_from_rng(&mut rand::rngs::OsRng).1;
1882        let protocol_key: NetworkKeyPair = get_key_pair_from_rng(&mut rand::rngs::OsRng).1;
1883        let account_key: AccountKeyPair = get_key_pair_from_rng(&mut rand::rngs::OsRng).1;
1884        let network_key: NetworkKeyPair = get_key_pair_from_rng(&mut rand::rngs::OsRng).1;
1885        let validator = ValidatorInfo {
1886            name: "0".into(),
1887            authority_key: authority_key.public().into(),
1888            protocol_key: protocol_key.public().clone(),
1889            account_address: IotaAddress::from(account_key.public()),
1890            network_key: network_key.public().clone(),
1891            gas_price: DEFAULT_VALIDATOR_GAS_PRICE,
1892            commission_rate: DEFAULT_COMMISSION_RATE,
1893            network_address: local_ip_utils::new_local_tcp_address_for_testing(),
1894            p2p_address: local_ip_utils::new_local_udp_address_for_testing(),
1895            primary_address: local_ip_utils::new_local_udp_address_for_testing(),
1896            description: String::new(),
1897            image_url: String::new(),
1898            project_url: String::new(),
1899        };
1900        let pop = generate_proof_of_possession(&authority_key, account_key.public().into());
1901        let mut builder = Builder::new().add_validator(validator, pop);
1902
1903        let genesis = builder.get_or_build_unsigned_genesis();
1904        for object in genesis.objects() {
1905            println!("ObjectID: {} Type: {:?}", object.id(), object.type_());
1906        }
1907        builder.save(dir.path()).unwrap();
1908        Builder::load(dir.path()).await.unwrap();
1909    }
1910}