1use std::{path::PathBuf, sync::Arc};
6
7use fastcrypto::traits::KeyPair;
8use iota_archival::reader::ArchiveReaderBalancer;
9use iota_config::{
10 ExecutionCacheConfig,
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_filtered::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_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_protocol_config(mut self, config: ProtocolConfig) -> Self {
107 assert!(self.protocol_config.replace(config).is_none());
108 self
109 }
110
111 pub fn with_reference_gas_price(mut self, reference_gas_price: u64) -> Self {
112 assert!(self.genesis.is_none());
115 assert!(
116 self.reference_gas_price
117 .replace(reference_gas_price)
118 .is_none()
119 );
120 self
121 }
122
123 pub fn with_genesis_and_keypair(
124 mut self,
125 genesis: &'a Genesis,
126 keypair: &'a AuthorityKeyPair,
127 ) -> Self {
128 assert!(self.genesis.replace(genesis).is_none());
129 assert!(self.node_keypair.replace(keypair).is_none());
130 self
131 }
132
133 pub fn with_keypair(mut self, keypair: &'a AuthorityKeyPair) -> Self {
134 assert!(self.node_keypair.replace(keypair).is_none());
135 self
136 }
137
138 pub fn with_network_config(self, config: &'a NetworkConfig, node_idx: usize) -> Self {
141 self.with_genesis_and_keypair(
142 &config.genesis,
143 config.validator_configs()[node_idx].authority_key_pair(),
144 )
145 }
146
147 pub fn disable_indexer(mut self) -> Self {
148 self.disable_indexer = true;
149 self
150 }
151
152 pub fn insert_genesis_checkpoint(mut self) -> Self {
153 self.insert_genesis_checkpoint = true;
154 self
155 }
156
157 pub fn with_expensive_safety_checks(mut self, config: ExpensiveSafetyCheckConfig) -> Self {
158 assert!(self.expensive_safety_checks.replace(config).is_none());
159 self
160 }
161
162 pub fn with_accounts(mut self, accounts: Vec<AccountConfig>) -> Self {
163 self.accounts = accounts;
164 self
165 }
166
167 pub fn with_authority_overload_config(mut self, config: AuthorityOverloadConfig) -> Self {
168 assert!(self.authority_overload_config.replace(config).is_none());
169 self
170 }
171
172 pub fn with_cache_config(mut self, config: ExecutionCacheConfig) -> Self {
173 self.cache_config = Some(config);
174 self
175 }
176
177 pub fn disable_execute_genesis_transactions(mut self) -> Self {
178 self.disable_execute_genesis_transactions = true;
179 self
180 }
181
182 pub fn with_chain_override(mut self, chain: Chain) -> Self {
183 self.chain_override = Some(chain);
184 self
185 }
186
187 pub async fn build(self) -> Arc<AuthorityState> {
188 let protocol_config = self.protocol_config.clone();
189
190 let local_network_config = {
197 let _genesis_guard = protocol_config.clone().map(|mut config| {
198 let framework_binary_format_version =
199 ProtocolConfig::get_for_version(config.version, Chain::Unknown)
200 .move_binary_format_version();
201 config.set_move_binary_format_version_for_testing(framework_binary_format_version);
202 ProtocolConfig::apply_overrides_for_testing(move |_, _| config.clone())
203 });
204
205 let mut local_network_config_builder =
206 iota_swarm_config::network_config_builder::ConfigBuilder::new_with_temp_dir()
207 .with_accounts(self.accounts)
208 .with_reference_gas_price(self.reference_gas_price.unwrap_or(500));
209 if let Some(protocol_config) = &self.protocol_config {
210 local_network_config_builder =
211 local_network_config_builder.with_protocol_version(protocol_config.version);
212 }
213 local_network_config_builder.build()
214 };
215
216 let _guard = protocol_config
219 .map(|config| ProtocolConfig::apply_overrides_for_testing(move |_, _| config.clone()));
220
221 let genesis = &self.genesis.unwrap_or(&local_network_config.genesis);
222 let genesis_committee = genesis.committee().unwrap();
223 let storage_dir = self
224 .store_base_path
225 .unwrap_or_else(|| iota_common::tempdir().keep());
226 let mut config = local_network_config.validator_configs()[0].clone();
227 let registry = Registry::new();
228 let mut pruner_db = None;
229 if config
230 .authority_store_pruning_config
231 .enable_compaction_filter
232 {
233 pruner_db = Some(Arc::new(AuthorityPrunerTables::open(
234 &storage_dir.join("store"),
235 )));
236 }
237 let compaction_filter = pruner_db
238 .clone()
239 .map(|db| ObjectsCompactionFilter::new(db, ®istry));
240
241 let authority_store = match self.store {
242 Some(store) => store,
243 None => {
244 let perpetual_tables_options = AuthorityPerpetualTablesOptions {
245 compaction_filter,
246 ..Default::default()
247 };
248 let perpetual_tables = Arc::new(AuthorityPerpetualTables::open(
249 &storage_dir.join("store"),
250 Some(perpetual_tables_options),
251 ));
252 AuthorityStore::open_with_committee_for_testing(
254 perpetual_tables,
255 &genesis_committee,
256 genesis,
257 )
258 .await
259 .unwrap()
260 }
261 };
262 if let Some(cache_config) = self.cache_config {
263 config.execution_cache_config = cache_config;
264 }
265
266 let keypair = if let Some(keypair) = self.node_keypair {
267 keypair.copy()
268 } else {
269 config.authority_key_pair().copy()
270 };
271
272 let secret = Arc::pin(keypair.copy());
273 let name: AuthorityName = secret.public().into();
274 let cache_metrics = Arc::new(ResolverMetrics::new(®istry));
275 let signature_verifier_metrics = SignatureVerifierMetrics::new(®istry);
276 let epoch_flags = EpochFlag::default_flags_for_new_epoch(&config);
277 let epoch_start_configuration = EpochStartConfiguration::new(
278 genesis.iota_system_object().into_epoch_start_state(),
279 *genesis.checkpoint().digest(),
280 &genesis.objects(),
281 epoch_flags,
282 )
283 .unwrap();
284 let expensive_safety_checks = self.expensive_safety_checks.unwrap_or_default();
285
286 let checkpoint_store = CheckpointStore::new(&storage_dir.join("checkpoints"));
287 let backpressure_manager =
288 BackpressureManager::new_from_checkpoint_store(&checkpoint_store);
289
290 let cache_traits = build_execution_cache(
291 &config.execution_cache_config,
292 ®istry,
293 &authority_store,
294 backpressure_manager.clone(),
295 );
296
297 let chain_id = ChainIdentifier::from(*genesis.checkpoint().digest());
298 let chain = match self.chain_override {
299 Some(chain) => chain,
300 None => chain_id.chain(),
301 };
302
303 let epoch_store = AuthorityPerEpochStore::new(
304 name,
305 Arc::new(genesis_committee.clone()),
306 &storage_dir.join("store"),
307 None,
308 EpochMetrics::new(®istry),
309 epoch_start_configuration,
310 cache_traits.backing_package_store.clone(),
311 cache_metrics,
312 signature_verifier_metrics,
313 &expensive_safety_checks,
314 (chain_id, chain),
315 checkpoint_store
316 .get_highest_executed_checkpoint_seq_number()
317 .unwrap()
318 .unwrap_or(0),
319 )
320 .expect("failed to create authority per epoch store");
321 let committee_store = Arc::new(CommitteeStore::new(
322 storage_dir.join("epochs"),
323 &genesis_committee,
324 None,
325 ));
326
327 if self.insert_genesis_checkpoint {
328 checkpoint_store.insert_genesis_checkpoint(
329 genesis.checkpoint(),
330 genesis.checkpoint_contents().clone(),
331 &epoch_store,
332 );
333 }
334 let index_store = if self.disable_indexer {
335 None
336 } else {
337 Some(Arc::new(IndexStore::new(
338 storage_dir.join("indexes"),
339 ®istry,
340 epoch_store
341 .protocol_config()
342 .max_move_identifier_len_as_option(),
343 )))
344 };
345 let grpc_indexes_store = if self.disable_indexer {
346 None
347 } else {
348 Some(Arc::new(
349 GrpcIndexesStore::new(
350 storage_dir.join(GRPC_INDEXES_DIR),
351 Arc::clone(&authority_store),
352 &checkpoint_store,
353 )
354 .await,
355 ))
356 };
357
358 let transaction_deny_config = self.transaction_deny_config.unwrap_or_default();
359 let certificate_deny_config = self.certificate_deny_config.unwrap_or_default();
360 let authority_overload_config = self.authority_overload_config.unwrap_or_default();
361 let pruning_config = AuthorityStorePruningConfig::default();
362
363 config.transaction_deny_config = transaction_deny_config;
364 config.certificate_deny_config = certificate_deny_config;
365 config.authority_overload_config = authority_overload_config;
366 config.authority_store_pruning_config = pruning_config;
367
368 let chain_identifier = ChainIdentifier::from(*genesis.checkpoint().digest());
369 let policy_config = config.policy_config.clone();
370 let firewall_config = config.firewall_config.clone();
371
372 let state = AuthorityState::new(
373 name,
374 secret,
375 SupportedProtocolVersions::SYSTEM_DEFAULT,
376 authority_store,
377 cache_traits,
378 epoch_store.clone(),
379 committee_store,
380 index_store,
381 grpc_indexes_store,
382 checkpoint_store,
383 ®istry,
384 genesis.objects(),
385 &DBCheckpointConfig::default(),
386 config.clone(),
387 ArchiveReaderBalancer::default(),
388 None,
389 chain_identifier,
390 pruner_db,
391 None,
392 policy_config,
393 firewall_config,
394 )
395 .await;
396
397 let consensus_client = Box::new(MockConsensusClient::new(
399 Arc::downgrade(&state),
400 ConsensusMode::Noop,
401 ));
402 let randomness_manager = RandomnessManager::try_new(
403 Arc::downgrade(&epoch_store),
404 consensus_client,
405 randomness::Handle::new_stub(),
406 &keypair,
407 )
408 .await;
409 if let Some(randomness_manager) = randomness_manager {
410 epoch_store
413 .set_randomness_manager(randomness_manager)
414 .await
415 .unwrap();
416 }
417
418 if !self.disable_execute_genesis_transactions {
419 state
425 .try_execute_immediately(
426 &VerifiedExecutableTransaction::new_from_checkpoint(
427 VerifiedTransaction::new_unchecked(genesis.transaction().clone()),
428 genesis.epoch(),
429 genesis.checkpoint().sequence_number,
430 ),
431 None,
432 &state.epoch_store_for_testing(),
433 )
434 .unwrap();
435
436 let batch = state.get_cache_commit().build_db_batch(
437 epoch_store.epoch(),
438 genesis.checkpoint().sequence_number,
439 &[*genesis.transaction().digest()],
440 );
441
442 state.get_cache_commit().commit_transaction_outputs(
443 epoch_store.epoch(),
444 batch,
445 &[*genesis.transaction().digest()],
446 );
447 }
448
449 if let Some(starting_objects) = self.starting_objects {
455 state
456 .insert_objects_unsafe_for_testing_only(starting_objects)
457 .await;
458 };
459 state
460 }
461}