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