transaction_fuzzer/
executor.rs

1// Copyright (c) Mysten Labs, Inc.
2// Copyright (c) The Diem Core Contributors
3// Modifications Copyright (c) 2024 IOTA Stiftung
4// SPDX-License-Identifier: Apache-2.0
5
6use std::{fmt::Debug, path::PathBuf, sync::Arc};
7
8use iota_core::{
9    authority::{AuthorityState, test_authority_builder::TestAuthorityBuilder},
10    test_utils::send_and_confirm_transaction,
11};
12use iota_move_build::BuildConfig;
13use iota_types::{
14    base_types::ObjectID,
15    effects::{TransactionEffects, TransactionEffectsAPI},
16    error::IotaError,
17    execution_status::{ExecutionFailureStatus, ExecutionStatus},
18    object::Object,
19    transaction::{Transaction, TransactionData},
20    utils::to_sender_signed_transaction,
21};
22use tokio::runtime::Runtime;
23
24use crate::account_universe::{AccountCurrent, PUBLISH_BUDGET};
25
26pub type ExecutionResult = Result<ExecutionStatus, IotaError>;
27
28fn build_test_modules(test_dir: &str) -> (Vec<u8>, Vec<Vec<u8>>) {
29    let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
30    path.extend(["data", test_dir]);
31    let with_unpublished_deps = false;
32    let config = BuildConfig::new_for_testing();
33    let package = config.build(&path).unwrap();
34    (
35        package.get_package_digest(with_unpublished_deps).to_vec(),
36        package.get_package_bytes(with_unpublished_deps),
37    )
38}
39
40// We want to look for either panics (in which case we won't hit this) or
41// invariant violations in which case we want to panic.
42pub fn assert_is_acceptable_result(result: &ExecutionResult) {
43    if let Ok(
44        e @ ExecutionStatus::Failure {
45            error: ExecutionFailureStatus::InvariantViolation,
46            command: _,
47        },
48    ) = result
49    {
50        panic!("Invariant violation: {e:#?}")
51    }
52}
53
54#[derive(Clone)]
55pub struct Executor {
56    pub state: Arc<AuthorityState>,
57    pub rt: Arc<Runtime>,
58}
59
60impl Debug for Executor {
61    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
62        f.debug_struct("Executor").finish()
63    }
64}
65
66impl Default for Executor {
67    fn default() -> Self {
68        Self::new()
69    }
70}
71
72impl Executor {
73    pub fn new() -> Self {
74        let rt = Runtime::new().unwrap();
75        let state = rt.block_on(TestAuthorityBuilder::new().build());
76        Self {
77            state,
78            rt: Arc::new(rt),
79        }
80    }
81
82    pub fn new_with_rgp(rgp: u64) -> Self {
83        let rt = Runtime::new().unwrap();
84        let state = rt.block_on(
85            TestAuthorityBuilder::new()
86                .with_reference_gas_price(rgp)
87                .build(),
88        );
89        Self {
90            state,
91            rt: Arc::new(rt),
92        }
93    }
94
95    pub fn get_reference_gas_price(&self) -> u64 {
96        self.state.reference_gas_price_for_testing().unwrap()
97    }
98
99    pub fn add_object(&mut self, object: Object) {
100        self.rt.block_on(self.state.insert_genesis_object(object));
101    }
102
103    pub fn add_objects(&mut self, objects: &[Object]) {
104        self.rt.block_on(self.state.insert_genesis_objects(objects));
105    }
106
107    pub fn execute_transaction(&mut self, txn: Transaction) -> ExecutionResult {
108        self.rt
109            .block_on(send_and_confirm_transaction(&self.state, None, txn))
110            .map(|(_, effects)| effects.into_data().status().clone())
111    }
112
113    pub fn publish(
114        &mut self,
115        package_name: &str,
116        dep_ids: Vec<ObjectID>,
117        account: &mut AccountCurrent,
118    ) -> TransactionEffects {
119        let (_, modules) = build_test_modules(package_name);
120        // let gas_obj_ref =
121        // account.current_coins.last().unwrap().compute_object_reference();
122        let gas_object = account.new_gas_object(self);
123        let data = TransactionData::new_module(
124            account.initial_data.account.address,
125            gas_object.compute_object_reference(),
126            modules,
127            dep_ids,
128            PUBLISH_BUDGET,
129            1000,
130        );
131        let txn = to_sender_signed_transaction(data, &account.initial_data.account.key);
132        let effects = self
133            .rt
134            .block_on(send_and_confirm_transaction(&self.state, None, txn))
135            .unwrap()
136            .1
137            .into_data();
138
139        assert!(
140            matches!(effects.status(), ExecutionStatus::Success),
141            "{:?}",
142            effects.status()
143        );
144        effects
145    }
146
147    pub fn execute_transactions(
148        &mut self,
149        txn: impl IntoIterator<Item = Transaction>,
150    ) -> Vec<ExecutionResult> {
151        txn.into_iter()
152            .map(|txn| self.execute_transaction(txn))
153            .collect()
154    }
155}