sui_types/effects/
effects_v1.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use crate::accumulator_event::AccumulatorEvent;
5use crate::base_types::{
6    EpochId, ObjectID, ObjectRef, SequenceNumber, SuiAddress, TransactionDigest, random_object_ref,
7};
8use crate::digests::{ObjectDigest, TransactionEventsDigest};
9use crate::effects::{InputConsensusObject, TransactionEffectsAPI, UnchangedConsensusKind};
10use crate::execution_status::{ExecutionFailureStatus, ExecutionStatus, MoveLocation};
11use crate::gas::GasCostSummary;
12use crate::object::Owner;
13use serde::{Deserialize, Serialize};
14use std::collections::{BTreeMap, HashSet};
15use std::fmt::{Display, Formatter, Write};
16
17use super::object_change::AccumulatorWriteV1;
18use super::{IDOperation, ObjectChange};
19
20/// The response from processing a transaction or a certified transaction
21#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)]
22pub struct TransactionEffectsV1 {
23    /// The status of the execution
24    status: ExecutionStatus,
25    /// The epoch when this transaction was executed.
26    executed_epoch: EpochId,
27    gas_used: GasCostSummary,
28    /// The version that every modified (mutated or deleted) object had before it was modified by
29    /// this transaction.
30    modified_at_versions: Vec<(ObjectID, SequenceNumber)>,
31    /// The object references of the shared objects used in this transaction. Empty if no shared objects were used.
32    shared_objects: Vec<ObjectRef>,
33    /// The transaction digest
34    transaction_digest: TransactionDigest,
35
36    // TODO: All the SequenceNumbers in the ObjectRefs below equal the same value (the lamport
37    // timestamp of the transaction).  Consider factoring this out into one place in the effects.
38    /// ObjectRef and owner of new objects created.
39    created: Vec<(ObjectRef, Owner)>,
40    /// ObjectRef and owner of mutated objects, including gas object.
41    mutated: Vec<(ObjectRef, Owner)>,
42    /// ObjectRef and owner of objects that are unwrapped in this transaction.
43    /// Unwrapped objects are objects that were wrapped into other objects in the past,
44    /// and just got extracted out.
45    unwrapped: Vec<(ObjectRef, Owner)>,
46    /// Object Refs of objects now deleted (the new refs).
47    deleted: Vec<ObjectRef>,
48    /// Object refs of objects previously wrapped in other objects but now deleted.
49    unwrapped_then_deleted: Vec<ObjectRef>,
50    /// Object refs of objects now wrapped in other objects.
51    wrapped: Vec<ObjectRef>,
52    /// The updated gas object reference. Have a dedicated field for convenient access.
53    /// It's also included in mutated.
54    gas_object: (ObjectRef, Owner),
55    /// The digest of the events emitted during execution,
56    /// can be None if the transaction does not emit any event.
57    events_digest: Option<TransactionEventsDigest>,
58    /// The set of transaction digests this transaction depends on.
59    dependencies: Vec<TransactionDigest>,
60}
61
62impl TransactionEffectsV1 {
63    pub fn new(
64        status: ExecutionStatus,
65        executed_epoch: EpochId,
66        gas_used: GasCostSummary,
67        modified_at_versions: Vec<(ObjectID, SequenceNumber)>,
68        shared_objects: Vec<ObjectRef>,
69        transaction_digest: TransactionDigest,
70        created: Vec<(ObjectRef, Owner)>,
71        mutated: Vec<(ObjectRef, Owner)>,
72        unwrapped: Vec<(ObjectRef, Owner)>,
73        deleted: Vec<ObjectRef>,
74        unwrapped_then_deleted: Vec<ObjectRef>,
75        wrapped: Vec<ObjectRef>,
76        gas_object: (ObjectRef, Owner),
77        events_digest: Option<TransactionEventsDigest>,
78        dependencies: Vec<TransactionDigest>,
79    ) -> Self {
80        Self {
81            status,
82            executed_epoch,
83            gas_used,
84            modified_at_versions,
85            shared_objects,
86            transaction_digest,
87            created,
88            mutated,
89            unwrapped,
90            deleted,
91            unwrapped_then_deleted,
92            wrapped,
93            gas_object,
94            events_digest,
95            dependencies,
96        }
97    }
98
99    pub fn modified_at_versions(&self) -> &[(ObjectID, SequenceNumber)] {
100        &self.modified_at_versions
101    }
102
103    pub fn mutated(&self) -> &[(ObjectRef, Owner)] {
104        &self.mutated
105    }
106
107    pub fn created(&self) -> &[(ObjectRef, Owner)] {
108        &self.created
109    }
110
111    pub fn unwrapped(&self) -> &[(ObjectRef, Owner)] {
112        &self.unwrapped
113    }
114
115    pub fn deleted(&self) -> &[ObjectRef] {
116        &self.deleted
117    }
118
119    pub fn wrapped(&self) -> &[ObjectRef] {
120        &self.wrapped
121    }
122
123    pub fn shared_objects(&self) -> &[ObjectRef] {
124        &self.shared_objects
125    }
126}
127
128impl TransactionEffectsAPI for TransactionEffectsV1 {
129    fn status(&self) -> &ExecutionStatus {
130        &self.status
131    }
132
133    fn into_status(self) -> ExecutionStatus {
134        self.status
135    }
136
137    fn executed_epoch(&self) -> EpochId {
138        self.executed_epoch
139    }
140
141    fn modified_at_versions(&self) -> Vec<(ObjectID, SequenceNumber)> {
142        self.modified_at_versions
143            .iter()
144            // V1 transaction effects "modified_at_versions" includes unwrapped_then_deleted
145            // objects, so in order to have parity with the V2 transaction effects semantics of
146            // "modified_at_versions", filter out any objects that are unwrapped_then_deleted'ed
147            .filter(|(object_id, _)| {
148                !self
149                    .unwrapped_then_deleted
150                    .iter()
151                    .any(|(deleted_id, _, _)| deleted_id == object_id)
152            })
153            .cloned()
154            .collect()
155    }
156
157    fn move_abort(&self) -> Option<(MoveLocation, u64)> {
158        let ExecutionStatus::Failure {
159            error: ExecutionFailureStatus::MoveAbort(move_location, code),
160            ..
161        } = self.status()
162        else {
163            return None;
164        };
165        Some((move_location.clone(), *code))
166    }
167
168    fn lamport_version(&self) -> SequenceNumber {
169        SequenceNumber::lamport_increment(self.modified_at_versions.iter().map(|(_, v)| *v))
170    }
171
172    fn old_object_metadata(&self) -> Vec<(ObjectRef, Owner)> {
173        unimplemented!("Only supposed by v2 and above");
174    }
175
176    fn input_consensus_objects(&self) -> Vec<InputConsensusObject> {
177        let modified: HashSet<_> = self.modified_at_versions.iter().map(|(r, _)| r).collect();
178        self.shared_objects
179            .iter()
180            .map(|r| {
181                if modified.contains(&r.0) {
182                    InputConsensusObject::Mutate(*r)
183                } else {
184                    InputConsensusObject::ReadOnly(*r)
185                }
186            })
187            .collect()
188    }
189
190    fn created(&self) -> Vec<(ObjectRef, Owner)> {
191        self.created.clone()
192    }
193
194    fn mutated(&self) -> Vec<(ObjectRef, Owner)> {
195        self.mutated.clone()
196    }
197
198    fn unwrapped(&self) -> Vec<(ObjectRef, Owner)> {
199        self.unwrapped.clone()
200    }
201
202    fn deleted(&self) -> Vec<ObjectRef> {
203        self.deleted.clone()
204    }
205
206    fn unwrapped_then_deleted(&self) -> Vec<ObjectRef> {
207        self.unwrapped_then_deleted.clone()
208    }
209
210    fn wrapped(&self) -> Vec<ObjectRef> {
211        self.wrapped.clone()
212    }
213
214    fn transferred_from_consensus(&self) -> Vec<ObjectRef> {
215        // Transferrable consensus objects cannot exist with effects v1
216        vec![]
217    }
218
219    fn transferred_to_consensus(&self) -> Vec<ObjectRef> {
220        // Transferrable consensus objects cannot exist with effects v1
221        vec![]
222    }
223
224    fn consensus_owner_changed(&self) -> Vec<ObjectRef> {
225        // Transferrable consensus objects cannot exist with effects v1
226        vec![]
227    }
228
229    fn object_changes(&self) -> Vec<ObjectChange> {
230        let modified_at: BTreeMap<_, _> = self.modified_at_versions.iter().copied().collect();
231
232        let created = self.created.iter().map(|((id, v, d), _)| ObjectChange {
233            id: *id,
234            input_version: None,
235            input_digest: None,
236            output_version: Some(*v),
237            output_digest: Some(*d),
238            id_operation: IDOperation::Created,
239        });
240
241        let mutated = self.mutated.iter().map(|((id, v, d), _)| ObjectChange {
242            id: *id,
243            input_version: modified_at.get(id).copied(),
244            input_digest: None,
245            output_version: Some(*v),
246            output_digest: Some(*d),
247            id_operation: IDOperation::None,
248        });
249
250        let unwrapped = self.unwrapped.iter().map(|((id, v, d), _)| ObjectChange {
251            id: *id,
252            input_version: None,
253            input_digest: None,
254            output_version: Some(*v),
255            output_digest: Some(*d),
256            id_operation: IDOperation::None,
257        });
258
259        let deleted = self.deleted.iter().map(|(id, _, _)| ObjectChange {
260            id: *id,
261            input_version: modified_at.get(id).copied(),
262            input_digest: None,
263            output_version: None,
264            output_digest: None,
265            id_operation: IDOperation::Deleted,
266        });
267
268        let unwrapped_then_deleted =
269            self.unwrapped_then_deleted
270                .iter()
271                .map(|(id, _, _)| ObjectChange {
272                    id: *id,
273                    input_version: None,
274                    input_digest: None,
275                    output_version: None,
276                    output_digest: None,
277                    id_operation: IDOperation::Deleted,
278                });
279
280        let wrapped = self.wrapped.iter().map(|(id, _, _)| ObjectChange {
281            id: *id,
282            input_version: modified_at.get(id).copied(),
283            input_digest: None,
284            output_version: None,
285            output_digest: None,
286            id_operation: IDOperation::None,
287        });
288
289        created
290            .chain(mutated)
291            .chain(unwrapped)
292            .chain(deleted)
293            .chain(unwrapped_then_deleted)
294            .chain(wrapped)
295            .collect()
296    }
297
298    fn written(&self) -> Vec<ObjectRef> {
299        unimplemented!("TransactionEffectsV1::written() never called in V1");
300    }
301
302    fn accumulator_events(&self) -> Vec<AccumulatorEvent> {
303        // v1 did not have accumulator events
304        vec![]
305    }
306
307    fn gas_object(&self) -> (ObjectRef, Owner) {
308        self.gas_object.clone()
309    }
310    fn events_digest(&self) -> Option<&TransactionEventsDigest> {
311        self.events_digest.as_ref()
312    }
313
314    fn dependencies(&self) -> &[TransactionDigest] {
315        &self.dependencies
316    }
317
318    fn transaction_digest(&self) -> &TransactionDigest {
319        &self.transaction_digest
320    }
321
322    fn gas_cost_summary(&self) -> &GasCostSummary {
323        &self.gas_used
324    }
325
326    fn unchanged_consensus_objects(&self) -> Vec<(ObjectID, UnchangedConsensusKind)> {
327        self.input_consensus_objects()
328            .iter()
329            .filter_map(|o| match o {
330                // In effects v1, the only unchanged consensus objects are read-only shared objects.
331                InputConsensusObject::ReadOnly(oref) => Some((
332                    oref.0,
333                    UnchangedConsensusKind::ReadOnlyRoot((oref.1, oref.2)),
334                )),
335                _ => None,
336            })
337            .collect()
338    }
339
340    fn accumulator_updates(&self) -> Vec<(ObjectID, AccumulatorWriteV1)> {
341        vec![]
342    }
343
344    fn status_mut_for_testing(&mut self) -> &mut ExecutionStatus {
345        &mut self.status
346    }
347
348    fn gas_cost_summary_mut_for_testing(&mut self) -> &mut GasCostSummary {
349        &mut self.gas_used
350    }
351
352    fn transaction_digest_mut_for_testing(&mut self) -> &mut TransactionDigest {
353        &mut self.transaction_digest
354    }
355
356    fn dependencies_mut_for_testing(&mut self) -> &mut Vec<TransactionDigest> {
357        &mut self.dependencies
358    }
359
360    fn unsafe_add_input_consensus_object_for_testing(&mut self, kind: InputConsensusObject) {
361        match kind {
362            InputConsensusObject::Mutate(obj_ref) => {
363                self.shared_objects.push(obj_ref);
364                self.modified_at_versions.push((obj_ref.0, obj_ref.1));
365            }
366            InputConsensusObject::ReadOnly(obj_ref) => {
367                self.shared_objects.push(obj_ref);
368            }
369            InputConsensusObject::ReadConsensusStreamEnded(id, version)
370            | InputConsensusObject::MutateConsensusStreamEnded(id, version) => {
371                self.shared_objects
372                    .push((id, version, ObjectDigest::OBJECT_DIGEST_DELETED));
373            }
374            InputConsensusObject::Cancelled(..) => {
375                panic!("Transaction cancellation is not supported in effect v1");
376            }
377        }
378    }
379
380    fn unsafe_add_deleted_live_object_for_testing(&mut self, object: ObjectRef) {
381        self.modified_at_versions.push((object.0, object.1));
382    }
383
384    fn unsafe_add_object_tombstone_for_testing(&mut self, object: ObjectRef) {
385        self.deleted.push(object);
386    }
387}
388
389impl Display for TransactionEffectsV1 {
390    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
391        let mut writer = String::new();
392        writeln!(writer, "Status : {:?}", self.status)?;
393        if !self.created.is_empty() {
394            writeln!(writer, "Created Objects:")?;
395            for ((id, _, _), owner) in &self.created {
396                writeln!(writer, "  - ID: {} , Owner: {}", id, owner)?;
397            }
398        }
399        if !self.mutated.is_empty() {
400            writeln!(writer, "Mutated Objects:")?;
401            for ((id, _, _), owner) in &self.mutated {
402                writeln!(writer, "  - ID: {} , Owner: {}", id, owner)?;
403            }
404        }
405        if !self.deleted.is_empty() {
406            writeln!(writer, "Deleted Objects:")?;
407            for (id, _, _) in &self.deleted {
408                writeln!(writer, "  - ID: {}", id)?;
409            }
410        }
411        if !self.wrapped.is_empty() {
412            writeln!(writer, "Wrapped Objects:")?;
413            for (id, _, _) in &self.wrapped {
414                writeln!(writer, "  - ID: {}", id)?;
415            }
416        }
417        if !self.unwrapped.is_empty() {
418            writeln!(writer, "Unwrapped Objects:")?;
419            for ((id, _, _), owner) in &self.unwrapped {
420                writeln!(writer, "  - ID: {} , Owner: {}", id, owner)?;
421            }
422        }
423        write!(f, "{}", writer)
424    }
425}
426
427impl Default for TransactionEffectsV1 {
428    fn default() -> Self {
429        TransactionEffectsV1 {
430            status: ExecutionStatus::Success,
431            executed_epoch: 0,
432            gas_used: GasCostSummary {
433                computation_cost: 0,
434                storage_cost: 0,
435                storage_rebate: 0,
436                non_refundable_storage_fee: 0,
437            },
438            modified_at_versions: Vec::new(),
439            shared_objects: Vec::new(),
440            transaction_digest: TransactionDigest::random(),
441            created: Vec::new(),
442            mutated: Vec::new(),
443            unwrapped: Vec::new(),
444            deleted: Vec::new(),
445            unwrapped_then_deleted: Vec::new(),
446            wrapped: Vec::new(),
447            gas_object: (
448                random_object_ref(),
449                Owner::AddressOwner(SuiAddress::default()),
450            ),
451            events_digest: None,
452            dependencies: Vec::new(),
453        }
454    }
455}