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_macros::nondeterministic;
20use iota_network::randomness;
21use iota_protocol_config::{Chain, ProtocolConfig};
22use iota_swarm_config::{genesis_config::AccountConfig, network_config::NetworkConfig};
23use iota_types::{
24 base_types::{AuthorityName, ObjectID},
25 crypto::AuthorityKeyPair,
26 digests::ChainIdentifier,
27 executable_transaction::VerifiedExecutableTransaction,
28 iota_system_state::IotaSystemStateTrait,
29 object::Object,
30 supported_protocol_versions::SupportedProtocolVersions,
31 transaction::VerifiedTransaction,
32};
33use prometheus::Registry;
34
35use super::{backpressure::BackpressureManager, epoch_start_configuration::EpochFlag};
36use crate::{
37 authority::{
38 AuthorityState, AuthorityStore,
39 authority_per_epoch_store::AuthorityPerEpochStore,
40 authority_store_pruner::ObjectsCompactionFilter,
41 authority_store_tables::{
42 AuthorityPerpetualTables, AuthorityPerpetualTablesOptions, AuthorityPrunerTables,
43 },
44 epoch_start_configuration::EpochStartConfiguration,
45 },
46 checkpoints::CheckpointStore,
47 epoch::{
48 committee_store::CommitteeStore, epoch_metrics::EpochMetrics, randomness::RandomnessManager,
49 },
50 execution_cache::build_execution_cache,
51 jsonrpc_index::IndexStore,
52 mock_consensus::{ConsensusMode, MockConsensusClient},
53 module_cache_metrics::ResolverMetrics,
54 rest_index::RestIndexStore,
55 signature_verifier::SignatureVerifierMetrics,
56};
57
58#[derive(Default, Clone)]
59pub struct TestAuthorityBuilder<'a> {
60 store_base_path: Option<PathBuf>,
61 store: Option<Arc<AuthorityStore>>,
62 transaction_deny_config: Option<TransactionDenyConfig>,
63 certificate_deny_config: Option<CertificateDenyConfig>,
64 protocol_config: Option<ProtocolConfig>,
65 reference_gas_price: Option<u64>,
66 node_keypair: Option<&'a AuthorityKeyPair>,
67 genesis: Option<&'a Genesis>,
68 starting_objects: Option<&'a [Object]>,
69 expensive_safety_checks: Option<ExpensiveSafetyCheckConfig>,
70 disable_indexer: bool,
71 accounts: Vec<AccountConfig>,
72 insert_genesis_checkpoint: bool,
75 authority_overload_config: Option<AuthorityOverloadConfig>,
76 cache_type: Option<ExecutionCacheType>,
77 cache_config: Option<ExecutionCacheConfig>,
78 disable_execute_genesis_transactions: bool,
79 chain_override: Option<Chain>,
80}
81
82impl<'a> TestAuthorityBuilder<'a> {
83 pub fn new() -> Self {
84 Self::default()
85 }
86
87 pub fn with_store_base_path(mut self, path: PathBuf) -> Self {
88 assert!(self.store_base_path.replace(path).is_none());
89 self
90 }
91
92 pub fn with_starting_objects(mut self, objects: &'a [Object]) -> Self {
93 assert!(self.starting_objects.replace(objects).is_none());
94 self
95 }
96
97 pub fn with_store(mut self, store: Arc<AuthorityStore>) -> Self {
98 assert!(self.store.replace(store).is_none());
99 self
100 }
101
102 pub fn with_transaction_deny_config(mut self, config: TransactionDenyConfig) -> Self {
103 assert!(self.transaction_deny_config.replace(config).is_none());
104 self
105 }
106
107 pub fn with_certificate_deny_config(mut self, config: CertificateDenyConfig) -> Self {
108 assert!(self.certificate_deny_config.replace(config).is_none());
109 self
110 }
111
112 pub fn with_protocol_config(mut self, config: ProtocolConfig) -> Self {
113 assert!(self.protocol_config.replace(config).is_none());
114 self
115 }
116
117 pub fn with_reference_gas_price(mut self, reference_gas_price: u64) -> Self {
118 assert!(self.genesis.is_none());
121 assert!(
122 self.reference_gas_price
123 .replace(reference_gas_price)
124 .is_none()
125 );
126 self
127 }
128
129 pub fn with_genesis_and_keypair(
130 mut self,
131 genesis: &'a Genesis,
132 keypair: &'a AuthorityKeyPair,
133 ) -> Self {
134 assert!(self.genesis.replace(genesis).is_none());
135 assert!(self.node_keypair.replace(keypair).is_none());
136 self
137 }
138
139 pub fn with_keypair(mut self, keypair: &'a AuthorityKeyPair) -> Self {
140 assert!(self.node_keypair.replace(keypair).is_none());
141 self
142 }
143
144 pub fn with_network_config(self, config: &'a NetworkConfig, node_idx: usize) -> Self {
147 self.with_genesis_and_keypair(
148 &config.genesis,
149 config.validator_configs()[node_idx].authority_key_pair(),
150 )
151 }
152
153 pub fn disable_indexer(mut self) -> Self {
154 self.disable_indexer = true;
155 self
156 }
157
158 pub fn insert_genesis_checkpoint(mut self) -> Self {
159 self.insert_genesis_checkpoint = true;
160 self
161 }
162
163 pub fn with_expensive_safety_checks(mut self, config: ExpensiveSafetyCheckConfig) -> Self {
164 assert!(self.expensive_safety_checks.replace(config).is_none());
165 self
166 }
167
168 pub fn with_accounts(mut self, accounts: Vec<AccountConfig>) -> Self {
169 self.accounts = accounts;
170 self
171 }
172
173 pub fn with_authority_overload_config(mut self, config: AuthorityOverloadConfig) -> Self {
174 assert!(self.authority_overload_config.replace(config).is_none());
175 self
176 }
177
178 pub fn with_cache_type(mut self, cache_type: ExecutionCacheType) -> Self {
179 self.cache_type = Some(cache_type);
180 self
181 }
182
183 pub fn with_cache_config(mut self, config: ExecutionCacheConfig) -> Self {
184 self.cache_config = Some(config);
185 self
186 }
187
188 pub fn disable_execute_genesis_transactions(mut self) -> Self {
189 self.disable_execute_genesis_transactions = true;
190 self
191 }
192
193 pub fn with_chain_override(mut self, chain: Chain) -> Self {
194 self.chain_override = Some(chain);
195 self
196 }
197
198 pub async fn build(self) -> Arc<AuthorityState> {
199 let mut local_network_config_builder =
200 iota_swarm_config::network_config_builder::ConfigBuilder::new_with_temp_dir()
201 .with_accounts(self.accounts)
202 .with_reference_gas_price(self.reference_gas_price.unwrap_or(500));
203 if let Some(protocol_config) = &self.protocol_config {
204 local_network_config_builder =
205 local_network_config_builder.with_protocol_version(protocol_config.version);
206 }
207
208 let local_network_config = local_network_config_builder.build();
209 let genesis = &self.genesis.unwrap_or(&local_network_config.genesis);
210 let genesis_committee = genesis.committee().unwrap();
211 let path = self.store_base_path.unwrap_or_else(|| {
212 let dir = std::env::temp_dir();
213 let store_base_path =
214 dir.join(format!("DB_{:?}", nondeterministic!(ObjectID::random())));
215 std::fs::create_dir(&store_base_path).unwrap();
216 store_base_path
217 });
218 let mut config = local_network_config.validator_configs()[0].clone();
219 let registry = Registry::new();
220 let mut pruner_db = None;
221 if config
222 .authority_store_pruning_config
223 .enable_compaction_filter
224 {
225 pruner_db = Some(Arc::new(AuthorityPrunerTables::open(&path.join("store"))));
226 }
227 let compaction_filter = pruner_db
228 .clone()
229 .map(|db| ObjectsCompactionFilter::new(db, ®istry));
230
231 let authority_store = match self.store {
232 Some(store) => store,
233 None => {
234 let perpetual_tables_options = AuthorityPerpetualTablesOptions {
235 compaction_filter,
236 ..Default::default()
237 };
238 let perpetual_tables = Arc::new(AuthorityPerpetualTables::open(
239 &path.join("store"),
240 Some(perpetual_tables_options),
241 ));
242 AuthorityStore::open_with_committee_for_testing(
244 perpetual_tables,
245 &genesis_committee,
246 genesis,
247 )
248 .await
249 .unwrap()
250 }
251 };
252 if let Some(cache_type) = self.cache_type {
253 config.execution_cache = cache_type;
254 }
255 if let Some(cache_config) = self.cache_config {
256 config.execution_cache_config = cache_config;
257 }
258
259 let keypair = if let Some(keypair) = self.node_keypair {
260 keypair.copy()
261 } else {
262 config.authority_key_pair().copy()
263 };
264
265 let secret = Arc::pin(keypair.copy());
266 let name: AuthorityName = secret.public().into();
267 let cache_metrics = Arc::new(ResolverMetrics::new(®istry));
268 let signature_verifier_metrics = SignatureVerifierMetrics::new(®istry);
269 let _guard = self
272 .protocol_config
273 .map(|config| ProtocolConfig::apply_overrides_for_testing(move |_, _| config.clone()));
274 let epoch_flags = EpochFlag::default_flags_for_new_epoch(&config);
275 let epoch_start_configuration = EpochStartConfiguration::new(
276 genesis.iota_system_object().into_epoch_start_state(),
277 *genesis.checkpoint().digest(),
278 &genesis.objects(),
279 epoch_flags,
280 )
281 .unwrap();
282 let expensive_safety_checks = self.expensive_safety_checks.unwrap_or_default();
283
284 let checkpoint_store = CheckpointStore::new(&path.join("checkpoints"));
285 let backpressure_manager =
286 BackpressureManager::new_from_checkpoint_store(&checkpoint_store);
287
288 let cache_traits = build_execution_cache(
289 &config.execution_cache_config,
290 &epoch_start_configuration,
291 ®istry,
292 &authority_store,
293 backpressure_manager.clone(),
294 );
295
296 let chain_id = ChainIdentifier::from(*genesis.checkpoint().digest());
297 let chain = match self.chain_override {
298 Some(chain) => chain,
299 None => chain_id.chain(),
300 };
301
302 let epoch_store = AuthorityPerEpochStore::new(
303 name,
304 Arc::new(genesis_committee.clone()),
305 &path.join("store"),
306 None,
307 EpochMetrics::new(®istry),
308 epoch_start_configuration,
309 cache_traits.backing_package_store.clone(),
310 cache_traits.object_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 let committee_store = Arc::new(CommitteeStore::new(
321 path.join("epochs"),
322 &genesis_committee,
323 None,
324 ));
325
326 if self.insert_genesis_checkpoint {
327 checkpoint_store.insert_genesis_checkpoint(
328 genesis.checkpoint(),
329 genesis.checkpoint_contents().clone(),
330 &epoch_store,
331 );
332 }
333 let index_store = if self.disable_indexer {
334 None
335 } else {
336 Some(Arc::new(IndexStore::new(
337 path.join("indexes"),
338 ®istry,
339 epoch_store
340 .protocol_config()
341 .max_move_identifier_len_as_option(),
342 false,
343 )))
344 };
345 let rest_index = if self.disable_indexer {
346 None
347 } else {
348 Some(Arc::new(RestIndexStore::new(
349 path.join("rest_index"),
350 &authority_store,
351 &checkpoint_store,
352 &epoch_store,
353 &cache_traits.backing_package_store,
354 )))
355 };
356
357 let transaction_deny_config = self.transaction_deny_config.unwrap_or_default();
358 let certificate_deny_config = self.certificate_deny_config.unwrap_or_default();
359 let authority_overload_config = self.authority_overload_config.unwrap_or_default();
360 let pruning_config = AuthorityStorePruningConfig::default();
361
362 config.transaction_deny_config = transaction_deny_config;
363 config.certificate_deny_config = certificate_deny_config;
364 config.authority_overload_config = authority_overload_config;
365 config.authority_store_pruning_config = pruning_config;
366
367 let chain_identifier = ChainIdentifier::from(*genesis.checkpoint().digest());
368
369 let state = AuthorityState::new(
370 name,
371 secret,
372 SupportedProtocolVersions::SYSTEM_DEFAULT,
373 authority_store,
374 cache_traits,
375 epoch_store.clone(),
376 committee_store,
377 index_store,
378 rest_index,
379 checkpoint_store,
380 ®istry,
381 genesis.objects(),
382 &DBCheckpointConfig::default(),
383 config.clone(),
384 ArchiveReaderBalancer::default(),
385 None,
386 chain_identifier,
387 pruner_db,
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 Some(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 state
431 .get_cache_commit()
432 .commit_transaction_outputs(epoch_store.epoch(), &[*genesis.transaction().digest()])
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}