sui_swarm_config/
network_config_builder.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use std::path::PathBuf;
5use std::time::Duration;
6use std::{num::NonZeroUsize, path::Path, sync::Arc};
7
8use mysten_common::ZipDebugEqIteratorExt;
9use mysten_common::in_test_configuration;
10use rand::rngs::OsRng;
11use sui_config::ExecutionCacheConfig;
12use sui_config::genesis::{TokenAllocation, TokenDistributionScheduleBuilder};
13use sui_config::node::AuthorityOverloadConfig;
14#[cfg(msim)]
15use sui_config::node::ExecutionTimeObserverConfig;
16use sui_config::node::FundsWithdrawSchedulerType;
17use sui_protocol_config::Chain;
18use sui_types::base_types::{AuthorityName, SuiAddress};
19use sui_types::committee::{Committee, ProtocolVersion};
20use sui_types::crypto::{
21    AccountKeyPair, AuthorityKeyPair, KeypairTraits, PublicKey, get_key_pair_from_rng,
22};
23use sui_types::object::Object;
24use sui_types::supported_protocol_versions::SupportedProtocolVersions;
25use sui_types::traffic_control::{PolicyConfig, RemoteFirewallConfig};
26
27use consensus_config::ObserverParameters;
28
29use crate::genesis_config::{AccountConfig, DEFAULT_GAS_AMOUNT, ValidatorGenesisConfigBuilder};
30use crate::genesis_config::{GenesisConfig, ValidatorGenesisConfig};
31use crate::network_config::NetworkConfig;
32use crate::node_config_builder::ValidatorConfigBuilder;
33
34pub type ValidatorObserverConfigCallback =
35    Arc<dyn Fn(usize) -> Option<ObserverParameters> + Send + Sync + 'static>;
36
37pub struct KeyPairWrapper {
38    pub account_key_pair: AccountKeyPair,
39    pub protocol_key_pair: Option<AuthorityKeyPair>,
40}
41
42impl Clone for KeyPairWrapper {
43    fn clone(&self) -> Self {
44        Self {
45            account_key_pair: self.account_key_pair.copy(),
46            protocol_key_pair: self.protocol_key_pair.as_ref().map(|k| k.copy()),
47        }
48    }
49}
50
51pub enum CommitteeConfig {
52    Size(NonZeroUsize),
53    Validators(Vec<ValidatorGenesisConfig>),
54    AccountKeys(Vec<AccountKeyPair>),
55    /// Indicates that a committee should be deterministically generated, using the provided rng
56    /// as a source of randomness as well as generating deterministic network port information.
57    Deterministic((NonZeroUsize, Option<Vec<KeyPairWrapper>>)),
58}
59
60pub type SupportedProtocolVersionsCallback = Arc<
61    dyn Fn(
62            usize,                 /* validator idx */
63            Option<AuthorityName>, /* None for fullnode */
64        ) -> SupportedProtocolVersions
65        + Send
66        + Sync
67        + 'static,
68>;
69
70#[derive(Clone)]
71pub enum ProtocolVersionsConfig {
72    // use SYSTEM_DEFAULT
73    Default,
74    // Use one range for all validators.
75    Global(SupportedProtocolVersions),
76    // A closure that returns the versions for each validator.
77    // TODO: This doesn't apply to fullnodes.
78    PerValidator(SupportedProtocolVersionsCallback),
79}
80
81pub type GlobalStateHashV2EnabledCallback = Arc<dyn Fn(usize) -> bool + Send + Sync + 'static>;
82
83#[derive(Clone)]
84pub enum GlobalStateHashV2EnabledConfig {
85    Global(bool),
86    PerValidator(GlobalStateHashV2EnabledCallback),
87}
88
89pub type FundsWithdrawSchedulerTypeCallback =
90    Arc<dyn Fn(usize) -> FundsWithdrawSchedulerType + Send + Sync + 'static>;
91
92#[derive(Clone)]
93pub enum FundsWithdrawSchedulerTypeConfig {
94    Global(FundsWithdrawSchedulerType),
95    PerValidator(FundsWithdrawSchedulerTypeCallback),
96}
97
98pub struct ConfigBuilder<R = OsRng> {
99    rng: Option<R>,
100    config_directory: PathBuf,
101    supported_protocol_versions_config: Option<ProtocolVersionsConfig>,
102    chain_override: Option<Chain>,
103    committee: CommitteeConfig,
104    genesis_config: Option<GenesisConfig>,
105    reference_gas_price: Option<u64>,
106    additional_objects: Vec<Object>,
107    jwk_fetch_interval: Option<Duration>,
108    num_unpruned_validators: Option<usize>,
109    authority_overload_config: Option<AuthorityOverloadConfig>,
110    execution_cache_config: Option<ExecutionCacheConfig>,
111    data_ingestion_dir: Option<PathBuf>,
112    policy_config: Option<PolicyConfig>,
113    firewall_config: Option<RemoteFirewallConfig>,
114    global_state_hash_v2_enabled_config: Option<GlobalStateHashV2EnabledConfig>,
115    funds_withdraw_scheduler_type_config: Option<FundsWithdrawSchedulerTypeConfig>,
116    state_sync_config: Option<sui_config::p2p::StateSyncConfig>,
117    #[cfg(msim)]
118    execution_time_observer_config: Option<ExecutionTimeObserverConfig>,
119    validator_observer_config: Option<ValidatorObserverConfigCallback>,
120}
121
122impl ConfigBuilder {
123    pub fn new<P: AsRef<Path>>(config_directory: P) -> Self {
124        // In test configuration, alternate scheduler types between validators
125        // so that half use Eager and half use Naive. This allows testing both
126        // scheduler implementations and catching any discrepancies via quorum comparison.
127        let funds_withdraw_scheduler_type_config = if in_test_configuration() {
128            Some(FundsWithdrawSchedulerTypeConfig::PerValidator(Arc::new(
129                |idx| {
130                    if idx % 2 == 0 {
131                        FundsWithdrawSchedulerType::Eager
132                    } else {
133                        FundsWithdrawSchedulerType::Naive
134                    }
135                },
136            )))
137        } else {
138            None
139        };
140
141        Self {
142            rng: Some(OsRng),
143            config_directory: config_directory.as_ref().into(),
144            supported_protocol_versions_config: None,
145            chain_override: None,
146            // FIXME: A network with only 1 validator does not have liveness.
147            // We need to change this. There are some tests that depend on it though.
148            committee: CommitteeConfig::Size(NonZeroUsize::new(1).unwrap()),
149            genesis_config: None,
150            reference_gas_price: None,
151            additional_objects: vec![],
152            jwk_fetch_interval: None,
153            num_unpruned_validators: None,
154            authority_overload_config: None,
155            execution_cache_config: None,
156            data_ingestion_dir: None,
157            policy_config: None,
158            firewall_config: None,
159            global_state_hash_v2_enabled_config: None,
160            funds_withdraw_scheduler_type_config,
161            state_sync_config: None,
162            #[cfg(msim)]
163            execution_time_observer_config: None,
164            validator_observer_config: None,
165        }
166    }
167
168    pub fn new_with_temp_dir() -> Self {
169        Self::new(mysten_common::tempdir().unwrap().keep())
170    }
171}
172
173impl<R> ConfigBuilder<R> {
174    pub fn committee(mut self, committee: CommitteeConfig) -> Self {
175        self.committee = committee;
176        self
177    }
178
179    pub fn committee_size(mut self, committee_size: NonZeroUsize) -> Self {
180        self.committee = CommitteeConfig::Size(committee_size);
181        self
182    }
183
184    pub fn deterministic_committee_size(mut self, committee_size: NonZeroUsize) -> Self {
185        self.committee = CommitteeConfig::Deterministic((committee_size, None));
186        self
187    }
188
189    pub fn deterministic_committee_validators(mut self, keys: Vec<KeyPairWrapper>) -> Self {
190        self.committee = CommitteeConfig::Deterministic((
191            NonZeroUsize::new(keys.len()).expect("Validator keys should be non empty"),
192            Some(keys),
193        ));
194        self
195    }
196
197    pub fn with_validator_account_keys(mut self, keys: Vec<AccountKeyPair>) -> Self {
198        self.committee = CommitteeConfig::AccountKeys(keys);
199        self
200    }
201
202    pub fn with_validators(mut self, validators: Vec<ValidatorGenesisConfig>) -> Self {
203        self.committee = CommitteeConfig::Validators(validators);
204        self
205    }
206
207    pub fn with_genesis_config(mut self, genesis_config: GenesisConfig) -> Self {
208        assert!(self.genesis_config.is_none(), "Genesis config already set");
209        self.genesis_config = Some(genesis_config);
210        self
211    }
212
213    pub fn with_chain_override(mut self, chain: Chain) -> Self {
214        assert!(self.chain_override.is_none(), "Chain override already set");
215        self.chain_override = Some(chain);
216        self
217    }
218
219    pub fn with_num_unpruned_validators(mut self, n: usize) -> Self {
220        self.num_unpruned_validators = Some(n);
221        self
222    }
223
224    pub fn with_jwk_fetch_interval(mut self, i: Duration) -> Self {
225        self.jwk_fetch_interval = Some(i);
226        self
227    }
228
229    pub fn with_data_ingestion_dir(mut self, path: PathBuf) -> Self {
230        self.data_ingestion_dir = Some(path);
231        self
232    }
233
234    pub fn with_reference_gas_price(mut self, reference_gas_price: u64) -> Self {
235        self.reference_gas_price = Some(reference_gas_price);
236        self
237    }
238
239    pub fn with_accounts(mut self, accounts: Vec<AccountConfig>) -> Self {
240        self.get_or_init_genesis_config().accounts = accounts;
241        self
242    }
243
244    pub fn with_chain_start_timestamp_ms(mut self, chain_start_timestamp_ms: u64) -> Self {
245        self.get_or_init_genesis_config()
246            .parameters
247            .chain_start_timestamp_ms = chain_start_timestamp_ms;
248        self
249    }
250
251    pub fn with_objects<I: IntoIterator<Item = Object>>(mut self, objects: I) -> Self {
252        self.additional_objects.extend(objects);
253        self
254    }
255
256    pub fn with_epoch_duration(mut self, epoch_duration_ms: u64) -> Self {
257        self.get_or_init_genesis_config()
258            .parameters
259            .epoch_duration_ms = epoch_duration_ms;
260        self
261    }
262
263    pub fn with_protocol_version(mut self, protocol_version: ProtocolVersion) -> Self {
264        self.get_or_init_genesis_config()
265            .parameters
266            .protocol_version = protocol_version;
267        self
268    }
269
270    pub fn with_supported_protocol_versions(mut self, c: SupportedProtocolVersions) -> Self {
271        self.supported_protocol_versions_config = Some(ProtocolVersionsConfig::Global(c));
272        self
273    }
274
275    pub fn with_supported_protocol_version_callback(
276        mut self,
277        func: SupportedProtocolVersionsCallback,
278    ) -> Self {
279        self.supported_protocol_versions_config = Some(ProtocolVersionsConfig::PerValidator(func));
280        self
281    }
282
283    pub fn with_supported_protocol_versions_config(mut self, c: ProtocolVersionsConfig) -> Self {
284        self.supported_protocol_versions_config = Some(c);
285        self
286    }
287
288    pub fn with_global_state_hash_v2_enabled(mut self, enabled: bool) -> Self {
289        self.global_state_hash_v2_enabled_config =
290            Some(GlobalStateHashV2EnabledConfig::Global(enabled));
291        self
292    }
293
294    pub fn with_global_state_hash_v2_enabled_callback(
295        mut self,
296        func: GlobalStateHashV2EnabledCallback,
297    ) -> Self {
298        self.global_state_hash_v2_enabled_config =
299            Some(GlobalStateHashV2EnabledConfig::PerValidator(func));
300        self
301    }
302
303    pub fn with_global_state_hash_v2_enabled_config(
304        mut self,
305        c: GlobalStateHashV2EnabledConfig,
306    ) -> Self {
307        self.global_state_hash_v2_enabled_config = Some(c);
308        self
309    }
310
311    pub fn with_funds_withdraw_scheduler_type(
312        mut self,
313        scheduler_type: FundsWithdrawSchedulerType,
314    ) -> Self {
315        self.funds_withdraw_scheduler_type_config =
316            Some(FundsWithdrawSchedulerTypeConfig::Global(scheduler_type));
317        self
318    }
319
320    pub fn with_funds_withdraw_scheduler_type_callback(
321        mut self,
322        func: FundsWithdrawSchedulerTypeCallback,
323    ) -> Self {
324        self.funds_withdraw_scheduler_type_config =
325            Some(FundsWithdrawSchedulerTypeConfig::PerValidator(func));
326        self
327    }
328
329    pub fn with_funds_withdraw_scheduler_type_config(
330        mut self,
331        c: FundsWithdrawSchedulerTypeConfig,
332    ) -> Self {
333        self.funds_withdraw_scheduler_type_config = Some(c);
334        self
335    }
336
337    #[cfg(msim)]
338    pub fn with_execution_time_observer_config(mut self, c: ExecutionTimeObserverConfig) -> Self {
339        self.execution_time_observer_config = Some(c);
340        self
341    }
342
343    pub fn with_validator_observer_config(mut self, c: ValidatorObserverConfigCallback) -> Self {
344        self.validator_observer_config = Some(c);
345        self
346    }
347
348    pub fn with_authority_overload_config(mut self, c: AuthorityOverloadConfig) -> Self {
349        self.authority_overload_config = Some(c);
350        self
351    }
352
353    pub fn with_execution_cache_config(mut self, c: ExecutionCacheConfig) -> Self {
354        self.execution_cache_config = Some(c);
355        self
356    }
357
358    pub fn with_policy_config(mut self, config: Option<PolicyConfig>) -> Self {
359        self.policy_config = config;
360        self
361    }
362
363    pub fn with_firewall_config(mut self, config: Option<RemoteFirewallConfig>) -> Self {
364        self.firewall_config = config;
365        self
366    }
367
368    pub fn rng<N: rand::RngCore + rand::CryptoRng>(self, rng: N) -> ConfigBuilder<N> {
369        ConfigBuilder {
370            rng: Some(rng),
371            config_directory: self.config_directory,
372            supported_protocol_versions_config: self.supported_protocol_versions_config,
373            committee: self.committee,
374            genesis_config: self.genesis_config,
375            chain_override: self.chain_override,
376            reference_gas_price: self.reference_gas_price,
377            additional_objects: self.additional_objects,
378            num_unpruned_validators: self.num_unpruned_validators,
379            jwk_fetch_interval: self.jwk_fetch_interval,
380            authority_overload_config: self.authority_overload_config,
381            execution_cache_config: self.execution_cache_config,
382            data_ingestion_dir: self.data_ingestion_dir,
383            policy_config: self.policy_config,
384            firewall_config: self.firewall_config,
385            global_state_hash_v2_enabled_config: self.global_state_hash_v2_enabled_config,
386            funds_withdraw_scheduler_type_config: self.funds_withdraw_scheduler_type_config,
387            state_sync_config: self.state_sync_config,
388            #[cfg(msim)]
389            execution_time_observer_config: self.execution_time_observer_config,
390            validator_observer_config: self.validator_observer_config,
391        }
392    }
393
394    pub fn with_state_sync_config(mut self, config: sui_config::p2p::StateSyncConfig) -> Self {
395        self.state_sync_config = Some(config);
396        self
397    }
398
399    fn get_or_init_genesis_config(&mut self) -> &mut GenesisConfig {
400        if self.genesis_config.is_none() {
401            self.genesis_config = Some(GenesisConfig::for_local_testing());
402        }
403        self.genesis_config.as_mut().unwrap()
404    }
405}
406
407impl<R: rand::RngCore + rand::CryptoRng> ConfigBuilder<R> {
408    //TODO right now we always randomize ports, we may want to have a default port configuration
409    pub fn build(self) -> NetworkConfig {
410        let committee = self.committee;
411
412        let mut rng = self.rng.unwrap();
413        let validators = match committee {
414            CommitteeConfig::Size(size) => {
415                // We always get fixed protocol keys from this function (which is isolated from
416                // external test randomness because it uses a fixed seed). Necessary because some
417                // tests call `make_tx_certs_and_signed_effects`, which locally forges a cert using
418                // this same committee.
419                let (_, keys) = Committee::new_simple_test_committee_of_size(size.into());
420
421                keys.into_iter()
422                    .map(|authority_key| {
423                        let mut builder = ValidatorGenesisConfigBuilder::new()
424                            .with_protocol_key_pair(authority_key);
425                        if let Some(rgp) = self.reference_gas_price {
426                            builder = builder.with_gas_price(rgp);
427                        }
428                        builder.build(&mut rng)
429                    })
430                    .collect::<Vec<_>>()
431            }
432
433            CommitteeConfig::Validators(v) => v,
434
435            CommitteeConfig::AccountKeys(keys) => {
436                // See above re fixed protocol keys
437                let (_, protocol_keys) = Committee::new_simple_test_committee_of_size(keys.len());
438                keys.into_iter()
439                    .zip_debug_eq(protocol_keys)
440                    .map(|(account_key, protocol_key)| {
441                        let mut builder = ValidatorGenesisConfigBuilder::new()
442                            .with_protocol_key_pair(protocol_key)
443                            .with_account_key_pair(account_key);
444                        if let Some(rgp) = self.reference_gas_price {
445                            builder = builder.with_gas_price(rgp);
446                        }
447                        builder.build(&mut rng)
448                    })
449                    .collect::<Vec<_>>()
450            }
451            CommitteeConfig::Deterministic((size, key_pair_wrappers)) => {
452                // If no keys are provided, generate them.
453                let keys = key_pair_wrappers.unwrap_or_else(|| {
454                    (0..size.get())
455                        .map(|_| KeyPairWrapper {
456                            account_key_pair: get_key_pair_from_rng(&mut rng).1,
457                            protocol_key_pair: None,
458                        })
459                        .collect()
460                });
461
462                let mut configs = vec![];
463                for (i, key) in keys.into_iter().enumerate() {
464                    let port_offset = 8000 + i * 10;
465                    let mut builder = ValidatorGenesisConfigBuilder::new()
466                        .with_ip("127.0.0.1".to_owned())
467                        .with_account_key_pair(key.account_key_pair)
468                        .with_deterministic_ports(port_offset as u16);
469                    if let Some(protocol_key_pair) = key.protocol_key_pair {
470                        builder = builder.with_protocol_key_pair(protocol_key_pair);
471                    }
472                    if let Some(rgp) = self.reference_gas_price {
473                        builder = builder.with_gas_price(rgp);
474                    }
475                    configs.push(builder.build(&mut rng));
476                }
477                configs
478            }
479        };
480
481        let genesis_config = self
482            .genesis_config
483            .unwrap_or_else(GenesisConfig::for_local_testing);
484
485        let (account_keys, allocations) = genesis_config.generate_accounts(&mut rng).unwrap();
486
487        let token_distribution_schedule = {
488            let mut builder = TokenDistributionScheduleBuilder::new();
489            for allocation in allocations {
490                builder.add_allocation(allocation);
491            }
492            // Add allocations for each validator
493            for validator in &validators {
494                let account_key: PublicKey = validator.account_key_pair.public();
495                let address = SuiAddress::from(&account_key);
496                // Give each validator some gas so they can pay for their transactions.
497                let gas_coin = TokenAllocation {
498                    recipient_address: address,
499                    amount_mist: DEFAULT_GAS_AMOUNT,
500                    staked_with_validator: None,
501                };
502                let stake = TokenAllocation {
503                    recipient_address: address,
504                    amount_mist: validator.stake,
505                    staked_with_validator: Some(address),
506                };
507                builder.add_allocation(gas_coin);
508                builder.add_allocation(stake);
509            }
510            builder.build()
511        };
512
513        let genesis = {
514            let mut builder = sui_genesis_builder::Builder::new()
515                .with_parameters(genesis_config.parameters)
516                .add_objects(self.additional_objects);
517
518            for (i, validator) in validators.iter().enumerate() {
519                let name = validator
520                    .name
521                    .clone()
522                    .unwrap_or(format!("validator-{i}").to_string());
523                let validator_info = validator.to_validator_info(name);
524                builder =
525                    builder.add_validator(validator_info.info, validator_info.proof_of_possession);
526            }
527
528            builder = builder.with_token_distribution_schedule(token_distribution_schedule);
529
530            for validator in &validators {
531                builder = builder.add_validator_signature(&validator.key_pair);
532            }
533
534            builder.build()
535        };
536
537        let validator_configs = validators
538            .into_iter()
539            .enumerate()
540            .map(|(idx, validator)| {
541                let mut builder = ValidatorConfigBuilder::new()
542                    .with_config_directory(self.config_directory.clone())
543                    .with_policy_config(self.policy_config.clone())
544                    .with_firewall_config(self.firewall_config.clone());
545
546                if let Some(chain) = self.chain_override {
547                    builder = builder.with_chain_override(chain);
548                }
549
550                if let Some(jwk_fetch_interval) = self.jwk_fetch_interval {
551                    builder = builder.with_jwk_fetch_interval(jwk_fetch_interval);
552                }
553
554                if let Some(authority_overload_config) = &self.authority_overload_config {
555                    builder =
556                        builder.with_authority_overload_config(authority_overload_config.clone());
557                }
558
559                if let Some(execution_cache_config) = &self.execution_cache_config {
560                    builder = builder.with_execution_cache_config(execution_cache_config.clone());
561                }
562
563                if let Some(path) = &self.data_ingestion_dir {
564                    builder = builder.with_data_ingestion_dir(path.clone());
565                }
566
567                if let Some(state_sync_config) = &self.state_sync_config {
568                    builder = builder.with_state_sync_config(state_sync_config.clone());
569                }
570
571                #[cfg(msim)]
572                if let Some(execution_time_observer_config) = &self.execution_time_observer_config {
573                    builder = builder.with_execution_time_observer_config(
574                        execution_time_observer_config.clone(),
575                    );
576                }
577
578                if let Some(spvc) = &self.supported_protocol_versions_config {
579                    let supported_versions = match spvc {
580                        ProtocolVersionsConfig::Default => {
581                            SupportedProtocolVersions::SYSTEM_DEFAULT
582                        }
583                        ProtocolVersionsConfig::Global(v) => *v,
584                        ProtocolVersionsConfig::PerValidator(func) => {
585                            func(idx, Some(validator.key_pair.public().into()))
586                        }
587                    };
588                    builder = builder.with_supported_protocol_versions(supported_versions);
589                }
590                if let Some(acc_v2_config) = &self.global_state_hash_v2_enabled_config {
591                    let global_state_hash_v2_enabled: bool = match acc_v2_config {
592                        GlobalStateHashV2EnabledConfig::Global(enabled) => *enabled,
593                        GlobalStateHashV2EnabledConfig::PerValidator(func) => func(idx),
594                    };
595                    builder =
596                        builder.with_global_state_hash_v2_enabled(global_state_hash_v2_enabled);
597                }
598                if let Some(scheduler_type_config) = &self.funds_withdraw_scheduler_type_config {
599                    let scheduler_type = match scheduler_type_config {
600                        FundsWithdrawSchedulerTypeConfig::Global(t) => *t,
601                        FundsWithdrawSchedulerTypeConfig::PerValidator(func) => func(idx),
602                    };
603                    builder = builder.with_funds_withdraw_scheduler_type(scheduler_type);
604                }
605                if let Some(observer_config_fn) = &self.validator_observer_config
606                    && let Some(observer_config) = observer_config_fn(idx)
607                {
608                    builder = builder.with_observer_config(observer_config);
609                }
610                if let Some(num_unpruned_validators) = self.num_unpruned_validators
611                    && idx < num_unpruned_validators
612                {
613                    builder = builder.with_unpruned_checkpoints();
614                }
615                builder.build(validator, genesis.clone())
616            })
617            .collect();
618        NetworkConfig {
619            validator_configs,
620            genesis,
621            account_keys,
622        }
623    }
624}
625
626#[cfg(test)]
627mod tests {
628    use sui_config::node::Genesis;
629
630    #[test]
631    fn serialize_genesis_config_in_place() {
632        let dir = tempfile::TempDir::new().unwrap();
633        let network_config = crate::network_config_builder::ConfigBuilder::new(&dir).build();
634        let genesis = network_config.genesis;
635
636        let g = Genesis::new(genesis);
637
638        let mut s = serde_yaml::to_string(&g).unwrap();
639        let loaded_genesis: Genesis = serde_yaml::from_str(&s).unwrap();
640        loaded_genesis
641            .genesis()
642            .unwrap()
643            .checkpoint_contents()
644            .digest(); // cache digest before comparing.
645        assert_eq!(g, loaded_genesis);
646
647        // If both in-place and file location are provided, prefer the in-place variant
648        s.push_str("\ngenesis-file-location: path/to/file");
649        let loaded_genesis: Genesis = serde_yaml::from_str(&s).unwrap();
650        loaded_genesis
651            .genesis()
652            .unwrap()
653            .checkpoint_contents()
654            .digest(); // cache digest before comparing.
655        assert_eq!(g, loaded_genesis);
656    }
657
658    #[test]
659    fn load_genesis_config_from_file() {
660        let file = tempfile::NamedTempFile::new().unwrap();
661        let genesis_config = Genesis::new_from_file(file.path());
662
663        let dir = tempfile::TempDir::new().unwrap();
664        let network_config = crate::network_config_builder::ConfigBuilder::new(&dir).build();
665        let genesis = network_config.genesis;
666        genesis.save(file.path()).unwrap();
667
668        let loaded_genesis = genesis_config.genesis().unwrap();
669        loaded_genesis.checkpoint_contents().digest(); // cache digest before comparing.
670        assert_eq!(&genesis, loaded_genesis);
671    }
672}
673
674#[cfg(test)]
675mod test {
676    use std::sync::Arc;
677    use sui_config::genesis::Genesis;
678    use sui_protocol_config::{Chain, ProtocolConfig, ProtocolVersion};
679    use sui_types::epoch_data::EpochData;
680    use sui_types::execution_params::ExecutionOrEarlyError;
681    use sui_types::gas::SuiGasStatus;
682    use sui_types::in_memory_storage::InMemoryStorage;
683    use sui_types::metrics::ExecutionMetrics;
684    use sui_types::sui_system_state::SuiSystemStateTrait;
685    use sui_types::transaction::CheckedInputObjects;
686
687    #[test]
688    fn roundtrip() {
689        let dir = tempfile::TempDir::new().unwrap();
690        let network_config = crate::network_config_builder::ConfigBuilder::new(&dir).build();
691        let genesis = network_config.genesis;
692
693        let s = serde_yaml::to_string(&genesis).unwrap();
694        let from_s: Genesis = serde_yaml::from_str(&s).unwrap();
695        // cache the digest so that the comparison succeeds.
696        from_s.checkpoint_contents().digest();
697        assert_eq!(genesis, from_s);
698    }
699
700    #[test]
701    fn genesis_transaction() {
702        let builder = crate::network_config_builder::ConfigBuilder::new_with_temp_dir();
703        let network_config = builder.build();
704        let genesis = network_config.genesis;
705        let protocol_version = ProtocolVersion::new(genesis.sui_system_object().protocol_version());
706        let protocol_config = ProtocolConfig::get_for_version(protocol_version, Chain::Unknown);
707
708        let genesis_transaction = genesis.transaction().clone();
709
710        let genesis_digest = *genesis_transaction.digest();
711
712        let silent = true;
713        let executor = sui_execution::executor(&protocol_config, silent)
714            .expect("Creating an executor should not fail here");
715
716        // Use a throwaway metrics registry for genesis transaction execution.
717        let registry = prometheus::Registry::new();
718        let metrics = Arc::new(ExecutionMetrics::new(&registry));
719        let expensive_checks = false;
720        let epoch = EpochData::new_test();
721        let transaction_data = &genesis_transaction.data().intent_message().value;
722        let (kind, signer, mut gas_data) = transaction_data.execution_parts();
723        gas_data.payment = vec![];
724        let input_objects = CheckedInputObjects::new_for_genesis(vec![]);
725
726        let (_inner_temp_store, _, effects, _timings, _execution_error) = executor
727            .execute_transaction_to_effects(
728                &InMemoryStorage::new(Vec::new()),
729                &protocol_config,
730                metrics,
731                expensive_checks,
732                ExecutionOrEarlyError::Ok(()),
733                &epoch.epoch_id(),
734                epoch.epoch_start_timestamp(),
735                input_objects,
736                gas_data,
737                SuiGasStatus::new_unmetered(),
738                kind,
739                None, // compat_args
740                signer,
741                genesis_digest,
742                &mut None,
743            );
744
745        assert_eq!(&effects, genesis.effects());
746    }
747}