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::{ObjectsCompactionFilter, PrunerWatermarks};
9use crate::authority::authority_store_tables::{
10 AuthorityPerpetualTables, AuthorityPerpetualTablesOptions, AuthorityPrunerTables,
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::execution_scheduler::SchedulingSource;
21use crate::jsonrpc_index::IndexStore;
22use crate::mock_consensus::{ConsensusMode, MockConsensusClient};
23use crate::module_cache_metrics::ResolverMetrics;
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 starting_objects: Option<&'a [Object]>,
63 expensive_safety_checks: Option<ExpensiveSafetyCheckConfig>,
64 disable_indexer: bool,
65 accounts: Vec<AccountConfig>,
66 insert_genesis_checkpoint: bool,
68 authority_overload_config: Option<AuthorityOverloadConfig>,
69 cache_config: Option<ExecutionCacheConfig>,
70 chain_override: Option<Chain>,
71}
72
73impl<'a> TestAuthorityBuilder<'a> {
74 pub fn new() -> Self {
75 Self::default()
76 }
77
78 pub fn with_store_base_path(mut self, path: PathBuf) -> Self {
79 assert!(self.store_base_path.replace(path).is_none());
80 self
81 }
82
83 pub fn with_starting_objects(mut self, objects: &'a [Object]) -> Self {
84 assert!(self.starting_objects.replace(objects).is_none());
85 self
86 }
87
88 pub fn with_store(mut self, store: Arc<AuthorityStore>) -> Self {
89 assert!(self.store.replace(store).is_none());
90 self
91 }
92
93 pub fn with_transaction_deny_config(mut self, config: TransactionDenyConfig) -> Self {
94 assert!(self.transaction_deny_config.replace(config).is_none());
95 self
96 }
97
98 pub fn with_certificate_deny_config(mut self, config: CertificateDenyConfig) -> Self {
99 assert!(self.certificate_deny_config.replace(config).is_none());
100 self
101 }
102
103 pub fn with_protocol_config(mut self, config: ProtocolConfig) -> Self {
104 assert!(self.protocol_config.replace(config).is_none());
105 self
106 }
107
108 pub fn with_reference_gas_price(mut self, reference_gas_price: u64) -> Self {
109 assert!(self.genesis.is_none());
111 assert!(
112 self.reference_gas_price
113 .replace(reference_gas_price)
114 .is_none()
115 );
116 self
117 }
118
119 pub fn with_genesis_and_keypair(
120 mut self,
121 genesis: &'a Genesis,
122 keypair: &'a AuthorityKeyPair,
123 ) -> Self {
124 assert!(self.genesis.replace(genesis).is_none());
125 assert!(self.node_keypair.replace(keypair).is_none());
126 self
127 }
128
129 pub fn with_keypair(mut self, keypair: &'a AuthorityKeyPair) -> Self {
130 assert!(self.node_keypair.replace(keypair).is_none());
131 self
132 }
133
134 pub fn with_network_config(self, config: &'a NetworkConfig, node_idx: usize) -> Self {
137 self.with_genesis_and_keypair(
138 &config.genesis,
139 config.validator_configs()[node_idx].protocol_key_pair(),
140 )
141 }
142
143 pub fn disable_indexer(mut self) -> Self {
144 self.disable_indexer = true;
145 self
146 }
147
148 pub fn insert_genesis_checkpoint(mut self) -> Self {
149 self.insert_genesis_checkpoint = true;
150 self
151 }
152
153 pub fn with_expensive_safety_checks(mut self, config: ExpensiveSafetyCheckConfig) -> Self {
154 assert!(self.expensive_safety_checks.replace(config).is_none());
155 self
156 }
157
158 pub fn with_accounts(mut self, accounts: Vec<AccountConfig>) -> Self {
159 self.accounts = accounts;
160 self
161 }
162
163 pub fn with_authority_overload_config(mut self, config: AuthorityOverloadConfig) -> Self {
164 assert!(self.authority_overload_config.replace(config).is_none());
165 self
166 }
167
168 pub fn with_cache_config(mut self, config: ExecutionCacheConfig) -> Self {
169 self.cache_config = Some(config);
170 self
171 }
172
173 pub fn with_chain_override(mut self, chain: Chain) -> Self {
174 self.chain_override = Some(chain);
175 self
176 }
177
178 pub async fn build(self) -> Arc<AuthorityState> {
179 let protocol_config = self.protocol_config.clone();
182 let _guard = protocol_config
183 .map(|config| ProtocolConfig::apply_overrides_for_testing(move |_, _| config.clone()));
184
185 let mut local_network_config_builder =
186 sui_swarm_config::network_config_builder::ConfigBuilder::new_with_temp_dir()
187 .with_accounts(self.accounts)
188 .with_reference_gas_price(self.reference_gas_price.unwrap_or(500));
189 if let Some(protocol_config) = &self.protocol_config {
190 local_network_config_builder =
191 local_network_config_builder.with_protocol_version(protocol_config.version);
192 }
193 let local_network_config = local_network_config_builder.build();
194 let genesis = &self.genesis.unwrap_or(&local_network_config.genesis);
195 let genesis_committee = genesis.committee().unwrap();
196 let path = self.store_base_path.unwrap_or_else(|| {
197 let dir = std::env::temp_dir();
198 let store_base_path =
199 dir.join(format!("DB_{:?}", nondeterministic!(ObjectID::random())));
200 std::fs::create_dir(&store_base_path).unwrap();
201 store_base_path
202 });
203 let mut config = local_network_config.validator_configs()[0].clone();
204 let registry = Registry::new();
205 let mut pruner_db = None;
206 if config
207 .authority_store_pruning_config
208 .enable_compaction_filter
209 {
210 pruner_db = Some(Arc::new(AuthorityPrunerTables::open(&path.join("store"))));
211 }
212 let compaction_filter = pruner_db
213 .clone()
214 .map(|db| ObjectsCompactionFilter::new(db, ®istry));
215
216 let authority_store = match self.store {
217 Some(store) => store,
218 None => {
219 let perpetual_tables_options = AuthorityPerpetualTablesOptions {
220 compaction_filter,
221 ..Default::default()
222 };
223 let perpetual_tables = Arc::new(AuthorityPerpetualTables::open(
224 &path.join("store"),
225 Some(perpetual_tables_options),
226 None,
227 ));
228 AuthorityStore::open_with_committee_for_testing(
230 perpetual_tables,
231 &genesis_committee,
232 genesis,
233 )
234 .await
235 .unwrap()
236 }
237 };
238 if let Some(cache_config) = self.cache_config {
239 config.execution_cache = cache_config;
240 }
241
242 let keypair = if let Some(keypair) = self.node_keypair {
243 keypair
244 } else {
245 config.protocol_key_pair()
246 };
247
248 let secret = Arc::pin(keypair.copy());
249 let name: AuthorityName = secret.public().into();
250 let cache_metrics = Arc::new(ResolverMetrics::new(®istry));
251 let signature_verifier_metrics = SignatureVerifierMetrics::new(®istry);
252 let epoch_flags = EpochFlag::default_flags_for_new_epoch(&config);
253 let epoch_start_configuration = EpochStartConfiguration::new(
254 genesis.sui_system_object().into_epoch_start_state(),
255 *genesis.checkpoint().digest(),
256 &genesis.objects(),
257 epoch_flags,
258 )
259 .unwrap();
260 let expensive_safety_checks = self.expensive_safety_checks.unwrap_or_default();
261
262 let pruner_watermarks = Arc::new(PrunerWatermarks::default());
263 let checkpoint_store =
264 CheckpointStore::new(&path.join("checkpoints"), pruner_watermarks.clone());
265 let backpressure_manager =
266 BackpressureManager::new_from_checkpoint_store(&checkpoint_store);
267
268 let cache_traits = build_execution_cache(
269 &Default::default(),
270 ®istry,
271 &authority_store,
272 backpressure_manager.clone(),
273 );
274
275 let chain_id = ChainIdentifier::from(*genesis.checkpoint().digest());
276 let chain = match self.chain_override {
277 Some(chain) => chain,
278 None => chain_id.chain(),
279 };
280
281 let epoch_store = AuthorityPerEpochStore::new(
282 name,
283 Arc::new(genesis_committee.clone()),
284 &path.join("store"),
285 None,
286 EpochMetrics::new(®istry),
287 epoch_start_configuration,
288 cache_traits.backing_package_store.clone(),
289 cache_traits.object_store.clone(),
290 cache_metrics,
291 signature_verifier_metrics,
292 &expensive_safety_checks,
293 (chain_id, chain),
294 checkpoint_store
295 .get_highest_executed_checkpoint_seq_number()
296 .unwrap()
297 .unwrap_or(0),
298 Arc::new(SubmittedTransactionCacheMetrics::new(®istry)),
299 )
300 .expect("failed to create authority per epoch store");
301 let committee_store = Arc::new(CommitteeStore::new(
302 path.join("epochs"),
303 &genesis_committee,
304 None,
305 ));
306
307 if self.insert_genesis_checkpoint {
308 checkpoint_store.insert_genesis_checkpoint(
309 genesis.checkpoint(),
310 genesis.checkpoint_contents().clone(),
311 &epoch_store,
312 );
313 }
314 let index_store = if self.disable_indexer {
315 None
316 } else {
317 Some(Arc::new(IndexStore::new(
318 path.join("indexes"),
319 ®istry,
320 epoch_store
321 .protocol_config()
322 .max_move_identifier_len_as_option(),
323 false,
324 )))
325 };
326 let rpc_index = if self.disable_indexer {
327 None
328 } else {
329 Some(Arc::new(
330 RpcIndexStore::new(
331 &path,
332 &authority_store,
333 &checkpoint_store,
334 &epoch_store,
335 &cache_traits.backing_package_store,
336 pruner_watermarks.checkpoint_id.clone(),
337 sui_config::RpcConfig::default(),
338 )
339 .await,
340 ))
341 };
342
343 let transaction_deny_config = self.transaction_deny_config.unwrap_or_default();
344 let certificate_deny_config = self.certificate_deny_config.unwrap_or_default();
345 let authority_overload_config = self.authority_overload_config.unwrap_or_default();
346 let mut pruning_config = AuthorityStorePruningConfig::default();
347 if !epoch_store
348 .protocol_config()
349 .simplified_unwrap_then_delete()
350 {
351 pruning_config.set_killswitch_tombstone_pruning(true);
353 }
354
355 config.transaction_deny_config = transaction_deny_config;
356 config.certificate_deny_config = certificate_deny_config;
357 config.authority_overload_config = authority_overload_config;
358 config.authority_store_pruning_config = pruning_config;
359
360 let chain_identifier = ChainIdentifier::from(*genesis.checkpoint().digest());
361 let policy_config = config.policy_config.clone();
362 let firewall_config = config.firewall_config.clone();
363
364 let state = AuthorityState::new(
365 name,
366 secret,
367 SupportedProtocolVersions::SYSTEM_DEFAULT,
368 authority_store,
369 cache_traits,
370 epoch_store.clone(),
371 committee_store,
372 index_store,
373 rpc_index,
374 checkpoint_store,
375 ®istry,
376 genesis.objects(),
377 &DBCheckpointConfig::default(),
378 config.clone(),
379 None,
380 chain_identifier,
381 pruner_db,
382 policy_config,
383 firewall_config,
384 Arc::new(PrunerWatermarks::default()),
385 )
386 .await;
387
388 if epoch_store.randomness_state_enabled() {
390 let consensus_client = Box::new(MockConsensusClient::new(
391 Arc::downgrade(&state),
392 ConsensusMode::Noop,
393 ));
394 let randomness_manager = RandomnessManager::try_new(
395 Arc::downgrade(&epoch_store),
396 consensus_client,
397 randomness::Handle::new_stub(),
398 config.protocol_key_pair(),
399 )
400 .await;
401 if let Some(randomness_manager) = randomness_manager {
402 epoch_store
405 .set_randomness_manager(randomness_manager)
406 .await
407 .unwrap();
408 }
409 }
410
411 state
416 .try_execute_immediately(
417 &VerifiedExecutableTransaction::new_from_checkpoint(
418 VerifiedTransaction::new_unchecked(genesis.transaction().clone()),
419 genesis.epoch(),
420 genesis.checkpoint().sequence_number,
421 ),
422 ExecutionEnv::new().with_scheduling_source(SchedulingSource::NonFastPath),
423 &state.epoch_store_for_testing(),
424 )
425 .await
426 .unwrap();
427
428 let batch = state
429 .get_cache_commit()
430 .build_db_batch(epoch_store.epoch(), &[*genesis.transaction().digest()]);
431
432 state.get_cache_commit().commit_transaction_outputs(
433 epoch_store.epoch(),
434 batch,
435 &[*genesis.transaction().digest()],
436 );
437
438 if let Some(starting_objects) = self.starting_objects {
444 state
445 .insert_objects_unsafe_for_testing_only(starting_objects)
446 .await
447 .unwrap();
448 };
449 state
450 }
451}