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