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