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