Skip to main content

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},
10    path::Path,
11    rc::Rc,
12    sync::Arc,
13};
14
15use anyhow::{Context, bail};
16use camino::Utf8Path;
17use fastcrypto::{hash::HashFunction, traits::KeyPair};
18use iota_config::genesis::{
19    Genesis, GenesisCeremonyParameters, GenesisChainParameters, TokenDistributionSchedule,
20    UnsignedGenesis,
21};
22use iota_execution::{self, Executor};
23use iota_framework::{BuiltInFramework, SystemPackage};
24use iota_genesis_common::{execute_genesis_transaction, get_genesis_protocol_config};
25use iota_protocol_config::{Chain, ProtocolConfig, ProtocolVersion};
26use iota_sdk_types::{
27    Address, CheckpointContents, CheckpointSummary, Command, Event, GenesisObject, Identifier,
28    ObjectId, Owner, TransactionDigest, TransactionEffects, TransactionEvents, Version,
29    crypto::{Intent, IntentMessage, IntentScope},
30};
31use iota_types::{
32    base_types::{ExecutionDigests, TxContext},
33    committee::Committee,
34    crypto::{
35        AuthorityKeyPair, AuthorityPublicKeyBytes, AuthoritySignInfo, AuthoritySignInfoTrait,
36        AuthoritySignature, DefaultHash, IotaAuthoritySignature,
37    },
38    deny_list_v1::DENY_LIST_CREATE_FUNC,
39    digests::ChainIdentifier,
40    epoch_data::EpochData,
41    gas_coin::GasCoin,
42    governance::StakedIota,
43    in_memory_storage::InMemoryStorage,
44    inner_temporary_store::InnerTemporaryStore,
45    iota_system_state::{IotaSystemState, IotaSystemStateTrait, get_iota_system_state},
46    messages_checkpoint::{
47        CertifiedCheckpointSummary, CheckpointContentsExt, CheckpointVersionSpecificData,
48        CheckpointVersionSpecificDataV1,
49    },
50    metrics::LimitsMetrics,
51    object::{MoveStructExt, Object},
52    programmable_transaction_builder::ProgrammableTransactionBuilder,
53    randomness_state::RANDOMNESS_STATE_CREATE_FUNCTION_NAME,
54    transaction::{
55        CallArg, CheckedInputObjects, InputObjectKind, ObjectReadResult, TransactionEnvelope,
56    },
57};
58use move_binary_format::CompiledModule;
59use tracing::trace;
60use validator_info::{GenesisValidatorInfo, GenesisValidatorMetadata, ValidatorInfo};
61
62pub mod validator_info;
63
64const GENESIS_BUILDER_COMMITTEE_DIR: &str = "committee";
65pub const GENESIS_BUILDER_PARAMETERS_FILE: &str = "parameters";
66const GENESIS_BUILDER_TOKEN_DISTRIBUTION_SCHEDULE_FILE: &str = "token-distribution-schedule";
67const GENESIS_BUILDER_SIGNATURE_DIR: &str = "signatures";
68const GENESIS_BUILDER_UNSIGNED_GENESIS_FILE: &str = "unsigned-genesis";
69const GENESIS_BUILDER_MIGRATION_LOGIC_REMOVAL_PROTOCOL_VERSION: u64 = 32;
70
71pub struct Builder {
72    parameters: GenesisCeremonyParameters,
73    token_distribution_schedule: Option<TokenDistributionSchedule>,
74    objects: BTreeMap<ObjectId, Object>,
75    validators: BTreeMap<AuthorityPublicKeyBytes, GenesisValidatorInfo>,
76    // Validator signatures over checkpoint
77    signatures: BTreeMap<AuthorityPublicKeyBytes, AuthoritySignInfo>,
78    built_genesis: Option<UnsignedGenesis>,
79}
80
81impl Default for Builder {
82    fn default() -> Self {
83        Self::new()
84    }
85}
86
87impl Builder {
88    pub fn new() -> Self {
89        Self {
90            parameters: Default::default(),
91            token_distribution_schedule: None,
92            objects: Default::default(),
93            validators: Default::default(),
94            signatures: Default::default(),
95            built_genesis: None,
96        }
97    }
98
99    pub fn with_parameters(mut self, parameters: GenesisCeremonyParameters) -> Self {
100        self.parameters = parameters;
101        self
102    }
103
104    /// Set the [`TokenDistributionSchedule`].
105    ///
106    /// # Panics
107    ///
108    /// Panics if the schedule is invalid, e.g. it contains timelocked stake,
109    /// which is not supported at genesis.
110    pub fn with_token_distribution_schedule(
111        mut self,
112        token_distribution_schedule: TokenDistributionSchedule,
113    ) -> Self {
114        token_distribution_schedule.validate();
115        self.token_distribution_schedule = Some(token_distribution_schedule);
116        self
117    }
118
119    pub fn with_protocol_version(mut self, v: ProtocolVersion) -> Self {
120        self.parameters.protocol_version = v;
121        self
122    }
123
124    pub fn add_object(mut self, object: Object) -> Self {
125        self.objects.insert(object.id(), object);
126        self
127    }
128
129    pub fn add_objects(mut self, objects: Vec<Object>) -> Self {
130        for object in objects {
131            self.objects.insert(object.id(), object);
132        }
133        self
134    }
135
136    pub fn add_validator(
137        mut self,
138        validator: ValidatorInfo,
139        proof_of_possession: AuthoritySignature,
140    ) -> Self {
141        self.validators.insert(
142            validator.authority_key(),
143            GenesisValidatorInfo {
144                info: validator,
145                proof_of_possession,
146            },
147        );
148        self
149    }
150
151    pub fn validators(&self) -> &BTreeMap<AuthorityPublicKeyBytes, GenesisValidatorInfo> {
152        &self.validators
153    }
154
155    pub fn add_validator_signature(mut self, keypair: &AuthorityKeyPair) -> Self {
156        let name = keypair.public().into();
157        assert!(
158            self.validators.contains_key(&name),
159            "provided keypair does not correspond to a validator in the validator set"
160        );
161
162        let UnsignedGenesis { checkpoint, .. } = self.get_or_build_unsigned_genesis();
163
164        let checkpoint_signature = {
165            let intent_msg = IntentMessage::new(
166                Intent::iota_app(IntentScope::CheckpointSummary),
167                checkpoint.clone(),
168            );
169            let signature = AuthoritySignature::new_secure(&intent_msg, &checkpoint.epoch, keypair);
170            AuthoritySignInfo {
171                epoch: checkpoint.epoch,
172                authority: name,
173                signature,
174            }
175        };
176
177        self.signatures.insert(name, checkpoint_signature);
178
179        self
180    }
181
182    pub fn unsigned_genesis_checkpoint(&self) -> Option<UnsignedGenesis> {
183        self.built_genesis.clone()
184    }
185
186    /// Evaluate the genesis [`TokenDistributionSchedule`]: use the schedule
187    /// given as input, if any, or instantiate a default schedule for the
188    /// validators otherwise.
189    fn resolve_token_distribution_schedule(&mut self) -> TokenDistributionSchedule {
190        self.token_distribution_schedule.take().unwrap_or_else(|| {
191            TokenDistributionSchedule::new_for_validators_with_default_allocation(
192                self.validators.values().map(|v| v.info.iota_address()),
193                self.parameters.protocol_version,
194            )
195        })
196    }
197
198    fn build_and_cache_unsigned_genesis(&mut self) {
199        // Verify that all input data is valid.
200        // Check that if extra objects are present then it is allowed by the parameters
201        // to add extra objects and it also validates the validator info
202        self.validate_inputs().unwrap();
203
204        let token_distribution_schedule = self.resolve_token_distribution_schedule();
205
206        // Verify that token distribution schedule is valid
207        token_distribution_schedule.validate();
208        token_distribution_schedule
209            .check_minimum_stake_for_validators(
210                self.validators.values().map(|v| v.info.iota_address()),
211                self.parameters.protocol_version,
212            )
213            .expect("all validators should have the required stake");
214
215        let unsigned_genesis = build_unsigned_genesis_data(
216            &self.parameters,
217            &token_distribution_schedule,
218            self.validators.values(),
219            self.objects.clone().into_values().collect::<Vec<_>>(),
220        );
221
222        self.built_genesis = Some(unsigned_genesis);
223        self.token_distribution_schedule = Some(token_distribution_schedule);
224    }
225
226    pub fn get_or_build_unsigned_genesis(&mut self) -> &UnsignedGenesis {
227        if self.built_genesis.is_none() {
228            self.build_and_cache_unsigned_genesis();
229        }
230        self.built_genesis
231            .as_ref()
232            .expect("genesis should have been built and cached")
233    }
234
235    fn committee(objects: &[Object]) -> Committee {
236        let iota_system_object =
237            get_iota_system_state(&objects).expect("IOTA System State object must always exist");
238        iota_system_object
239            .get_current_epoch_committee()
240            .committee()
241            .clone()
242    }
243
244    pub fn protocol_version(&self) -> ProtocolVersion {
245        self.parameters.protocol_version
246    }
247
248    pub fn build(mut self) -> Genesis {
249        if self.built_genesis.is_none() {
250            self.build_and_cache_unsigned_genesis();
251        }
252
253        // Verify that all on-chain state was properly created
254        self.validate().unwrap();
255
256        let UnsignedGenesis {
257            checkpoint,
258            checkpoint_contents,
259            transaction,
260            effects,
261            events,
262            objects,
263        } = self
264            .built_genesis
265            .take()
266            .expect("genesis should have been built");
267
268        let committee = Self::committee(&objects);
269
270        let checkpoint = {
271            let signatures = self.signatures.clone().into_values().collect();
272
273            CertifiedCheckpointSummary::new(checkpoint, signatures, &committee).unwrap()
274        };
275
276        Genesis::new(
277            checkpoint,
278            checkpoint_contents,
279            transaction,
280            effects,
281            events,
282            objects,
283        )
284    }
285
286    /// Validates the entire state of the build, no matter what the internal
287    /// state is (input collection phase or output phase)
288    pub fn validate(&self) -> anyhow::Result<(), anyhow::Error> {
289        self.validate_inputs()?;
290        self.validate_token_distribution_schedule()?;
291        self.validate_output();
292        Ok(())
293    }
294
295    /// Runs through validation checks on the input values present in the
296    /// builder
297    fn validate_inputs(&self) -> anyhow::Result<(), anyhow::Error> {
298        if !self.parameters.allow_insertion_of_extra_objects && !self.objects.is_empty() {
299            bail!("extra objects are disallowed");
300        }
301
302        for validator in self.validators.values() {
303            validator.validate().with_context(|| {
304                format!(
305                    "metadata for validator {} is invalid",
306                    validator.info.name()
307                )
308            })?;
309        }
310
311        Ok(())
312    }
313
314    /// Runs through validation checks on the input token distribution schedule
315    fn validate_token_distribution_schedule(&self) -> anyhow::Result<(), anyhow::Error> {
316        if let Some(token_distribution_schedule) = &self.token_distribution_schedule {
317            token_distribution_schedule.validate();
318            token_distribution_schedule.check_minimum_stake_for_validators(
319                self.validators.values().map(|v| v.info.iota_address()),
320                self.parameters.protocol_version,
321            )?;
322        }
323
324        Ok(())
325    }
326
327    /// Runs through validation checks on the generated output (the initial
328    /// chain state) based on the input values present in the builder
329    fn validate_output(&self) {
330        // If genesis hasn't been built yet, just early return as there is nothing to
331        // validate yet
332        let Some(unsigned_genesis) = self.unsigned_genesis_checkpoint() else {
333            return;
334        };
335
336        let GenesisChainParameters {
337            protocol_version,
338            chain_start_timestamp_ms,
339            epoch_duration_ms,
340            max_validator_count,
341            min_validator_joining_stake,
342            validator_low_stake_threshold,
343            validator_very_low_stake_threshold,
344            validator_low_stake_grace_period,
345        } = self.parameters.to_genesis_chain_parameters();
346
347        // In non-testing code, genesis type must always be V1.
348        let system_state = match unsigned_genesis.iota_system_object() {
349            IotaSystemState::V1(inner) => inner,
350            IotaSystemState::V2(_) => unreachable!(),
351            #[cfg(msim)]
352            _ => {
353                // Types other than V1 used in simtests do not need to be validated.
354                return;
355            }
356        };
357
358        assert!(unsigned_genesis.has_randomness_state_object());
359
360        assert!(unsigned_genesis.has_coin_deny_list_object());
361
362        assert_eq!(
363            self.validators.len(),
364            system_state.validators.active_validators.len()
365        );
366        let mut address_to_pool_id = BTreeMap::new();
367        for (validator, onchain_validator) in self
368            .validators
369            .values()
370            .zip(system_state.validators.active_validators.iter())
371        {
372            let metadata = onchain_validator.verified_metadata();
373
374            // Validators should not have duplicate addresses so the result of insertion
375            // should be None.
376            assert!(
377                address_to_pool_id
378                    .insert(metadata.iota_address, onchain_validator.staking_pool.id)
379                    .is_none()
380            );
381            assert_eq!(validator.info.iota_address(), metadata.iota_address);
382            assert_eq!(validator.info.authority_key(), metadata.iota_pubkey_bytes());
383            assert_eq!(validator.info.network_key, metadata.network_pubkey);
384            assert_eq!(validator.info.protocol_key, metadata.protocol_pubkey);
385            assert_eq!(
386                validator.proof_of_possession.as_ref().to_vec(),
387                metadata.proof_of_possession_bytes
388            );
389            assert_eq!(validator.info.name(), &metadata.name);
390            assert_eq!(validator.info.description, metadata.description);
391            assert_eq!(validator.info.image_url, metadata.image_url);
392            assert_eq!(validator.info.project_url, metadata.project_url);
393            assert_eq!(validator.info.network_address(), &metadata.net_address);
394            assert_eq!(validator.info.p2p_address, metadata.p2p_address);
395            assert_eq!(validator.info.primary_address, metadata.primary_address);
396
397            assert_eq!(validator.info.gas_price, onchain_validator.gas_price);
398            assert_eq!(
399                validator.info.commission_rate,
400                onchain_validator.commission_rate
401            );
402        }
403
404        assert_eq!(system_state.epoch, 0);
405        assert_eq!(system_state.protocol_version, protocol_version);
406        assert_eq!(system_state.storage_fund.non_refundable_balance.value(), 0);
407        assert_eq!(
408            system_state
409                .storage_fund
410                .total_object_storage_rebates
411                .value(),
412            0
413        );
414
415        assert_eq!(system_state.parameters.epoch_duration_ms, epoch_duration_ms);
416        assert_eq!(
417            system_state.parameters.max_validator_count,
418            max_validator_count,
419        );
420        assert_eq!(
421            system_state.parameters.min_validator_joining_stake,
422            min_validator_joining_stake,
423        );
424        assert_eq!(
425            system_state.parameters.validator_low_stake_threshold,
426            validator_low_stake_threshold,
427        );
428        assert_eq!(
429            system_state.parameters.validator_very_low_stake_threshold,
430            validator_very_low_stake_threshold,
431        );
432        assert_eq!(
433            system_state.parameters.validator_low_stake_grace_period,
434            validator_low_stake_grace_period,
435        );
436
437        assert!(!system_state.safe_mode);
438        assert_eq!(
439            system_state.epoch_start_timestamp_ms,
440            chain_start_timestamp_ms,
441        );
442        assert_eq!(system_state.validators.pending_removals.len(), 0);
443        assert_eq!(
444            system_state
445                .validators
446                .pending_active_validators
447                .contents
448                .size,
449            0
450        );
451        assert_eq!(system_state.validators.inactive_validators.size, 0);
452        assert_eq!(system_state.validators.validator_candidates.size, 0);
453
454        // Check distribution is correct
455        let token_distribution_schedule = self.token_distribution_schedule.clone().unwrap();
456
457        let allocations_amount: u64 = token_distribution_schedule
458            .allocations
459            .iter()
460            .map(|allocation| allocation.amount_nanos)
461            .sum();
462
463        assert_eq!(
464            system_state.iota_treasury_cap.total_supply().value,
465            token_distribution_schedule.pre_minted_supply + allocations_amount
466        );
467
468        let mut gas_objects: BTreeMap<ObjectId, (&Object, GasCoin)> = unsigned_genesis
469            .objects()
470            .iter()
471            .filter_map(|o| GasCoin::try_from(o).ok().map(|g| (o.id(), (o, g))))
472            .collect();
473        let mut staked_iota_objects: BTreeMap<ObjectId, (&Object, StakedIota)> = unsigned_genesis
474            .objects()
475            .iter()
476            .filter_map(|o| StakedIota::try_from(o).ok().map(|s| (o.id(), (o, s))))
477            .collect();
478
479        for allocation in token_distribution_schedule.allocations {
480            if let Some(staked_with_validator) = allocation.staked_with_validator {
481                let staking_pool_id = *address_to_pool_id
482                    .get(&staked_with_validator)
483                    .expect("staking pool should exist");
484                let staked_iota_object_id = staked_iota_objects
485                    .iter()
486                    .find(|(_k, (o, s))| {
487                        let Owner::Address(owner) = &o.owner else {
488                            panic!("gas object owner must be address owner");
489                        };
490                        *owner == allocation.recipient_address
491                            && s.principal() == allocation.amount_nanos
492                            && s.pool_id() == staking_pool_id
493                    })
494                    .map(|(k, _)| *k)
495                    .expect("all allocations should be present");
496                let staked_iota_object =
497                    staked_iota_objects.remove(&staked_iota_object_id).unwrap();
498                assert_eq!(
499                    staked_iota_object.0.owner,
500                    Owner::Address(allocation.recipient_address)
501                );
502                assert_eq!(staked_iota_object.1.principal(), allocation.amount_nanos);
503                assert_eq!(staked_iota_object.1.pool_id(), staking_pool_id);
504                assert_eq!(staked_iota_object.1.activation_epoch(), 0);
505            } else {
506                let gas_object_id = gas_objects
507                    .iter()
508                    .find(|(_k, (o, g))| {
509                        if let Owner::Address(owner) = &o.owner {
510                            *owner == allocation.recipient_address
511                                && g.value() == allocation.amount_nanos
512                        } else {
513                            false
514                        }
515                    })
516                    .map(|(k, _)| *k)
517                    .expect("all allocations should be present");
518                let gas_object = gas_objects.remove(&gas_object_id).unwrap();
519                assert_eq!(
520                    gas_object.0.owner,
521                    Owner::Address(allocation.recipient_address)
522                );
523                assert_eq!(gas_object.1.value(), allocation.amount_nanos,);
524            }
525        }
526
527        // All Gas and staked objects should be accounted for
528        if !self.parameters.allow_insertion_of_extra_objects {
529            assert!(gas_objects.is_empty());
530            assert!(staked_iota_objects.is_empty());
531        }
532
533        let committee = system_state.get_current_epoch_committee();
534        for signature in self.signatures.values() {
535            if !self.validators.contains_key(&signature.authority) {
536                panic!("found signature for unknown validator: {signature:#?}");
537            }
538
539            signature
540                .verify_secure(
541                    unsigned_genesis.checkpoint(),
542                    Intent::iota_app(IntentScope::CheckpointSummary),
543                    committee.committee(),
544                )
545                .expect("signature should be valid");
546        }
547    }
548
549    pub fn load<P: AsRef<Path>>(path: P) -> anyhow::Result<Self, anyhow::Error> {
550        let path = path.as_ref();
551        let path: &Utf8Path = path.try_into()?;
552        trace!("Reading Genesis Builder from {}", path);
553
554        if !path.is_dir() {
555            bail!("path must be a directory");
556        }
557
558        // Load parameters
559        let parameters_file = path.join(GENESIS_BUILDER_PARAMETERS_FILE);
560        let parameters = serde_yaml::from_slice(&fs::read(&parameters_file).context(format!(
561            "unable to read genesis parameters file {parameters_file}"
562        ))?)
563        .context("unable to deserialize genesis parameters")?;
564
565        let token_distribution_schedule_file =
566            path.join(GENESIS_BUILDER_TOKEN_DISTRIBUTION_SCHEDULE_FILE);
567        let token_distribution_schedule = if token_distribution_schedule_file.exists() {
568            Some(TokenDistributionSchedule::from_csv(fs::File::open(
569                token_distribution_schedule_file,
570            )?)?)
571        } else {
572            None
573        };
574
575        // Load validator infos
576        let mut committee = BTreeMap::new();
577        for entry in path.join(GENESIS_BUILDER_COMMITTEE_DIR).read_dir_utf8()? {
578            let entry = entry?;
579            if entry.file_name().starts_with('.') {
580                continue;
581            }
582
583            let path = entry.path();
584            let validator_info: GenesisValidatorInfo = serde_yaml::from_slice(&fs::read(path)?)
585                .with_context(|| format!("unable to load validator info for {path}"))?;
586            committee.insert(validator_info.info.authority_key(), validator_info);
587        }
588
589        // Load Signatures
590        let mut signatures = BTreeMap::new();
591        for entry in path.join(GENESIS_BUILDER_SIGNATURE_DIR).read_dir_utf8()? {
592            let entry = entry?;
593            if entry.file_name().starts_with('.') {
594                continue;
595            }
596
597            let path = entry.path();
598            let sigs: AuthoritySignInfo = bcs::from_bytes(&fs::read(path)?)
599                .with_context(|| format!("unable to load validator signature for {path}"))?;
600            signatures.insert(sigs.authority, sigs);
601        }
602
603        let mut builder = Self {
604            parameters,
605            token_distribution_schedule,
606            objects: Default::default(),
607            validators: committee,
608            signatures,
609            built_genesis: None, // Leave this as none, will build and compare below
610        };
611
612        let unsigned_genesis_file = path.join(GENESIS_BUILDER_UNSIGNED_GENESIS_FILE);
613        if unsigned_genesis_file.exists() {
614            let reader = BufReader::new(File::open(unsigned_genesis_file)?);
615            let loaded_genesis: UnsignedGenesis = bcs::from_reader(reader)?;
616
617            // If we have a built genesis, then we must have a token_distribution_schedule
618            // present as well.
619            assert!(
620                builder.token_distribution_schedule.is_some(),
621                "If a built genesis is present, then there must also be a token-distribution-schedule present"
622            );
623
624            // Verify loaded genesis matches one build from the constituent parts
625            loaded_genesis.checkpoint_contents.digest(); // cache digest before compare
626            assert!(
627                *builder.get_or_build_unsigned_genesis() == loaded_genesis,
628                "loaded genesis does not match built genesis"
629            );
630
631            // Just to double check that its set after building above
632            assert!(builder.unsigned_genesis_checkpoint().is_some());
633        }
634
635        Ok(builder)
636    }
637
638    pub fn save<P: AsRef<Path>>(self, path: P) -> anyhow::Result<(), anyhow::Error> {
639        let path = path.as_ref();
640        trace!("Writing Genesis Builder to {}", path.display());
641
642        fs::create_dir_all(path)?;
643
644        // Write parameters
645        let parameters_file = path.join(GENESIS_BUILDER_PARAMETERS_FILE);
646        fs::write(parameters_file, serde_yaml::to_string(&self.parameters)?)?;
647
648        if let Some(token_distribution_schedule) = &self.token_distribution_schedule {
649            token_distribution_schedule.to_csv(fs::File::create(
650                path.join(GENESIS_BUILDER_TOKEN_DISTRIBUTION_SCHEDULE_FILE),
651            )?)?;
652        }
653
654        // Write Signatures
655        let signature_dir = path.join(GENESIS_BUILDER_SIGNATURE_DIR);
656        std::fs::create_dir_all(&signature_dir)?;
657        for (pubkey, sigs) in self.signatures {
658            let name = self.validators.get(&pubkey).unwrap().info.name();
659            fs::write(signature_dir.join(name), &bcs::to_bytes(&sigs)?)?;
660        }
661
662        // Write validator infos
663        let committee_dir = path.join(GENESIS_BUILDER_COMMITTEE_DIR);
664        fs::create_dir_all(&committee_dir)?;
665
666        for (_pubkey, validator) in self.validators {
667            fs::write(
668                committee_dir.join(validator.info.name()),
669                &serde_yaml::to_string(&validator)?,
670            )?;
671        }
672
673        if let Some(genesis) = &self.built_genesis {
674            let mut write = BufWriter::new(File::create(
675                path.join(GENESIS_BUILDER_UNSIGNED_GENESIS_FILE),
676            )?);
677            bcs::serialize_into(&mut write, &genesis)?;
678        }
679
680        Ok(())
681    }
682}
683
684// Create a Genesis Txn Context to be used when generating genesis objects by
685// hashing all of the inputs into genesis ans using that as our "Txn Digest".
686// This is done to ensure that coin objects created between chains are unique
687fn create_genesis_context(
688    epoch_data: &EpochData,
689    genesis_chain_parameters: &GenesisChainParameters,
690    genesis_validators: &[GenesisValidatorMetadata],
691    token_distribution_schedule: &TokenDistributionSchedule,
692    system_packages: &[SystemPackage],
693    protocol_config: &ProtocolConfig,
694) -> Rc<RefCell<TxContext>> {
695    let mut hasher = DefaultHash::default();
696    hasher.update(b"iota-genesis");
697    hasher.update(bcs::to_bytes(genesis_chain_parameters).unwrap());
698    hasher.update(bcs::to_bytes(genesis_validators).unwrap());
699    hasher.update(bcs::to_bytes(token_distribution_schedule).unwrap());
700    for system_package in system_packages {
701        hasher.update(bcs::to_bytes(&system_package.bytes).unwrap());
702    }
703
704    let hash = hasher.finalize();
705    let genesis_transaction_digest = TransactionDigest::new(hash.into());
706
707    let tx_context = TxContext::new(
708        &Address::ZERO,
709        &genesis_transaction_digest,
710        epoch_data,
711        0,
712        0,
713        0,
714        None,
715        protocol_config,
716    );
717
718    Rc::new(RefCell::new(tx_context))
719}
720
721fn build_unsigned_genesis_data<'info>(
722    parameters: &GenesisCeremonyParameters,
723    token_distribution_schedule: &TokenDistributionSchedule,
724    validators: impl Iterator<Item = &'info GenesisValidatorInfo>,
725    objects: Vec<Object>,
726) -> UnsignedGenesis {
727    if !parameters.allow_insertion_of_extra_objects && !objects.is_empty() {
728        panic!(
729            "insertion of extra objects at genesis time is prohibited due to 'allow_insertion_of_extra_objects' parameter"
730        );
731    }
732
733    let genesis_chain_parameters = parameters.to_genesis_chain_parameters();
734    let genesis_validators = validators
735        .cloned()
736        .map(GenesisValidatorMetadata::from)
737        .collect::<Vec<_>>();
738
739    let epoch_data = EpochData::new_genesis(genesis_chain_parameters.chain_start_timestamp_ms);
740
741    // Get the correct system packages for our protocol version. If we cannot find
742    // the snapshot that means that we must be at the latest version and we
743    // should use the latest version of the framework.
744    let mut system_packages =
745        iota_framework_snapshot::load_bytecode_snapshot(parameters.protocol_version.as_u64())
746            .unwrap_or_else(|_| BuiltInFramework::iter_system_packages().cloned().collect());
747
748    // if system packages are provided in `objects`, update them with the provided
749    // bytes. This is a no-op under normal conditions and only an issue with
750    // certain tests.
751    update_system_packages_from_objects(&mut system_packages, &objects);
752
753    let protocol_config = get_genesis_protocol_config(parameters.protocol_version);
754
755    let genesis_ctx = create_genesis_context(
756        &epoch_data,
757        &genesis_chain_parameters,
758        &genesis_validators,
759        token_distribution_schedule,
760        &system_packages,
761        &protocol_config,
762    );
763
764    // Use a throwaway metrics registry for genesis transaction execution.
765    let registry = prometheus_filtered::Registry::new();
766    let metrics = Arc::new(LimitsMetrics::new(&registry));
767
768    // In here the main genesis objects are created. This means the main system
769    // objects and the ones that are created at genesis like the network coin.
770    let (genesis_objects, events) = create_genesis_objects(
771        genesis_ctx,
772        objects,
773        &genesis_validators,
774        &genesis_chain_parameters,
775        token_distribution_schedule,
776        system_packages,
777        metrics.clone(),
778    );
779
780    // Create the main genesis transaction of kind `GenesisTransaction`
781    let (genesis_transaction, genesis_effects, genesis_events, genesis_objects) =
782        create_genesis_transaction(
783            genesis_objects,
784            events,
785            &protocol_config,
786            metrics,
787            &epoch_data,
788        );
789
790    let (checkpoint, checkpoint_contents) = create_genesis_checkpoint(
791        &protocol_config,
792        parameters,
793        &genesis_transaction,
794        &genesis_effects,
795    );
796
797    UnsignedGenesis {
798        checkpoint,
799        checkpoint_contents,
800        transaction: genesis_transaction,
801        effects: genesis_effects,
802        events: genesis_events,
803        objects: genesis_objects,
804    }
805}
806
807// Some tests provide an override of the system packages via objects to the
808// genesis builder. When that happens we need to update the system packages with
809// the new bytes provided. Mock system packages in protocol config tests are an
810// example of that (today the only example).
811// The problem here arises from the fact that if regular system packages are
812// pushed first *AND* if any of them is loaded in the loader cache, there is no
813// way to override them with the provided object (no way to mock properly).
814// System packages are loaded only from internal dependencies (a system package
815// depending on some other), and in that case they would be loaded in the
816// VM/loader cache. The Bridge is an example of that and what led to this code.
817// The bridge depends on `iota_system` which is mocked in some tests, but would
818// be in the loader cache courtesy of the Bridge, thus causing the problem.
819fn update_system_packages_from_objects(
820    system_packages: &mut Vec<SystemPackage>,
821    objects: &[Object],
822) {
823    // Filter `objects` for system packages, and make `SystemPackage`s out of them.
824    let system_package_overrides: BTreeMap<ObjectId, Vec<Vec<u8>>> = objects
825        .iter()
826        .filter_map(|obj| {
827            let pkg = obj.data.as_opt_package()?;
828            pkg.id().is_system_package().then(|| {
829                (
830                    pkg.id(),
831                    pkg.serialized_module_map().values().cloned().collect(),
832                )
833            })
834        })
835        .collect();
836
837    // Replace packages in `system_packages` that are present in `objects` with
838    // their counterparts from the previous step.
839    for package in system_packages {
840        if let Some(overrides) = system_package_overrides.get(&package.id).cloned() {
841            package.bytes = overrides;
842        }
843    }
844}
845
846fn create_genesis_checkpoint(
847    protocol_config: &ProtocolConfig,
848    parameters: &GenesisCeremonyParameters,
849    system_genesis_transaction: &TransactionEnvelope,
850    system_genesis_tx_effects: &TransactionEffects,
851) -> (CheckpointSummary, CheckpointContents) {
852    let genesis_execution_digests = ExecutionDigests {
853        transaction: *system_genesis_transaction.digest(),
854        effects: system_genesis_tx_effects.digest(),
855    };
856
857    let contents = CheckpointContents::new_with_digests_and_signatures(
858        vec![genesis_execution_digests],
859        vec![vec![]],
860    );
861    let version_specific_data =
862        match protocol_config.checkpoint_summary_version_specific_data_as_option() {
863            None | Some(0) => Vec::new(),
864            Some(1) => bcs::to_bytes(&CheckpointVersionSpecificData::V1(
865                CheckpointVersionSpecificDataV1::default(),
866            ))
867            .unwrap(),
868            _ => unimplemented!("unrecognized version_specific_data version for CheckpointSummary"),
869        };
870    let checkpoint = CheckpointSummary {
871        epoch: 0,
872        sequence_number: 0,
873        network_total_transactions: contents.len().try_into().unwrap(),
874        contents_digest: contents.digest(),
875        previous_digest: None,
876        epoch_rolling_gas_cost_summary: Default::default(),
877        end_of_epoch_data: None,
878        timestamp_ms: parameters.chain_start_timestamp_ms,
879        version_specific_data,
880        checkpoint_commitments: Default::default(),
881    };
882
883    (checkpoint, contents)
884}
885
886fn create_genesis_transaction(
887    objects: Vec<Object>,
888    events: Vec<Event>,
889    protocol_config: &ProtocolConfig,
890    metrics: Arc<LimitsMetrics>,
891    epoch_data: &EpochData,
892) -> (
893    TransactionEnvelope,
894    TransactionEffects,
895    TransactionEvents,
896    Vec<Object>,
897) {
898    let genesis_transaction = {
899        let genesis_objects = objects
900            .into_iter()
901            .map(|mut object| {
902                if let Some(o) = object.data.as_opt_mut_struct() {
903                    o.decrement_version_to(Version::MIN_VALID_INCL);
904                }
905
906                if let Owner::Shared(initial_shared_version) = &mut object.owner {
907                    *initial_shared_version = Version::MIN_VALID_INCL;
908                }
909
910                let object = object.into_inner();
911                GenesisObject::new(object.data, object.owner)
912            })
913            .collect();
914
915        iota_types::transaction::VerifiedTransaction::new_genesis_transaction(
916            genesis_objects,
917            events,
918        )
919        .into_inner()
920    };
921
922    // execute txn to effects
923    let (effects, events, objects) =
924        execute_genesis_transaction(epoch_data, protocol_config, metrics, &genesis_transaction);
925
926    (genesis_transaction, effects, events, objects)
927}
928
929fn create_genesis_objects(
930    genesis_ctx: Rc<RefCell<TxContext>>,
931    input_objects: Vec<Object>,
932    validators: &[GenesisValidatorMetadata],
933    parameters: &GenesisChainParameters,
934    token_distribution_schedule: &TokenDistributionSchedule,
935    system_packages: Vec<SystemPackage>,
936    metrics: Arc<LimitsMetrics>,
937) -> (Vec<Object>, Vec<Event>) {
938    let mut store = InMemoryStorage::new(Vec::new());
939    let mut events = Vec::new();
940    // We don't know the chain ID here since we haven't yet created the genesis
941    // checkpoint. However since we know there are no chain specific protocol
942    // config options in genesis, we use Chain::Unknown here.
943    let protocol_config = ProtocolConfig::get_for_version(
944        ProtocolVersion::new(parameters.protocol_version),
945        Chain::Unknown,
946    );
947
948    let silent = true;
949    let executor = iota_execution::executor(&protocol_config, silent, None)
950        .expect("Creating an executor should not fail here");
951
952    for system_package in system_packages.into_iter() {
953        let tx_events = process_package(
954            &mut store,
955            executor.as_ref(),
956            genesis_ctx.clone(),
957            &system_package.modules(),
958            system_package.dependencies,
959            &protocol_config,
960            metrics.clone(),
961        )
962        .expect("Processing a package should not fail here");
963
964        events.extend(tx_events.0);
965    }
966
967    for object in input_objects {
968        store.insert_object(object);
969    }
970
971    generate_genesis_system_object(
972        &mut store,
973        executor.as_ref(),
974        validators,
975        genesis_ctx,
976        parameters,
977        token_distribution_schedule,
978        metrics,
979    )
980    .expect("Genesis creation should not fail here");
981
982    (store.into_inner().into_values().collect(), events)
983}
984
985pub(crate) fn process_package(
986    store: &mut InMemoryStorage,
987    executor: &dyn Executor,
988    ctx: Rc<RefCell<TxContext>>,
989    modules: &[CompiledModule],
990    dependencies: Vec<ObjectId>,
991    protocol_config: &ProtocolConfig,
992    metrics: Arc<LimitsMetrics>,
993) -> anyhow::Result<TransactionEvents> {
994    let dependency_objects = store.get_objects(&dependencies);
995    // When publishing genesis packages, since the std framework packages all have
996    // non-zero addresses, they will be considered as dependencies even though they
997    // are not. Hence input_objects contain objects that don't exist on-chain
998    // because they are yet to be published.
999    #[cfg(debug_assertions)]
1000    {
1001        use std::collections::HashSet;
1002
1003        use move_core_types::account_address::AccountAddress;
1004
1005        let to_be_published_addresses: HashSet<_> = modules
1006            .iter()
1007            .map(|module| *module.self_id().address())
1008            .collect();
1009        assert!(
1010            // An object either exists on-chain, or is one of the packages to be published.
1011            dependencies
1012                .iter()
1013                .zip(dependency_objects.iter())
1014                .all(|(dependency, obj_opt)| obj_opt.is_some()
1015                    || to_be_published_addresses
1016                        .contains(&AccountAddress::new(dependency.into_bytes())))
1017        );
1018    }
1019    let loaded_dependencies: Vec<_> = dependencies
1020        .iter()
1021        .zip(dependency_objects)
1022        .filter_map(|(dependency, object)| {
1023            Some(ObjectReadResult::new(
1024                InputObjectKind::MovePackage(*dependency),
1025                object?.clone().into(),
1026            ))
1027        })
1028        .collect();
1029
1030    let module_bytes = modules
1031        .iter()
1032        .map(|m| {
1033            let mut buf = vec![];
1034            m.serialize_with_version(m.version, &mut buf).unwrap();
1035            buf
1036        })
1037        .collect();
1038    let pt = {
1039        let mut builder = ProgrammableTransactionBuilder::new();
1040        // executing in Genesis mode does not create an `UpgradeCap`.
1041        builder.command(Command::new_publish(module_bytes, dependencies));
1042        builder.finish()
1043    };
1044    let InnerTemporaryStore {
1045        written, events, ..
1046    } = executor.update_genesis_state(
1047        &*store,
1048        protocol_config,
1049        metrics,
1050        ctx,
1051        CheckedInputObjects::new_for_genesis(loaded_dependencies),
1052        pt,
1053    )?;
1054
1055    store.finish(written);
1056
1057    Ok(events)
1058}
1059
1060pub fn generate_genesis_system_object(
1061    store: &mut InMemoryStorage,
1062    executor: &dyn Executor,
1063    genesis_validators: &[GenesisValidatorMetadata],
1064    genesis_ctx: Rc<RefCell<TxContext>>,
1065    genesis_chain_parameters: &GenesisChainParameters,
1066    token_distribution_schedule: &TokenDistributionSchedule,
1067    metrics: Arc<LimitsMetrics>,
1068) -> anyhow::Result<()> {
1069    let protocol_config = ProtocolConfig::get_for_version(
1070        ProtocolVersion::new(genesis_chain_parameters.protocol_version),
1071        ChainIdentifier::default().chain(),
1072    );
1073
1074    let pt = {
1075        let mut builder = ProgrammableTransactionBuilder::new();
1076        // Step 1: Create the IotaSystemState UID
1077        let iota_system_state_uid = builder.programmable_move_call(
1078            ObjectId::FRAMEWORK,
1079            Identifier::OBJECT_MODULE,
1080            Identifier::from_static("iota_system_state"),
1081            vec![],
1082            vec![],
1083        );
1084
1085        // Step 2: Create and share the Clock.
1086        builder.move_call(
1087            ObjectId::FRAMEWORK,
1088            Identifier::CLOCK_MODULE,
1089            Identifier::from_static("create"),
1090            vec![],
1091            vec![],
1092        )?;
1093
1094        // Create the randomness state_object
1095        builder.move_call(
1096            ObjectId::FRAMEWORK,
1097            Identifier::RANDOM_MODULE,
1098            RANDOMNESS_STATE_CREATE_FUNCTION_NAME,
1099            vec![],
1100            vec![],
1101        )?;
1102
1103        // Create the deny list
1104        builder.move_call(
1105            ObjectId::FRAMEWORK,
1106            Identifier::DENY_LIST_MODULE,
1107            DENY_LIST_CREATE_FUNC,
1108            vec![],
1109            vec![],
1110        )?;
1111
1112        // Step 4: Create the IOTA Coin Treasury Cap.
1113        let iota_treasury_cap = builder.programmable_move_call(
1114            ObjectId::FRAMEWORK,
1115            Identifier::IOTA_MODULE,
1116            Identifier::from_static("new"),
1117            vec![],
1118            vec![],
1119        );
1120
1121        // Step 5: Create System Admin Cap.
1122        let system_admin_cap = builder.programmable_move_call(
1123            ObjectId::FRAMEWORK,
1124            Identifier::SYSTEM_ADMIN_CAP_MODULE,
1125            Identifier::from_static("new_system_admin_cap"),
1126            vec![],
1127            vec![],
1128        );
1129
1130        // Step 6: Run genesis.
1131        // The first argument is the system state uid we got from step 1 and the second
1132        // one is the IOTA `TreasuryCap` we got from step 4.
1133        let mut arguments = vec![iota_system_state_uid, iota_treasury_cap];
1134        let mut call_arg_arguments = vec![
1135            CallArg::pure(&genesis_chain_parameters),
1136            CallArg::pure(&genesis_validators),
1137            CallArg::pure(&token_distribution_schedule),
1138        ]
1139        .into_iter()
1140        .map(|a| builder.input(a))
1141        .collect::<anyhow::Result<_, _>>()?;
1142        arguments.append(&mut call_arg_arguments);
1143        if genesis_chain_parameters.protocol_version
1144            < GENESIS_BUILDER_MIGRATION_LOGIC_REMOVAL_PROTOCOL_VERSION
1145        {
1146            // For older protocol versions, e.g., for running some specific tests, we need
1147            // to pass the timelock genesis label as an argument, but as a None value.
1148            arguments.push(builder.input(CallArg::pure(&None::<String>))?);
1149        }
1150        arguments.push(system_admin_cap);
1151        builder.programmable_move_call(
1152            ObjectId::SYSTEM,
1153            Identifier::from_static("genesis"),
1154            Identifier::from_static("create"),
1155            vec![],
1156            arguments,
1157        );
1158
1159        builder.finish()
1160    };
1161
1162    let InnerTemporaryStore { mut written, .. } = executor.update_genesis_state(
1163        &*store,
1164        &protocol_config,
1165        metrics,
1166        genesis_ctx,
1167        CheckedInputObjects::new_for_genesis(vec![]),
1168        pt,
1169    )?;
1170
1171    // update the value of the clock to match the chain start time
1172    {
1173        let object = written.get_mut(&ObjectId::CLOCK).unwrap();
1174        object
1175            .data
1176            .as_opt_mut_struct()
1177            .unwrap()
1178            .set_clock_timestamp_ms_unchecked(genesis_chain_parameters.chain_start_timestamp_ms);
1179    }
1180
1181    store.finish(written);
1182
1183    Ok(())
1184}
1185
1186#[cfg(test)]
1187mod test {
1188    use fastcrypto::traits::KeyPair;
1189    use iota_config::{
1190        genesis::*,
1191        local_ip_utils,
1192        node::{DEFAULT_COMMISSION_RATE, DEFAULT_VALIDATOR_GAS_PRICE},
1193    };
1194    use iota_protocol_config::ProtocolVersion;
1195    use iota_sdk_types::Address;
1196    use iota_types::crypto::{
1197        AuthorityKeyPair, NetworkKeyPair, generate_proof_of_possession, get_key_pair_from_rng,
1198    };
1199
1200    use crate::{Builder, validator_info::ValidatorInfo};
1201
1202    #[test]
1203    fn allocation_csv() {
1204        // No genesis is being built in this test, so there is no protocol version to
1205        // thread through; use the current version.
1206        let schedule = TokenDistributionSchedule::new_for_validators_with_default_allocation(
1207            [Address::random(), Address::random()],
1208            ProtocolVersion::MAX,
1209        );
1210        let mut output = Vec::new();
1211
1212        schedule.to_csv(&mut output).unwrap();
1213
1214        let parsed_schedule = TokenDistributionSchedule::from_csv(output.as_slice()).unwrap();
1215
1216        assert_eq!(schedule, parsed_schedule);
1217
1218        std::io::Write::write_all(&mut std::io::stdout(), &output).unwrap();
1219    }
1220
1221    #[test]
1222    #[cfg_attr(msim, ignore)]
1223    fn ceremony() {
1224        let dir = tempfile::TempDir::new().unwrap();
1225
1226        let authority_key: AuthorityKeyPair = get_key_pair_from_rng(&mut rand::rngs::OsRng).1;
1227        let protocol_key: NetworkKeyPair = get_key_pair_from_rng(&mut rand::rngs::OsRng).1;
1228        let account_address = Address::random();
1229        let network_key: NetworkKeyPair = get_key_pair_from_rng(&mut rand::rngs::OsRng).1;
1230        let validator = ValidatorInfo {
1231            name: "0".into(),
1232            authority_key: authority_key.public().into(),
1233            protocol_key: protocol_key.public().clone(),
1234            account_address,
1235            network_key: network_key.public().clone(),
1236            gas_price: DEFAULT_VALIDATOR_GAS_PRICE,
1237            commission_rate: DEFAULT_COMMISSION_RATE,
1238            network_address: local_ip_utils::new_local_tcp_address_for_testing(),
1239            p2p_address: local_ip_utils::new_local_udp_address_for_testing(),
1240            primary_address: local_ip_utils::new_local_udp_address_for_testing(),
1241            description: String::new(),
1242            image_url: String::new(),
1243            project_url: String::new(),
1244        };
1245        let pop = generate_proof_of_possession(&authority_key, account_address);
1246        let mut builder = Builder::new().add_validator(validator, pop);
1247
1248        let genesis = builder.get_or_build_unsigned_genesis();
1249        for object in genesis.objects() {
1250            println!(
1251                "ObjectId: {} Type: {:?}",
1252                object.id(),
1253                object.data.opt_object_type()
1254            );
1255        }
1256        builder.save(dir.path()).unwrap();
1257        Builder::load(dir.path()).unwrap();
1258    }
1259}