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