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