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