1use super::ExecutionEnv;
5use super::backpressure::BackpressureManager;
6use super::epoch_start_configuration::EpochFlag;
7use crate::authority::authority_per_epoch_store::AuthorityPerEpochStore;
8use crate::authority::authority_store_pruner::PrunerWatermarks;
9use crate::authority::authority_store_tables::{
10 AuthorityPerpetualTables, AuthorityPerpetualTablesOptions,
11};
12use crate::authority::epoch_start_configuration::EpochStartConfiguration;
13use crate::authority::submitted_transaction_cache::SubmittedTransactionCacheMetrics;
14use crate::authority::{AuthorityState, AuthorityStore};
15use crate::checkpoints::CheckpointStore;
16use crate::epoch::committee_store::CommitteeStore;
17use crate::epoch::epoch_metrics::EpochMetrics;
18use crate::epoch::randomness::RandomnessManager;
19use crate::execution_cache::build_execution_cache;
20use crate::jsonrpc_index::IndexStore;
21use crate::mock_consensus::{ConsensusMode, MockConsensusClient};
22use crate::module_cache_metrics::ResolverMetrics;
23use crate::rpc_index::RpcIndexStore;
24use crate::signature_verifier::SignatureVerifierMetrics;
25use fastcrypto::traits::KeyPair;
26use prometheus::Registry;
27use std::path::PathBuf;
28use std::sync::Arc;
29use sui_config::ExecutionCacheConfig;
30use sui_config::certificate_deny_config::CertificateDenyConfig;
31use sui_config::genesis::Genesis;
32use sui_config::node::AuthorityOverloadConfig;
33use sui_config::node::{
34 AuthorityStorePruningConfig, DBCheckpointConfig, ExpensiveSafetyCheckConfig,
35};
36use sui_config::transaction_deny_config::TransactionDenyConfig;
37use sui_macros::nondeterministic;
38use sui_network::randomness;
39use sui_protocol_config::{Chain, ProtocolConfig};
40use sui_swarm_config::genesis_config::AccountConfig;
41use sui_swarm_config::network_config::NetworkConfig;
42use sui_types::base_types::{AuthorityName, ObjectID};
43use sui_types::crypto::AuthorityKeyPair;
44use sui_types::digests::ChainIdentifier;
45use sui_types::executable_transaction::VerifiedExecutableTransaction;
46use sui_types::object::Object;
47use sui_types::sui_system_state::SuiSystemStateTrait;
48use sui_types::supported_protocol_versions::SupportedProtocolVersions;
49use sui_types::transaction::VerifiedTransaction;
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 network_config: Option<&'a NetworkConfig>,
63 starting_objects: Option<&'a [Object]>,
64 expensive_safety_checks: Option<ExpensiveSafetyCheckConfig>,
65 disable_indexer: bool,
66 accounts: Vec<AccountConfig>,
67 insert_genesis_checkpoint: bool,
69 authority_overload_config: Option<AuthorityOverloadConfig>,
70 cache_config: Option<ExecutionCacheConfig>,
71 chain_override: Option<Chain>,
72 dev_inspect_disabled: bool,
73 skip_rpc_index_init: bool,
75 skip_genesis_owner_index: bool,
77}
78
79impl<'a> TestAuthorityBuilder<'a> {
80 pub fn new() -> Self {
81 Self::default()
82 }
83
84 pub fn with_store_base_path(mut self, path: PathBuf) -> Self {
85 assert!(self.store_base_path.replace(path).is_none());
86 self
87 }
88
89 pub fn with_starting_objects(mut self, objects: &'a [Object]) -> Self {
90 assert!(self.starting_objects.replace(objects).is_none());
91 self
92 }
93
94 pub fn with_store(mut self, store: Arc<AuthorityStore>) -> Self {
95 assert!(self.store.replace(store).is_none());
96 self
97 }
98
99 pub fn with_transaction_deny_config(mut self, config: TransactionDenyConfig) -> Self {
100 assert!(self.transaction_deny_config.replace(config).is_none());
101 self
102 }
103
104 pub fn with_dev_inspect_disabled(mut self) -> Self {
105 self.dev_inspect_disabled = true;
106 self
107 }
108
109 pub fn with_certificate_deny_config(mut self, config: CertificateDenyConfig) -> Self {
110 assert!(self.certificate_deny_config.replace(config).is_none());
111 self
112 }
113
114 pub fn with_protocol_config(mut self, config: ProtocolConfig) -> Self {
115 assert!(self.protocol_config.replace(config).is_none());
116 self
117 }
118
119 pub fn with_reference_gas_price(mut self, reference_gas_price: u64) -> Self {
120 assert!(self.genesis.is_none());
122 assert!(
123 self.reference_gas_price
124 .replace(reference_gas_price)
125 .is_none()
126 );
127 self
128 }
129
130 pub fn with_genesis_and_keypair(
131 mut self,
132 genesis: &'a Genesis,
133 keypair: &'a AuthorityKeyPair,
134 ) -> Self {
135 assert!(self.genesis.replace(genesis).is_none());
136 assert!(self.node_keypair.replace(keypair).is_none());
137 self
138 }
139
140 pub fn with_keypair(mut self, keypair: &'a AuthorityKeyPair) -> Self {
141 assert!(self.node_keypair.replace(keypair).is_none());
142 self
143 }
144
145 pub fn with_network_config(self, config: &'a NetworkConfig, node_idx: usize) -> Self {
148 self.with_genesis_and_keypair(
149 &config.genesis,
150 config.validator_configs()[node_idx].protocol_key_pair(),
151 )
152 }
153
154 pub fn with_shared_network_config(mut self, config: &'a NetworkConfig) -> Self {
157 assert!(self.network_config.replace(config).is_none());
158 self
159 }
160
161 pub fn disable_indexer(mut self) -> Self {
162 self.disable_indexer = true;
163 self
164 }
165
166 pub fn skip_rpc_index_init(mut self) -> Self {
169 self.skip_rpc_index_init = true;
170 self
171 }
172
173 pub fn skip_genesis_owner_index(mut self) -> Self {
176 self.skip_genesis_owner_index = true;
177 self
178 }
179
180 pub fn insert_genesis_checkpoint(mut self) -> Self {
181 self.insert_genesis_checkpoint = true;
182 self
183 }
184
185 pub fn with_expensive_safety_checks(mut self, config: ExpensiveSafetyCheckConfig) -> Self {
186 assert!(self.expensive_safety_checks.replace(config).is_none());
187 self
188 }
189
190 pub fn with_accounts(mut self, accounts: Vec<AccountConfig>) -> Self {
191 self.accounts = accounts;
192 self
193 }
194
195 pub fn with_authority_overload_config(mut self, config: AuthorityOverloadConfig) -> Self {
196 assert!(self.authority_overload_config.replace(config).is_none());
197 self
198 }
199
200 pub fn with_cache_config(mut self, config: ExecutionCacheConfig) -> Self {
201 self.cache_config = Some(config);
202 self
203 }
204
205 pub fn with_chain_override(mut self, chain: Chain) -> Self {
206 self.chain_override = Some(chain);
207 self
208 }
209
210 pub async fn build(self) -> Arc<AuthorityState> {
211 let _guard = self
219 .protocol_config
220 .clone()
221 .map(|config| ProtocolConfig::apply_overrides_for_testing(move |_, _| config.clone()));
222
223 let owned_network_config;
225 let local_network_config: &NetworkConfig = if let Some(config) = self.network_config {
226 config
227 } else {
228 let mut local_network_config_builder =
229 sui_swarm_config::network_config_builder::ConfigBuilder::new_with_temp_dir()
230 .with_accounts(self.accounts)
231 .with_reference_gas_price(self.reference_gas_price.unwrap_or(500));
232 if let Some(protocol_config) = &self.protocol_config {
233 local_network_config_builder =
234 local_network_config_builder.with_protocol_version(protocol_config.version);
235 }
236 owned_network_config = local_network_config_builder.build();
237 &owned_network_config
238 };
239 let genesis = &self.genesis.unwrap_or(&local_network_config.genesis);
240 let genesis_committee = genesis.committee();
241 let path = self.store_base_path.unwrap_or_else(|| {
242 let dir = std::env::temp_dir();
243 let store_base_path =
244 dir.join(format!("DB_{:?}", nondeterministic!(ObjectID::random())));
245 std::fs::create_dir(&store_base_path).unwrap();
246 store_base_path
247 });
248 let mut config = local_network_config.validator_configs()[0].clone();
249 let registry = Registry::new();
250
251 let authority_store = match self.store {
252 Some(store) => store,
253 None => {
254 let perpetual_tables_options = AuthorityPerpetualTablesOptions::default();
255 let perpetual_tables = Arc::new(AuthorityPerpetualTables::open(
256 &path.join("store"),
257 Some(perpetual_tables_options),
258 None,
259 ));
260 AuthorityStore::open_with_committee_for_testing(
262 perpetual_tables,
263 &genesis_committee,
264 genesis,
265 )
266 .await
267 .unwrap()
268 }
269 };
270
271 if let Some(cache_config) = self.cache_config {
272 config.execution_cache = cache_config;
273 }
274
275 let keypair = if let Some(keypair) = self.node_keypair {
276 keypair
277 } else {
278 config.protocol_key_pair()
279 };
280
281 let secret = Arc::pin(keypair.copy());
282 let name: AuthorityName = secret.public().into();
283 let cache_metrics = Arc::new(ResolverMetrics::new(®istry));
284 let signature_verifier_metrics = SignatureVerifierMetrics::new(®istry);
285 let epoch_flags = EpochFlag::default_flags_for_new_epoch(&config);
286 let epoch_start_configuration = EpochStartConfiguration::new(
287 genesis.sui_system_object().into_epoch_start_state(),
288 *genesis.checkpoint().digest(),
289 &genesis.objects(),
290 epoch_flags,
291 )
292 .unwrap();
293 let expensive_safety_checks = self.expensive_safety_checks.unwrap_or_default();
294
295 let pruner_watermarks = Arc::new(PrunerWatermarks::default());
296 let checkpoint_store =
297 CheckpointStore::new(&path.join("checkpoints"), pruner_watermarks.clone());
298 let backpressure_manager =
299 BackpressureManager::new_from_checkpoint_store(&checkpoint_store);
300
301 let cache_traits = build_execution_cache(
302 &Default::default(),
303 ®istry,
304 &authority_store,
305 backpressure_manager.clone(),
306 );
307
308 let chain_id = ChainIdentifier::from(*genesis.checkpoint().digest());
309 let chain = match self.chain_override {
310 Some(chain) => chain,
311 None => chain_id.chain(),
312 };
313
314 let epoch_store = AuthorityPerEpochStore::new(
315 name,
316 Arc::new(genesis_committee.clone()),
317 &path.join("store"),
318 None,
319 EpochMetrics::new(®istry),
320 epoch_start_configuration,
321 cache_traits.backing_package_store.clone(),
322 cache_traits.object_store.clone(),
323 cache_metrics,
324 signature_verifier_metrics,
325 &expensive_safety_checks,
326 (chain_id, chain),
327 checkpoint_store
328 .get_highest_executed_checkpoint_seq_number()
329 .unwrap()
330 .unwrap_or(0),
331 0,
332 Arc::new(SubmittedTransactionCacheMetrics::new(®istry)),
333 )
334 .expect("failed to create authority per epoch store");
335
336 let committee_store = Arc::new(CommitteeStore::new(
337 path.join("epochs"),
338 &genesis_committee,
339 None,
340 ));
341
342 if self.insert_genesis_checkpoint {
343 checkpoint_store.insert_genesis_checkpoint(
344 genesis.checkpoint(),
345 genesis.checkpoint_contents().clone(),
346 &epoch_store,
347 );
348 }
349 let index_store = if self.disable_indexer {
350 None
351 } else {
352 Some(Arc::new(IndexStore::new(
353 path.join("indexes"),
354 ®istry,
355 epoch_store
356 .protocol_config()
357 .max_move_identifier_len_as_option(),
358 false,
359 )))
360 };
361
362 let rpc_index = if self.disable_indexer {
363 None
364 } else if self.skip_rpc_index_init {
365 Some(Arc::new(RpcIndexStore::new_without_init(&path)))
366 } else {
367 Some(Arc::new(
368 RpcIndexStore::new(
369 &path,
370 &authority_store,
371 &checkpoint_store,
372 &epoch_store,
373 &cache_traits.backing_package_store,
374 pruner_watermarks.checkpoint_id.clone(),
375 sui_config::RpcConfig::default(),
376 )
377 .await,
378 ))
379 };
380
381 let transaction_deny_config = self.transaction_deny_config.unwrap_or_default();
382 let certificate_deny_config = self.certificate_deny_config.unwrap_or_default();
383 let authority_overload_config = self.authority_overload_config.unwrap_or_default();
384 let mut pruning_config = AuthorityStorePruningConfig::default();
385 if !epoch_store
386 .protocol_config()
387 .simplified_unwrap_then_delete()
388 {
389 pruning_config.set_killswitch_tombstone_pruning(true);
391 }
392
393 config.transaction_deny_config = transaction_deny_config;
394 config.certificate_deny_config = certificate_deny_config;
395 config.authority_overload_config = authority_overload_config;
396 config.authority_store_pruning_config = pruning_config;
397 config.dev_inspect_disabled = self.dev_inspect_disabled;
398
399 let chain_identifier = ChainIdentifier::from(*genesis.checkpoint().digest());
400 let policy_config = config.policy_config.clone();
401 let firewall_config = config.firewall_config.clone();
402
403 let genesis_objects_for_index = if self.skip_genesis_owner_index {
404 &[][..]
405 } else {
406 genesis.objects()
407 };
408 let state = AuthorityState::new(
409 name,
410 secret,
411 SupportedProtocolVersions::SYSTEM_DEFAULT,
412 authority_store,
413 cache_traits,
414 epoch_store.clone(),
415 committee_store,
416 index_store,
417 rpc_index,
418 checkpoint_store,
419 ®istry,
420 genesis_objects_for_index,
421 &DBCheckpointConfig::default(),
422 config.clone(),
423 chain_identifier,
424 policy_config,
425 firewall_config,
426 Arc::new(PrunerWatermarks::default()),
427 )
428 .await;
429
430 if epoch_store.randomness_state_enabled() {
432 let consensus_client = Box::new(MockConsensusClient::new(
433 Arc::downgrade(&state),
434 ConsensusMode::Noop,
435 ));
436 let randomness_manager = RandomnessManager::try_new(
437 Arc::downgrade(&epoch_store),
438 consensus_client,
439 randomness::Handle::new_stub(),
440 config.protocol_key_pair(),
441 )
442 .await;
443 if let Some(randomness_manager) = randomness_manager {
444 epoch_store
447 .set_randomness_manager(randomness_manager)
448 .await
449 .unwrap();
450 }
451 }
452
453 state
458 .try_execute_immediately(
459 &VerifiedExecutableTransaction::new_from_checkpoint(
460 VerifiedTransaction::new_unchecked(genesis.transaction().clone()),
461 genesis.epoch(),
462 genesis.checkpoint().sequence_number,
463 ),
464 ExecutionEnv::new(),
465 &state.epoch_store_for_testing(),
466 )
467 .await
468 .unwrap();
469
470 let batch = state
471 .get_cache_commit()
472 .build_db_batch(epoch_store.epoch(), &[*genesis.transaction().digest()]);
473
474 state.get_cache_commit().commit_transaction_outputs(
475 epoch_store.epoch(),
476 batch,
477 &[*genesis.transaction().digest()],
478 );
479
480 if let Some(starting_objects) = self.starting_objects {
486 state
487 .insert_objects_unsafe_for_testing_only(starting_objects)
488 .await
489 .unwrap();
490 };
491
492 state
493 }
494}