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_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    // Validator signatures over checkpoint
71    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        // Verify that all input data is valid
178        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        // Verify that all on-chain state was properly created
245        self.validate().unwrap();
246
247        genesis
248    }
249
250    /// Validates the entire state of the build, no matter what the internal state is (input
251    /// collection phase or output phase)
252    pub fn validate(&self) -> anyhow::Result<(), anyhow::Error> {
253        self.validate_inputs()?;
254        self.validate_output();
255        Ok(())
256    }
257
258    /// Runs through validation checks on the input values present in the builder
259    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    /// Runs through validation checks on the generated output (the initial chain state) based on
284    /// the input values present in the builder
285    fn validate_output(&self) {
286        // If genesis hasn't been built yet, just early return as there is nothing to validate yet
287        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        // In non-testing code, genesis type must always be V1.
307        let system_state = match unsigned_genesis.sui_system_object() {
308            SuiSystemState::V1(inner) => inner,
309            SuiSystemState::V2(_) => unreachable!(),
310            #[cfg(msim)]
311            _ => {
312                // Types other than V1 used in simtests do not need to be validated.
313                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            // Validators should not have duplicate addresses so the result of insertion should be None.
353            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        // Check distribution is correct
457        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        // All Gas and staked objects should be accounted for
522        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        // Load parameters
553        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        // Load validator infos
570        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        // Load Signatures
586        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, // Leave this as none, will build and compare below
607        };
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            // If we have a built genesis, then we must have a token_distribution_schedule present
615            // as well.
616            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            // Verify loaded genesis matches one build from the constituent parts
622            let built = builder.build_unsigned_genesis_checkpoint();
623            loaded_genesis.checkpoint_contents.digest(); // cache digest before compare
624            assert_eq!(
625                built, loaded_genesis,
626                "loaded genesis does not match built genesis"
627            );
628
629            // Just to double check that its set after building above
630            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        // Write parameters
643        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        // Write Signatures
653        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        // Write validator infos
662        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
685// Create a Genesis Txn Digest to be used when generating genesis objects by hashing all of the
686// inputs into genesis ans using that as our "Txn Digest". This is done to ensure that coin objects
687// created between chains are unique
688fn 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    // We have a circular dependency here. Protocol config depends on chain ID, which
709    // depends on genesis checkpoint (digest), which depends on genesis transaction, which
710    // depends on protocol config.
711    //
712    // ChainIdentifier::default().chain() which can be overridden by the
713    // SUI_PROTOCOL_CONFIG_CHAIN_OVERRIDE if necessary
714    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    // Get the correct system packages for our protocol version. If we cannot find the snapshot
744    // that means that we must be at the latest version and we should use the latest version of the
745    // framework.
746    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    // if system packages are provided in `objects`, update them with the provided bytes.
751    // This is a no-op under normal conditions and only an issue with certain tests.
752    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    // Use a throwaway metrics registry for genesis transaction execution.
762    let registry = prometheus::Registry::new();
763    let metrics = Arc::new(LimitsMetrics::new(&registry));
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
797// Some tests provide an override of the system packages via objects to the genesis builder.
798// When that happens we need to update the system packages with the new bytes provided.
799// Mock system packages in protocol config tests are an example of that (today the only
800// example).
801// The problem here arises from the fact that if regular system packages are pushed first
802// *AND* if any of them is loaded in the loader cache, there is no way to override them
803// with the provided object (no way to mock properly).
804// System packages are loaded only from internal dependencies (a system package depending on
805// some other), and in that case they would be loaded in the VM/loader cache.
806// The Bridge is an example of that and what led to this code. The bridge depends
807// on `sui_system` which is mocked in some tests, but would be in the loader
808// cache courtesy of the Bridge, thus causing the problem.
809fn update_system_packages_from_objects(
810    system_packages: &mut Vec<SystemPackage>,
811    objects: &[Object],
812) {
813    // Filter `objects` for system packages, and make `SystemPackage`s out of them.
814    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    // Replace packages in `system_packages` that are present in `objects` with their counterparts
828    // from the previous step.
829    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    // execute txn to effects
913    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    // We don't know the chain ID here since we haven't yet created the genesis checkpoint.
968    // However since we know there are no chain specific protool config options in genesis,
969    // we use Chain::Unknown here.
970    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    // When publishing genesis packages, since the std framework packages all have
1026    // non-zero addresses, [`Transaction::input_objects_in_compiled_modules`] will consider
1027    // them as dependencies even though they are not. Hence input_objects contain objects
1028    // that don't exist on-chain because they are yet to be published.
1029    #[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            // An object either exists on-chain, or is one of the packages to be published.
1038            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        // executing in Genesis mode does not create an `UpgradeCap`.
1067        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        // Step 1: Create the SuiSystemState UID
1104        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        // Step 2: Create and share the Clock.
1113        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        // Step 3: Create ProtocolConfig-controlled system objects, unless disabled (which only
1122        // happens in tests).
1123        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            // TODO(bridge): this needs to be passed in as a parameter for next testnet regenesis
1178            // Hardcoding chain id to SuiCustom
1179            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        // Step 4: Mint the supply of SUI.
1190        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        // Step 5: Run genesis.
1199        // The first argument is the system state uid we got from step 1 and the second one is the SUI supply we
1200        // got from step 3.
1201        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    // update the value of the clock to match the chain start time
1233    {
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}