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::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}
80
81impl<'a> TestAuthorityBuilder<'a> {
82 pub fn new() -> Self {
83 Self::default()
84 }
85
86 pub fn with_store_base_path(mut self, path: PathBuf) -> Self {
87 assert!(self.store_base_path.replace(path).is_none());
88 self
89 }
90
91 pub fn with_starting_objects(mut self, objects: &'a [Object]) -> Self {
92 assert!(self.starting_objects.replace(objects).is_none());
93 self
94 }
95
96 pub fn with_store(mut self, store: Arc<AuthorityStore>) -> Self {
97 assert!(self.store.replace(store).is_none());
98 self
99 }
100
101 pub fn with_transaction_deny_config(mut self, config: TransactionDenyConfig) -> Self {
102 assert!(self.transaction_deny_config.replace(config).is_none());
103 self
104 }
105
106 pub fn with_certificate_deny_config(mut self, config: CertificateDenyConfig) -> Self {
107 assert!(self.certificate_deny_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_type(mut self, cache_type: ExecutionCacheType) -> Self {
178 self.cache_type = Some(cache_type);
179 self
180 }
181
182 pub fn with_cache_config(mut self, config: ExecutionCacheConfig) -> Self {
183 self.cache_config = Some(config);
184 self
185 }
186
187 pub fn disable_execute_genesis_transactions(mut self) -> Self {
188 self.disable_execute_genesis_transactions = true;
189 self
190 }
191
192 pub async fn build(self) -> Arc<AuthorityState> {
193 let mut local_network_config_builder =
194 iota_swarm_config::network_config_builder::ConfigBuilder::new_with_temp_dir()
195 .with_accounts(self.accounts)
196 .with_reference_gas_price(self.reference_gas_price.unwrap_or(500));
197 if let Some(protocol_config) = &self.protocol_config {
198 local_network_config_builder =
199 local_network_config_builder.with_protocol_version(protocol_config.version);
200 }
201
202 let local_network_config = local_network_config_builder.build();
203 let genesis = &self.genesis.unwrap_or(&local_network_config.genesis);
204 let genesis_committee = genesis.committee().unwrap();
205 let path = self.store_base_path.unwrap_or_else(|| {
206 let dir = std::env::temp_dir();
207 let store_base_path =
208 dir.join(format!("DB_{:?}", nondeterministic!(ObjectID::random())));
209 std::fs::create_dir(&store_base_path).unwrap();
210 store_base_path
211 });
212 let mut config = local_network_config.validator_configs()[0].clone();
213 let registry = Registry::new();
214 let mut pruner_db = None;
215 if config
216 .authority_store_pruning_config
217 .enable_compaction_filter
218 {
219 pruner_db = Some(Arc::new(AuthorityPrunerTables::open(&path.join("store"))));
220 }
221 let compaction_filter = pruner_db
222 .clone()
223 .map(|db| ObjectsCompactionFilter::new(db, ®istry));
224
225 let authority_store = match self.store {
226 Some(store) => store,
227 None => {
228 let perpetual_tables_options = AuthorityPerpetualTablesOptions {
229 compaction_filter,
230 ..Default::default()
231 };
232 let perpetual_tables = Arc::new(AuthorityPerpetualTables::open(
233 &path.join("store"),
234 Some(perpetual_tables_options),
235 ));
236 AuthorityStore::open_with_committee_for_testing(
238 perpetual_tables,
239 &genesis_committee,
240 genesis,
241 )
242 .await
243 .unwrap()
244 }
245 };
246 if let Some(cache_type) = self.cache_type {
247 config.execution_cache = cache_type;
248 }
249 if let Some(cache_config) = self.cache_config {
250 config.execution_cache_config = cache_config;
251 }
252
253 let keypair = if let Some(keypair) = self.node_keypair {
254 keypair.copy()
255 } else {
256 config.authority_key_pair().copy()
257 };
258
259 let secret = Arc::pin(keypair.copy());
260 let name: AuthorityName = secret.public().into();
261 let cache_metrics = Arc::new(ResolverMetrics::new(®istry));
262 let signature_verifier_metrics = SignatureVerifierMetrics::new(®istry);
263 let _guard = self
266 .protocol_config
267 .map(|config| ProtocolConfig::apply_overrides_for_testing(move |_, _| config.clone()));
268 let epoch_flags = EpochFlag::default_flags_for_new_epoch(&config);
269 let epoch_start_configuration = EpochStartConfiguration::new(
270 genesis.iota_system_object().into_epoch_start_state(),
271 *genesis.checkpoint().digest(),
272 &genesis.objects(),
273 epoch_flags,
274 )
275 .unwrap();
276 let expensive_safety_checks = self.expensive_safety_checks.unwrap_or_default();
277
278 let checkpoint_store = CheckpointStore::new(&path.join("checkpoints"));
279 let backpressure_manager =
280 BackpressureManager::new_from_checkpoint_store(&checkpoint_store);
281
282 let cache_traits = build_execution_cache(
283 &config.execution_cache_config,
284 &epoch_start_configuration,
285 ®istry,
286 &authority_store,
287 backpressure_manager.clone(),
288 );
289
290 let epoch_store = AuthorityPerEpochStore::new(
291 name,
292 Arc::new(genesis_committee.clone()),
293 &path.join("store"),
294 None,
295 EpochMetrics::new(®istry),
296 epoch_start_configuration,
297 cache_traits.backing_package_store.clone(),
298 cache_traits.object_store.clone(),
299 cache_metrics,
300 signature_verifier_metrics,
301 &expensive_safety_checks,
302 ChainIdentifier::from(*genesis.checkpoint().digest()),
303 checkpoint_store
304 .get_highest_executed_checkpoint_seq_number()
305 .unwrap()
306 .unwrap_or(0),
307 );
308 let committee_store = Arc::new(CommitteeStore::new(
309 path.join("epochs"),
310 &genesis_committee,
311 None,
312 ));
313
314 if self.insert_genesis_checkpoint {
315 checkpoint_store.insert_genesis_checkpoint(
316 genesis.checkpoint(),
317 genesis.checkpoint_contents().clone(),
318 &epoch_store,
319 );
320 }
321 let index_store = if self.disable_indexer {
322 None
323 } else {
324 Some(Arc::new(IndexStore::new(
325 path.join("indexes"),
326 ®istry,
327 epoch_store
328 .protocol_config()
329 .max_move_identifier_len_as_option(),
330 false,
331 )))
332 };
333 let rest_index = if self.disable_indexer {
334 None
335 } else {
336 Some(Arc::new(RestIndexStore::new(
337 path.join("rest_index"),
338 &authority_store,
339 &checkpoint_store,
340 &epoch_store,
341 &cache_traits.backing_package_store,
342 )))
343 };
344
345 let transaction_deny_config = self.transaction_deny_config.unwrap_or_default();
346 let certificate_deny_config = self.certificate_deny_config.unwrap_or_default();
347 let authority_overload_config = self.authority_overload_config.unwrap_or_default();
348 let pruning_config = AuthorityStorePruningConfig::default();
349
350 config.transaction_deny_config = transaction_deny_config;
351 config.certificate_deny_config = certificate_deny_config;
352 config.authority_overload_config = authority_overload_config;
353 config.authority_store_pruning_config = pruning_config;
354
355 let chain_identifier = ChainIdentifier::from(*genesis.checkpoint().digest());
356
357 let state = AuthorityState::new(
358 name,
359 secret,
360 SupportedProtocolVersions::SYSTEM_DEFAULT,
361 authority_store,
362 cache_traits,
363 epoch_store.clone(),
364 committee_store,
365 index_store,
366 rest_index,
367 checkpoint_store,
368 ®istry,
369 genesis.objects(),
370 &DBCheckpointConfig::default(),
371 config.clone(),
372 ArchiveReaderBalancer::default(),
373 None,
374 chain_identifier,
375 pruner_db,
376 )
377 .await;
378
379 let consensus_client = Box::new(MockConsensusClient::new(
381 Arc::downgrade(&state),
382 ConsensusMode::Noop,
383 ));
384 let randomness_manager = RandomnessManager::try_new(
385 Arc::downgrade(&epoch_store),
386 consensus_client,
387 randomness::Handle::new_stub(),
388 &keypair,
389 )
390 .await;
391 if let Some(randomness_manager) = randomness_manager {
392 epoch_store
395 .set_randomness_manager(randomness_manager)
396 .await
397 .unwrap();
398 }
399
400 if !self.disable_execute_genesis_transactions {
401 state
407 .try_execute_immediately(
408 &VerifiedExecutableTransaction::new_from_checkpoint(
409 VerifiedTransaction::new_unchecked(genesis.transaction().clone()),
410 genesis.epoch(),
411 genesis.checkpoint().sequence_number,
412 ),
413 None,
414 &state.epoch_store_for_testing(),
415 )
416 .unwrap();
417
418 state
419 .get_cache_commit()
420 .commit_transaction_outputs(epoch_store.epoch(), &[*genesis.transaction().digest()])
421 }
422
423 if let Some(starting_objects) = self.starting_objects {
429 state
430 .insert_objects_unsafe_for_testing_only(starting_objects)
431 .await;
432 };
433 state
434 }
435}