sui_types/
execution.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use crate::{
5    MoveTypeTagTrait, SUI_SYSTEM_ADDRESS,
6    accumulator_event::AccumulatorEvent,
7    base_types::{ObjectID, ObjectRef, SequenceNumber},
8    digests::{ObjectDigest, TransactionDigest},
9    error::SuiError,
10    event::Event,
11    is_system_package,
12    object::{Data, Object, Owner},
13    storage::{BackingPackageStore, ObjectChange},
14    sui_system_state::SUI_SYSTEM_STATE_INNER_MODULE_NAME,
15    transaction::{Argument, Command, SharedObjectMutability},
16    type_input::TypeInput,
17};
18use move_core_types::{
19    ident_str,
20    identifier::IdentStr,
21    language_storage::{StructTag, TypeTag},
22};
23use serde::{Deserialize, Serialize};
24use std::collections::{BTreeMap, BTreeSet};
25use std::time::Duration;
26
27const EXECUTION_TIME_OBSERVATION_CHUNK_KEY_STRUCT: &IdentStr =
28    ident_str!("ExecutionTimeObservationChunkKey");
29
30/// A type containing all of the information needed to work in execution with an object whose
31/// consensus stream is ended, and when committing the execution effects of the transaction.
32/// This holds:
33/// 0. The object ID.
34/// 1. The version.
35/// 2. Whether the object appeared as mutable (or owned) in the transaction, or as read-only.
36/// 3. The transaction digest of the previous transaction that used this object mutably or
37///    took it by value.
38pub type ConsensusStreamEndedInfo = (
39    ObjectID,
40    SequenceNumber,
41    SharedObjectMutability,
42    TransactionDigest,
43);
44
45/// A sequence of information about removed consensus objects in the transaction's inputs.
46pub type ConsensusStreamEndedObjects = Vec<ConsensusStreamEndedInfo>;
47
48#[derive(Clone, Debug, PartialEq, Eq)]
49pub enum SharedInput {
50    Existing(ObjectRef),
51    ConsensusStreamEnded(ConsensusStreamEndedInfo),
52    Cancelled((ObjectID, SequenceNumber)),
53}
54
55#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
56pub struct DynamicallyLoadedObjectMetadata {
57    pub version: SequenceNumber,
58    pub digest: ObjectDigest,
59    pub owner: Owner,
60    pub storage_rebate: u64,
61    pub previous_transaction: TransactionDigest,
62}
63
64/// View of the store necessary to produce the layouts of types.
65pub trait TypeLayoutStore: BackingPackageStore {}
66impl<T> TypeLayoutStore for T where T: BackingPackageStore {}
67
68#[derive(Debug)]
69pub enum ExecutionResults {
70    V1(ExecutionResultsV1),
71    V2(ExecutionResultsV2),
72}
73
74#[derive(Debug)]
75pub struct ExecutionResultsV1 {
76    pub object_changes: BTreeMap<ObjectID, ObjectChange>,
77    pub user_events: Vec<Event>,
78}
79
80/// Used by sui-execution v1 and above, to capture the execution results from Move.
81/// The results represent the primitive information that can then be used to construct
82/// both transaction effects V1 and V2.
83#[derive(Debug, Default)]
84pub struct ExecutionResultsV2 {
85    /// All objects written regardless of whether they were mutated, created, or unwrapped.
86    pub written_objects: BTreeMap<ObjectID, Object>,
87    /// All objects that existed prior to this transaction, and are modified in this transaction.
88    /// This includes any type of modification, including mutated, wrapped and deleted objects.
89    pub modified_objects: BTreeSet<ObjectID>,
90    /// All object IDs created in this transaction.
91    pub created_object_ids: BTreeSet<ObjectID>,
92    /// All object IDs deleted in this transaction.
93    /// No object ID should be in both created_object_ids and deleted_object_ids.
94    pub deleted_object_ids: BTreeSet<ObjectID>,
95    /// All Move events emitted in this transaction.
96    pub user_events: Vec<Event>,
97    /// All accumulator events emitted in this transaction.
98    pub accumulator_events: Vec<AccumulatorEvent>,
99
100    /// Used to track SUI conservation in settlement transactions. Settlement transactions
101    /// gather up withdraws and deposits from other transactions, and record them to accumulator
102    /// fields. The settlement transaction records the total amount of SUI being disbursed here,
103    /// so that we can verify that the amount stored in the fields at the end of the transaction
104    /// is correct.
105    pub settlement_input_sui: u64,
106    pub settlement_output_sui: u64,
107}
108
109pub type ExecutionResult = (
110    /*  mutable_reference_outputs */ Vec<(Argument, Vec<u8>, TypeTag)>,
111    /*  return_values */ Vec<(Vec<u8>, TypeTag)>,
112);
113
114impl ExecutionResultsV2 {
115    pub fn drop_writes(&mut self) {
116        self.written_objects.clear();
117        self.modified_objects.clear();
118        self.created_object_ids.clear();
119        self.deleted_object_ids.clear();
120        self.user_events.clear();
121        self.accumulator_events.clear();
122    }
123
124    pub fn merge_results(&mut self, new_results: Self) {
125        self.written_objects.extend(new_results.written_objects);
126        self.modified_objects.extend(new_results.modified_objects);
127        self.created_object_ids
128            .extend(new_results.created_object_ids);
129        self.deleted_object_ids
130            .extend(new_results.deleted_object_ids);
131        self.user_events.extend(new_results.user_events);
132        self.accumulator_events
133            .extend(new_results.accumulator_events);
134        self.settlement_input_sui += new_results.settlement_input_sui;
135        self.settlement_output_sui += new_results.settlement_output_sui;
136    }
137
138    pub fn update_version_and_previous_tx(
139        &mut self,
140        lamport_version: SequenceNumber,
141        prev_tx: TransactionDigest,
142        input_objects: &BTreeMap<ObjectID, Object>,
143        reshare_at_initial_version: bool,
144    ) {
145        for (id, obj) in self.written_objects.iter_mut() {
146            // TODO: We can now get rid of the following logic by passing in lamport version
147            // into the execution layer, and create new objects using the lamport version directly.
148
149            // Update the version for the written object.
150            match &mut obj.data {
151                Data::Move(obj) => {
152                    // Move objects all get the transaction's lamport timestamp
153                    obj.increment_version_to(lamport_version);
154                }
155
156                Data::Package(pkg) => {
157                    // Modified packages get their version incremented (this is a special case that
158                    // only applies to system packages).  All other packages can only be created,
159                    // and they are left alone.
160                    if self.modified_objects.contains(id) {
161                        debug_assert!(is_system_package(*id));
162                        pkg.increment_version();
163                    }
164                }
165            }
166
167            // Record the version that the shared object was created at in its owner field.  Note,
168            // this only works because shared objects must be created as shared (not created as
169            // owned in one transaction and later converted to shared in another).
170            if let Owner::Shared {
171                initial_shared_version,
172            } = &mut obj.owner
173            {
174                if self.created_object_ids.contains(id) {
175                    assert_eq!(
176                        *initial_shared_version,
177                        SequenceNumber::new(),
178                        "Initial version should be blank before this point for {id:?}",
179                    );
180                    *initial_shared_version = lamport_version;
181                }
182
183                // Update initial_shared_version for reshared objects
184                if reshare_at_initial_version
185                    && let Some(Owner::Shared {
186                        initial_shared_version: previous_initial_shared_version,
187                    }) = input_objects.get(id).map(|obj| &obj.owner)
188                {
189                    debug_assert!(!self.created_object_ids.contains(id));
190                    debug_assert!(!self.deleted_object_ids.contains(id));
191                    debug_assert!(
192                        *initial_shared_version == SequenceNumber::new()
193                            || *initial_shared_version == *previous_initial_shared_version
194                    );
195
196                    *initial_shared_version = *previous_initial_shared_version;
197                }
198            }
199
200            // Record start version for ConsensusAddressOwner objects.
201            if let Owner::ConsensusAddressOwner {
202                start_version,
203                owner,
204            } = &mut obj.owner
205            {
206                debug_assert!(!self.deleted_object_ids.contains(id));
207
208                if let Some(Owner::ConsensusAddressOwner {
209                    start_version: previous_start_version,
210                    owner: previous_owner,
211                }) = input_objects.get(id).map(|obj| &obj.owner)
212                {
213                    if owner == previous_owner {
214                        // Assign existing start_version in case a ConsensusAddressOwner object was
215                        // transferred to the same owner.
216                        *start_version = *previous_start_version;
217                    } else {
218                        // If owner changes, we need to begin a new stream.
219                        *start_version = lamport_version;
220                    }
221                } else {
222                    // ConsensusAddressOwner object was created, transferred from another Owner
223                    // type, or unwrapped, so we begin a new stream.
224                    *start_version = lamport_version;
225                }
226            }
227
228            obj.previous_transaction = prev_tx;
229        }
230    }
231}
232
233#[derive(Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Clone, Serialize, Deserialize)]
234pub enum ExecutionTimeObservationKey {
235    // Contains all the fields from `ProgrammableMoveCall` besides `arguments`.
236    MoveEntryPoint {
237        /// The package containing the module and function.
238        package: ObjectID,
239        /// The specific module in the package containing the function.
240        module: String,
241        /// The function to be called.
242        function: String,
243        /// The type arguments to the function.
244        /// NOTE: This field is currently not populated.
245        type_arguments: Vec<TypeInput>,
246    },
247    TransferObjects,
248    SplitCoins,
249    MergeCoins,
250    Publish, // special case: should not be used; we only use hard-coded estimate for this
251    MakeMoveVec,
252    Upgrade,
253}
254
255impl std::fmt::Display for ExecutionTimeObservationKey {
256    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
257        match self {
258            ExecutionTimeObservationKey::MoveEntryPoint {
259                module, function, ..
260            } => {
261                write!(f, "{}:{}", module, function)
262            }
263            ExecutionTimeObservationKey::TransferObjects => write!(f, "TransferObjects"),
264            ExecutionTimeObservationKey::SplitCoins => write!(f, "SplitCoins"),
265            ExecutionTimeObservationKey::MergeCoins => write!(f, "MergeCoins"),
266            ExecutionTimeObservationKey::Publish => write!(f, "Publish"),
267            ExecutionTimeObservationKey::MakeMoveVec => write!(f, "MakeMoveVec"),
268            ExecutionTimeObservationKey::Upgrade => write!(f, "Upgrade"),
269        }
270    }
271}
272
273impl ExecutionTimeObservationKey {
274    pub fn is_move_call(&self) -> bool {
275        matches!(self, ExecutionTimeObservationKey::MoveEntryPoint { .. })
276    }
277
278    pub fn from_command(command: &Command) -> Self {
279        match command {
280            Command::MoveCall(call) => ExecutionTimeObservationKey::MoveEntryPoint {
281                package: call.package,
282                module: call.module.clone(),
283                function: call.function.clone(),
284                type_arguments: vec![],
285            },
286            Command::TransferObjects(_, _) => ExecutionTimeObservationKey::TransferObjects,
287            Command::SplitCoins(_, _) => ExecutionTimeObservationKey::SplitCoins,
288            Command::MergeCoins(_, _) => ExecutionTimeObservationKey::MergeCoins,
289            Command::Publish(_, _) => ExecutionTimeObservationKey::Publish,
290            Command::MakeMoveVec(_, _) => ExecutionTimeObservationKey::MakeMoveVec,
291            Command::Upgrade(_, _, _, _) => ExecutionTimeObservationKey::Upgrade,
292        }
293    }
294
295    pub fn default_duration(&self) -> Duration {
296        match self {
297            ExecutionTimeObservationKey::MoveEntryPoint { .. } => Duration::from_millis(1),
298            ExecutionTimeObservationKey::TransferObjects => Duration::from_millis(1),
299            ExecutionTimeObservationKey::SplitCoins => Duration::from_millis(1),
300            ExecutionTimeObservationKey::MergeCoins => Duration::from_millis(1),
301            ExecutionTimeObservationKey::Publish => Duration::from_millis(3),
302            ExecutionTimeObservationKey::MakeMoveVec => Duration::from_millis(1),
303            ExecutionTimeObservationKey::Upgrade => Duration::from_millis(3),
304        }
305    }
306}
307
308#[derive(Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Clone, Serialize, Deserialize)]
309pub struct ExecutionTimeObservationChunkKey {
310    pub chunk_index: u64,
311}
312
313impl MoveTypeTagTrait for ExecutionTimeObservationChunkKey {
314    fn get_type_tag() -> TypeTag {
315        TypeTag::Struct(Box::new(StructTag {
316            address: SUI_SYSTEM_ADDRESS,
317            module: SUI_SYSTEM_STATE_INNER_MODULE_NAME.to_owned(),
318            name: EXECUTION_TIME_OBSERVATION_CHUNK_KEY_STRUCT.to_owned(),
319            type_params: vec![],
320        }))
321    }
322}
323
324#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
325pub enum ExecutionTiming {
326    Success(Duration),
327    Abort(Duration),
328}
329
330impl ExecutionTiming {
331    pub fn is_abort(&self) -> bool {
332        matches!(self, ExecutionTiming::Abort(_))
333    }
334
335    pub fn duration(&self) -> Duration {
336        match self {
337            ExecutionTiming::Success(duration) => *duration,
338            ExecutionTiming::Abort(duration) => *duration,
339        }
340    }
341}
342
343pub type ResultWithTimings<R, E> = Result<(R, Vec<ExecutionTiming>), (E, Vec<ExecutionTiming>)>;
344
345/// Captures the output of executing a transaction in the execution driver.
346#[derive(Debug)]
347pub enum ExecutionOutput<T> {
348    /// The expected typical path - transaction executed successfully.
349    Success(T),
350    /// Validator has halted at epoch end or epoch mismatch. This is a valid state that should
351    /// be handled gracefully.
352    EpochEnded,
353    /// Execution failed with an error. This should never happen - we use fatal! when encountered.
354    Fatal(SuiError),
355    /// Execution should be retried later due to unsatisfied constraints such as insufficient object
356    /// balance withdrawals that require waiting for the balance to reach a deterministic amount.
357    /// When this happens, the transaction is auto-rescheduled from AuthorityState.
358    RetryLater,
359}
360
361impl<T> ExecutionOutput<T> {
362    /// Unwraps the ExecutionOutput, panicking if it's not Success.
363    /// This is primarily for test code.
364    pub fn unwrap(self) -> T {
365        match self {
366            ExecutionOutput::Success(value) => value,
367            ExecutionOutput::EpochEnded => {
368                panic!("called `ExecutionOutput::unwrap()` on `EpochEnded`")
369            }
370            ExecutionOutput::Fatal(e) => {
371                panic!("called `ExecutionOutput::unwrap()` on `Fatal`: {e}")
372            }
373            ExecutionOutput::RetryLater => {
374                panic!("called `ExecutionOutput::unwrap()` on `RetryLater`")
375            }
376        }
377    }
378
379    /// Expect the execution output to be an error (i.e. not Success).
380    pub fn unwrap_err<S>(self) -> ExecutionOutput<S> {
381        match self {
382            Self::Success(_) => {
383                panic!("called `ExecutionOutput::unwrap_err()` on `Success`")
384            }
385            Self::EpochEnded => ExecutionOutput::EpochEnded,
386            Self::Fatal(e) => ExecutionOutput::Fatal(e),
387            Self::RetryLater => ExecutionOutput::RetryLater,
388        }
389    }
390}