1pub mod args;
9pub mod offchain_state;
10pub mod programmable_transaction_test_parser;
11mod simulator_persisted_store;
12pub mod test_adapter;
13
14use std::{path::Path, sync::Arc};
15
16use iota_core::authority::{
17 AuthorityState, authority_per_epoch_store::TxLockGuard,
18 authority_test_utils::send_and_confirm_transaction_with_execution_error,
19};
20use iota_json_rpc::authority_state::StateRead;
21use iota_json_rpc_types::{DevInspectResults, DryRunTransactionBlockResponse, EventFilter};
22use iota_sdk_types::{
23 Address, CheckpointContentsDigest, CheckpointDigest, Event, ObjectId, TransactionDigest,
24 TransactionKind,
25};
26use iota_storage::key_value_store::TransactionKeyValueStore;
27use iota_types::{
28 base_types::VersionNumber,
29 committee::EpochId,
30 effects::{TransactionEffects, TransactionEvents},
31 error::{ExecutionError, IotaError, IotaResult},
32 executable_transaction::{ExecutableTransaction, VerifiedExecutableTransaction},
33 iota_system_state::{
34 IotaSystemStateTrait, epoch_start_iota_system_state::EpochStartSystemStateTrait,
35 iota_system_state_summary::IotaSystemStateSummary,
36 },
37 messages_checkpoint::VerifiedCheckpoint,
38 object::Object,
39 storage::{ObjectStore, ReadStore},
40 transaction::{InputObjects, Transaction, TransactionData},
41};
42pub use move_transactional_test_runner::framework::{
43 create_adapter, run_tasks_with_adapter, run_test_impl,
44};
45use rand::rngs::StdRng;
46use simulacrum::{Simulacrum, SimulatorStore};
47use simulator_persisted_store::PersistedStore;
48use test_adapter::{IotaTestAdapter, PRE_COMPILED};
49
50#[cfg_attr(not(msim), tokio::main)]
51#[cfg_attr(msim, msim::main)]
52pub async fn run_test(path: &Path) -> Result<(), Box<dyn std::error::Error>> {
53 let (_guard, _filter_handle) = telemetry_subscribers::TelemetryConfig::new()
54 .with_env()
55 .init();
56 run_test_impl::<IotaTestAdapter>(path, Some(std::sync::Arc::new(PRE_COMPILED.clone()))).await?;
57 Ok(())
58}
59
60pub struct ValidatorWithFullnode {
61 pub validator: Arc<AuthorityState>,
62 pub fullnode: Arc<AuthorityState>,
63 pub kv_store: Arc<TransactionKeyValueStore>,
64}
65
66#[async_trait::async_trait]
68pub trait TransactionalAdapter: Send + Sync + ReadStore {
69 async fn execute_txn(
70 &mut self,
71 transaction: Transaction,
72 ) -> anyhow::Result<(TransactionEffects, Option<ExecutionError>)>;
73
74 async fn read_input_objects(&self, transaction: Transaction) -> IotaResult<InputObjects>;
75
76 fn prepare_txn(
77 &self,
78 transaction: Transaction,
79 input_objects: InputObjects,
80 ) -> anyhow::Result<(TransactionEffects, Option<ExecutionError>)>;
81
82 async fn create_checkpoint(&mut self) -> anyhow::Result<VerifiedCheckpoint>;
83
84 async fn advance_clock(
85 &mut self,
86 duration: std::time::Duration,
87 ) -> anyhow::Result<TransactionEffects>;
88
89 async fn advance_epoch(&mut self) -> anyhow::Result<()>;
90
91 async fn request_gas(
92 &mut self,
93 address: Address,
94 amount: u64,
95 ) -> anyhow::Result<TransactionEffects>;
96
97 async fn dry_run_transaction_block(
98 &self,
99 transaction_block: TransactionData,
100 transaction_digest: TransactionDigest,
101 ) -> IotaResult<DryRunTransactionBlockResponse>;
102
103 async fn dev_inspect_transaction_block(
104 &self,
105 sender: Address,
106 transaction_kind: TransactionKind,
107 gas_price: Option<u64>,
108 ) -> IotaResult<DevInspectResults>;
109
110 async fn query_tx_events_asc(
111 &self,
112 tx_digest: &TransactionDigest,
113 limit: usize,
114 ) -> IotaResult<Vec<Event>>;
115
116 async fn get_active_validator_addresses(&self) -> IotaResult<Vec<Address>>;
117}
118
119#[async_trait::async_trait]
120impl TransactionalAdapter for ValidatorWithFullnode {
121 async fn execute_txn(
122 &mut self,
123 transaction: Transaction,
124 ) -> anyhow::Result<(TransactionEffects, Option<ExecutionError>)> {
125 let with_shared = transaction.contains_shared_object();
126 let (_, effects, execution_error) = send_and_confirm_transaction_with_execution_error(
127 &self.validator,
128 Some(&self.fullnode),
129 transaction,
130 with_shared,
131 false,
132 )
133 .await?;
134 Ok((effects.into_data(), execution_error))
135 }
136
137 async fn read_input_objects(&self, transaction: Transaction) -> IotaResult<InputObjects> {
138 let tx = VerifiedExecutableTransaction::new_unchecked(
139 ExecutableTransaction::new_from_data_and_sig(
140 transaction.data().clone(),
141 iota_types::executable_transaction::CertificateProof::Checkpoint(0, 0),
142 ),
143 );
144
145 let epoch_store = self.validator.load_epoch_store_one_call_per_task().clone();
146 self.validator
147 .read_objects_for_execution(&TxLockGuard::guard_for_tests(), &tx, &epoch_store)
148 .map(|(tx_input_objects, _)| tx_input_objects)
149 }
150
151 fn prepare_txn(
152 &self,
153 transaction: Transaction,
154 input_objects: InputObjects,
155 ) -> anyhow::Result<(TransactionEffects, Option<ExecutionError>)> {
156 let tx = VerifiedExecutableTransaction::new_unchecked(
157 ExecutableTransaction::new_from_data_and_sig(
158 transaction.data().clone(),
159 iota_types::executable_transaction::CertificateProof::Checkpoint(0, 0),
160 ),
161 );
162
163 let epoch_store = self.validator.load_epoch_store_one_call_per_task().clone();
164 let (_, effects, error) =
165 self.validator
166 .prepare_transaction_for_benchmark(&tx, input_objects, &epoch_store)?;
167 Ok((effects, error))
168 }
169
170 async fn dry_run_transaction_block(
171 &self,
172 transaction_block: TransactionData,
173 transaction_digest: TransactionDigest,
174 ) -> IotaResult<DryRunTransactionBlockResponse> {
175 self.fullnode
176 .dry_exec_transaction(transaction_block, transaction_digest)
177 .map(|result| result.0)
178 }
179
180 async fn dev_inspect_transaction_block(
181 &self,
182 sender: Address,
183 transaction_kind: TransactionKind,
184 gas_price: Option<u64>,
185 ) -> IotaResult<DevInspectResults> {
186 self.fullnode
187 .dev_inspect_transaction_block(
188 sender,
189 transaction_kind,
190 gas_price,
191 None,
192 None,
193 None,
194 None,
195 None,
196 )
197 .await
198 }
199
200 async fn query_tx_events_asc(
201 &self,
202 tx_digest: &TransactionDigest,
203 limit: usize,
204 ) -> IotaResult<Vec<Event>> {
205 Ok(self
206 .validator
207 .query_events(
208 &self.kv_store,
209 EventFilter::Transaction(*tx_digest),
210 None,
211 limit,
212 false,
213 )
214 .await
215 .unwrap_or_default()
216 .into_iter()
217 .map(|iota_event| iota_event.into())
218 .collect())
219 }
220
221 async fn create_checkpoint(&mut self) -> anyhow::Result<VerifiedCheckpoint> {
222 unimplemented!("create_checkpoint not supported")
223 }
224
225 async fn advance_clock(
226 &mut self,
227 _duration: std::time::Duration,
228 ) -> anyhow::Result<TransactionEffects> {
229 unimplemented!("advance_clock not supported")
230 }
231
232 async fn advance_epoch(&mut self) -> anyhow::Result<()> {
233 self.validator.reconfigure_for_testing().await;
234 self.fullnode.reconfigure_for_testing().await;
235 Ok(())
236 }
237
238 async fn request_gas(
239 &mut self,
240 _address: Address,
241 _amount: u64,
242 ) -> anyhow::Result<TransactionEffects> {
243 unimplemented!("request_gas not supported")
244 }
245
246 async fn get_active_validator_addresses(&self) -> IotaResult<Vec<Address>> {
247 let system_state_summary = self
248 .fullnode
249 .get_system_state()
250 .map_err(|e| {
251 IotaError::IotaSystemStateRead(format!(
252 "Failed to get system state from fullnode: {e}"
253 ))
254 })?
255 .into_iota_system_state_summary();
256 let active_validators = match system_state_summary {
257 IotaSystemStateSummary::V1(inner) => inner.active_validators,
258 IotaSystemStateSummary::V2(inner) => inner.active_validators,
259 _ => unimplemented!(
260 "a new IotaSystemStateSummary enum variant was added and needs to be handled"
261 ),
262 };
263
264 Ok(active_validators
265 .iter()
266 .map(|x| x.iota_address)
267 .collect::<Vec<_>>())
268 }
269}
270
271impl ReadStore for ValidatorWithFullnode {
272 fn try_get_committee(
273 &self,
274 _epoch: EpochId,
275 ) -> iota_types::storage::error::Result<Option<Arc<iota_types::committee::Committee>>> {
276 todo!()
277 }
278
279 fn try_get_latest_epoch_id(&self) -> iota_types::storage::error::Result<EpochId> {
280 Ok(self.validator.epoch_store_for_testing().epoch())
281 }
282
283 fn try_get_latest_checkpoint(&self) -> iota_types::storage::error::Result<VerifiedCheckpoint> {
284 let sequence_number = self
285 .validator
286 .get_latest_checkpoint_sequence_number()
287 .unwrap();
288 self.try_get_checkpoint_by_sequence_number(sequence_number)
289 .map(|c| c.unwrap())
290 }
291
292 fn try_get_highest_verified_checkpoint(
293 &self,
294 ) -> iota_types::storage::error::Result<VerifiedCheckpoint> {
295 todo!()
296 }
297
298 fn try_get_highest_synced_checkpoint(
299 &self,
300 ) -> iota_types::storage::error::Result<VerifiedCheckpoint> {
301 todo!()
302 }
303
304 fn try_get_lowest_available_checkpoint(
305 &self,
306 ) -> iota_types::storage::error::Result<iota_types::messages_checkpoint::CheckpointSequenceNumber>
307 {
308 todo!()
309 }
310
311 fn try_get_checkpoint_by_digest(
312 &self,
313 _digest: &CheckpointDigest,
314 ) -> iota_types::storage::error::Result<Option<VerifiedCheckpoint>> {
315 todo!()
316 }
317
318 fn try_get_checkpoint_by_sequence_number(
319 &self,
320 sequence_number: iota_types::messages_checkpoint::CheckpointSequenceNumber,
321 ) -> iota_types::storage::error::Result<Option<VerifiedCheckpoint>> {
322 self.validator
323 .get_checkpoint_store()
324 .get_checkpoint_by_sequence_number(sequence_number)
325 .map_err(iota_types::storage::error::Error::custom)
326 }
327
328 fn try_get_checkpoint_contents_by_digest(
329 &self,
330 digest: &CheckpointContentsDigest,
331 ) -> iota_types::storage::error::Result<
332 Option<iota_types::messages_checkpoint::CheckpointContents>,
333 > {
334 self.validator
335 .get_checkpoint_store()
336 .get_checkpoint_contents(digest)
337 .map_err(iota_types::storage::error::Error::custom)
338 }
339
340 fn try_get_checkpoint_contents_by_sequence_number(
341 &self,
342 _sequence_number: iota_types::messages_checkpoint::CheckpointSequenceNumber,
343 ) -> iota_types::storage::error::Result<
344 Option<iota_types::messages_checkpoint::CheckpointContents>,
345 > {
346 todo!()
347 }
348
349 fn try_get_transaction(
350 &self,
351 tx_digest: &TransactionDigest,
352 ) -> iota_types::storage::error::Result<Option<Arc<iota_types::transaction::VerifiedTransaction>>>
353 {
354 self.validator
355 .get_transaction_cache_reader()
356 .try_get_transaction_block(tx_digest)
357 .map_err(iota_types::storage::error::Error::custom)
358 }
359
360 fn try_get_transaction_effects(
361 &self,
362 tx_digest: &TransactionDigest,
363 ) -> iota_types::storage::error::Result<Option<TransactionEffects>> {
364 self.validator
365 .get_transaction_cache_reader()
366 .try_get_executed_effects(tx_digest)
367 .map_err(iota_types::storage::error::Error::custom)
368 }
369
370 fn try_get_events(
371 &self,
372 digest: &TransactionDigest,
373 ) -> iota_types::storage::error::Result<Option<TransactionEvents>> {
374 self.validator
375 .get_transaction_cache_reader()
376 .try_get_events(digest)
377 .map_err(iota_types::storage::error::Error::custom)
378 }
379
380 fn try_get_full_checkpoint_contents_by_sequence_number(
381 &self,
382 _sequence_number: iota_types::messages_checkpoint::CheckpointSequenceNumber,
383 ) -> iota_types::storage::error::Result<
384 Option<iota_types::messages_checkpoint::FullCheckpointContents>,
385 > {
386 todo!()
387 }
388
389 fn try_get_full_checkpoint_contents(
390 &self,
391 _digest: &CheckpointContentsDigest,
392 ) -> iota_types::storage::error::Result<
393 Option<iota_types::messages_checkpoint::FullCheckpointContents>,
394 > {
395 todo!()
396 }
397}
398
399impl ObjectStore for ValidatorWithFullnode {
400 fn try_get_object(
401 &self,
402 object_id: &ObjectId,
403 ) -> Result<Option<Object>, iota_types::storage::error::Error> {
404 self.validator.get_object_store().try_get_object(object_id)
405 }
406
407 fn try_get_object_by_key(
408 &self,
409 object_id: &ObjectId,
410 version: VersionNumber,
411 ) -> Result<Option<Object>, iota_types::storage::error::Error> {
412 self.validator
413 .get_object_store()
414 .try_get_object_by_key(object_id, version)
415 }
416}
417
418#[async_trait::async_trait]
419impl TransactionalAdapter for Simulacrum<StdRng, PersistedStore> {
420 async fn execute_txn(
421 &mut self,
422 transaction: Transaction,
423 ) -> anyhow::Result<(TransactionEffects, Option<ExecutionError>)> {
424 Ok(self.execute_transaction(transaction)?)
425 }
426
427 async fn read_input_objects(&self, _transaction: Transaction) -> IotaResult<InputObjects> {
428 unimplemented!("read_input_objects not supported in simulator mode")
429 }
430
431 fn prepare_txn(
432 &self,
433 _transaction: Transaction,
434 _input_objects: InputObjects,
435 ) -> anyhow::Result<(TransactionEffects, Option<ExecutionError>)> {
436 unimplemented!("prepare_txn not supported in simulator mode")
437 }
438
439 async fn dev_inspect_transaction_block(
440 &self,
441 _sender: Address,
442 _transaction_kind: TransactionKind,
443 _gas_price: Option<u64>,
444 ) -> IotaResult<DevInspectResults> {
445 unimplemented!("dev_inspect_transaction_block not supported in simulator mode")
446 }
447
448 async fn dry_run_transaction_block(
449 &self,
450 _transaction_block: TransactionData,
451 _transaction_digest: TransactionDigest,
452 ) -> IotaResult<DryRunTransactionBlockResponse> {
453 unimplemented!("dry_run_transaction_block not supported in simulator mode")
454 }
455
456 async fn query_tx_events_asc(
457 &self,
458 tx_digest: &TransactionDigest,
459 _limit: usize,
460 ) -> IotaResult<Vec<Event>> {
461 match self.try_get_events(tx_digest)? {
462 Some(events) => Ok(events.0),
463 None => Ok(Vec::new()),
464 }
465 }
466
467 async fn create_checkpoint(&mut self) -> anyhow::Result<VerifiedCheckpoint> {
468 Ok(Simulacrum::create_checkpoint(self))
469 }
470
471 async fn advance_clock(
472 &mut self,
473 duration: std::time::Duration,
474 ) -> anyhow::Result<TransactionEffects> {
475 Ok(Simulacrum::advance_clock(self, duration))
476 }
477
478 async fn advance_epoch(&mut self) -> anyhow::Result<()> {
479 Simulacrum::advance_epoch(self);
480 Ok(())
481 }
482
483 async fn request_gas(
484 &mut self,
485 address: Address,
486 amount: u64,
487 ) -> anyhow::Result<TransactionEffects> {
488 Simulacrum::request_gas(self, address, amount)
489 }
490
491 async fn get_active_validator_addresses(&self) -> IotaResult<Vec<Address>> {
492 Ok(self.epoch_start_state().get_validator_addresses())
496 }
497}