1use anyhow::{Context, bail};
5use camino::Utf8Path;
6use fastcrypto::hash::HashFunction;
7use fastcrypto::traits::KeyPair;
8use move_core_types::ident_str;
9use mysten_common::ZipDebugEqIteratorExt;
10use shared_crypto::intent::{Intent, IntentMessage, IntentScope};
11use std::collections::BTreeMap;
12use std::fs;
13use std::path::Path;
14use std::sync::Arc;
15use sui_config::genesis::{
16 Genesis, GenesisCeremonyParameters, GenesisChainParameters, TokenDistributionSchedule,
17 UnsignedGenesis,
18};
19use sui_execution::{self, Executor};
20use sui_framework::{BuiltInFramework, SystemPackage};
21use sui_protocol_config::{Chain, ProtocolConfig, ProtocolVersion};
22use sui_types::base_types::{ExecutionDigests, ObjectID, SequenceNumber, TransactionDigest};
23use sui_types::bridge::{BRIDGE_CREATE_FUNCTION_NAME, BRIDGE_MODULE_NAME, BridgeChainId};
24use sui_types::committee::Committee;
25use sui_types::crypto::{
26 AuthorityKeyPair, AuthorityPublicKeyBytes, AuthoritySignInfo, AuthoritySignInfoTrait,
27 AuthoritySignature, DefaultHash, SuiAuthoritySignature,
28};
29use sui_types::deny_list_v1::{DENY_LIST_CREATE_FUNC, DENY_LIST_MODULE};
30use sui_types::digests::ChainIdentifier;
31use sui_types::effects::{TransactionEffects, TransactionEffectsAPI, TransactionEvents};
32use sui_types::epoch_data::EpochData;
33use sui_types::execution_params::ExecutionOrEarlyError;
34use sui_types::gas::SuiGasStatus;
35use sui_types::gas_coin::GasCoin;
36use sui_types::governance::StakedSui;
37use sui_types::id::UID;
38use sui_types::in_memory_storage::InMemoryStorage;
39use sui_types::inner_temporary_store::InnerTemporaryStore;
40use sui_types::is_system_package;
41use sui_types::message_envelope::Message;
42use sui_types::messages_checkpoint::{
43 CertifiedCheckpointSummary, CheckpointContents, CheckpointSummary,
44 CheckpointVersionSpecificData, CheckpointVersionSpecificDataV1,
45};
46use sui_types::metrics::ExecutionMetrics;
47use sui_types::object::{Object, Owner};
48use sui_types::programmable_transaction_builder::ProgrammableTransactionBuilder;
49use sui_types::sui_system_state::{SuiSystemState, SuiSystemStateTrait, get_sui_system_state};
50use sui_types::transaction::{CallArg, CheckedInputObjects, Transaction};
51use sui_types::{BRIDGE_ADDRESS, SUI_BRIDGE_OBJECT_ID, SUI_FRAMEWORK_ADDRESS, SUI_SYSTEM_ADDRESS};
52use tracing::trace;
53use validator_info::{GenesisValidatorInfo, GenesisValidatorMetadata, ValidatorInfo};
54
55pub mod validator_info;
56
57const GENESIS_BUILDER_COMMITTEE_DIR: &str = "committee";
58const GENESIS_BUILDER_PARAMETERS_FILE: &str = "parameters";
59const GENESIS_BUILDER_TOKEN_DISTRIBUTION_SCHEDULE_FILE: &str = "token-distribution-schedule";
60const GENESIS_BUILDER_SIGNATURE_DIR: &str = "signatures";
61const GENESIS_BUILDER_UNSIGNED_GENESIS_FILE: &str = "unsigned-genesis";
62
63pub struct Builder {
64 parameters: GenesisCeremonyParameters,
65 token_distribution_schedule: Option<TokenDistributionSchedule>,
66 objects: BTreeMap<ObjectID, Object>,
67 validators: BTreeMap<AuthorityPublicKeyBytes, GenesisValidatorInfo>,
68 signatures: BTreeMap<AuthorityPublicKeyBytes, AuthoritySignInfo>,
70 built_genesis: Option<UnsignedGenesis>,
71}
72
73impl Default for Builder {
74 fn default() -> Self {
75 Self::new()
76 }
77}
78
79impl Builder {
80 pub fn new() -> Self {
81 Self {
82 parameters: Default::default(),
83 token_distribution_schedule: None,
84 objects: Default::default(),
85 validators: Default::default(),
86 signatures: Default::default(),
87 built_genesis: None,
88 }
89 }
90
91 pub fn with_parameters(mut self, parameters: GenesisCeremonyParameters) -> Self {
92 self.parameters = parameters;
93 self
94 }
95
96 pub fn with_token_distribution_schedule(
97 mut self,
98 token_distribution_schedule: TokenDistributionSchedule,
99 ) -> Self {
100 self.token_distribution_schedule = Some(token_distribution_schedule);
101 self
102 }
103
104 pub fn with_protocol_version(mut self, v: ProtocolVersion) -> Self {
105 self.parameters.protocol_version = v;
106 self
107 }
108
109 pub fn add_object(mut self, object: Object) -> Self {
110 self.objects.insert(object.id(), object);
111 self
112 }
113
114 pub fn add_objects(mut self, objects: Vec<Object>) -> Self {
115 for object in objects {
116 self.objects.insert(object.id(), object);
117 }
118 self
119 }
120
121 pub fn add_validator(
122 mut self,
123 validator: ValidatorInfo,
124 proof_of_possession: AuthoritySignature,
125 ) -> Self {
126 self.validators.insert(
127 validator.protocol_key(),
128 GenesisValidatorInfo {
129 info: validator,
130 proof_of_possession,
131 },
132 );
133 self
134 }
135
136 pub fn validators(&self) -> &BTreeMap<AuthorityPublicKeyBytes, GenesisValidatorInfo> {
137 &self.validators
138 }
139
140 pub fn add_validator_signature(mut self, keypair: &AuthorityKeyPair) -> Self {
141 let UnsignedGenesis { checkpoint, .. } = self.build_unsigned_genesis_checkpoint();
142
143 let name = keypair.public().into();
144 assert!(
145 self.validators.contains_key(&name),
146 "provided keypair does not correspond to a validator in the validator set"
147 );
148 let checkpoint_signature = {
149 let intent_msg = IntentMessage::new(
150 Intent::sui_app(IntentScope::CheckpointSummary),
151 checkpoint.clone(),
152 );
153 let signature = AuthoritySignature::new_secure(&intent_msg, &checkpoint.epoch, keypair);
154 AuthoritySignInfo {
155 epoch: checkpoint.epoch,
156 authority: name,
157 signature,
158 }
159 };
160
161 self.signatures.insert(name, checkpoint_signature);
162
163 self
164 }
165
166 pub fn unsigned_genesis_checkpoint(&self) -> Option<UnsignedGenesis> {
167 self.built_genesis.clone()
168 }
169
170 pub fn build_unsigned_genesis_checkpoint(&mut self) -> UnsignedGenesis {
171 if let Some(built_genesis) = &self.built_genesis {
172 return built_genesis.clone();
173 }
174
175 self.validate().unwrap();
177
178 let objects = self.objects.clone().into_values().collect::<Vec<_>>();
179 let validators = self.validators.clone().into_values().collect::<Vec<_>>();
180
181 let token_distribution_schedule =
182 if let Some(token_distribution_schedule) = &self.token_distribution_schedule {
183 token_distribution_schedule.clone()
184 } else {
185 TokenDistributionSchedule::new_for_validators_with_default_allocation(
186 validators.iter().map(|v| v.info.sui_address()),
187 )
188 };
189
190 self.built_genesis = Some(build_unsigned_genesis_data(
191 &self.parameters,
192 &token_distribution_schedule,
193 &validators,
194 &objects,
195 ));
196
197 self.token_distribution_schedule = Some(token_distribution_schedule);
198
199 self.built_genesis.clone().unwrap()
200 }
201
202 fn committee(objects: &[Object]) -> Committee {
203 let sui_system_object =
204 get_sui_system_state(&objects).expect("Sui System State object must always exist");
205 sui_system_object
206 .get_current_epoch_committee()
207 .committee()
208 .clone()
209 }
210
211 pub fn protocol_version(&self) -> ProtocolVersion {
212 self.parameters.protocol_version
213 }
214
215 pub fn build(mut self) -> Genesis {
216 let UnsignedGenesis {
217 checkpoint,
218 checkpoint_contents,
219 transaction,
220 effects,
221 events,
222 objects,
223 } = self.build_unsigned_genesis_checkpoint();
224
225 let committee = Self::committee(&objects);
226
227 let checkpoint = {
228 let signatures = self.signatures.clone().into_values().collect();
229
230 CertifiedCheckpointSummary::new(checkpoint, signatures, &committee).unwrap()
231 };
232
233 let genesis = Genesis::new(
234 checkpoint,
235 checkpoint_contents,
236 transaction,
237 effects,
238 events,
239 objects,
240 );
241
242 self.validate().unwrap();
244
245 genesis
246 }
247
248 pub fn validate(&self) -> anyhow::Result<(), anyhow::Error> {
251 self.validate_inputs()?;
252 self.validate_output();
253 Ok(())
254 }
255
256 fn validate_inputs(&self) -> anyhow::Result<(), anyhow::Error> {
258 if !self.parameters.allow_insertion_of_extra_objects && !self.objects.is_empty() {
259 bail!("extra objects are disallowed");
260 }
261
262 for validator in self.validators.values() {
263 validator.validate().with_context(|| {
264 format!(
265 "metadata for validator {} is invalid",
266 validator.info.name()
267 )
268 })?;
269 }
270
271 if let Some(token_distribution_schedule) = &self.token_distribution_schedule {
272 token_distribution_schedule.validate();
273 token_distribution_schedule.check_all_stake_operations_are_for_valid_validators(
274 self.validators.values().map(|v| v.info.sui_address()),
275 );
276 }
277
278 Ok(())
279 }
280
281 fn validate_output(&self) {
284 let Some(unsigned_genesis) = self.unsigned_genesis_checkpoint() else {
286 return;
287 };
288
289 let GenesisChainParameters {
290 protocol_version,
291 chain_start_timestamp_ms,
292 epoch_duration_ms,
293 stake_subsidy_start_epoch,
294 stake_subsidy_initial_distribution_amount,
295 stake_subsidy_period_length,
296 stake_subsidy_decrease_rate,
297 max_validator_count,
298 min_validator_joining_stake,
299 validator_low_stake_threshold,
300 validator_very_low_stake_threshold,
301 validator_low_stake_grace_period,
302 } = self.parameters.to_genesis_chain_parameters();
303
304 let system_state = match unsigned_genesis.sui_system_object() {
306 SuiSystemState::V1(inner) => inner,
307 SuiSystemState::V2(_) => unreachable!(),
308 #[cfg(msim)]
309 _ => {
310 return;
312 }
313 };
314
315 let protocol_config = get_genesis_protocol_config(ProtocolVersion::new(protocol_version));
316
317 if protocol_config.create_authenticator_state_in_genesis() {
318 let authenticator_state = unsigned_genesis.authenticator_state_object().unwrap();
319 assert!(authenticator_state.active_jwks.is_empty());
320 } else {
321 assert!(unsigned_genesis.authenticator_state_object().is_none());
322 }
323 assert_eq!(
324 protocol_config.random_beacon(),
325 unsigned_genesis.has_randomness_state_object()
326 );
327
328 assert_eq!(
329 protocol_config.enable_bridge(),
330 unsigned_genesis.has_bridge_object()
331 );
332
333 assert_eq!(
334 protocol_config.enable_coin_deny_list_v1(),
335 unsigned_genesis.coin_deny_list_state().is_some(),
336 );
337
338 assert_eq!(
339 self.validators.len(),
340 system_state.validators.active_validators.len()
341 );
342 let mut address_to_pool_id = BTreeMap::new();
343 for (validator, onchain_validator) in self
344 .validators
345 .values()
346 .zip_debug_eq(system_state.validators.active_validators.iter())
347 {
348 let metadata = onchain_validator.verified_metadata();
349
350 assert!(
352 address_to_pool_id
353 .insert(metadata.sui_address, onchain_validator.staking_pool.id)
354 .is_none()
355 );
356 assert_eq!(validator.info.sui_address(), metadata.sui_address);
357 assert_eq!(validator.info.protocol_key(), metadata.sui_pubkey_bytes());
358 assert_eq!(validator.info.network_key, metadata.network_pubkey);
359 assert_eq!(validator.info.worker_key, metadata.worker_pubkey);
360 assert_eq!(
361 validator.proof_of_possession.as_ref().to_vec(),
362 metadata.proof_of_possession_bytes
363 );
364 assert_eq!(validator.info.name(), &metadata.name);
365 assert_eq!(validator.info.description, metadata.description);
366 assert_eq!(validator.info.image_url, metadata.image_url);
367 assert_eq!(validator.info.project_url, metadata.project_url);
368 assert_eq!(validator.info.network_address(), &metadata.net_address);
369 assert_eq!(validator.info.p2p_address, metadata.p2p_address);
370 assert_eq!(
371 validator.info.narwhal_primary_address,
372 metadata.primary_address
373 );
374 assert_eq!(
375 validator.info.narwhal_worker_address,
376 metadata.worker_address
377 );
378
379 assert_eq!(validator.info.gas_price, onchain_validator.gas_price);
380 assert_eq!(
381 validator.info.commission_rate,
382 onchain_validator.commission_rate
383 );
384 }
385
386 assert_eq!(system_state.epoch, 0);
387 assert_eq!(system_state.protocol_version, protocol_version);
388 assert_eq!(system_state.storage_fund.non_refundable_balance.value(), 0);
389 assert_eq!(
390 system_state
391 .storage_fund
392 .total_object_storage_rebates
393 .value(),
394 0
395 );
396
397 assert_eq!(system_state.parameters.epoch_duration_ms, epoch_duration_ms);
398 assert_eq!(
399 system_state.parameters.stake_subsidy_start_epoch,
400 stake_subsidy_start_epoch,
401 );
402 assert_eq!(
403 system_state.parameters.max_validator_count,
404 max_validator_count,
405 );
406 assert_eq!(
407 system_state.parameters.min_validator_joining_stake,
408 min_validator_joining_stake,
409 );
410 assert_eq!(
411 system_state.parameters.validator_low_stake_threshold,
412 validator_low_stake_threshold,
413 );
414 assert_eq!(
415 system_state.parameters.validator_very_low_stake_threshold,
416 validator_very_low_stake_threshold,
417 );
418 assert_eq!(
419 system_state.parameters.validator_low_stake_grace_period,
420 validator_low_stake_grace_period,
421 );
422
423 assert_eq!(system_state.stake_subsidy.distribution_counter, 0);
424 assert_eq!(
425 system_state.stake_subsidy.current_distribution_amount,
426 stake_subsidy_initial_distribution_amount,
427 );
428 assert_eq!(
429 system_state.stake_subsidy.stake_subsidy_period_length,
430 stake_subsidy_period_length,
431 );
432 assert_eq!(
433 system_state.stake_subsidy.stake_subsidy_decrease_rate,
434 stake_subsidy_decrease_rate,
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 assert_eq!(
457 system_state.stake_subsidy.balance.value(),
458 token_distribution_schedule.stake_subsidy_fund_mist
459 );
460
461 let mut gas_objects: BTreeMap<ObjectID, (&Object, GasCoin)> = unsigned_genesis
462 .objects()
463 .iter()
464 .filter_map(|o| GasCoin::try_from(o).ok().map(|g| (o.id(), (o, g))))
465 .collect();
466 let mut staked_sui_objects: BTreeMap<ObjectID, (&Object, StakedSui)> = unsigned_genesis
467 .objects()
468 .iter()
469 .filter_map(|o| StakedSui::try_from(o).ok().map(|s| (o.id(), (o, s))))
470 .collect();
471
472 for allocation in token_distribution_schedule.allocations {
473 if let Some(staked_with_validator) = allocation.staked_with_validator {
474 let staking_pool_id = *address_to_pool_id
475 .get(&staked_with_validator)
476 .expect("staking pool should exist");
477 let staked_sui_object_id = staked_sui_objects
478 .iter()
479 .find(|(_k, (o, s))| {
480 let Owner::AddressOwner(owner) = &o.owner else {
481 panic!("gas object owner must be address owner");
482 };
483 *owner == allocation.recipient_address
484 && s.principal() == allocation.amount_mist
485 && s.pool_id() == staking_pool_id
486 })
487 .map(|(k, _)| *k)
488 .expect("all allocations should be present");
489 let staked_sui_object = staked_sui_objects.remove(&staked_sui_object_id).unwrap();
490 assert_eq!(
491 staked_sui_object.0.owner,
492 Owner::AddressOwner(allocation.recipient_address)
493 );
494 assert_eq!(staked_sui_object.1.principal(), allocation.amount_mist);
495 assert_eq!(staked_sui_object.1.pool_id(), staking_pool_id);
496 assert_eq!(staked_sui_object.1.activation_epoch(), 0);
497 } else {
498 let gas_object_id = gas_objects
499 .iter()
500 .find(|(_k, (o, g))| {
501 if let Owner::AddressOwner(owner) = &o.owner {
502 *owner == allocation.recipient_address
503 && g.value() == allocation.amount_mist
504 } else {
505 false
506 }
507 })
508 .map(|(k, _)| *k)
509 .expect("all allocations should be present");
510 let gas_object = gas_objects.remove(&gas_object_id).unwrap();
511 assert_eq!(
512 gas_object.0.owner,
513 Owner::AddressOwner(allocation.recipient_address)
514 );
515 assert_eq!(gas_object.1.value(), allocation.amount_mist,);
516 }
517 }
518
519 if !self.parameters.allow_insertion_of_extra_objects {
521 assert!(gas_objects.is_empty());
522 assert!(staked_sui_objects.is_empty());
523 }
524
525 let committee = system_state.get_current_epoch_committee();
526 for signature in self.signatures.values() {
527 if !self.validators.contains_key(&signature.authority) {
528 panic!("found signature for unknown validator: {:#?}", signature);
529 }
530
531 signature
532 .verify_secure(
533 unsigned_genesis.checkpoint(),
534 Intent::sui_app(IntentScope::CheckpointSummary),
535 committee.committee(),
536 )
537 .expect("signature should be valid");
538 }
539 }
540
541 pub fn load<P: AsRef<Path>>(path: P) -> anyhow::Result<Self, anyhow::Error> {
542 let path = path.as_ref();
543 let path: &Utf8Path = path.try_into()?;
544 trace!("Reading Genesis Builder from {}", path);
545
546 if !path.is_dir() {
547 bail!("path must be a directory");
548 }
549
550 let parameters_file = path.join(GENESIS_BUILDER_PARAMETERS_FILE);
552 let parameters = serde_yaml::from_slice(
553 &fs::read(parameters_file).context("unable to read genesis parameters file")?,
554 )
555 .context("unable to deserialize genesis parameters")?;
556
557 let token_distribution_schedule_file =
558 path.join(GENESIS_BUILDER_TOKEN_DISTRIBUTION_SCHEDULE_FILE);
559 let token_distribution_schedule = if token_distribution_schedule_file.exists() {
560 Some(TokenDistributionSchedule::from_csv(fs::File::open(
561 token_distribution_schedule_file,
562 )?)?)
563 } else {
564 None
565 };
566
567 let mut committee = BTreeMap::new();
569 for entry in path.join(GENESIS_BUILDER_COMMITTEE_DIR).read_dir_utf8()? {
570 let entry = entry?;
571 if entry.file_name().starts_with('.') {
572 continue;
573 }
574
575 let path = entry.path();
576 let validator_info_bytes = fs::read(path)?;
577 let validator_info: GenesisValidatorInfo =
578 serde_yaml::from_slice(&validator_info_bytes)
579 .with_context(|| format!("unable to load validator info for {path}"))?;
580 committee.insert(validator_info.info.protocol_key(), validator_info);
581 }
582
583 let mut signatures = BTreeMap::new();
585 for entry in path.join(GENESIS_BUILDER_SIGNATURE_DIR).read_dir_utf8()? {
586 let entry = entry?;
587 if entry.file_name().starts_with('.') {
588 continue;
589 }
590
591 let path = entry.path();
592 let signature_bytes = fs::read(path)?;
593 let sigs: AuthoritySignInfo = bcs::from_bytes(&signature_bytes)
594 .with_context(|| format!("unable to load validator signatrue for {path}"))?;
595 signatures.insert(sigs.authority, sigs);
596 }
597
598 let mut builder = Self {
599 parameters,
600 token_distribution_schedule,
601 objects: Default::default(),
602 validators: committee,
603 signatures,
604 built_genesis: None, };
606
607 let unsigned_genesis_file = path.join(GENESIS_BUILDER_UNSIGNED_GENESIS_FILE);
608 if unsigned_genesis_file.exists() {
609 let unsigned_genesis_bytes = fs::read(unsigned_genesis_file)?;
610 let loaded_genesis: UnsignedGenesis = bcs::from_bytes(&unsigned_genesis_bytes)?;
611
612 assert!(
615 builder.token_distribution_schedule.is_some(),
616 "If a built genesis is present, then there must also be a token-distribution-schedule present"
617 );
618
619 let built = builder.build_unsigned_genesis_checkpoint();
621 loaded_genesis.checkpoint_contents.digest(); assert_eq!(
623 built, loaded_genesis,
624 "loaded genesis does not match built genesis"
625 );
626
627 assert!(builder.unsigned_genesis_checkpoint().is_some());
629 }
630
631 Ok(builder)
632 }
633
634 pub fn save<P: AsRef<Path>>(self, path: P) -> anyhow::Result<(), anyhow::Error> {
635 let path = path.as_ref();
636 trace!("Writing Genesis Builder to {}", path.display());
637
638 fs::create_dir_all(path)?;
639
640 let parameters_file = path.join(GENESIS_BUILDER_PARAMETERS_FILE);
642 fs::write(parameters_file, serde_yaml::to_string(&self.parameters)?)?;
643
644 if let Some(token_distribution_schedule) = &self.token_distribution_schedule {
645 token_distribution_schedule.to_csv(fs::File::create(
646 path.join(GENESIS_BUILDER_TOKEN_DISTRIBUTION_SCHEDULE_FILE),
647 )?)?;
648 }
649
650 let signature_dir = path.join(GENESIS_BUILDER_SIGNATURE_DIR);
652 std::fs::create_dir_all(&signature_dir)?;
653 for (pubkey, sigs) in self.signatures {
654 let sig_bytes = bcs::to_bytes(&sigs)?;
655 let name = self.validators.get(&pubkey).unwrap().info.name();
656 fs::write(signature_dir.join(name), sig_bytes)?;
657 }
658
659 let committee_dir = path.join(GENESIS_BUILDER_COMMITTEE_DIR);
661 fs::create_dir_all(&committee_dir)?;
662
663 for (_pubkey, validator) in self.validators {
664 let validator_info_bytes = serde_yaml::to_string(&validator)?;
665 fs::write(
666 committee_dir.join(validator.info.name()),
667 validator_info_bytes,
668 )?;
669 }
670
671 if let Some(genesis) = &self.built_genesis {
672 let genesis_bytes = bcs::to_bytes(&genesis)?;
673 fs::write(
674 path.join(GENESIS_BUILDER_UNSIGNED_GENESIS_FILE),
675 genesis_bytes,
676 )?;
677 }
678
679 Ok(())
680 }
681}
682
683fn create_genesis_digest(
687 genesis_chain_parameters: &GenesisChainParameters,
688 genesis_validators: &[GenesisValidatorMetadata],
689 token_distribution_schedule: &TokenDistributionSchedule,
690 system_packages: &[SystemPackage],
691) -> TransactionDigest {
692 let mut hasher = DefaultHash::default();
693 hasher.update(b"sui-genesis");
694 bcs::serialize_into(&mut hasher, genesis_chain_parameters).unwrap();
695 bcs::serialize_into(&mut hasher, genesis_validators).unwrap();
696 bcs::serialize_into(&mut hasher, token_distribution_schedule).unwrap();
697 for system_package in system_packages {
698 bcs::serialize_into(&mut hasher, &system_package.bytes).unwrap();
699 }
700
701 let hash = hasher.finalize();
702 TransactionDigest::new(hash.into())
703}
704
705fn get_genesis_protocol_config(version: ProtocolVersion) -> ProtocolConfig {
706 ProtocolConfig::get_for_version(version, ChainIdentifier::default().chain())
713}
714
715fn build_unsigned_genesis_data(
716 parameters: &GenesisCeremonyParameters,
717 token_distribution_schedule: &TokenDistributionSchedule,
718 validators: &[GenesisValidatorInfo],
719 objects: &[Object],
720) -> UnsignedGenesis {
721 if !parameters.allow_insertion_of_extra_objects && !objects.is_empty() {
722 panic!(
723 "insertion of extra objects at genesis time is prohibited due to 'allow_insertion_of_extra_objects' parameter"
724 );
725 }
726
727 let genesis_chain_parameters = parameters.to_genesis_chain_parameters();
728 let genesis_validators = validators
729 .iter()
730 .cloned()
731 .map(GenesisValidatorMetadata::from)
732 .collect::<Vec<_>>();
733
734 token_distribution_schedule.validate();
735 token_distribution_schedule.check_all_stake_operations_are_for_valid_validators(
736 genesis_validators.iter().map(|v| v.sui_address),
737 );
738
739 let epoch_data = EpochData::new_genesis(genesis_chain_parameters.chain_start_timestamp_ms);
740
741 let mut system_packages =
745 sui_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);
751
752 let genesis_digest = create_genesis_digest(
753 &genesis_chain_parameters,
754 &genesis_validators,
755 token_distribution_schedule,
756 &system_packages,
757 );
758
759 let registry = prometheus::Registry::new();
761 let metrics = Arc::new(ExecutionMetrics::new(®istry));
762
763 let objects = create_genesis_objects(
764 &epoch_data,
765 &genesis_digest,
766 objects,
767 &genesis_validators,
768 &genesis_chain_parameters,
769 token_distribution_schedule,
770 system_packages,
771 metrics.clone(),
772 );
773
774 let protocol_config = get_genesis_protocol_config(parameters.protocol_version);
775
776 let (genesis_transaction, genesis_effects, genesis_events, objects) =
777 create_genesis_transaction(objects, &protocol_config, metrics, &epoch_data);
778 let (checkpoint, checkpoint_contents) = create_genesis_checkpoint(
779 &protocol_config,
780 parameters,
781 &genesis_transaction,
782 &genesis_effects,
783 );
784
785 UnsignedGenesis {
786 checkpoint,
787 checkpoint_contents,
788 transaction: genesis_transaction,
789 effects: genesis_effects,
790 events: genesis_events,
791 objects,
792 }
793}
794
795fn update_system_packages_from_objects(
808 system_packages: &mut Vec<SystemPackage>,
809 objects: &[Object],
810) {
811 let system_package_overrides: BTreeMap<ObjectID, Vec<Vec<u8>>> = objects
813 .iter()
814 .filter_map(|obj| {
815 let pkg = obj.data.try_as_package()?;
816 is_system_package(pkg.id()).then(|| {
817 (
818 pkg.id(),
819 pkg.serialized_module_map().values().cloned().collect(),
820 )
821 })
822 })
823 .collect();
824
825 for package in system_packages {
828 if let Some(overrides) = system_package_overrides.get(&package.id).cloned() {
829 package.bytes = overrides;
830 }
831 }
832}
833
834fn create_genesis_checkpoint(
835 protocol_config: &ProtocolConfig,
836 parameters: &GenesisCeremonyParameters,
837 transaction: &Transaction,
838 effects: &TransactionEffects,
839) -> (CheckpointSummary, CheckpointContents) {
840 let execution_digests = ExecutionDigests {
841 transaction: *transaction.digest(),
842 effects: effects.digest(),
843 };
844 let contents =
845 CheckpointContents::new_with_digests_and_signatures([execution_digests], vec![vec![]]);
846 let version_specific_data =
847 match protocol_config.checkpoint_summary_version_specific_data_as_option() {
848 None | Some(0) => Vec::new(),
849 Some(1) => bcs::to_bytes(&CheckpointVersionSpecificData::V1(
850 CheckpointVersionSpecificDataV1::default(),
851 ))
852 .unwrap(),
853 _ => unimplemented!("unrecognized version_specific_data version for CheckpointSummary"),
854 };
855 let checkpoint = CheckpointSummary {
856 epoch: 0,
857 sequence_number: 0,
858 network_total_transactions: contents.size().try_into().unwrap(),
859 content_digest: *contents.digest(),
860 previous_digest: None,
861 epoch_rolling_gas_cost_summary: Default::default(),
862 end_of_epoch_data: None,
863 timestamp_ms: parameters.chain_start_timestamp_ms,
864 version_specific_data,
865 checkpoint_commitments: Default::default(),
866 };
867
868 (checkpoint, contents)
869}
870
871fn create_genesis_transaction(
872 objects: Vec<Object>,
873 protocol_config: &ProtocolConfig,
874 metrics: Arc<ExecutionMetrics>,
875 epoch_data: &EpochData,
876) -> (
877 Transaction,
878 TransactionEffects,
879 TransactionEvents,
880 Vec<Object>,
881) {
882 let genesis_transaction = {
883 let genesis_objects = objects
884 .into_iter()
885 .map(|mut object| {
886 if let Some(o) = object.data.try_as_move_mut() {
887 o.decrement_version_to(SequenceNumber::MIN);
888 }
889
890 if let Owner::Shared {
891 initial_shared_version,
892 } = &mut object.owner
893 {
894 *initial_shared_version = SequenceNumber::MIN;
895 }
896
897 let object = object.into_inner();
898 sui_types::transaction::GenesisObject::RawObject {
899 data: object.data,
900 owner: object.owner,
901 }
902 })
903 .collect();
904
905 sui_types::transaction::VerifiedTransaction::new_genesis_transaction(genesis_objects)
906 .into_inner()
907 };
908
909 let genesis_digest = *genesis_transaction.digest();
910 let (effects, events, objects) = {
912 let silent = true;
913
914 let executor = sui_execution::executor(protocol_config, silent)
915 .expect("Creating an executor should not fail here");
916
917 let expensive_checks = false;
918 let transaction_data = &genesis_transaction.data().intent_message().value;
919 let (kind, signer, mut gas_data) = transaction_data.execution_parts();
920 gas_data.payment = vec![];
921 let input_objects = CheckedInputObjects::new_for_genesis(vec![]);
922 let (inner_temp_store, _, effects, _timings, _execution_error) = executor
923 .execute_transaction_to_effects_and_execution_error(
924 &InMemoryStorage::new(Vec::new()),
925 protocol_config,
926 metrics,
927 expensive_checks,
928 ExecutionOrEarlyError::ok(None),
929 &epoch_data.epoch_id(),
930 epoch_data.epoch_start_timestamp(),
931 input_objects,
932 gas_data,
933 SuiGasStatus::new_unmetered(),
934 kind,
935 None, signer,
937 genesis_digest,
938 &mut None,
939 );
940 assert!(inner_temp_store.input_objects.is_empty());
941 assert!(inner_temp_store.mutable_inputs.is_empty());
942 assert!(effects.mutated().is_empty());
943 assert!(effects.unwrapped().is_empty());
944 assert!(effects.deleted().is_empty());
945 assert!(effects.wrapped().is_empty());
946 assert!(effects.unwrapped_then_deleted().is_empty());
947
948 let objects = inner_temp_store.written.into_values().collect();
949 (effects, inner_temp_store.events, objects)
950 };
951
952 (genesis_transaction, effects, events, objects)
953}
954
955fn create_genesis_objects(
956 epoch_data: &EpochData,
957 genesis_digest: &TransactionDigest,
958 input_objects: &[Object],
959 validators: &[GenesisValidatorMetadata],
960 parameters: &GenesisChainParameters,
961 token_distribution_schedule: &TokenDistributionSchedule,
962 system_packages: Vec<SystemPackage>,
963 metrics: Arc<ExecutionMetrics>,
964) -> Vec<Object> {
965 let mut store = InMemoryStorage::new(Vec::new());
966 let protocol_config = ProtocolConfig::get_for_version(
970 ProtocolVersion::new(parameters.protocol_version),
971 Chain::Unknown,
972 );
973
974 let silent = true;
975 let executor = sui_execution::executor(&protocol_config, silent)
976 .expect("Creating an executor should not fail here");
977
978 for system_package in system_packages.into_iter() {
979 process_package(&mut store, system_package).unwrap();
980 }
981
982 {
983 for object in input_objects {
984 store.insert_object(object.to_owned());
985 }
986 }
987
988 generate_genesis_system_object(
989 &mut store,
990 executor.as_ref(),
991 validators,
992 epoch_data,
993 genesis_digest,
994 parameters,
995 token_distribution_schedule,
996 metrics,
997 )
998 .unwrap();
999
1000 store.into_inner().into_values().collect()
1001}
1002
1003fn process_package(
1004 store: &mut InMemoryStorage,
1005 system_package: SystemPackage,
1006) -> anyhow::Result<()> {
1007 #[cfg(debug_assertions)]
1008 {
1009 use move_core_types::account_address::AccountAddress;
1010 let to_be_published_addresses: std::collections::HashSet<_> = system_package
1011 .modules()
1012 .iter()
1013 .map(|module| *module.self_id().address())
1014 .collect();
1015 let dependencies = &system_package.dependencies;
1016 let dependency_objects = store.get_objects(dependencies);
1017 assert!(
1018 dependencies
1020 .iter()
1021 .zip_debug_eq(dependency_objects.iter())
1022 .all(|(dependency, obj_opt)| obj_opt.is_some()
1023 || to_be_published_addresses.contains(&AccountAddress::from(*dependency)))
1024 );
1025 }
1026 store.insert_object(system_package.genesis_object());
1028
1029 Ok(())
1030}
1031
1032pub fn generate_genesis_system_object(
1033 store: &mut InMemoryStorage,
1034 executor: &dyn Executor,
1035 genesis_validators: &[GenesisValidatorMetadata],
1036 epoch_data: &EpochData,
1037 genesis_digest: &TransactionDigest,
1038 genesis_chain_parameters: &GenesisChainParameters,
1039 token_distribution_schedule: &TokenDistributionSchedule,
1040 metrics: Arc<ExecutionMetrics>,
1041) -> anyhow::Result<()> {
1042 let protocol_config = ProtocolConfig::get_for_version(
1043 ProtocolVersion::new(genesis_chain_parameters.protocol_version),
1044 ChainIdentifier::default().chain(),
1045 );
1046
1047 let pt = {
1048 let mut builder = ProgrammableTransactionBuilder::new();
1049 let sui_system_state_uid = builder.programmable_move_call(
1051 SUI_FRAMEWORK_ADDRESS.into(),
1052 ident_str!("object").to_owned(),
1053 ident_str!("sui_system_state").to_owned(),
1054 vec![],
1055 vec![],
1056 );
1057
1058 builder.move_call(
1060 SUI_FRAMEWORK_ADDRESS.into(),
1061 ident_str!("clock").to_owned(),
1062 ident_str!("create").to_owned(),
1063 vec![],
1064 vec![],
1065 )?;
1066
1067 if protocol_config.create_authenticator_state_in_genesis() {
1070 builder.move_call(
1071 SUI_FRAMEWORK_ADDRESS.into(),
1072 ident_str!("authenticator_state").to_owned(),
1073 ident_str!("create").to_owned(),
1074 vec![],
1075 vec![],
1076 )?;
1077 }
1078 if protocol_config.random_beacon() {
1079 builder.move_call(
1080 SUI_FRAMEWORK_ADDRESS.into(),
1081 ident_str!("random").to_owned(),
1082 ident_str!("create").to_owned(),
1083 vec![],
1084 vec![],
1085 )?;
1086 }
1087
1088 if protocol_config.create_root_accumulator_object() {
1089 builder.move_call(
1090 SUI_FRAMEWORK_ADDRESS.into(),
1091 ident_str!("accumulator").to_owned(),
1092 ident_str!("create").to_owned(),
1093 vec![],
1094 vec![],
1095 )?;
1096 }
1097
1098 if protocol_config.enable_coin_registry() {
1099 builder.move_call(
1100 SUI_FRAMEWORK_ADDRESS.into(),
1101 ident_str!("coin_registry").to_owned(),
1102 ident_str!("create").to_owned(),
1103 vec![],
1104 vec![],
1105 )?;
1106 }
1107
1108 if protocol_config.enable_display_registry() {
1109 builder.move_call(
1110 SUI_FRAMEWORK_ADDRESS.into(),
1111 ident_str!("display_registry").to_owned(),
1112 ident_str!("create").to_owned(),
1113 vec![],
1114 vec![],
1115 )?;
1116 }
1117
1118 if protocol_config.enable_coin_deny_list_v1() {
1119 builder.move_call(
1120 SUI_FRAMEWORK_ADDRESS.into(),
1121 DENY_LIST_MODULE.to_owned(),
1122 DENY_LIST_CREATE_FUNC.to_owned(),
1123 vec![],
1124 vec![],
1125 )?;
1126 }
1127
1128 if protocol_config.enable_bridge() {
1129 let bridge_uid = builder
1130 .input(CallArg::Pure(UID::new(SUI_BRIDGE_OBJECT_ID).to_bcs_bytes()))
1131 .unwrap();
1132 let bridge_chain_id = builder.pure(BridgeChainId::SuiCustom).unwrap();
1135 builder.programmable_move_call(
1136 BRIDGE_ADDRESS.into(),
1137 BRIDGE_MODULE_NAME.to_owned(),
1138 BRIDGE_CREATE_FUNCTION_NAME.to_owned(),
1139 vec![],
1140 vec![bridge_uid, bridge_chain_id],
1141 );
1142 }
1143
1144 if protocol_config.address_aliases() {
1145 builder.move_call(
1146 SUI_FRAMEWORK_ADDRESS.into(),
1147 ident_str!("address_alias").to_owned(),
1148 ident_str!("create").to_owned(),
1149 vec![],
1150 vec![],
1151 )?;
1152 }
1153
1154 let sui_supply = builder.programmable_move_call(
1156 SUI_FRAMEWORK_ADDRESS.into(),
1157 ident_str!("sui").to_owned(),
1158 ident_str!("new").to_owned(),
1159 vec![],
1160 vec![],
1161 );
1162
1163 let mut arguments = vec![sui_system_state_uid, sui_supply];
1167 let mut call_arg_arguments = vec![
1168 CallArg::Pure(bcs::to_bytes(&genesis_chain_parameters).unwrap()),
1169 CallArg::Pure(bcs::to_bytes(&genesis_validators).unwrap()),
1170 CallArg::Pure(bcs::to_bytes(&token_distribution_schedule).unwrap()),
1171 ]
1172 .into_iter()
1173 .map(|a| builder.input(a))
1174 .collect::<anyhow::Result<_, _>>()?;
1175 arguments.append(&mut call_arg_arguments);
1176 builder.programmable_move_call(
1177 SUI_SYSTEM_ADDRESS.into(),
1178 ident_str!("genesis").to_owned(),
1179 ident_str!("create").to_owned(),
1180 vec![],
1181 arguments,
1182 );
1183 builder.finish()
1184 };
1185
1186 let InnerTemporaryStore { mut written, .. } = executor.update_genesis_state(
1187 &*store,
1188 &protocol_config,
1189 metrics,
1190 epoch_data.epoch_id(),
1191 epoch_data.epoch_start_timestamp(),
1192 genesis_digest,
1193 CheckedInputObjects::new_for_genesis(vec![]),
1194 pt,
1195 )?;
1196
1197 {
1199 let object = written.get_mut(&sui_types::SUI_CLOCK_OBJECT_ID).unwrap();
1200 object
1201 .data
1202 .try_as_move_mut()
1203 .unwrap()
1204 .set_clock_timestamp_ms_unsafe(genesis_chain_parameters.chain_start_timestamp_ms);
1205 }
1206
1207 store.finish(written);
1208
1209 Ok(())
1210}
1211
1212#[cfg(test)]
1213mod test {
1214 use crate::Builder;
1215 use crate::validator_info::ValidatorInfo;
1216 use fastcrypto::traits::KeyPair;
1217 use sui_config::genesis::*;
1218 use sui_config::local_ip_utils;
1219 use sui_config::node::DEFAULT_COMMISSION_RATE;
1220 use sui_config::node::DEFAULT_VALIDATOR_GAS_PRICE;
1221 use sui_types::base_types::SuiAddress;
1222 use sui_types::crypto::{
1223 AccountKeyPair, AuthorityKeyPair, NetworkKeyPair, generate_proof_of_possession,
1224 get_key_pair_from_rng,
1225 };
1226
1227 #[test]
1228 fn allocation_csv() {
1229 let schedule = TokenDistributionSchedule::new_for_validators_with_default_allocation([
1230 SuiAddress::random_for_testing_only(),
1231 SuiAddress::random_for_testing_only(),
1232 ]);
1233 let mut output = Vec::new();
1234
1235 schedule.to_csv(&mut output).unwrap();
1236
1237 let parsed_schedule = TokenDistributionSchedule::from_csv(output.as_slice()).unwrap();
1238
1239 assert_eq!(schedule, parsed_schedule);
1240
1241 std::io::Write::write_all(&mut std::io::stdout(), &output).unwrap();
1242 }
1243
1244 #[test]
1245 #[cfg_attr(msim, ignore)]
1246 fn ceremony() {
1247 let dir = tempfile::TempDir::new().unwrap();
1248
1249 let key: AuthorityKeyPair = get_key_pair_from_rng(&mut rand::rngs::OsRng).1;
1250 let worker_key: NetworkKeyPair = get_key_pair_from_rng(&mut rand::rngs::OsRng).1;
1251 let account_key: AccountKeyPair = get_key_pair_from_rng(&mut rand::rngs::OsRng).1;
1252 let network_key: NetworkKeyPair = get_key_pair_from_rng(&mut rand::rngs::OsRng).1;
1253 let validator = ValidatorInfo {
1254 name: "0".into(),
1255 protocol_key: key.public().into(),
1256 worker_key: worker_key.public().clone(),
1257 account_address: SuiAddress::from(account_key.public()),
1258 network_key: network_key.public().clone(),
1259 gas_price: DEFAULT_VALIDATOR_GAS_PRICE,
1260 commission_rate: DEFAULT_COMMISSION_RATE,
1261 network_address: local_ip_utils::new_local_tcp_address_for_testing(),
1262 p2p_address: local_ip_utils::new_local_udp_address_for_testing(),
1263 narwhal_primary_address: local_ip_utils::new_local_udp_address_for_testing(),
1264 narwhal_worker_address: local_ip_utils::new_local_udp_address_for_testing(),
1265 description: String::new(),
1266 image_url: String::new(),
1267 project_url: String::new(),
1268 };
1269 let pop = generate_proof_of_possession(&key, account_key.public().into());
1270 let mut builder = Builder::new().add_validator(validator, pop);
1271
1272 let genesis = builder.build_unsigned_genesis_checkpoint();
1273 for object in genesis.objects() {
1274 println!("ObjectID: {} Type: {:?}", object.id(), object.type_());
1275 }
1276 builder.save(dir.path()).unwrap();
1277 Builder::load(dir.path()).unwrap();
1278 }
1279}