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