sui_genesis_builder/
lib.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use 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    // Validator signatures over checkpoint
69    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        // Verify that all input data is valid
176        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        // Verify that all on-chain state was properly created
243        self.validate().unwrap();
244
245        genesis
246    }
247
248    /// Validates the entire state of the build, no matter what the internal state is (input
249    /// collection phase or output phase)
250    pub fn validate(&self) -> anyhow::Result<(), anyhow::Error> {
251        self.validate_inputs()?;
252        self.validate_output();
253        Ok(())
254    }
255
256    /// Runs through validation checks on the input values present in the builder
257    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    /// Runs through validation checks on the generated output (the initial chain state) based on
282    /// the input values present in the builder
283    fn validate_output(&self) {
284        // If genesis hasn't been built yet, just early return as there is nothing to validate yet
285        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        // In non-testing code, genesis type must always be V1.
305        let system_state = match unsigned_genesis.sui_system_object() {
306            SuiSystemState::V1(inner) => inner,
307            SuiSystemState::V2(_) => unreachable!(),
308            #[cfg(msim)]
309            _ => {
310                // Types other than V1 used in simtests do not need to be validated.
311                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            // Validators should not have duplicate addresses so the result of insertion should be None.
351            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        // Check distribution is correct
455        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        // All Gas and staked objects should be accounted for
520        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        // Load parameters
551        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        // Load validator infos
568        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        // Load Signatures
584        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, // Leave this as none, will build and compare below
605        };
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            // If we have a built genesis, then we must have a token_distribution_schedule present
613            // as well.
614            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            // Verify loaded genesis matches one build from the constituent parts
620            let built = builder.build_unsigned_genesis_checkpoint();
621            loaded_genesis.checkpoint_contents.digest(); // cache digest before compare
622            assert_eq!(
623                built, loaded_genesis,
624                "loaded genesis does not match built genesis"
625            );
626
627            // Just to double check that its set after building above
628            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        // Write parameters
641        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        // Write Signatures
651        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        // Write validator infos
660        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
683// Create a Genesis Txn Digest to be used when generating genesis objects by hashing all of the
684// inputs into genesis ans using that as our "Txn Digest". This is done to ensure that coin objects
685// created between chains are unique
686fn 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    // We have a circular dependency here. Protocol config depends on chain ID, which
707    // depends on genesis checkpoint (digest), which depends on genesis transaction, which
708    // depends on protocol config.
709    //
710    // ChainIdentifier::default().chain() which can be overridden by the
711    // SUI_PROTOCOL_CONFIG_CHAIN_OVERRIDE if necessary
712    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    // Get the correct system packages for our protocol version. If we cannot find the snapshot
742    // that means that we must be at the latest version and we should use the latest version of the
743    // framework.
744    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    // if system packages are provided in `objects`, update them with the provided bytes.
749    // This is a no-op under normal conditions and only an issue with certain tests.
750    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    // Use a throwaway metrics registry for genesis transaction execution.
760    let registry = prometheus::Registry::new();
761    let metrics = Arc::new(ExecutionMetrics::new(&registry));
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
795// Some tests provide an override of the system packages via objects to the genesis builder.
796// When that happens we need to update the system packages with the new bytes provided.
797// Mock system packages in protocol config tests are an example of that (today the only
798// example).
799// The problem here arises from the fact that if regular system packages are pushed first
800// *AND* if any of them is loaded in the loader cache, there is no way to override them
801// with the provided object (no way to mock properly).
802// System packages are loaded only from internal dependencies (a system package depending on
803// some other), and in that case they would be loaded in the VM/loader cache.
804// The Bridge is an example of that and what led to this code. The bridge depends
805// on `sui_system` which is mocked in some tests, but would be in the loader
806// cache courtesy of the Bridge, thus causing the problem.
807fn update_system_packages_from_objects(
808    system_packages: &mut Vec<SystemPackage>,
809    objects: &[Object],
810) {
811    // Filter `objects` for system packages, and make `SystemPackage`s out of them.
812    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    // Replace packages in `system_packages` that are present in `objects` with their counterparts
826    // from the previous step.
827    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    // execute txn to effects
911    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, // compat_args
936                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    // We don't know the chain ID here since we haven't yet created the genesis checkpoint.
967    // However since we know there are no chain specific protool config options in genesis,
968    // we use Chain::Unknown here.
969    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            // An object either exists on-chain, or is one of the packages to be published.
1019            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    // This is genesis, so insert the system package objects directly without going through Move.
1027    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        // Step 1: Create the SuiSystemState UID
1050        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        // Step 2: Create and share the Clock.
1059        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        // Step 3: Create ProtocolConfig-controlled system objects, unless disabled (which only
1068        // happens in tests).
1069        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            // TODO(bridge): this needs to be passed in as a parameter for next testnet regenesis
1133            // Hardcoding chain id to SuiCustom
1134            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        // Step 4: Mint the supply of SUI.
1155        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        // Step 5: Run genesis.
1164        // The first argument is the system state uid we got from step 1 and the second one is the SUI supply we
1165        // got from step 3.
1166        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    // update the value of the clock to match the chain start time
1198    {
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}