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::mock_consensus::{ConsensusMode, MockConsensusClient};
21use crate::module_cache_metrics::ResolverMetrics;
22use crate::randomness_round_receiver::RandomnessRoundReceiverHandle;
23use crate::signature_verifier::SignatureVerifierMetrics;
24use fastcrypto::traits::KeyPair;
25use prometheus::Registry;
26use std::path::PathBuf;
27use std::sync::Arc;
28use sui_config::ExecutionCacheConfig;
29use sui_config::certificate_deny_config::CertificateDenyConfig;
30use sui_config::genesis::Genesis;
31use sui_config::node::AuthorityOverloadConfig;
32use sui_config::node::{
33 AuthorityStorePruningConfig, DBCheckpointConfig, ExpensiveSafetyCheckConfig,
34};
35use sui_config::transaction_deny_config::TransactionDenyConfig;
36use sui_macros::nondeterministic;
37use sui_network::randomness;
38use sui_protocol_config::{Chain, ProtocolConfig};
39use sui_swarm_config::genesis_config::AccountConfig;
40use sui_swarm_config::network_config::NetworkConfig;
41use sui_types::base_types::{AuthorityName, ObjectID};
42use sui_types::crypto::AuthorityKeyPair;
43use sui_types::digests::ChainIdentifier;
44use sui_types::executable_transaction::VerifiedExecutableTransaction;
45use sui_types::object::Object;
46use sui_types::sui_system_state::SuiSystemStateTrait;
47use sui_types::supported_protocol_versions::SupportedProtocolVersions;
48use sui_types::transaction::VerifiedTransaction;
49
50#[derive(Default, Clone)]
51pub struct TestAuthorityBuilder<'a> {
52 store_base_path: Option<PathBuf>,
53 store: Option<Arc<AuthorityStore>>,
54 transaction_deny_config: Option<TransactionDenyConfig>,
55 certificate_deny_config: Option<CertificateDenyConfig>,
56 protocol_config: Option<ProtocolConfig>,
57 reference_gas_price: Option<u64>,
58 node_keypair: Option<&'a AuthorityKeyPair>,
59 genesis: Option<&'a Genesis>,
60 network_config: Option<&'a NetworkConfig>,
62 starting_objects: Option<&'a [Object]>,
63 expensive_safety_checks: Option<ExpensiveSafetyCheckConfig>,
64 accounts: Vec<AccountConfig>,
65 insert_genesis_checkpoint: bool,
67 authority_overload_config: Option<AuthorityOverloadConfig>,
68 cache_config: Option<ExecutionCacheConfig>,
69 chain_override: Option<Chain>,
70 dev_inspect_disabled: bool,
71 recent_submission_dedup_window_ms: Option<u64>,
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_dev_inspect_disabled(mut self) -> Self {
100 self.dev_inspect_disabled = true;
101 self
102 }
103
104 pub fn with_certificate_deny_config(mut self, config: CertificateDenyConfig) -> Self {
105 assert!(self.certificate_deny_config.replace(config).is_none());
106 self
107 }
108
109 pub fn with_protocol_config(mut self, config: ProtocolConfig) -> Self {
110 assert!(self.protocol_config.replace(config).is_none());
111 self
112 }
113
114 pub fn with_reference_gas_price(mut self, reference_gas_price: u64) -> Self {
115 assert!(self.genesis.is_none());
117 assert!(
118 self.reference_gas_price
119 .replace(reference_gas_price)
120 .is_none()
121 );
122 self
123 }
124
125 pub fn with_genesis_and_keypair(
126 mut self,
127 genesis: &'a Genesis,
128 keypair: &'a AuthorityKeyPair,
129 ) -> Self {
130 assert!(self.genesis.replace(genesis).is_none());
131 assert!(self.node_keypair.replace(keypair).is_none());
132 self
133 }
134
135 pub fn with_keypair(mut self, keypair: &'a AuthorityKeyPair) -> Self {
136 assert!(self.node_keypair.replace(keypair).is_none());
137 self
138 }
139
140 pub fn with_network_config(self, config: &'a NetworkConfig, node_idx: usize) -> Self {
143 self.with_genesis_and_keypair(
144 &config.genesis,
145 config.validator_configs()[node_idx].protocol_key_pair(),
146 )
147 }
148
149 pub fn with_shared_network_config(mut self, config: &'a NetworkConfig) -> Self {
152 assert!(self.network_config.replace(config).is_none());
153 self
154 }
155
156 pub fn insert_genesis_checkpoint(mut self) -> Self {
157 self.insert_genesis_checkpoint = true;
158 self
159 }
160
161 pub fn with_expensive_safety_checks(mut self, config: ExpensiveSafetyCheckConfig) -> Self {
162 assert!(self.expensive_safety_checks.replace(config).is_none());
163 self
164 }
165
166 pub fn with_accounts(mut self, accounts: Vec<AccountConfig>) -> Self {
167 self.accounts = accounts;
168 self
169 }
170
171 pub fn with_authority_overload_config(mut self, config: AuthorityOverloadConfig) -> Self {
172 assert!(self.authority_overload_config.replace(config).is_none());
173 self
174 }
175
176 pub fn with_cache_config(mut self, config: ExecutionCacheConfig) -> Self {
177 self.cache_config = Some(config);
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 fn with_recent_submission_dedup_window_ms(mut self, window_ms: u64) -> Self {
187 assert!(
188 self.recent_submission_dedup_window_ms
189 .replace(window_ms)
190 .is_none()
191 );
192 self
193 }
194
195 pub async fn build(self) -> Arc<AuthorityState> {
196 let _guard = self
201 .protocol_config
202 .clone()
203 .map(|config| ProtocolConfig::apply_overrides_for_testing(move |_, _| config.clone()));
204
205 let owned_network_config;
207 let local_network_config: &NetworkConfig = if let Some(config) = self.network_config {
208 config
209 } else {
210 let mut local_network_config_builder =
211 sui_swarm_config::network_config_builder::ConfigBuilder::new_with_temp_dir()
212 .with_accounts(self.accounts)
213 .with_reference_gas_price(self.reference_gas_price.unwrap_or(500));
214 if let Some(protocol_config) = &self.protocol_config {
215 local_network_config_builder =
216 local_network_config_builder.with_protocol_version(protocol_config.version);
217 }
218 owned_network_config = local_network_config_builder.build();
219 &owned_network_config
220 };
221 let genesis = &self.genesis.unwrap_or(&local_network_config.genesis);
222 let genesis_committee = genesis.committee();
223 let path = self.store_base_path.unwrap_or_else(|| {
224 let dir = std::env::temp_dir();
225 let store_base_path =
226 dir.join(format!("DB_{:?}", nondeterministic!(ObjectID::random())));
227 std::fs::create_dir(&store_base_path).unwrap();
228 store_base_path
229 });
230 let mut config = local_network_config.validator_configs()[0].clone();
231 let registry = Registry::new();
232
233 let authority_store = match self.store {
234 Some(store) => store,
235 None => {
236 let perpetual_tables_options = AuthorityPerpetualTablesOptions::default();
237 let perpetual_tables = Arc::new(AuthorityPerpetualTables::open(
238 &path.join("store"),
239 Some(perpetual_tables_options),
240 None,
241 ));
242 AuthorityStore::open_with_committee_for_testing(
244 perpetual_tables,
245 &genesis_committee,
246 genesis,
247 )
248 .await
249 .unwrap()
250 }
251 };
252
253 if let Some(cache_config) = self.cache_config {
254 config.execution_cache = cache_config;
255 }
256
257 let keypair = if let Some(keypair) = self.node_keypair {
258 keypair
259 } else {
260 config.protocol_key_pair()
261 };
262
263 let secret = Arc::pin(keypair.copy());
264 let name: AuthorityName = secret.public().into();
265 let cache_metrics = Arc::new(ResolverMetrics::new(®istry));
266 let signature_verifier_metrics = SignatureVerifierMetrics::new(®istry);
267 let epoch_flags = EpochFlag::default_flags_for_new_epoch(&config);
268 let epoch_start_configuration = EpochStartConfiguration::new(
269 genesis.sui_system_object().into_epoch_start_state(),
270 *genesis.checkpoint().digest(),
271 &genesis.objects(),
272 epoch_flags,
273 )
274 .unwrap();
275 let expensive_safety_checks = self.expensive_safety_checks.unwrap_or_default();
276
277 let pruner_watermarks = Arc::new(PrunerWatermarks::default());
278 let checkpoint_store =
279 CheckpointStore::new(&path.join("checkpoints"), pruner_watermarks.clone());
280 let backpressure_manager =
281 BackpressureManager::new_from_checkpoint_store(&checkpoint_store);
282
283 let cache_traits = build_execution_cache(
284 &Default::default(),
285 ®istry,
286 &authority_store,
287 backpressure_manager.clone(),
288 );
289
290 let chain_id = ChainIdentifier::from(*genesis.checkpoint().digest());
291 let chain = match self.chain_override {
292 Some(chain) => chain,
293 None => chain_id.chain(),
294 };
295
296 let epoch_store = AuthorityPerEpochStore::new(
297 name,
298 Arc::new(genesis_committee.clone()),
299 &path.join("store"),
300 None,
301 EpochMetrics::new(®istry),
302 epoch_start_configuration,
303 cache_traits.backing_package_store.clone(),
304 cache_traits.object_store.clone(),
305 cache_metrics,
306 signature_verifier_metrics,
307 &expensive_safety_checks,
308 (chain_id, chain),
309 checkpoint_store
310 .get_highest_executed_checkpoint_seq_number()
311 .unwrap()
312 .unwrap_or(0),
313 0,
314 Arc::new(SubmittedTransactionCacheMetrics::new(®istry)),
315 None,
316 )
317 .expect("failed to create authority per epoch store");
318
319 let committee_store = Arc::new(CommitteeStore::new(
320 path.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 transaction_deny_config = self.transaction_deny_config.unwrap_or_default();
333 let certificate_deny_config = self.certificate_deny_config.unwrap_or_default();
334 let authority_overload_config = self.authority_overload_config.unwrap_or_default();
335 let mut pruning_config = AuthorityStorePruningConfig::default();
336 if !epoch_store
337 .protocol_config()
338 .simplified_unwrap_then_delete()
339 {
340 pruning_config.set_killswitch_tombstone_pruning(true);
342 }
343
344 config.transaction_deny_config = transaction_deny_config;
345 config.certificate_deny_config = certificate_deny_config;
346 config.authority_overload_config = authority_overload_config;
347 config.authority_store_pruning_config = pruning_config;
348 config.dev_inspect_disabled = self.dev_inspect_disabled;
349 if let Some(window_ms) = self.recent_submission_dedup_window_ms {
350 config.recent_submission_dedup_window_ms = Some(window_ms);
351 }
352
353 let chain_identifier = ChainIdentifier::from(*genesis.checkpoint().digest());
354 let policy_config = config.policy_config.clone();
355 let firewall_config = config.firewall_config.clone();
356
357 let state = AuthorityState::new(
358 name,
359 secret,
360 SupportedProtocolVersions::SYSTEM_DEFAULT,
361 authority_store,
362 cache_traits,
363 epoch_store.clone(),
364 committee_store,
365 None,
366 checkpoint_store,
367 ®istry,
368 &DBCheckpointConfig::default(),
369 config.clone(),
370 chain_identifier,
371 policy_config,
372 firewall_config,
373 Arc::new(PrunerWatermarks::default()),
374 )
375 .await;
376
377 if epoch_store.randomness_state_enabled() {
379 let consensus_client = Box::new(MockConsensusClient::new(
380 Arc::downgrade(&state),
381 ConsensusMode::Noop,
382 ));
383 let randomness_manager = RandomnessManager::try_new(
384 Arc::downgrade(&epoch_store),
385 consensus_client,
386 randomness::Handle::new_stub(),
387 Some(config.protocol_key_pair()),
388 RandomnessRoundReceiverHandle::new_for_testing(),
389 )
390 .await;
391 if let Some(randomness_manager) = randomness_manager {
392 epoch_store
395 .set_randomness_manager(randomness_manager)
396 .await
397 .unwrap();
398 }
399 }
400
401 state
406 .try_execute_immediately(
407 &VerifiedExecutableTransaction::new_from_checkpoint(
408 VerifiedTransaction::new_unchecked(genesis.transaction().clone()),
409 genesis.epoch(),
410 genesis.checkpoint().sequence_number,
411 ),
412 ExecutionEnv::new(),
413 &state.epoch_store_for_testing(),
414 )
415 .unwrap();
416
417 let batch = state
418 .get_cache_commit()
419 .build_db_batch(epoch_store.epoch(), &[*genesis.transaction().digest()]);
420
421 state.get_cache_commit().commit_transaction_outputs(
422 epoch_store.epoch(),
423 batch,
424 &[*genesis.transaction().digest()],
425 );
426
427 if let Some(starting_objects) = self.starting_objects {
433 state
434 .insert_objects_unsafe_for_testing_only(starting_objects)
435 .await
436 .unwrap();
437 };
438
439 state
440 }
441}