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