Skip to main content

iota_core/authority/
test_authority_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::{path::PathBuf, sync::Arc};
6
7use fastcrypto::traits::KeyPair;
8use iota_config::{
9    ExecutionCacheConfig,
10    certificate_deny_config::CertificateDenyConfig,
11    genesis::Genesis,
12    node::{AuthorityOverloadConfig, AuthorityStorePruningConfig, ExpensiveSafetyCheckConfig},
13    transaction_deny_config::TransactionDenyConfig,
14    verifier_signing_config::VerifierSigningConfig,
15};
16use iota_network::randomness;
17use iota_protocol_config::{Chain, ProtocolConfig};
18use iota_swarm_config::{genesis_config::AccountConfig, network_config::NetworkConfig};
19use iota_types::{
20    base_types::AuthorityName, crypto::AuthorityKeyPair, digests::ChainIdentifier,
21    executable_transaction::VerifiedExecutableTransaction, iota_system_state::IotaSystemStateTrait,
22    object::Object, supported_protocol_versions::SupportedProtocolVersions,
23    transaction::VerifiedTransaction,
24};
25use prometheus_filtered::Registry;
26
27use super::{
28    ExecutionEnv, backpressure::BackpressureManager, epoch_start_configuration::EpochFlag,
29};
30use crate::{
31    authority::{
32        AuthorityState, AuthorityStore,
33        authority_per_epoch_store::AuthorityPerEpochStore,
34        authority_store_tables::{AuthorityPerpetualTables, AuthorityPerpetualTablesOptions},
35        epoch_start_configuration::EpochStartConfiguration,
36    },
37    checkpoints::CheckpointStore,
38    epoch::{
39        committee_store::CommitteeStore, epoch_metrics::EpochMetrics, randomness::RandomnessManager,
40    },
41    execution_cache::build_execution_cache,
42    grpc_indexes::{GRPC_INDEXES_DIR, GrpcIndexesStore},
43    jsonrpc_index::IndexStore,
44    mock_consensus::{ConsensusMode, MockConsensusClient},
45    module_cache_metrics::ResolverMetrics,
46    signature_verifier::SignatureVerifierMetrics,
47};
48
49#[derive(Default, Clone)]
50pub struct TestAuthorityBuilder<'a> {
51    store_base_path: Option<PathBuf>,
52    store: Option<Arc<AuthorityStore>>,
53    transaction_deny_config: Option<TransactionDenyConfig>,
54    certificate_deny_config: Option<CertificateDenyConfig>,
55    verifier_signing_config: Option<VerifierSigningConfig>,
56    protocol_config: Option<ProtocolConfig>,
57    reference_gas_price: Option<u64>,
58    node_keypair: Option<&'a AuthorityKeyPair>,
59    genesis: Option<&'a Genesis>,
60    starting_objects: Option<&'a [Object]>,
61    expensive_safety_checks: Option<ExpensiveSafetyCheckConfig>,
62    disable_indexer: bool,
63    accounts: Vec<AccountConfig>,
64    /// By default, we don't insert the genesis checkpoint, which isn't needed
65    /// by most tests.
66    insert_genesis_checkpoint: bool,
67    authority_overload_config: Option<AuthorityOverloadConfig>,
68    cache_config: Option<ExecutionCacheConfig>,
69    disable_execute_genesis_transactions: bool,
70    chain_override: Option<Chain>,
71}
72
73impl<'a> TestAuthorityBuilder<'a> {
74    pub fn new() -> Self {
75        Self::default()
76    }
77
78    pub fn with_store_base_path(mut self, path: PathBuf) -> Self {
79        assert!(self.store_base_path.replace(path).is_none());
80        self
81    }
82
83    pub fn with_starting_objects(mut self, objects: &'a [Object]) -> Self {
84        assert!(self.starting_objects.replace(objects).is_none());
85        self
86    }
87
88    pub fn with_store(mut self, store: Arc<AuthorityStore>) -> Self {
89        assert!(self.store.replace(store).is_none());
90        self
91    }
92
93    pub fn with_transaction_deny_config(mut self, config: TransactionDenyConfig) -> Self {
94        assert!(self.transaction_deny_config.replace(config).is_none());
95        self
96    }
97
98    pub fn with_certificate_deny_config(mut self, config: CertificateDenyConfig) -> Self {
99        assert!(self.certificate_deny_config.replace(config).is_none());
100        self
101    }
102
103    pub fn with_verifier_signing_config(mut self, config: VerifierSigningConfig) -> Self {
104        assert!(self.verifier_signing_config.replace(config).is_none());
105        self
106    }
107
108    pub fn with_protocol_config(mut self, config: ProtocolConfig) -> Self {
109        assert!(self.protocol_config.replace(config).is_none());
110        self
111    }
112
113    pub fn with_reference_gas_price(mut self, reference_gas_price: u64) -> Self {
114        // If genesis is already set then setting rgp is meaningless since it will be
115        // overwritten.
116        assert!(self.genesis.is_none());
117        assert!(
118            self.reference_gas_price
119                .replace(reference_gas_price)
120                .is_none()
121        );
122        self
123    }
124
125    pub fn with_genesis_and_keypair(
126        mut self,
127        genesis: &'a Genesis,
128        keypair: &'a AuthorityKeyPair,
129    ) -> Self {
130        assert!(self.genesis.replace(genesis).is_none());
131        assert!(self.node_keypair.replace(keypair).is_none());
132        self
133    }
134
135    pub fn with_keypair(mut self, keypair: &'a AuthorityKeyPair) -> Self {
136        assert!(self.node_keypair.replace(keypair).is_none());
137        self
138    }
139
140    /// When providing a network config, we will use the \node_idx validator's
141    /// key as the keypair for the new node.
142    pub fn with_network_config(self, config: &'a NetworkConfig, node_idx: usize) -> Self {
143        self.with_genesis_and_keypair(
144            &config.genesis,
145            config.validator_configs()[node_idx].authority_key_pair(),
146        )
147    }
148
149    pub fn disable_indexer(mut self) -> Self {
150        self.disable_indexer = true;
151        self
152    }
153
154    pub fn insert_genesis_checkpoint(mut self) -> Self {
155        self.insert_genesis_checkpoint = true;
156        self
157    }
158
159    pub fn with_expensive_safety_checks(mut self, config: ExpensiveSafetyCheckConfig) -> Self {
160        assert!(self.expensive_safety_checks.replace(config).is_none());
161        self
162    }
163
164    pub fn with_accounts(mut self, accounts: Vec<AccountConfig>) -> Self {
165        self.accounts = accounts;
166        self
167    }
168
169    pub fn with_authority_overload_config(mut self, config: AuthorityOverloadConfig) -> Self {
170        assert!(self.authority_overload_config.replace(config).is_none());
171        self
172    }
173
174    pub fn with_cache_config(mut self, config: ExecutionCacheConfig) -> Self {
175        self.cache_config = Some(config);
176        self
177    }
178
179    pub fn disable_execute_genesis_transactions(mut self) -> Self {
180        self.disable_execute_genesis_transactions = true;
181        self
182    }
183
184    pub fn with_chain_override(mut self, chain: Chain) -> Self {
185        self.chain_override = Some(chain);
186        self
187    }
188
189    pub async fn build(self) -> Arc<AuthorityState> {
190        let protocol_config = self.protocol_config.clone();
191
192        // Genesis must build the system framework at the binary format version it was
193        // compiled with. A test override that lowers `move_binary_format_version`
194        // must not apply while genesis verifies the system packages.
195        // Build genesis with the framework's binary format version
196        // restored, then apply the unmodified override below for transaction
197        // execution.
198        let local_network_config = {
199            let _genesis_guard = protocol_config.clone().map(|mut config| {
200                let framework_binary_format_version =
201                    ProtocolConfig::get_for_version(config.version, Chain::Unknown)
202                        .move_binary_format_version();
203                config.set_move_binary_format_version_for_testing(framework_binary_format_version);
204                ProtocolConfig::apply_overrides_for_testing(move |_, _| config.clone())
205            });
206
207            let mut local_network_config_builder =
208                iota_swarm_config::network_config_builder::ConfigBuilder::new_with_temp_dir()
209                    .with_accounts(self.accounts)
210                    .with_reference_gas_price(self.reference_gas_price.unwrap_or(500));
211            if let Some(protocol_config) = &self.protocol_config {
212                local_network_config_builder =
213                    local_network_config_builder.with_protocol_version(protocol_config.version);
214            }
215            local_network_config_builder.build()
216        };
217
218        // `_guard` must be declared here so it is not dropped before
219        // `AuthorityPerEpochStore::new` is called
220        let _guard = protocol_config
221            .map(|config| ProtocolConfig::apply_overrides_for_testing(move |_, _| config.clone()));
222
223        let genesis = &self.genesis.unwrap_or(&local_network_config.genesis);
224        let genesis_committee = genesis.committee().unwrap();
225        let storage_dir = self
226            .store_base_path
227            .unwrap_or_else(|| iota_common::tempdir().keep());
228        let mut config = local_network_config.validator_configs()[0].clone();
229        let registry = Registry::new();
230        let authority_store = match self.store {
231            Some(store) => store,
232            None => {
233                let perpetual_tables_options = AuthorityPerpetualTablesOptions::default();
234                let perpetual_tables = Arc::new(AuthorityPerpetualTables::open(
235                    &storage_dir.join("store"),
236                    Some(perpetual_tables_options),
237                ));
238                // unwrap ok - for testing only.
239                AuthorityStore::open_with_committee_for_testing(
240                    perpetual_tables,
241                    &genesis_committee,
242                    genesis,
243                )
244                .await
245                .unwrap()
246            }
247        };
248        if let Some(cache_config) = self.cache_config {
249            config.execution_cache_config = cache_config;
250        }
251
252        let keypair = if let Some(keypair) = self.node_keypair {
253            keypair.copy()
254        } else {
255            config.authority_key_pair().copy()
256        };
257
258        let secret = Arc::pin(keypair.copy());
259        let name: AuthorityName = secret.public().into();
260        let cache_metrics = Arc::new(ResolverMetrics::new(&registry));
261        let signature_verifier_metrics = SignatureVerifierMetrics::new(&registry);
262        let epoch_flags = EpochFlag::default_flags_for_new_epoch(&config);
263        let epoch_start_configuration = EpochStartConfiguration::new(
264            genesis.iota_system_object().into_epoch_start_state(),
265            *genesis.checkpoint().digest(),
266            &genesis.objects(),
267            epoch_flags,
268        )
269        .unwrap();
270        let expensive_safety_checks = self.expensive_safety_checks.unwrap_or_default();
271
272        let checkpoint_store = CheckpointStore::new(&storage_dir.join("checkpoints"));
273        let backpressure_manager =
274            BackpressureManager::new_from_checkpoint_store(&checkpoint_store);
275
276        let cache_traits = build_execution_cache(
277            &config.execution_cache_config,
278            &registry,
279            &authority_store,
280            backpressure_manager.clone(),
281        );
282
283        let chain_id = ChainIdentifier::from(*genesis.checkpoint().digest());
284        let chain = match self.chain_override {
285            Some(chain) => chain,
286            None => chain_id.chain(),
287        };
288
289        let epoch_store = AuthorityPerEpochStore::new(
290            name,
291            Arc::new(genesis_committee.clone()),
292            &storage_dir.join("store"),
293            None,
294            EpochMetrics::new(&registry),
295            epoch_start_configuration,
296            cache_traits.backing_package_store.clone(),
297            cache_metrics,
298            signature_verifier_metrics,
299            &expensive_safety_checks,
300            (chain_id, chain),
301            checkpoint_store
302                .get_highest_executed_checkpoint_seq_number()
303                .unwrap()
304                .unwrap_or(0),
305        )
306        .expect("failed to create authority per epoch store");
307        let committee_store = Arc::new(CommitteeStore::new(
308            storage_dir.join("epochs"),
309            &genesis_committee,
310            None,
311        ));
312
313        if self.insert_genesis_checkpoint {
314            checkpoint_store.insert_genesis_checkpoint(
315                genesis.checkpoint(),
316                genesis.checkpoint_contents().clone(),
317                &epoch_store,
318            );
319        }
320        let index_store = if self.disable_indexer {
321            None
322        } else {
323            Some(Arc::new(IndexStore::new(
324                storage_dir.join("indexes"),
325                &registry,
326                epoch_store
327                    .protocol_config()
328                    .max_move_identifier_len_as_option(),
329            )))
330        };
331        let grpc_indexes_store = if self.disable_indexer {
332            None
333        } else {
334            Some(Arc::new(
335                GrpcIndexesStore::new(
336                    storage_dir.join(GRPC_INDEXES_DIR),
337                    Arc::clone(&authority_store),
338                    &checkpoint_store,
339                )
340                .await,
341            ))
342        };
343
344        let transaction_deny_config = self.transaction_deny_config.unwrap_or_default();
345        let certificate_deny_config = self.certificate_deny_config.unwrap_or_default();
346        let verifier_signing_config = self.verifier_signing_config.unwrap_or_default();
347        let authority_overload_config = self.authority_overload_config.unwrap_or_default();
348        let pruning_config = AuthorityStorePruningConfig::default();
349
350        config.transaction_deny_config = transaction_deny_config;
351        config.certificate_deny_config = certificate_deny_config;
352        config.verifier_signing_config = verifier_signing_config;
353        config.authority_overload_config = authority_overload_config;
354        config.authority_store_pruning_config = pruning_config;
355
356        let chain_identifier = ChainIdentifier::from(*genesis.checkpoint().digest());
357        let policy_config = config.policy_config.clone();
358        let firewall_config = config.firewall_config.clone();
359
360        let state = AuthorityState::new(
361            name,
362            secret,
363            SupportedProtocolVersions::SYSTEM_DEFAULT,
364            authority_store,
365            cache_traits,
366            epoch_store.clone(),
367            committee_store,
368            index_store,
369            grpc_indexes_store,
370            checkpoint_store,
371            &registry,
372            genesis.objects(),
373            config.clone(),
374            None,
375            chain_identifier,
376            None,
377            policy_config,
378            firewall_config,
379        )
380        .await;
381
382        // Set up randomness with no-op consensus (DKG will not complete).
383        let consensus_client = Box::new(MockConsensusClient::new(
384            Arc::downgrade(&state),
385            ConsensusMode::Noop,
386        ));
387        let randomness_manager = RandomnessManager::try_new(
388            Arc::downgrade(&epoch_store),
389            consensus_client,
390            randomness::Handle::new_stub(),
391            &keypair,
392        )
393        .await;
394        if let Ok(randomness_manager) = randomness_manager {
395            // Randomness might fail if test configuration does not permit DKG init.
396            // In that case, skip setting it up.
397            epoch_store
398                .set_randomness_manager(randomness_manager)
399                .await
400                .unwrap();
401        }
402
403        if !self.disable_execute_genesis_transactions {
404            // For any type of local testing that does not actually spawn a node, the
405            // checkpoint executor won't be started, which means we won't actually
406            // execute the genesis transaction. In that case, the genesis objects
407            // (e.g. all the genesis test coins) won't be accessible. Executing it
408            // explicitly makes sure all genesis objects are ready for use.
409            state
410                .try_execute_immediately(
411                    &VerifiedExecutableTransaction::new_from_checkpoint(
412                        VerifiedTransaction::new_unchecked(genesis.transaction().clone()),
413                        genesis.epoch(),
414                        genesis.checkpoint().sequence_number,
415                    ),
416                    ExecutionEnv::new(),
417                    &state.epoch_store_for_testing(),
418                )
419                .unwrap();
420
421            let batch = state.get_cache_commit().build_db_batch(
422                epoch_store.epoch(),
423                genesis.checkpoint().sequence_number,
424                &[*genesis.transaction().digest()],
425            );
426
427            state.get_cache_commit().commit_transaction_outputs(
428                epoch_store.epoch(),
429                batch,
430                &[*genesis.transaction().digest()],
431            );
432        }
433
434        // We want to insert these objects directly instead of relying on genesis
435        // because genesis process would set the previous transaction field for
436        // these objects, which would change their object digest. This makes it
437        // difficult to write tests that want to use these objects directly.
438        // TODO: we should probably have a better way to do this.
439        if let Some(starting_objects) = self.starting_objects {
440            state
441                .insert_objects_unsafe_for_testing_only(starting_objects)
442                .await;
443        };
444        state
445    }
446}