Skip to main content

iota_swarm_config/
network_config_builder.rs

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