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    /// Keeps the objects that `read_transaction_from_cache` returns for
51    /// transactions executed from now on readable until the returned value is
52    /// dropped. Take it before executing transactions whose objects are read
53    /// from the cache afterwards. Executors that do not prune retain nothing.
54    fn retain_cached_objects(&self) -> Box<dyn Send + Sync> {
55        Box::new(())
56    }
57
58    /// Read authoritative effects, events, and input/output objects for a
59    /// locally-executed transaction from the cache. Used by callers that
60    /// have already waited for checkpoint inclusion and want to discard any
61    /// uncertified single-validator copies.
62    ///
63    /// Returns `Ok(None)` if the tx is not in the cache, or if the executor
64    /// does not maintain a local cache (e.g. simulacrum).
65    fn read_transaction_from_cache(
66        &self,
67        digest: &TransactionDigest,
68        include_events: bool,
69        include_input_objects: bool,
70        include_output_objects: bool,
71    ) -> Result<Option<CachedTransactionData>, IotaError> {
72        // Default: no cache — safe fallback for executors like simulacrum.
73        let _ = (
74            digest,
75            include_events,
76            include_input_objects,
77            include_output_objects,
78        );
79        Ok(None)
80    }
81}
82
83/// Authoritative per-transaction data read from a local cache.
84pub struct CachedTransactionData {
85    pub effects: TransactionEffects,
86    pub events: Option<TransactionEvents>,
87    pub input_objects: Option<Vec<Object>>,
88    pub output_objects: Option<Vec<Object>>,
89}
90
91pub struct SimulateTransactionResult {
92    pub effects: TransactionEffects,
93    pub events: Option<TransactionEvents>,
94    /// Every object the transaction ran with as input — including immutable
95    /// and read-only shared inputs, the packages it calls, and the gas coins
96    /// (the mock one included) — plus the runtime-loaded objects (e.g. dynamic
97    /// fields) it modified, at their pre-state versions, keyed by id.
98    pub input_objects: BTreeMap<ObjectId, Object>,
99    pub output_objects: BTreeMap<ObjectId, Object>,
100    /// The return values and mutable-reference outputs of every command, under
101    /// either [`VmChecks`] — both run through the executor's dev-inspect entry
102    /// point, which collects them regardless of which checks are in force.
103    pub execution_result: Result<Vec<ExecutionResult>, ExecutionError>,
104    pub mock_gas_id: Option<ObjectId>,
105    pub suggested_gas_price: Option<u64>,
106    /// The gas the simulation ran with, once whatever the transaction left
107    /// unset was filled in. Callers reporting the transaction back should
108    /// use this rather than re-deriving it, which would read a possibly
109    /// different epoch.
110    pub gas_data: GasPayment,
111}
112
113/// Which Move VM checks a simulation runs with.
114///
115/// This is the only thing that separates the two ways a transaction can be
116/// simulated, so it is what callers pick between: a dry run wants
117/// [`VmChecks::Enabled`], a dev inspect wants [`VmChecks::Disabled`].
118#[derive(Default, Debug, Copy, Clone)]
119pub enum VmChecks {
120    /// Run the transaction as it would run on chain: the same input and gas
121    /// checks a validator applies, and metering against the transaction's own
122    /// budget.
123    #[default]
124    Enabled,
125    /// Relax the rules around entry functions and argument values, so that any
126    /// Move function can be called and any value built from its bytes. Input
127    /// checks are reduced to the ones execution cannot proceed without.
128    Disabled,
129}
130
131impl VmChecks {
132    pub fn disabled(self) -> bool {
133        matches!(self, Self::Disabled)
134    }
135
136    pub fn enabled(self) -> bool {
137        matches!(self, Self::Enabled)
138    }
139}