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