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