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