1use std::{
6 collections::{BTreeMap, HashMap, HashSet},
7 sync::Arc,
8};
9
10use iota_core::{
11 authority::{
12 AuthorityState, ExecutionEnv,
13 authority_per_epoch_store::AuthorityPerEpochStore,
14 shared_object_version_manager::{AssignedTxAndVersions, AssignedVersions, Schedulable},
15 test_authority_builder::TestAuthorityBuilder,
16 },
17 authority_server::{ValidatorService, ValidatorServiceMetrics},
18 checkpoints::checkpoint_executor::CheckpointExecutor,
19 consensus_adapter::{
20 ConnectionMonitorStatusForTests, ConsensusAdapter, ConsensusAdapterMetrics,
21 },
22 execution_scheduler::ExecutionSchedulerAPI,
23 global_state_hasher::GlobalStateHasher,
24 mock_consensus::{ConsensusMode, MockConsensusClient},
25};
26use iota_protocol_config::ProtocolConfig;
27use iota_sdk_types::{Address, ObjectReference, TransactionDigest, TransactionEffects};
28use iota_test_transaction_builder::{PublishData, TestTransactionBuilder};
29use iota_types::{
30 base_types::AuthorityName,
31 committee::Committee,
32 crypto::{AccountPrivateKey, AuthoritySignature, Signer},
33 effects::{TransactionEffectsAPI, TransactionEffectsExt},
34 executable_transaction::VerifiedExecutableTransaction,
35 messages_checkpoint::{VerifiedCheckpoint, VerifiedCheckpointContents},
36 messages_grpc::HandleTransactionResponse,
37 mock_checkpoint_builder::{MockCheckpointBuilder, ValidatorKeypairProvider},
38 object::Object,
39 transaction::{
40 CertifiedTransaction, DEFAULT_VALIDATOR_GAS_PRICE, SenderSignedTransactionAPI,
41 TransactionAPI, TransactionEnvelope, VerifiedCertificate, VerifiedTransaction,
42 },
43 transaction_executor::VmChecks,
44};
45
46use crate::{command::Component, mock_storage::InMemoryObjectStore};
47
48#[derive(Clone)]
49pub struct SingleValidator {
50 validator_service: Arc<ValidatorService>,
51 epoch_store: Arc<AuthorityPerEpochStore>,
52}
53
54impl SingleValidator {
55 pub(crate) async fn new(genesis_objects: &[Object], component: Component) -> Self {
56 let mut protocol_config = ProtocolConfig::get_for_max_version_UNSAFE();
61 protocol_config.set_enable_pcool_flow_for_testing(false);
62 let validator = TestAuthorityBuilder::new()
63 .with_protocol_config(protocol_config)
64 .disable_indexer()
65 .with_starting_objects(genesis_objects)
66 .insert_genesis_checkpoint()
68 .build()
69 .await;
70 let epoch_store = validator.epoch_store_for_testing().clone();
71 let consensus_mode = match component {
72 Component::ValidatorWithFakeConsensus => ConsensusMode::DirectSequencing,
73 _ => ConsensusMode::Noop,
74 };
75 let consensus_adapter = Arc::new(ConsensusAdapter::new(
76 Arc::new(MockConsensusClient::new(
77 Arc::downgrade(&validator),
78 consensus_mode,
79 )),
80 validator.checkpoint_store.clone(),
81 validator.name,
82 Arc::new(ConnectionMonitorStatusForTests {}),
83 100_000,
84 100_000,
85 None,
86 None,
87 ConsensusAdapterMetrics::new_test(),
88 50,
89 ));
90 let validator_service = Arc::new(ValidatorService::new_for_tests(
94 validator,
95 consensus_adapter,
96 Arc::new(ValidatorServiceMetrics::new_for_tests()),
97 ));
98 Self {
99 validator_service,
100 epoch_store,
101 }
102 }
103
104 pub fn get_validator(&self) -> &Arc<AuthorityState> {
105 self.validator_service.validator_state()
106 }
107
108 pub fn get_epoch_store(&self) -> &Arc<AuthorityPerEpochStore> {
109 &self.epoch_store
110 }
111
112 pub async fn publish_package(
115 &self,
116 publish_data: PublishData,
117 sender: Address,
118 private_key: &AccountPrivateKey,
119 gas: ObjectReference,
120 ) -> (ObjectReference, ObjectReference) {
121 let tx_builder = TestTransactionBuilder::new(sender, gas, DEFAULT_VALIDATOR_GAS_PRICE)
122 .publish_with_data(publish_data);
123 let transaction = tx_builder.build_and_sign(private_key);
124 let effects = self.execute_raw_transaction(transaction).await;
125 let package = effects
126 .all_changed_objects()
127 .into_iter()
128 .filter_map(|(changed, _)| {
129 changed
130 .owner()
131 .is_immutable()
132 .then_some(*changed.reference())
133 })
134 .next()
135 .unwrap();
136 let updated_gas = *effects.gas_object().reference();
137 (package, updated_gas)
138 }
139
140 pub async fn execute_raw_transaction(
141 &self,
142 transaction: TransactionEnvelope,
143 ) -> TransactionEffects {
144 let executable = VerifiedExecutableTransaction::new_from_quorum_execution(
145 VerifiedTransaction::new_unchecked(transaction),
146 0,
147 );
148 let effects = self
149 .get_validator()
150 .try_execute_immediately(&executable, ExecutionEnv::new(), &self.epoch_store)
151 .unwrap()
152 .0;
153 assert!(effects.status().is_success());
154 effects
155 }
156
157 pub async fn execute_dry_run(&self, transaction: TransactionEnvelope) -> TransactionEffects {
158 let effects = self
159 .get_validator()
160 .simulate_transaction_for_benchmark(
161 transaction.data().transaction().clone(),
162 VmChecks::Enabled,
163 )
164 .unwrap()
165 .effects;
166 assert!(effects.status().is_success());
167 effects
168 }
169
170 pub async fn execute_certificate(
171 &self,
172 cert: CertifiedTransaction,
173 assigned_versions: &AssignedVersions,
174 component: Component,
175 ) -> TransactionEffects {
176 let effects = match component {
177 Component::Baseline => {
178 let cert = VerifiedExecutableTransaction::new_from_certificate(
179 VerifiedCertificate::new_unchecked(cert),
180 );
181 self.get_validator()
182 .try_execute_immediately(
183 &cert,
184 ExecutionEnv::new().with_assigned_versions(assigned_versions.clone()),
185 &self.epoch_store,
186 )
187 .unwrap()
188 .0
189 }
190 Component::WithTxManager => {
191 let cert = VerifiedCertificate::new_unchecked(cert);
192 if cert.contains_shared_object() {
193 self.get_validator().execution_scheduler().enqueue(
197 vec![(
198 VerifiedExecutableTransaction::new_from_certificate(cert.clone())
199 .into(),
200 ExecutionEnv::new().with_assigned_versions(assigned_versions.clone()),
201 )],
202 &self.epoch_store,
203 );
204 }
205 self.get_validator()
206 .wait_for_certificate_execution(&cert, &self.epoch_store)
207 .await
208 .unwrap()
209 }
210 Component::ValidatorWithoutConsensus | Component::ValidatorWithFakeConsensus => {
211 let response = self
212 .validator_service
213 .execute_certificate_for_testing(cert)
214 .await
215 .unwrap()
216 .into_inner();
217 response.signed_effects.into_data()
218 }
219 Component::TxnSigning | Component::CheckpointExecutor | Component::ExecutionOnly => {
220 unreachable!()
221 }
222 };
223 assert!(effects.status().is_success());
224 effects
225 }
226
227 pub(crate) async fn execute_transaction_in_memory(
228 &self,
229 store: InMemoryObjectStore,
230 transaction: CertifiedTransaction,
231 assigned_versions: &AssignedVersions,
232 ) -> TransactionEffects {
233 let input_objects = transaction.transaction().input_objects().unwrap();
234 let objects = store
235 .read_objects_for_execution(&transaction.key(), assigned_versions, &input_objects)
236 .unwrap();
237
238 let executable = VerifiedExecutableTransaction::new_from_certificate(
239 VerifiedCertificate::new_unchecked(transaction),
240 );
241 let (gas_status, input_objects) = iota_transaction_checks::check_certificate_input(
242 &executable,
243 objects,
244 self.epoch_store.protocol_config(),
245 self.epoch_store.reference_gas_price(),
246 )
247 .unwrap();
248 let (kind, signer, gas_data) = executable.transaction().execution_parts();
249 let (inner_temp_store, _, effects, _timings, _) =
250 self.epoch_store.executor().execute_transaction_to_effects(
251 &store,
252 self.epoch_store.protocol_config(),
253 self.get_validator().metrics.limits_metrics.clone(),
254 false,
255 &HashSet::new(),
256 &self.epoch_store.epoch(),
257 0,
258 input_objects,
259 gas_data,
260 gas_status,
261 kind,
262 signer,
263 *executable.digest(),
264 &mut None,
265 );
266 assert!(effects.status().is_success());
267 store.commit_objects(inner_temp_store);
268 effects
269 }
270
271 pub async fn sign_transaction(
272 &self,
273 transaction: TransactionEnvelope,
274 ) -> HandleTransactionResponse {
275 self.validator_service
276 .handle_transaction_for_benchmarking(transaction)
277 .await
278 .unwrap()
279 .into_inner()
280 }
281
282 pub(crate) async fn build_checkpoints(
283 &self,
284 transactions: Vec<CertifiedTransaction>,
285 mut all_effects: BTreeMap<TransactionDigest, TransactionEffects>,
286 checkpoint_size: usize,
287 ) -> Vec<(VerifiedCheckpoint, VerifiedCheckpointContents)> {
288 let mut builder = MockCheckpointBuilder::new(
289 self.get_validator()
290 .get_checkpoint_store()
291 .get_latest_certified_checkpoint()
292 .unwrap()
293 .unwrap(),
294 );
295 let mut checkpoints = vec![];
296 for transaction in transactions {
297 let effects = all_effects.remove(transaction.digest()).unwrap();
298 builder.push_transaction(
299 VerifiedTransaction::new_unchecked(transaction.into_unsigned()),
300 effects,
301 );
302 if builder.size() == checkpoint_size {
303 let (checkpoint, _, full_contents) = builder.build(self, 0);
304 checkpoints.push((checkpoint, full_contents));
305 }
306 }
307 if builder.size() > 0 {
308 let (checkpoint, _, full_contents) = builder.build(self, 0);
309 checkpoints.push((checkpoint, full_contents));
310 }
311 checkpoints
312 }
313
314 pub fn create_checkpoint_executor(&self) -> CheckpointExecutor {
315 let validator = self.get_validator();
316 CheckpointExecutor::new_for_tests(
317 self.epoch_store.clone(),
318 validator.get_checkpoint_store().clone(),
319 validator.clone(),
320 Arc::new(GlobalStateHasher::new_for_tests(
321 validator.get_global_state_hash_store().clone(),
322 )),
323 )
324 }
325
326 pub(crate) fn create_in_memory_store(&self) -> InMemoryObjectStore {
327 let objects: HashMap<_, _> = self
328 .get_validator()
329 .get_global_state_hash_store()
330 .iter_cached_live_object_set_for_testing()
331 .map(|o| (o.object.id(), o.object))
332 .collect();
333 InMemoryObjectStore::new(objects)
334 }
335
336 pub(crate) async fn assigned_shared_object_versions(
337 &self,
338 transactions: &[CertifiedTransaction],
339 ) -> AssignedTxAndVersions {
340 let transactions: Vec<_> = transactions
341 .iter()
342 .map(|tx| {
343 VerifiedExecutableTransaction::new_from_certificate(
344 VerifiedCertificate::new_unchecked(tx.clone()),
345 )
346 })
347 .collect();
348 let assignables: Vec<_> = transactions.iter().map(Schedulable::Transaction).collect();
349 self.epoch_store
350 .assign_shared_object_versions_idempotent(
351 self.get_validator().get_object_cache_reader().as_ref(),
352 assignables.iter(),
353 )
354 .unwrap()
355 }
356}
357
358impl ValidatorKeypairProvider for SingleValidator {
359 fn get_validator_key(&self, name: &AuthorityName) -> &dyn Signer<AuthoritySignature> {
360 assert_eq!(name, &self.get_validator().name);
361 &*self.get_validator().secret
362 }
363
364 fn get_committee(&self) -> &Committee {
365 self.epoch_store.committee().as_ref()
366 }
367}