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 BRIDGE_ADDRESS, IOTA_BRIDGE_OBJECT_ID, IOTA_FRAMEWORK_PACKAGE_ID, IOTA_SYSTEM_ADDRESS,
34 balance::{BALANCE_MODULE_NAME, Balance},
35 base_types::{
36 ExecutionDigests, IotaAddress, ObjectID, ObjectRef, SequenceNumber, TransactionDigest,
37 TxContext,
38 },
39 bridge::{BRIDGE_CREATE_FUNCTION_NAME, BRIDGE_MODULE_NAME, BridgeChainId},
40 committee::Committee,
41 crypto::{
42 AuthorityKeyPair, AuthorityPublicKeyBytes, AuthoritySignInfo, AuthoritySignInfoTrait,
43 AuthoritySignature, DefaultHash, IotaAuthoritySignature,
44 },
45 deny_list_v1::{DENY_LIST_CREATE_FUNC, DENY_LIST_MODULE},
46 digests::ChainIdentifier,
47 effects::{TransactionEffects, TransactionEvents},
48 epoch_data::EpochData,
49 event::Event,
50 gas_coin::{GAS, GasCoin, STARDUST_TOTAL_SUPPLY_NANOS},
51 governance::StakedIota,
52 id::UID,
53 in_memory_storage::InMemoryStorage,
54 inner_temporary_store::InnerTemporaryStore,
55 iota_system_state::{IotaSystemState, IotaSystemStateTrait, get_iota_system_state},
56 is_system_package,
57 message_envelope::Message,
58 messages_checkpoint::{
59 CertifiedCheckpointSummary, CheckpointContents, CheckpointSummary,
60 CheckpointVersionSpecificData, CheckpointVersionSpecificDataV1,
61 },
62 metrics::LimitsMetrics,
63 object::{Object, Owner},
64 programmable_transaction_builder::ProgrammableTransactionBuilder,
65 randomness_state::{RANDOMNESS_MODULE_NAME, RANDOMNESS_STATE_CREATE_FUNCTION_NAME},
66 system_admin_cap::IOTA_SYSTEM_ADMIN_CAP_MODULE_NAME,
67 timelock::{
68 stardust_upgrade_label::STARDUST_UPGRADE_LABEL_VALUE,
69 timelocked_staked_iota::TimelockedStakedIota,
70 },
71 transaction::{
72 CallArg, CheckedInputObjects, Command, InputObjectKind, ObjectArg, ObjectReadResult,
73 Transaction,
74 },
75};
76use move_binary_format::CompiledModule;
77use move_core_types::ident_str;
78use serde::{Deserialize, Serialize};
79use shared_crypto::intent::{Intent, IntentMessage, IntentScope};
80use stake::GenesisStake;
81use stardust::migration::MigrationObjects;
82use tracing::trace;
83use validator_info::{GenesisValidatorInfo, GenesisValidatorMetadata, ValidatorInfo};
84
85pub mod genesis_build_effects;
86mod stake;
87pub mod stardust;
88pub mod validator_info;
89
90const GENESIS_BUILDER_COMMITTEE_DIR: &str = "committee";
91pub const GENESIS_BUILDER_PARAMETERS_FILE: &str = "parameters";
92const GENESIS_BUILDER_TOKEN_DISTRIBUTION_SCHEDULE_FILE: &str = "token-distribution-schedule";
93const GENESIS_BUILDER_SIGNATURE_DIR: &str = "signatures";
94const GENESIS_BUILDER_UNSIGNED_GENESIS_FILE: &str = "unsigned-genesis";
95const GENESIS_BUILDER_MIGRATION_SOURCES_FILE: &str = "migration-sources";
96const GENESIS_BUILDER_DELEGATOR_FILE: &str = "delegator";
97const GENESIS_BUILDER_DELEGATOR_MAP_FILE: &str = "delegator-map";
98
99pub const OBJECT_SNAPSHOT_FILE_PATH: &str = "stardust_object_snapshot.bin";
100pub const IOTA_OBJECT_SNAPSHOT_URL: &str = "https://stardust-objects.s3.eu-central-1.amazonaws.com/iota/alphanet/latest/stardust_object_snapshot.bin.gz";
101
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_eq!(
561 protocol_config.enable_bridge(),
562 unsigned_genesis.has_bridge_object()
563 );
564
565 assert!(unsigned_genesis.has_coin_deny_list_object());
566
567 assert_eq!(
568 self.validators.len(),
569 system_state.validators.active_validators.len()
570 );
571 let mut address_to_pool_id = BTreeMap::new();
572 for (validator, onchain_validator) in self
573 .validators
574 .values()
575 .zip(system_state.validators.active_validators.iter())
576 {
577 let metadata = onchain_validator.verified_metadata();
578
579 assert!(
582 address_to_pool_id
583 .insert(metadata.iota_address, onchain_validator.staking_pool.id)
584 .is_none()
585 );
586 assert_eq!(validator.info.iota_address(), metadata.iota_address);
587 assert_eq!(validator.info.authority_key(), metadata.iota_pubkey_bytes());
588 assert_eq!(validator.info.network_key, metadata.network_pubkey);
589 assert_eq!(validator.info.protocol_key, metadata.protocol_pubkey);
590 assert_eq!(
591 validator.proof_of_possession.as_ref().to_vec(),
592 metadata.proof_of_possession_bytes
593 );
594 assert_eq!(validator.info.name(), &metadata.name);
595 assert_eq!(validator.info.description, metadata.description);
596 assert_eq!(validator.info.image_url, metadata.image_url);
597 assert_eq!(validator.info.project_url, metadata.project_url);
598 assert_eq!(validator.info.network_address(), &metadata.net_address);
599 assert_eq!(validator.info.p2p_address, metadata.p2p_address);
600 assert_eq!(validator.info.primary_address, metadata.primary_address);
601
602 assert_eq!(validator.info.gas_price, onchain_validator.gas_price);
603 assert_eq!(
604 validator.info.commission_rate,
605 onchain_validator.commission_rate
606 );
607 }
608
609 assert_eq!(system_state.epoch, 0);
610 assert_eq!(system_state.protocol_version, protocol_version);
611 assert_eq!(system_state.storage_fund.non_refundable_balance.value(), 0);
612 assert_eq!(
613 system_state
614 .storage_fund
615 .total_object_storage_rebates
616 .value(),
617 0
618 );
619
620 assert_eq!(system_state.parameters.epoch_duration_ms, epoch_duration_ms);
621 assert_eq!(
622 system_state.parameters.max_validator_count,
623 max_validator_count,
624 );
625 assert_eq!(
626 system_state.parameters.min_validator_joining_stake,
627 min_validator_joining_stake,
628 );
629 assert_eq!(
630 system_state.parameters.validator_low_stake_threshold,
631 validator_low_stake_threshold,
632 );
633 assert_eq!(
634 system_state.parameters.validator_very_low_stake_threshold,
635 validator_very_low_stake_threshold,
636 );
637 assert_eq!(
638 system_state.parameters.validator_low_stake_grace_period,
639 validator_low_stake_grace_period,
640 );
641
642 assert!(!system_state.safe_mode);
643 assert_eq!(
644 system_state.epoch_start_timestamp_ms,
645 chain_start_timestamp_ms,
646 );
647 assert_eq!(system_state.validators.pending_removals.len(), 0);
648 assert_eq!(
649 system_state
650 .validators
651 .pending_active_validators
652 .contents
653 .size,
654 0
655 );
656 assert_eq!(system_state.validators.inactive_validators.size, 0);
657 assert_eq!(system_state.validators.validator_candidates.size, 0);
658
659 let token_distribution_schedule = self.token_distribution_schedule.clone().unwrap();
661
662 let allocations_amount: u64 = token_distribution_schedule
663 .allocations
664 .iter()
665 .map(|allocation| allocation.amount_nanos)
666 .sum();
667
668 assert_eq!(
669 system_state.iota_treasury_cap.total_supply().value,
670 token_distribution_schedule.pre_minted_supply + allocations_amount
671 );
672
673 let mut gas_objects: BTreeMap<ObjectID, (&Object, GasCoin)> = unsigned_genesis
674 .objects()
675 .iter()
676 .filter_map(|o| GasCoin::try_from(o).ok().map(|g| (o.id(), (o, g))))
677 .collect();
678 let mut staked_iota_objects: BTreeMap<ObjectID, (&Object, StakedIota)> = unsigned_genesis
679 .objects()
680 .iter()
681 .filter_map(|o| StakedIota::try_from(o).ok().map(|s| (o.id(), (o, s))))
682 .collect();
683 let mut timelock_staked_iota_objects: BTreeMap<ObjectID, (&Object, TimelockedStakedIota)> =
684 unsigned_genesis
685 .objects()
686 .iter()
687 .filter_map(|o| {
688 TimelockedStakedIota::try_from(o)
689 .ok()
690 .map(|s| (o.id(), (o, s)))
691 })
692 .collect();
693
694 for allocation in token_distribution_schedule.allocations {
695 if let Some(staked_with_validator) = allocation.staked_with_validator {
696 let staking_pool_id = *address_to_pool_id
697 .get(&staked_with_validator)
698 .expect("staking pool should exist");
699 if let Some(expiration) = allocation.staked_with_timelock_expiration {
700 let timelock_staked_iota_object_id = timelock_staked_iota_objects
701 .iter()
702 .find(|(_k, (o, s))| {
703 let Owner::AddressOwner(owner) = &o.owner else {
704 panic!("gas object owner must be address owner");
705 };
706 *owner == allocation.recipient_address
707 && s.principal() == allocation.amount_nanos
708 && s.pool_id() == staking_pool_id
709 && s.expiration_timestamp_ms() == expiration
710 })
711 .map(|(k, _)| *k)
712 .expect("all allocations should be present");
713 let timelock_staked_iota_object = timelock_staked_iota_objects
714 .remove(&timelock_staked_iota_object_id)
715 .unwrap();
716 assert_eq!(
717 timelock_staked_iota_object.0.owner,
718 Owner::AddressOwner(allocation.recipient_address)
719 );
720 assert_eq!(
721 timelock_staked_iota_object.1.principal(),
722 allocation.amount_nanos
723 );
724 assert_eq!(timelock_staked_iota_object.1.pool_id(), staking_pool_id);
725 assert_eq!(timelock_staked_iota_object.1.activation_epoch(), 0);
726 } else {
727 let staked_iota_object_id = staked_iota_objects
728 .iter()
729 .find(|(_k, (o, s))| {
730 let Owner::AddressOwner(owner) = &o.owner else {
731 panic!("gas object owner must be address owner");
732 };
733 *owner == allocation.recipient_address
734 && s.principal() == allocation.amount_nanos
735 && s.pool_id() == staking_pool_id
736 })
737 .map(|(k, _)| *k)
738 .expect("all allocations should be present");
739 let staked_iota_object =
740 staked_iota_objects.remove(&staked_iota_object_id).unwrap();
741 assert_eq!(
742 staked_iota_object.0.owner,
743 Owner::AddressOwner(allocation.recipient_address)
744 );
745 assert_eq!(staked_iota_object.1.principal(), allocation.amount_nanos);
746 assert_eq!(staked_iota_object.1.pool_id(), staking_pool_id);
747 assert_eq!(staked_iota_object.1.activation_epoch(), 0);
748 }
749 } else {
750 let gas_object_id = gas_objects
751 .iter()
752 .find(|(_k, (o, g))| {
753 if let Owner::AddressOwner(owner) = &o.owner {
754 *owner == allocation.recipient_address
755 && g.value() == allocation.amount_nanos
756 } else {
757 false
758 }
759 })
760 .map(|(k, _)| *k)
761 .expect("all allocations should be present");
762 let gas_object = gas_objects.remove(&gas_object_id).unwrap();
763 assert_eq!(
764 gas_object.0.owner,
765 Owner::AddressOwner(allocation.recipient_address)
766 );
767 assert_eq!(gas_object.1.value(), allocation.amount_nanos,);
768 }
769 }
770
771 if !self.parameters.allow_insertion_of_extra_objects {
773 assert!(gas_objects.is_empty());
774 assert!(staked_iota_objects.is_empty());
775 assert!(timelock_staked_iota_objects.is_empty());
776 }
777
778 let committee = system_state.get_current_epoch_committee();
779 for signature in self.signatures.values() {
780 if !self.validators.contains_key(&signature.authority) {
781 panic!("found signature for unknown validator: {:#?}", signature);
782 }
783
784 signature
785 .verify_secure(
786 unsigned_genesis.checkpoint(),
787 Intent::iota_app(IntentScope::CheckpointSummary),
788 committee.committee(),
789 )
790 .expect("signature should be valid");
791 }
792
793 if let Some(migration_tx_data) = &self.migration_tx_data {
795 migration_tx_data
796 .validate_total_supply(token_distribution_schedule.pre_minted_supply)
797 .expect("the migration data does not contain the expected total supply");
798 migration_tx_data
799 .validate_from_unsigned_genesis(&unsigned_genesis)
800 .expect("the migration data is corrupted");
801 } else {
802 assert!(
803 !self.contains_migrations(),
804 "genesis that contains migration should have migration data"
805 );
806 }
807 }
808
809 pub async fn load<P: AsRef<Path>>(path: P) -> anyhow::Result<Self, anyhow::Error> {
810 let path = path.as_ref();
811 let path: &Utf8Path = path.try_into()?;
812 trace!("Reading Genesis Builder from {}", path);
813
814 if !path.is_dir() {
815 bail!("path must be a directory");
816 }
817
818 let parameters_file = path.join(GENESIS_BUILDER_PARAMETERS_FILE);
820 let parameters = serde_yaml::from_slice(&fs::read(¶meters_file).context(format!(
821 "unable to read genesis parameters file {parameters_file}"
822 ))?)
823 .context("unable to deserialize genesis parameters")?;
824
825 let migration_sources_file = path.join(GENESIS_BUILDER_MIGRATION_SOURCES_FILE);
827 let migration_sources: Vec<SnapshotSource> = if migration_sources_file.exists() {
828 serde_json::from_slice(
829 &fs::read(migration_sources_file)
830 .context("unable to read migration sources file")?,
831 )
832 .context("unable to deserialize migration sources")?
833 } else {
834 Default::default()
835 };
836
837 let token_distribution_schedule_file =
838 path.join(GENESIS_BUILDER_TOKEN_DISTRIBUTION_SCHEDULE_FILE);
839 let token_distribution_schedule = if token_distribution_schedule_file.exists() {
840 Some(TokenDistributionSchedule::from_csv(fs::File::open(
841 token_distribution_schedule_file,
842 )?)?)
843 } else {
844 None
845 };
846
847 let mut committee = BTreeMap::new();
849 for entry in path.join(GENESIS_BUILDER_COMMITTEE_DIR).read_dir_utf8()? {
850 let entry = entry?;
851 if entry.file_name().starts_with('.') {
852 continue;
853 }
854
855 let path = entry.path();
856 let validator_info: GenesisValidatorInfo = serde_yaml::from_slice(&fs::read(path)?)
857 .with_context(|| format!("unable to load validator info for {path}"))?;
858 committee.insert(validator_info.info.authority_key(), validator_info);
859 }
860
861 let mut signatures = BTreeMap::new();
863 for entry in path.join(GENESIS_BUILDER_SIGNATURE_DIR).read_dir_utf8()? {
864 let entry = entry?;
865 if entry.file_name().starts_with('.') {
866 continue;
867 }
868
869 let path = entry.path();
870 let sigs: AuthoritySignInfo = bcs::from_bytes(&fs::read(path)?)
871 .with_context(|| format!("unable to load validator signature for {path}"))?;
872 signatures.insert(sigs.authority, sigs);
873 }
874
875 let migration_tx_data: Option<MigrationTxData> = if !migration_sources.is_empty() {
877 let migration_tx_data_file = path.join(IOTA_GENESIS_MIGRATION_TX_DATA_FILENAME);
878 Some(MigrationTxData::load(migration_tx_data_file)?)
879 } else {
880 None
881 };
882
883 let delegator_file = path.join(GENESIS_BUILDER_DELEGATOR_FILE);
885 let delegator = if delegator_file.exists() {
886 Some(serde_json::from_slice(&fs::read(delegator_file)?)?)
887 } else {
888 None
889 };
890
891 let delegator_map_file = path.join(GENESIS_BUILDER_DELEGATOR_MAP_FILE);
893 let delegator_map = if delegator_map_file.exists() {
894 Some(Delegations::from_csv(fs::File::open(delegator_map_file)?)?)
895 } else {
896 None
897 };
898
899 let delegation = delegator
900 .map(GenesisDelegation::OneToAll)
901 .or(delegator_map.map(GenesisDelegation::ManyToMany));
902
903 let mut builder = Self {
904 parameters,
905 token_distribution_schedule,
906 objects: Default::default(),
907 validators: committee,
908 signatures,
909 built_genesis: None, migration_objects: Default::default(),
911 genesis_stake: Default::default(),
912 migration_sources,
913 migration_tx_data,
914 delegation,
915 };
916
917 let unsigned_genesis_file = path.join(GENESIS_BUILDER_UNSIGNED_GENESIS_FILE);
918 if unsigned_genesis_file.exists() {
919 let reader = BufReader::new(File::open(unsigned_genesis_file)?);
920 let loaded_genesis: UnsignedGenesis =
921 tokio::task::spawn_blocking(move || bcs::from_reader(reader)).await??;
922
923 assert!(
926 builder.token_distribution_schedule.is_some(),
927 "If a built genesis is present, then there must also be a token-distribution-schedule present"
928 );
929
930 builder = tokio::task::spawn_blocking(move || {
932 builder.get_or_build_unsigned_genesis();
933 builder
934 })
935 .await?;
936 loaded_genesis.checkpoint_contents.digest(); assert!(
938 *builder.get_or_build_unsigned_genesis() == loaded_genesis,
939 "loaded genesis does not match built genesis"
940 );
941
942 assert!(builder.unsigned_genesis_checkpoint().is_some());
944 }
945
946 Ok(builder)
947 }
948
949 pub fn save<P: AsRef<Path>>(self, path: P) -> anyhow::Result<(), anyhow::Error> {
950 let path = path.as_ref();
951 trace!("Writing Genesis Builder to {}", path.display());
952
953 fs::create_dir_all(path)?;
954
955 let parameters_file = path.join(GENESIS_BUILDER_PARAMETERS_FILE);
957 fs::write(parameters_file, serde_yaml::to_string(&self.parameters)?)?;
958
959 if let Some(token_distribution_schedule) = &self.token_distribution_schedule {
960 token_distribution_schedule.to_csv(fs::File::create(
961 path.join(GENESIS_BUILDER_TOKEN_DISTRIBUTION_SCHEDULE_FILE),
962 )?)?;
963 }
964
965 let signature_dir = path.join(GENESIS_BUILDER_SIGNATURE_DIR);
967 std::fs::create_dir_all(&signature_dir)?;
968 for (pubkey, sigs) in self.signatures {
969 let name = self.validators.get(&pubkey).unwrap().info.name();
970 fs::write(signature_dir.join(name), &bcs::to_bytes(&sigs)?)?;
971 }
972
973 let committee_dir = path.join(GENESIS_BUILDER_COMMITTEE_DIR);
975 fs::create_dir_all(&committee_dir)?;
976
977 for (_pubkey, validator) in self.validators {
978 fs::write(
979 committee_dir.join(validator.info.name()),
980 &serde_yaml::to_string(&validator)?,
981 )?;
982 }
983
984 if let Some(genesis) = &self.built_genesis {
985 let mut write = BufWriter::new(File::create(
986 path.join(GENESIS_BUILDER_UNSIGNED_GENESIS_FILE),
987 )?);
988 bcs::serialize_into(&mut write, &genesis)?;
989 }
990
991 if !self.migration_sources.is_empty() {
992 let file = path.join(GENESIS_BUILDER_MIGRATION_SOURCES_FILE);
993 fs::write(file, serde_json::to_string(&self.migration_sources)?)?;
994
995 let file = path.join(IOTA_GENESIS_MIGRATION_TX_DATA_FILENAME);
997 self.migration_tx_data
998 .expect("migration data should exist")
999 .save(file)?;
1000 }
1001
1002 if let Some(delegation) = &self.delegation {
1003 match delegation {
1004 GenesisDelegation::OneToAll(delegator) => {
1005 let file = path.join(GENESIS_BUILDER_DELEGATOR_FILE);
1007 let delegator_json = serde_json::to_string(delegator)?;
1008 fs::write(file, delegator_json)?;
1009 }
1010 GenesisDelegation::ManyToMany(delegator_map) => {
1011 delegator_map.to_csv(fs::File::create(
1013 path.join(GENESIS_BUILDER_DELEGATOR_MAP_FILE),
1014 )?)?;
1015 }
1016 }
1017 }
1018
1019 Ok(())
1020 }
1021}
1022
1023fn create_genesis_context(
1027 epoch_data: &EpochData,
1028 genesis_chain_parameters: &GenesisChainParameters,
1029 genesis_validators: &[GenesisValidatorMetadata],
1030 token_distribution_schedule: &TokenDistributionSchedule,
1031 system_packages: &[SystemPackage],
1032) -> TxContext {
1033 let mut hasher = DefaultHash::default();
1034 hasher.update(b"iota-genesis");
1035 hasher.update(bcs::to_bytes(genesis_chain_parameters).unwrap());
1036 hasher.update(bcs::to_bytes(genesis_validators).unwrap());
1037 hasher.update(bcs::to_bytes(token_distribution_schedule).unwrap());
1038 for system_package in system_packages {
1039 hasher.update(bcs::to_bytes(&system_package.bytes).unwrap());
1040 }
1041
1042 let hash = hasher.finalize();
1043 let genesis_transaction_digest = TransactionDigest::new(hash.into());
1044
1045 TxContext::new(
1046 &IotaAddress::default(),
1047 &genesis_transaction_digest,
1048 epoch_data,
1049 )
1050}
1051
1052fn build_unsigned_genesis_data<'info>(
1053 parameters: &GenesisCeremonyParameters,
1054 token_distribution_schedule: &TokenDistributionSchedule,
1055 validators: impl Iterator<Item = &'info GenesisValidatorInfo>,
1056 objects: Vec<Object>,
1057 genesis_stake: &mut GenesisStake,
1058 migration_objects: &mut MigrationObjects,
1059) -> (UnsignedGenesis, MigrationTxData) {
1060 if !parameters.allow_insertion_of_extra_objects && !objects.is_empty() {
1061 panic!(
1062 "insertion of extra objects at genesis time is prohibited due to 'allow_insertion_of_extra_objects' parameter"
1063 );
1064 }
1065
1066 let genesis_chain_parameters = parameters.to_genesis_chain_parameters();
1067 let genesis_validators = validators
1068 .cloned()
1069 .map(GenesisValidatorMetadata::from)
1070 .collect::<Vec<_>>();
1071
1072 let epoch_data = EpochData::new_genesis(genesis_chain_parameters.chain_start_timestamp_ms);
1073
1074 let mut system_packages =
1078 iota_framework_snapshot::load_bytecode_snapshot(parameters.protocol_version.as_u64())
1079 .unwrap_or_else(|_| BuiltInFramework::iter_system_packages().cloned().collect());
1080
1081 update_system_packages_from_objects(&mut system_packages, &objects);
1085
1086 let mut genesis_ctx = create_genesis_context(
1087 &epoch_data,
1088 &genesis_chain_parameters,
1089 &genesis_validators,
1090 token_distribution_schedule,
1091 &system_packages,
1092 );
1093
1094 let registry = prometheus::Registry::new();
1096 let metrics = Arc::new(LimitsMetrics::new(®istry));
1097 let mut txs_data: TransactionsData = BTreeMap::new();
1098 let protocol_config = get_genesis_protocol_config(parameters.protocol_version);
1099
1100 let (genesis_objects, events) = create_genesis_objects(
1103 &mut genesis_ctx,
1104 objects,
1105 &genesis_validators,
1106 &genesis_chain_parameters,
1107 token_distribution_schedule,
1108 system_packages,
1109 metrics.clone(),
1110 );
1111
1112 if !genesis_stake.is_empty() {
1115 let migration_objects = destroy_staked_migration_objects(
1124 &mut genesis_ctx,
1125 migration_objects.take_objects(),
1126 &genesis_objects,
1127 &genesis_chain_parameters,
1128 genesis_stake,
1129 metrics.clone(),
1130 );
1131 txs_data = create_migration_tx_data(
1134 migration_objects,
1135 &protocol_config,
1136 metrics.clone(),
1137 &epoch_data,
1138 );
1139 }
1140
1141 let (genesis_transaction, genesis_effects, genesis_events, genesis_objects) =
1143 create_genesis_transaction(
1144 genesis_objects,
1145 events,
1146 &protocol_config,
1147 metrics,
1148 &epoch_data,
1149 );
1150
1151 let (checkpoint, checkpoint_contents) = create_genesis_checkpoint(
1154 &protocol_config,
1155 parameters,
1156 &genesis_transaction,
1157 &genesis_effects,
1158 &txs_data,
1159 );
1160
1161 (
1162 UnsignedGenesis {
1163 checkpoint,
1164 checkpoint_contents,
1165 transaction: genesis_transaction,
1166 effects: genesis_effects,
1167 events: genesis_events,
1168 objects: genesis_objects,
1169 },
1170 MigrationTxData::new(txs_data),
1172 )
1173}
1174
1175fn create_migration_tx_data(
1180 migration_objects: Vec<Object>,
1181 protocol_config: &ProtocolConfig,
1182 metrics: Arc<LimitsMetrics>,
1183 epoch_data: &EpochData,
1184) -> TransactionsData {
1185 let mut txs_data = TransactionsData::new();
1186 let migration_tx_max_amount = protocol_config
1187 .max_transactions_per_checkpoint_as_option()
1188 .unwrap_or(MAX_AMOUNT_OF_TX_PER_CHECKPOINT)
1189 - 1;
1190 let chunk_size = migration_objects.len() / (migration_tx_max_amount as usize) + 1;
1191
1192 for objects_per_chunk in migration_objects.chunks(chunk_size) {
1193 let (migration_transaction, migration_effects, migration_events, _) =
1194 create_genesis_transaction(
1195 objects_per_chunk.to_vec(),
1196 vec![],
1197 protocol_config,
1198 metrics.clone(),
1199 epoch_data,
1200 );
1201
1202 txs_data.insert(
1203 *migration_transaction.digest(),
1204 (migration_transaction, migration_effects, migration_events),
1205 );
1206 }
1207
1208 txs_data
1209}
1210
1211fn update_system_packages_from_objects(
1224 system_packages: &mut Vec<SystemPackage>,
1225 objects: &[Object],
1226) {
1227 let system_package_overrides: BTreeMap<ObjectID, Vec<Vec<u8>>> = objects
1229 .iter()
1230 .filter_map(|obj| {
1231 let pkg = obj.data.try_as_package()?;
1232 is_system_package(pkg.id()).then(|| {
1233 (
1234 pkg.id(),
1235 pkg.serialized_module_map().values().cloned().collect(),
1236 )
1237 })
1238 })
1239 .collect();
1240
1241 for package in system_packages {
1244 if let Some(overrides) = system_package_overrides.get(&package.id).cloned() {
1245 package.bytes = overrides;
1246 }
1247 }
1248}
1249
1250fn create_genesis_checkpoint(
1251 protocol_config: &ProtocolConfig,
1252 parameters: &GenesisCeremonyParameters,
1253 system_genesis_transaction: &Transaction,
1254 system_genesis_tx_effects: &TransactionEffects,
1255 migration_tx_data: &TransactionsData,
1256) -> (CheckpointSummary, CheckpointContents) {
1257 let genesis_execution_digests = ExecutionDigests {
1258 transaction: *system_genesis_transaction.digest(),
1259 effects: system_genesis_tx_effects.digest(),
1260 };
1261
1262 let mut execution_digests = vec![genesis_execution_digests];
1263
1264 for (_, effects, _) in migration_tx_data.values() {
1265 execution_digests.push(effects.execution_digests());
1266 }
1267
1268 let execution_digests_len = execution_digests.len();
1269
1270 let contents = CheckpointContents::new_with_digests_and_signatures(
1271 execution_digests,
1272 vec![vec![]; execution_digests_len],
1273 );
1274 let version_specific_data =
1275 match protocol_config.checkpoint_summary_version_specific_data_as_option() {
1276 None | Some(0) => Vec::new(),
1277 Some(1) => bcs::to_bytes(&CheckpointVersionSpecificData::V1(
1278 CheckpointVersionSpecificDataV1::default(),
1279 ))
1280 .unwrap(),
1281 _ => unimplemented!("unrecognized version_specific_data version for CheckpointSummary"),
1282 };
1283 let checkpoint = CheckpointSummary {
1284 epoch: 0,
1285 sequence_number: 0,
1286 network_total_transactions: contents.size().try_into().unwrap(),
1287 content_digest: *contents.digest(),
1288 previous_digest: None,
1289 epoch_rolling_gas_cost_summary: Default::default(),
1290 end_of_epoch_data: None,
1291 timestamp_ms: parameters.chain_start_timestamp_ms,
1292 version_specific_data,
1293 checkpoint_commitments: Default::default(),
1294 };
1295
1296 (checkpoint, contents)
1297}
1298
1299fn create_genesis_transaction(
1300 objects: Vec<Object>,
1301 events: Vec<Event>,
1302 protocol_config: &ProtocolConfig,
1303 metrics: Arc<LimitsMetrics>,
1304 epoch_data: &EpochData,
1305) -> (
1306 Transaction,
1307 TransactionEffects,
1308 TransactionEvents,
1309 Vec<Object>,
1310) {
1311 let genesis_transaction = {
1312 let genesis_objects = objects
1313 .into_iter()
1314 .map(|mut object| {
1315 if let Some(o) = object.data.try_as_move_mut() {
1316 o.decrement_version_to(SequenceNumber::MIN);
1317 }
1318
1319 if let Owner::Shared {
1320 initial_shared_version,
1321 } = &mut object.owner
1322 {
1323 *initial_shared_version = SequenceNumber::MIN;
1324 }
1325
1326 let object = object.into_inner();
1327 iota_types::transaction::GenesisObject::RawObject {
1328 data: object.data,
1329 owner: object.owner,
1330 }
1331 })
1332 .collect();
1333
1334 iota_types::transaction::VerifiedTransaction::new_genesis_transaction(
1335 genesis_objects,
1336 events,
1337 )
1338 .into_inner()
1339 };
1340
1341 let (effects, events, objects) =
1343 execute_genesis_transaction(epoch_data, protocol_config, metrics, &genesis_transaction);
1344
1345 (genesis_transaction, effects, events, objects)
1346}
1347
1348fn create_genesis_objects(
1349 genesis_ctx: &mut TxContext,
1350 input_objects: Vec<Object>,
1351 validators: &[GenesisValidatorMetadata],
1352 parameters: &GenesisChainParameters,
1353 token_distribution_schedule: &TokenDistributionSchedule,
1354 system_packages: Vec<SystemPackage>,
1355 metrics: Arc<LimitsMetrics>,
1356) -> (Vec<Object>, Vec<Event>) {
1357 let mut store = InMemoryStorage::new(Vec::new());
1358 let mut events = Vec::new();
1359 let protocol_config = ProtocolConfig::get_for_version(
1363 ProtocolVersion::new(parameters.protocol_version),
1364 Chain::Unknown,
1365 );
1366
1367 let silent = true;
1368 let executor = iota_execution::executor(&protocol_config, silent, None)
1369 .expect("Creating an executor should not fail here");
1370
1371 for system_package in system_packages.into_iter() {
1372 let tx_events = process_package(
1373 &mut store,
1374 executor.as_ref(),
1375 genesis_ctx,
1376 &system_package.modules(),
1377 system_package.dependencies,
1378 &protocol_config,
1379 metrics.clone(),
1380 )
1381 .expect("Processing a package should not fail here");
1382
1383 events.extend(tx_events.data.into_iter());
1384 }
1385
1386 for object in input_objects {
1387 store.insert_object(object);
1388 }
1389
1390 generate_genesis_system_object(
1391 &mut store,
1392 executor.as_ref(),
1393 validators,
1394 genesis_ctx,
1395 parameters,
1396 token_distribution_schedule,
1397 metrics,
1398 )
1399 .expect("Genesis creation should not fail here");
1400
1401 (store.into_inner().into_values().collect(), events)
1402}
1403
1404pub(crate) fn process_package(
1405 store: &mut InMemoryStorage,
1406 executor: &dyn Executor,
1407 ctx: &mut TxContext,
1408 modules: &[CompiledModule],
1409 dependencies: Vec<ObjectID>,
1410 protocol_config: &ProtocolConfig,
1411 metrics: Arc<LimitsMetrics>,
1412) -> anyhow::Result<TransactionEvents> {
1413 let dependency_objects = store.get_objects(&dependencies);
1414 #[cfg(debug_assertions)]
1420 {
1421 use std::collections::HashSet;
1422
1423 use move_core_types::account_address::AccountAddress;
1424
1425 let to_be_published_addresses: HashSet<_> = modules
1426 .iter()
1427 .map(|module| *module.self_id().address())
1428 .collect();
1429 assert!(
1430 dependencies
1432 .iter()
1433 .zip(dependency_objects.iter())
1434 .all(|(dependency, obj_opt)| obj_opt.is_some()
1435 || to_be_published_addresses.contains(&AccountAddress::from(*dependency)))
1436 );
1437 }
1438 let loaded_dependencies: Vec<_> = dependencies
1439 .iter()
1440 .zip(dependency_objects)
1441 .filter_map(|(dependency, object)| {
1442 Some(ObjectReadResult::new(
1443 InputObjectKind::MovePackage(*dependency),
1444 object?.clone().into(),
1445 ))
1446 })
1447 .collect();
1448
1449 let module_bytes = modules
1450 .iter()
1451 .map(|m| {
1452 let mut buf = vec![];
1453 m.serialize_with_version(m.version, &mut buf).unwrap();
1454 buf
1455 })
1456 .collect();
1457 let pt = {
1458 let mut builder = ProgrammableTransactionBuilder::new();
1459 builder.command(Command::Publish(module_bytes, dependencies));
1461 builder.finish()
1462 };
1463 let InnerTemporaryStore {
1464 written, events, ..
1465 } = executor.update_genesis_state(
1466 &*store,
1467 protocol_config,
1468 metrics,
1469 ctx,
1470 CheckedInputObjects::new_for_genesis(loaded_dependencies),
1471 pt,
1472 )?;
1473
1474 store.finish(written);
1475
1476 Ok(events)
1477}
1478
1479pub fn generate_genesis_system_object(
1480 store: &mut InMemoryStorage,
1481 executor: &dyn Executor,
1482 genesis_validators: &[GenesisValidatorMetadata],
1483 genesis_ctx: &mut TxContext,
1484 genesis_chain_parameters: &GenesisChainParameters,
1485 token_distribution_schedule: &TokenDistributionSchedule,
1486 metrics: Arc<LimitsMetrics>,
1487) -> anyhow::Result<()> {
1488 let protocol_config = ProtocolConfig::get_for_version(
1489 ProtocolVersion::new(genesis_chain_parameters.protocol_version),
1490 ChainIdentifier::default().chain(),
1491 );
1492
1493 let pt = {
1494 let mut builder = ProgrammableTransactionBuilder::new();
1495 let iota_system_state_uid = builder.programmable_move_call(
1497 IOTA_FRAMEWORK_PACKAGE_ID,
1498 ident_str!("object").to_owned(),
1499 ident_str!("iota_system_state").to_owned(),
1500 vec![],
1501 vec![],
1502 );
1503
1504 builder.move_call(
1506 IOTA_FRAMEWORK_PACKAGE_ID,
1507 ident_str!("clock").to_owned(),
1508 ident_str!("create").to_owned(),
1509 vec![],
1510 vec![],
1511 )?;
1512
1513 if protocol_config.create_authenticator_state_in_genesis() {
1516 builder.move_call(
1517 IOTA_FRAMEWORK_PACKAGE_ID,
1518 ident_str!("authenticator_state").to_owned(),
1519 ident_str!("create").to_owned(),
1520 vec![],
1521 vec![],
1522 )?;
1523 }
1524
1525 builder.move_call(
1527 IOTA_FRAMEWORK_PACKAGE_ID,
1528 RANDOMNESS_MODULE_NAME.to_owned(),
1529 RANDOMNESS_STATE_CREATE_FUNCTION_NAME.to_owned(),
1530 vec![],
1531 vec![],
1532 )?;
1533
1534 builder.move_call(
1536 IOTA_FRAMEWORK_PACKAGE_ID,
1537 DENY_LIST_MODULE.to_owned(),
1538 DENY_LIST_CREATE_FUNC.to_owned(),
1539 vec![],
1540 vec![],
1541 )?;
1542
1543 if protocol_config.enable_bridge() {
1544 let bridge_uid = builder
1545 .input(CallArg::Pure(
1546 UID::new(IOTA_BRIDGE_OBJECT_ID).to_bcs_bytes(),
1547 ))
1548 .unwrap();
1549 let bridge_chain_id = builder.pure(BridgeChainId::IotaCustom).unwrap();
1552 builder.programmable_move_call(
1553 BRIDGE_ADDRESS.into(),
1554 BRIDGE_MODULE_NAME.to_owned(),
1555 BRIDGE_CREATE_FUNCTION_NAME.to_owned(),
1556 vec![],
1557 vec![bridge_uid, bridge_chain_id],
1558 );
1559 }
1560
1561 let iota_treasury_cap = builder.programmable_move_call(
1563 IOTA_FRAMEWORK_PACKAGE_ID,
1564 ident_str!("iota").to_owned(),
1565 ident_str!("new").to_owned(),
1566 vec![],
1567 vec![],
1568 );
1569
1570 let pre_minted_supply_amount = builder
1571 .pure(token_distribution_schedule.pre_minted_supply)
1572 .expect("serialization of u64 should succeed");
1573 let pre_minted_supply = builder.programmable_move_call(
1574 IOTA_FRAMEWORK_PACKAGE_ID,
1575 ident_str!("iota").to_owned(),
1576 ident_str!("mint_balance").to_owned(),
1577 vec![],
1578 vec![iota_treasury_cap, pre_minted_supply_amount],
1579 );
1580
1581 builder.programmable_move_call(
1582 IOTA_FRAMEWORK_PACKAGE_ID,
1583 BALANCE_MODULE_NAME.to_owned(),
1584 ident_str!("destroy_genesis_supply").to_owned(),
1585 vec![GAS::type_tag()],
1586 vec![pre_minted_supply],
1587 );
1588
1589 let system_admin_cap = builder.programmable_move_call(
1591 IOTA_FRAMEWORK_PACKAGE_ID,
1592 IOTA_SYSTEM_ADMIN_CAP_MODULE_NAME.to_owned(),
1593 ident_str!("new_system_admin_cap").to_owned(),
1594 vec![],
1595 vec![],
1596 );
1597
1598 let mut arguments = vec![iota_system_state_uid, iota_treasury_cap];
1602 let mut call_arg_arguments = vec![
1603 CallArg::Pure(bcs::to_bytes(&genesis_chain_parameters).unwrap()),
1604 CallArg::Pure(bcs::to_bytes(&genesis_validators).unwrap()),
1605 CallArg::Pure(bcs::to_bytes(&token_distribution_schedule).unwrap()),
1606 CallArg::Pure(bcs::to_bytes(&Some(STARDUST_UPGRADE_LABEL_VALUE)).unwrap()),
1607 ]
1608 .into_iter()
1609 .map(|a| builder.input(a))
1610 .collect::<anyhow::Result<_, _>>()?;
1611 arguments.append(&mut call_arg_arguments);
1612 arguments.push(system_admin_cap);
1613 builder.programmable_move_call(
1614 IOTA_SYSTEM_ADDRESS.into(),
1615 ident_str!("genesis").to_owned(),
1616 ident_str!("create").to_owned(),
1617 vec![],
1618 arguments,
1619 );
1620
1621 builder.finish()
1622 };
1623
1624 let InnerTemporaryStore { mut written, .. } = executor.update_genesis_state(
1625 &*store,
1626 &protocol_config,
1627 metrics,
1628 genesis_ctx,
1629 CheckedInputObjects::new_for_genesis(vec![]),
1630 pt,
1631 )?;
1632
1633 {
1635 let object = written.get_mut(&iota_types::IOTA_CLOCK_OBJECT_ID).unwrap();
1636 object
1637 .data
1638 .try_as_move_mut()
1639 .unwrap()
1640 .set_clock_timestamp_ms_unsafe(genesis_chain_parameters.chain_start_timestamp_ms);
1641 }
1642
1643 store.finish(written);
1644
1645 Ok(())
1646}
1647
1648fn destroy_staked_migration_objects(
1653 genesis_ctx: &mut TxContext,
1654 migration_objects: Vec<Object>,
1655 genesis_objects: &[Object],
1656 parameters: &GenesisChainParameters,
1657 genesis_stake: &mut GenesisStake,
1658 metrics: Arc<LimitsMetrics>,
1659) -> Vec<Object> {
1660 let mut store = InMemoryStorage::new(genesis_objects.to_owned());
1662 let protocol_config = ProtocolConfig::get_for_version(
1663 ProtocolVersion::new(parameters.protocol_version),
1664 Chain::Unknown,
1665 );
1666 let silent = true;
1667 let executor = iota_execution::executor(&protocol_config, silent, None)
1668 .expect("Creating an executor should not fail here");
1669
1670 for object in migration_objects {
1671 store.insert_object(object);
1672 }
1673
1674 split_timelocks(
1677 &mut store,
1678 executor.as_ref(),
1679 genesis_ctx,
1680 parameters,
1681 &genesis_stake.take_timelocks_to_split(),
1682 metrics.clone(),
1683 )
1684 .expect("Splitting timelocks should not fail here");
1685
1686 let mut intermediate_store = store.into_inner();
1688
1689 for (id, _, _) in genesis_stake.take_gas_coins_to_destroy() {
1695 intermediate_store.remove(&id);
1696 }
1697 for (id, _, _) in genesis_stake.take_timelocks_to_destroy() {
1698 intermediate_store.remove(&id);
1699 }
1700
1701 for genesis_object in genesis_objects.iter() {
1703 intermediate_store.remove(&genesis_object.id());
1704 }
1705
1706 intermediate_store.into_values().collect()
1707}
1708
1709pub fn split_timelocks(
1711 store: &mut InMemoryStorage,
1712 executor: &dyn Executor,
1713 genesis_ctx: &mut TxContext,
1714 genesis_chain_parameters: &GenesisChainParameters,
1715 timelocks_to_split: &[(ObjectRef, u64, IotaAddress)],
1716 metrics: Arc<LimitsMetrics>,
1717) -> anyhow::Result<()> {
1718 let protocol_config = ProtocolConfig::get_for_version(
1719 ProtocolVersion::new(genesis_chain_parameters.protocol_version),
1720 ChainIdentifier::default().chain(),
1721 );
1722
1723 let mut timelock_split_input_objects: Vec<ObjectReadResult> = vec![];
1728 let pt = {
1729 let mut builder = ProgrammableTransactionBuilder::new();
1730 for (timelock, surplus_amount, recipient) in timelocks_to_split {
1731 timelock_split_input_objects.push(ObjectReadResult::new(
1732 InputObjectKind::ImmOrOwnedMoveObject(*timelock),
1733 store.get_object(&timelock.0).unwrap().clone().into(),
1734 ));
1735 let arguments = vec![
1736 builder.obj(ObjectArg::ImmOrOwnedObject(*timelock))?,
1737 builder.pure(surplus_amount)?,
1738 ];
1739 let surplus_timelock = builder.programmable_move_call(
1740 IOTA_FRAMEWORK_PACKAGE_ID,
1741 ident_str!("timelock").to_owned(),
1742 ident_str!("split").to_owned(),
1743 vec![GAS::type_tag()],
1744 arguments,
1745 );
1746 let arguments = vec![surplus_timelock, builder.pure(*recipient)?];
1747 builder.programmable_move_call(
1748 IOTA_FRAMEWORK_PACKAGE_ID,
1749 ident_str!("timelock").to_owned(),
1750 ident_str!("transfer").to_owned(),
1751 vec![Balance::type_tag(GAS::type_tag())],
1752 arguments,
1753 );
1754 }
1755 builder.finish()
1756 };
1757
1758 let InnerTemporaryStore { written, .. } = executor.update_genesis_state(
1762 &*store,
1763 &protocol_config,
1764 metrics,
1765 genesis_ctx,
1766 CheckedInputObjects::new_for_genesis(timelock_split_input_objects),
1767 pt,
1768 )?;
1769
1770 store.finish(written);
1772
1773 for ((id, _, _), _, _) in timelocks_to_split {
1776 store.remove_object(*id);
1777 }
1778
1779 Ok(())
1780}
1781
1782#[derive(Clone, Debug, Deserialize, Serialize)]
1783pub enum SnapshotSource {
1784 Local(PathBuf),
1786 S3(SnapshotUrl),
1788}
1789
1790impl SnapshotSource {
1791 pub fn to_reader(&self) -> anyhow::Result<Box<dyn Read>> {
1793 Ok(match self {
1794 SnapshotSource::Local(path) => Box::new(BufReader::new(File::open(path)?)),
1795 SnapshotSource::S3(snapshot_url) => Box::new(snapshot_url.to_reader()?),
1796 })
1797 }
1798}
1799
1800impl From<SnapshotUrl> for SnapshotSource {
1801 fn from(value: SnapshotUrl) -> Self {
1802 Self::S3(value)
1803 }
1804}
1805
1806#[derive(Debug, Clone, Deserialize, Serialize)]
1808pub enum SnapshotUrl {
1809 Iota,
1810 Test(Url),
1812}
1813
1814impl std::fmt::Display for SnapshotUrl {
1815 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1816 match self {
1817 SnapshotUrl::Iota => "iota".fmt(f),
1818 SnapshotUrl::Test(url) => url.as_str().fmt(f),
1819 }
1820 }
1821}
1822
1823impl FromStr for SnapshotUrl {
1824 type Err = anyhow::Error;
1825
1826 fn from_str(s: &str) -> Result<Self, Self::Err> {
1827 if let Ok(url) = reqwest::Url::from_str(s) {
1828 return Ok(Self::Test(url));
1829 }
1830 Ok(match s.to_lowercase().as_str() {
1831 "iota" => Self::Iota,
1832 e => bail!("unsupported snapshot url: {e}"),
1833 })
1834 }
1835}
1836
1837impl SnapshotUrl {
1838 pub fn to_url(&self) -> Url {
1840 match self {
1841 Self::Iota => Url::parse(IOTA_OBJECT_SNAPSHOT_URL).expect("should be valid URL"),
1842 Self::Test(url) => url.clone(),
1843 }
1844 }
1845
1846 pub fn to_reader(&self) -> anyhow::Result<impl Read> {
1848 Ok(GzDecoder::new(BufReader::new(reqwest::blocking::get(
1849 self.to_url(),
1850 )?)))
1851 }
1852}
1853
1854#[cfg(test)]
1855mod test {
1856 use fastcrypto::traits::KeyPair;
1857 use iota_config::{
1858 genesis::*,
1859 local_ip_utils,
1860 node::{DEFAULT_COMMISSION_RATE, DEFAULT_VALIDATOR_GAS_PRICE},
1861 };
1862 use iota_types::{
1863 base_types::IotaAddress,
1864 crypto::{
1865 AccountKeyPair, AuthorityKeyPair, NetworkKeyPair, generate_proof_of_possession,
1866 get_key_pair_from_rng,
1867 },
1868 };
1869
1870 use crate::{Builder, validator_info::ValidatorInfo};
1871
1872 #[test]
1873 fn allocation_csv() {
1874 let schedule = TokenDistributionSchedule::new_for_validators_with_default_allocation([
1875 IotaAddress::random_for_testing_only(),
1876 IotaAddress::random_for_testing_only(),
1877 ]);
1878 let mut output = Vec::new();
1879
1880 schedule.to_csv(&mut output).unwrap();
1881
1882 let parsed_schedule = TokenDistributionSchedule::from_csv(output.as_slice()).unwrap();
1883
1884 assert_eq!(schedule, parsed_schedule);
1885
1886 std::io::Write::write_all(&mut std::io::stdout(), &output).unwrap();
1887 }
1888
1889 #[tokio::test]
1890 #[cfg_attr(msim, ignore)]
1891 async fn ceremony() {
1892 let dir = tempfile::TempDir::new().unwrap();
1893
1894 let authority_key: AuthorityKeyPair = get_key_pair_from_rng(&mut rand::rngs::OsRng).1;
1895 let protocol_key: NetworkKeyPair = get_key_pair_from_rng(&mut rand::rngs::OsRng).1;
1896 let account_key: AccountKeyPair = get_key_pair_from_rng(&mut rand::rngs::OsRng).1;
1897 let network_key: NetworkKeyPair = get_key_pair_from_rng(&mut rand::rngs::OsRng).1;
1898 let validator = ValidatorInfo {
1899 name: "0".into(),
1900 authority_key: authority_key.public().into(),
1901 protocol_key: protocol_key.public().clone(),
1902 account_address: IotaAddress::from(account_key.public()),
1903 network_key: network_key.public().clone(),
1904 gas_price: DEFAULT_VALIDATOR_GAS_PRICE,
1905 commission_rate: DEFAULT_COMMISSION_RATE,
1906 network_address: local_ip_utils::new_local_tcp_address_for_testing(),
1907 p2p_address: local_ip_utils::new_local_udp_address_for_testing(),
1908 primary_address: local_ip_utils::new_local_udp_address_for_testing(),
1909 description: String::new(),
1910 image_url: String::new(),
1911 project_url: String::new(),
1912 };
1913 let pop = generate_proof_of_possession(&authority_key, account_key.public().into());
1914 let mut builder = Builder::new().add_validator(validator, pop);
1915
1916 let genesis = builder.get_or_build_unsigned_genesis();
1917 for object in genesis.objects() {
1918 println!("ObjectID: {} Type: {:?}", object.id(), object.type_());
1919 }
1920 builder.save(dir.path()).unwrap();
1921 Builder::load(dir.path()).await.unwrap();
1922 }
1923}