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