1use 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_pruner::ObjectsCompactionFilter,
35 authority_store_tables::{
36 AuthorityPerpetualTables, AuthorityPerpetualTablesOptions, AuthorityPrunerTables,
37 },
38 epoch_start_configuration::EpochStartConfiguration,
39 },
40 checkpoints::CheckpointStore,
41 epoch::{
42 committee_store::CommitteeStore, epoch_metrics::EpochMetrics, randomness::RandomnessManager,
43 },
44 execution_cache::build_execution_cache,
45 grpc_indexes::{GRPC_INDEXES_DIR, GrpcIndexesStore},
46 jsonrpc_index::IndexStore,
47 mock_consensus::{ConsensusMode, MockConsensusClient},
48 module_cache_metrics::ResolverMetrics,
49 signature_verifier::SignatureVerifierMetrics,
50};
51
52#[derive(Default, Clone)]
53pub struct TestAuthorityBuilder<'a> {
54 store_base_path: Option<PathBuf>,
55 store: Option<Arc<AuthorityStore>>,
56 transaction_deny_config: Option<TransactionDenyConfig>,
57 certificate_deny_config: Option<CertificateDenyConfig>,
58 verifier_signing_config: Option<VerifierSigningConfig>,
59 protocol_config: Option<ProtocolConfig>,
60 reference_gas_price: Option<u64>,
61 node_keypair: Option<&'a AuthorityKeyPair>,
62 genesis: Option<&'a Genesis>,
63 starting_objects: Option<&'a [Object]>,
64 expensive_safety_checks: Option<ExpensiveSafetyCheckConfig>,
65 disable_indexer: bool,
66 accounts: Vec<AccountConfig>,
67 insert_genesis_checkpoint: bool,
70 authority_overload_config: Option<AuthorityOverloadConfig>,
71 cache_config: Option<ExecutionCacheConfig>,
72 disable_execute_genesis_transactions: bool,
73 chain_override: Option<Chain>,
74}
75
76impl<'a> TestAuthorityBuilder<'a> {
77 pub fn new() -> Self {
78 Self::default()
79 }
80
81 pub fn with_store_base_path(mut self, path: PathBuf) -> Self {
82 assert!(self.store_base_path.replace(path).is_none());
83 self
84 }
85
86 pub fn with_starting_objects(mut self, objects: &'a [Object]) -> Self {
87 assert!(self.starting_objects.replace(objects).is_none());
88 self
89 }
90
91 pub fn with_store(mut self, store: Arc<AuthorityStore>) -> Self {
92 assert!(self.store.replace(store).is_none());
93 self
94 }
95
96 pub fn with_transaction_deny_config(mut self, config: TransactionDenyConfig) -> Self {
97 assert!(self.transaction_deny_config.replace(config).is_none());
98 self
99 }
100
101 pub fn with_certificate_deny_config(mut self, config: CertificateDenyConfig) -> Self {
102 assert!(self.certificate_deny_config.replace(config).is_none());
103 self
104 }
105
106 pub fn with_verifier_signing_config(mut self, config: VerifierSigningConfig) -> Self {
107 assert!(self.verifier_signing_config.replace(config).is_none());
108 self
109 }
110
111 pub fn with_protocol_config(mut self, config: ProtocolConfig) -> Self {
112 assert!(self.protocol_config.replace(config).is_none());
113 self
114 }
115
116 pub fn with_reference_gas_price(mut self, reference_gas_price: u64) -> Self {
117 assert!(self.genesis.is_none());
120 assert!(
121 self.reference_gas_price
122 .replace(reference_gas_price)
123 .is_none()
124 );
125 self
126 }
127
128 pub fn with_genesis_and_keypair(
129 mut self,
130 genesis: &'a Genesis,
131 keypair: &'a AuthorityKeyPair,
132 ) -> Self {
133 assert!(self.genesis.replace(genesis).is_none());
134 assert!(self.node_keypair.replace(keypair).is_none());
135 self
136 }
137
138 pub fn with_keypair(mut self, keypair: &'a AuthorityKeyPair) -> Self {
139 assert!(self.node_keypair.replace(keypair).is_none());
140 self
141 }
142
143 pub fn with_network_config(self, config: &'a NetworkConfig, node_idx: usize) -> Self {
146 self.with_genesis_and_keypair(
147 &config.genesis,
148 config.validator_configs()[node_idx].authority_key_pair(),
149 )
150 }
151
152 pub fn disable_indexer(mut self) -> Self {
153 self.disable_indexer = true;
154 self
155 }
156
157 pub fn insert_genesis_checkpoint(mut self) -> Self {
158 self.insert_genesis_checkpoint = true;
159 self
160 }
161
162 pub fn with_expensive_safety_checks(mut self, config: ExpensiveSafetyCheckConfig) -> Self {
163 assert!(self.expensive_safety_checks.replace(config).is_none());
164 self
165 }
166
167 pub fn with_accounts(mut self, accounts: Vec<AccountConfig>) -> Self {
168 self.accounts = accounts;
169 self
170 }
171
172 pub fn with_authority_overload_config(mut self, config: AuthorityOverloadConfig) -> Self {
173 assert!(self.authority_overload_config.replace(config).is_none());
174 self
175 }
176
177 pub fn with_cache_config(mut self, config: ExecutionCacheConfig) -> Self {
178 self.cache_config = Some(config);
179 self
180 }
181
182 pub fn disable_execute_genesis_transactions(mut self) -> Self {
183 self.disable_execute_genesis_transactions = true;
184 self
185 }
186
187 pub fn with_chain_override(mut self, chain: Chain) -> Self {
188 self.chain_override = Some(chain);
189 self
190 }
191
192 pub async fn build(self) -> Arc<AuthorityState> {
193 let protocol_config = self.protocol_config.clone();
194
195 let local_network_config = {
202 let _genesis_guard = protocol_config.clone().map(|mut config| {
203 let framework_binary_format_version =
204 ProtocolConfig::get_for_version(config.version, Chain::Unknown)
205 .move_binary_format_version();
206 config.set_move_binary_format_version_for_testing(framework_binary_format_version);
207 ProtocolConfig::apply_overrides_for_testing(move |_, _| config.clone())
208 });
209
210 let mut local_network_config_builder =
211 iota_swarm_config::network_config_builder::ConfigBuilder::new_with_temp_dir()
212 .with_accounts(self.accounts)
213 .with_reference_gas_price(self.reference_gas_price.unwrap_or(500));
214 if let Some(protocol_config) = &self.protocol_config {
215 local_network_config_builder =
216 local_network_config_builder.with_protocol_version(protocol_config.version);
217 }
218 local_network_config_builder.build()
219 };
220
221 let _guard = protocol_config
224 .map(|config| ProtocolConfig::apply_overrides_for_testing(move |_, _| config.clone()));
225
226 let genesis = &self.genesis.unwrap_or(&local_network_config.genesis);
227 let genesis_committee = genesis.committee().unwrap();
228 let storage_dir = self
229 .store_base_path
230 .unwrap_or_else(|| iota_common::tempdir().keep());
231 let mut config = local_network_config.validator_configs()[0].clone();
232 let registry = Registry::new();
233 let mut pruner_db = None;
234 if config
235 .authority_store_pruning_config
236 .enable_compaction_filter
237 {
238 pruner_db = Some(Arc::new(AuthorityPrunerTables::open(
239 &storage_dir.join("store"),
240 )));
241 }
242 let compaction_filter = pruner_db
243 .clone()
244 .map(|db| ObjectsCompactionFilter::new(db, ®istry));
245
246 let authority_store = match self.store {
247 Some(store) => store,
248 None => {
249 let perpetual_tables_options = AuthorityPerpetualTablesOptions {
250 compaction_filter,
251 ..Default::default()
252 };
253 let perpetual_tables = Arc::new(AuthorityPerpetualTables::open(
254 &storage_dir.join("store"),
255 Some(perpetual_tables_options),
256 ));
257 AuthorityStore::open_with_committee_for_testing(
259 perpetual_tables,
260 &genesis_committee,
261 genesis,
262 )
263 .await
264 .unwrap()
265 }
266 };
267 if let Some(cache_config) = self.cache_config {
268 config.execution_cache_config = cache_config;
269 }
270
271 let keypair = if let Some(keypair) = self.node_keypair {
272 keypair.copy()
273 } else {
274 config.authority_key_pair().copy()
275 };
276
277 let secret = Arc::pin(keypair.copy());
278 let name: AuthorityName = secret.public().into();
279 let cache_metrics = Arc::new(ResolverMetrics::new(®istry));
280 let signature_verifier_metrics = SignatureVerifierMetrics::new(®istry);
281 let epoch_flags = EpochFlag::default_flags_for_new_epoch(&config);
282 let epoch_start_configuration = EpochStartConfiguration::new(
283 genesis.iota_system_object().into_epoch_start_state(),
284 *genesis.checkpoint().digest(),
285 &genesis.objects(),
286 epoch_flags,
287 )
288 .unwrap();
289 let expensive_safety_checks = self.expensive_safety_checks.unwrap_or_default();
290
291 let checkpoint_store = CheckpointStore::new(&storage_dir.join("checkpoints"));
292 let backpressure_manager =
293 BackpressureManager::new_from_checkpoint_store(&checkpoint_store);
294
295 let cache_traits = build_execution_cache(
296 &config.execution_cache_config,
297 ®istry,
298 &authority_store,
299 backpressure_manager.clone(),
300 );
301
302 let chain_id = ChainIdentifier::from(*genesis.checkpoint().digest());
303 let chain = match self.chain_override {
304 Some(chain) => chain,
305 None => chain_id.chain(),
306 };
307
308 let epoch_store = AuthorityPerEpochStore::new(
309 name,
310 Arc::new(genesis_committee.clone()),
311 &storage_dir.join("store"),
312 None,
313 EpochMetrics::new(®istry),
314 epoch_start_configuration,
315 cache_traits.backing_package_store.clone(),
316 cache_metrics,
317 signature_verifier_metrics,
318 &expensive_safety_checks,
319 (chain_id, chain),
320 checkpoint_store
321 .get_highest_executed_checkpoint_seq_number()
322 .unwrap()
323 .unwrap_or(0),
324 )
325 .expect("failed to create authority per epoch store");
326 let committee_store = Arc::new(CommitteeStore::new(
327 storage_dir.join("epochs"),
328 &genesis_committee,
329 None,
330 ));
331
332 if self.insert_genesis_checkpoint {
333 checkpoint_store.insert_genesis_checkpoint(
334 genesis.checkpoint(),
335 genesis.checkpoint_contents().clone(),
336 &epoch_store,
337 );
338 }
339 let index_store = if self.disable_indexer {
340 None
341 } else {
342 Some(Arc::new(IndexStore::new(
343 storage_dir.join("indexes"),
344 ®istry,
345 epoch_store
346 .protocol_config()
347 .max_move_identifier_len_as_option(),
348 )))
349 };
350 let grpc_indexes_store = if self.disable_indexer {
351 None
352 } else {
353 Some(Arc::new(
354 GrpcIndexesStore::new(
355 storage_dir.join(GRPC_INDEXES_DIR),
356 Arc::clone(&authority_store),
357 &checkpoint_store,
358 )
359 .await,
360 ))
361 };
362
363 let transaction_deny_config = self.transaction_deny_config.unwrap_or_default();
364 let certificate_deny_config = self.certificate_deny_config.unwrap_or_default();
365 let verifier_signing_config = self.verifier_signing_config.unwrap_or_default();
366 let authority_overload_config = self.authority_overload_config.unwrap_or_default();
367 let pruning_config = AuthorityStorePruningConfig::default();
368
369 config.transaction_deny_config = transaction_deny_config;
370 config.certificate_deny_config = certificate_deny_config;
371 config.verifier_signing_config = verifier_signing_config;
372 config.authority_overload_config = authority_overload_config;
373 config.authority_store_pruning_config = pruning_config;
374
375 let chain_identifier = ChainIdentifier::from(*genesis.checkpoint().digest());
376 let policy_config = config.policy_config.clone();
377 let firewall_config = config.firewall_config.clone();
378
379 let state = AuthorityState::new(
380 name,
381 secret,
382 SupportedProtocolVersions::SYSTEM_DEFAULT,
383 authority_store,
384 cache_traits,
385 epoch_store.clone(),
386 committee_store,
387 index_store,
388 grpc_indexes_store,
389 checkpoint_store,
390 ®istry,
391 genesis.objects(),
392 config.clone(),
393 None,
394 chain_identifier,
395 pruner_db,
396 None,
397 policy_config,
398 firewall_config,
399 )
400 .await;
401
402 let consensus_client = Box::new(MockConsensusClient::new(
404 Arc::downgrade(&state),
405 ConsensusMode::Noop,
406 ));
407 let randomness_manager = RandomnessManager::try_new(
408 Arc::downgrade(&epoch_store),
409 consensus_client,
410 randomness::Handle::new_stub(),
411 &keypair,
412 )
413 .await;
414 if let Ok(randomness_manager) = randomness_manager {
415 epoch_store
418 .set_randomness_manager(randomness_manager)
419 .await
420 .unwrap();
421 }
422
423 if !self.disable_execute_genesis_transactions {
424 state
430 .try_execute_immediately(
431 &VerifiedExecutableTransaction::new_from_checkpoint(
432 VerifiedTransaction::new_unchecked(genesis.transaction().clone()),
433 genesis.epoch(),
434 genesis.checkpoint().sequence_number,
435 ),
436 ExecutionEnv::new(),
437 &state.epoch_store_for_testing(),
438 )
439 .unwrap();
440
441 let batch = state.get_cache_commit().build_db_batch(
442 epoch_store.epoch(),
443 genesis.checkpoint().sequence_number,
444 &[*genesis.transaction().digest()],
445 );
446
447 state.get_cache_commit().commit_transaction_outputs(
448 epoch_store.epoch(),
449 batch,
450 &[*genesis.transaction().digest()],
451 );
452 }
453
454 if let Some(starting_objects) = self.starting_objects {
460 state
461 .insert_objects_unsafe_for_testing_only(starting_objects)
462 .await;
463 };
464 state
465 }
466}