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_macros::nondeterministic;
20use iota_network::randomness;
21use iota_protocol_config::ProtocolConfig;
22use iota_storage::IndexStore;
23use iota_swarm_config::{genesis_config::AccountConfig, network_config::NetworkConfig};
24use iota_types::{
25 base_types::{AuthorityName, ObjectID},
26 crypto::AuthorityKeyPair,
27 digests::ChainIdentifier,
28 executable_transaction::VerifiedExecutableTransaction,
29 iota_system_state::IotaSystemStateTrait,
30 object::Object,
31 supported_protocol_versions::SupportedProtocolVersions,
32 transaction::VerifiedTransaction,
33};
34use prometheus::Registry;
35
36use super::epoch_start_configuration::EpochFlag;
37use crate::{
38 authority::{
39 AuthorityState, AuthorityStore, authority_per_epoch_store::AuthorityPerEpochStore,
40 authority_store_tables::AuthorityPerpetualTables,
41 epoch_start_configuration::EpochStartConfiguration,
42 },
43 checkpoints::CheckpointStore,
44 epoch::{
45 committee_store::CommitteeStore, epoch_metrics::EpochMetrics, randomness::RandomnessManager,
46 },
47 execution_cache::build_execution_cache,
48 mock_consensus::{ConsensusMode, MockConsensusClient},
49 module_cache_metrics::ResolverMetrics,
50 rest_index::RestIndexStore,
51 signature_verifier::SignatureVerifierMetrics,
52};
53
54#[derive(Default, Clone)]
55pub struct TestAuthorityBuilder<'a> {
56 store_base_path: Option<PathBuf>,
57 store: Option<Arc<AuthorityStore>>,
58 transaction_deny_config: Option<TransactionDenyConfig>,
59 certificate_deny_config: Option<CertificateDenyConfig>,
60 protocol_config: Option<ProtocolConfig>,
61 reference_gas_price: Option<u64>,
62 node_keypair: Option<&'a AuthorityKeyPair>,
63 genesis: Option<&'a Genesis>,
64 starting_objects: Option<&'a [Object]>,
65 expensive_safety_checks: Option<ExpensiveSafetyCheckConfig>,
66 disable_indexer: bool,
67 accounts: Vec<AccountConfig>,
68 insert_genesis_checkpoint: bool,
71 authority_overload_config: Option<AuthorityOverloadConfig>,
72 cache_config: Option<ExecutionCacheConfig>,
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 async fn build(self) -> Arc<AuthorityState> {
177 let mut local_network_config_builder =
178 iota_swarm_config::network_config_builder::ConfigBuilder::new_with_temp_dir()
179 .with_accounts(self.accounts)
180 .with_reference_gas_price(self.reference_gas_price.unwrap_or(500));
181 if let Some(protocol_config) = &self.protocol_config {
182 local_network_config_builder =
183 local_network_config_builder.with_protocol_version(protocol_config.version);
184 }
185
186 let local_network_config = local_network_config_builder.build();
187 let genesis = &self.genesis.unwrap_or(&local_network_config.genesis);
188 let genesis_committee = genesis.committee().unwrap();
189 let path = self.store_base_path.unwrap_or_else(|| {
190 let dir = std::env::temp_dir();
191 let store_base_path =
192 dir.join(format!("DB_{:?}", nondeterministic!(ObjectID::random())));
193 std::fs::create_dir(&store_base_path).unwrap();
194 store_base_path
195 });
196 let authority_store = match self.store {
197 Some(store) => store,
198 None => {
199 let perpetual_tables =
200 Arc::new(AuthorityPerpetualTables::open(&path.join("store"), None));
201 AuthorityStore::open_with_committee_for_testing(
203 perpetual_tables,
204 &genesis_committee,
205 genesis,
206 0,
207 )
208 .await
209 .unwrap()
210 }
211 };
212 let mut config = local_network_config.validator_configs()[0].clone();
213 if let Some(cache_config) = self.cache_config {
214 config.execution_cache = cache_config;
215 }
216
217 let keypair = if let Some(keypair) = self.node_keypair {
218 keypair.copy()
219 } else {
220 config.authority_key_pair().copy()
221 };
222
223 let secret = Arc::pin(keypair.copy());
224 let name: AuthorityName = secret.public().into();
225 let registry = Registry::new();
226 let cache_metrics = Arc::new(ResolverMetrics::new(®istry));
227 let signature_verifier_metrics = SignatureVerifierMetrics::new(®istry);
228 let _guard = self
231 .protocol_config
232 .map(|config| ProtocolConfig::apply_overrides_for_testing(move |_, _| config.clone()));
233 let epoch_flags = EpochFlag::default_flags_for_new_epoch(&config);
234 let epoch_start_configuration = EpochStartConfiguration::new(
235 genesis.iota_system_object().into_epoch_start_state(),
236 *genesis.checkpoint().digest(),
237 &genesis.objects(),
238 epoch_flags,
239 )
240 .unwrap();
241 let expensive_safety_checks = self.expensive_safety_checks.unwrap_or_default();
242
243 let cache_traits =
244 build_execution_cache(&epoch_start_configuration, ®istry, &authority_store);
245
246 let epoch_store = AuthorityPerEpochStore::new(
247 name,
248 Arc::new(genesis_committee.clone()),
249 &path.join("store"),
250 None,
251 EpochMetrics::new(®istry),
252 epoch_start_configuration,
253 cache_traits.backing_package_store.clone(),
254 cache_traits.object_store.clone(),
255 cache_metrics,
256 signature_verifier_metrics,
257 &expensive_safety_checks,
258 ChainIdentifier::from(*genesis.checkpoint().digest()),
259 );
260 let committee_store = Arc::new(CommitteeStore::new(
261 path.join("epochs"),
262 &genesis_committee,
263 None,
264 ));
265
266 let checkpoint_store = CheckpointStore::new(&path.join("checkpoints"));
267 if self.insert_genesis_checkpoint {
268 checkpoint_store.insert_genesis_checkpoint(
269 genesis.checkpoint(),
270 genesis.checkpoint_contents().clone(),
271 &epoch_store,
272 );
273 }
274 let index_store = if self.disable_indexer {
275 None
276 } else {
277 Some(Arc::new(IndexStore::new(
278 path.join("indexes"),
279 ®istry,
280 epoch_store
281 .protocol_config()
282 .max_move_identifier_len_as_option(),
283 false,
284 )))
285 };
286 let rest_index = if self.disable_indexer {
287 None
288 } else {
289 Some(Arc::new(RestIndexStore::new(
290 path.join("rest_index"),
291 &authority_store,
292 &checkpoint_store,
293 &epoch_store,
294 &cache_traits.backing_package_store,
295 )))
296 };
297
298 let transaction_deny_config = self.transaction_deny_config.unwrap_or_default();
299 let certificate_deny_config = self.certificate_deny_config.unwrap_or_default();
300 let authority_overload_config = self.authority_overload_config.unwrap_or_default();
301 let pruning_config = AuthorityStorePruningConfig::default();
302
303 config.transaction_deny_config = transaction_deny_config;
304 config.certificate_deny_config = certificate_deny_config;
305 config.authority_overload_config = authority_overload_config;
306 config.authority_store_pruning_config = pruning_config;
307
308 let state = AuthorityState::new(
309 name,
310 secret,
311 SupportedProtocolVersions::SYSTEM_DEFAULT,
312 authority_store,
313 cache_traits,
314 epoch_store.clone(),
315 committee_store,
316 index_store,
317 rest_index,
318 checkpoint_store,
319 ®istry,
320 genesis.objects(),
321 &DBCheckpointConfig::default(),
322 config.clone(),
323 usize::MAX,
324 ArchiveReaderBalancer::default(),
325 None,
326 )
327 .await;
328
329 let consensus_client = Box::new(MockConsensusClient::new(
331 Arc::downgrade(&state),
332 ConsensusMode::Noop,
333 ));
334 let randomness_manager = RandomnessManager::try_new(
335 Arc::downgrade(&epoch_store),
336 consensus_client,
337 randomness::Handle::new_stub(),
338 &keypair,
339 )
340 .await;
341 if let Some(randomness_manager) = randomness_manager {
342 epoch_store
345 .set_randomness_manager(randomness_manager)
346 .await
347 .unwrap();
348 }
349
350 state
356 .try_execute_immediately(
357 &VerifiedExecutableTransaction::new_from_checkpoint(
358 VerifiedTransaction::new_unchecked(genesis.transaction().clone()),
359 genesis.epoch(),
360 genesis.checkpoint().sequence_number,
361 ),
362 None,
363 &state.epoch_store_for_testing(),
364 )
365 .await
366 .unwrap();
367
368 if let Some(starting_objects) = self.starting_objects {
374 state
375 .insert_objects_unsafe_for_testing_only(starting_objects)
376 .await
377 .unwrap();
378 };
379 state
380 }
381}