Skip to main content

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