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