Skip to main content

iota_types/
transaction_executor.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use std::{collections::BTreeMap, time::Duration};
6
7use iota_sdk_types::{
8    GasPayment, ObjectId, Transaction, TransactionDigest, TransactionEffects, TransactionEvents,
9};
10
11use crate::{
12    error::{ExecutionError, IotaError},
13    execution::ExecutionResult,
14    messages_checkpoint::CheckpointSequenceNumber,
15    object::Object,
16    quorum_driver_types::{
17        ExecuteTransactionRequestV1, ExecuteTransactionResponseV1, QuorumDriverError,
18    },
19};
20
21/// Trait to define the interface for how the REST service interacts with a
22/// QuorumDriver or a simulated transaction executor.
23#[async_trait::async_trait]
24pub trait TransactionExecutor: Send + Sync {
25    async fn execute_transaction(
26        &self,
27        request: ExecuteTransactionRequestV1,
28        skip_certification: bool,
29        client_addr: Option<std::net::SocketAddr>,
30    ) -> Result<ExecuteTransactionResponseV1, QuorumDriverError>;
31
32    fn simulate_transaction(
33        &self,
34        transaction: Transaction,
35        checks: VmChecks,
36    ) -> Result<SimulateTransactionResult, IotaError>;
37
38    /// Wait for the given transactions to be included in a checkpoint.
39    ///
40    /// Returns a mapping from transaction digest to
41    /// `(checkpoint_sequence_number, checkpoint_timestamp_ms)`.
42    /// On timeout, returns partial results for any transactions that were
43    /// already checkpointed.
44    async fn wait_for_checkpoint_inclusion(
45        &self,
46        digests: &[TransactionDigest],
47        timeout: Duration,
48    ) -> Result<BTreeMap<TransactionDigest, (CheckpointSequenceNumber, u64)>, IotaError>;
49
50    /// Read authoritative effects, events, and input/output objects for a
51    /// locally-executed transaction from the cache. Used by callers that
52    /// have already waited for checkpoint inclusion and want to discard any
53    /// uncertified single-validator copies.
54    ///
55    /// Returns `Ok(None)` if the tx is not in the cache, or if the executor
56    /// does not maintain a local cache (e.g. simulacrum).
57    fn read_transaction_from_cache(
58        &self,
59        digest: &TransactionDigest,
60        include_events: bool,
61        include_input_objects: bool,
62        include_output_objects: bool,
63    ) -> Result<Option<CachedTransactionData>, IotaError> {
64        // Default: no cache — safe fallback for executors like simulacrum.
65        let _ = (
66            digest,
67            include_events,
68            include_input_objects,
69            include_output_objects,
70        );
71        Ok(None)
72    }
73}
74
75/// Authoritative per-transaction data read from a local cache.
76pub struct CachedTransactionData {
77    pub effects: TransactionEffects,
78    pub events: Option<TransactionEvents>,
79    pub input_objects: Option<Vec<Object>>,
80    pub output_objects: Option<Vec<Object>>,
81}
82
83pub struct SimulateTransactionResult {
84    pub effects: TransactionEffects,
85    pub events: Option<TransactionEvents>,
86    /// Every object the transaction ran with as input — including immutable
87    /// and read-only shared inputs, the packages it calls, and the gas coins
88    /// (the mock one included) — plus the runtime-loaded objects (e.g. dynamic
89    /// fields) it modified, at their pre-state versions, keyed by id.
90    pub input_objects: BTreeMap<ObjectId, Object>,
91    pub output_objects: BTreeMap<ObjectId, Object>,
92    /// The return values and mutable-reference outputs of every command, under
93    /// either [`VmChecks`] — both run through the executor's dev-inspect entry
94    /// point, which collects them regardless of which checks are in force.
95    pub execution_result: Result<Vec<ExecutionResult>, ExecutionError>,
96    pub mock_gas_id: Option<ObjectId>,
97    pub suggested_gas_price: Option<u64>,
98    /// The gas the simulation ran with, once whatever the transaction left
99    /// unset was filled in. Callers reporting the transaction back should
100    /// use this rather than re-deriving it, which would read a possibly
101    /// different epoch.
102    pub gas_data: GasPayment,
103}
104
105/// Which Move VM checks a simulation runs with.
106///
107/// This is the only thing that separates the two ways a transaction can be
108/// simulated, so it is what callers pick between: a dry run wants
109/// [`VmChecks::Enabled`], a dev inspect wants [`VmChecks::Disabled`].
110#[derive(Default, Debug, Copy, Clone)]
111pub enum VmChecks {
112    /// Run the transaction as it would run on chain: the same input and gas
113    /// checks a validator applies, and metering against the transaction's own
114    /// budget.
115    #[default]
116    Enabled,
117    /// Relax the rules around entry functions and argument values, so that any
118    /// Move function can be called and any value built from its bytes. Input
119    /// checks are reduced to the ones execution cannot proceed without.
120    Disabled,
121}
122
123impl VmChecks {
124    pub fn disabled(self) -> bool {
125        matches!(self, Self::Disabled)
126    }
127
128    pub fn enabled(self) -> bool {
129        matches!(self, Self::Enabled)
130    }
131}