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