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