iota_core/
transaction_outputs.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use std::{
6    collections::{HashMap, HashSet},
7    sync::Arc,
8};
9
10use iota_types::{
11    base_types::ObjectRef,
12    effects::{TransactionEffects, TransactionEffectsAPI, TransactionEvents},
13    inner_temporary_store::{InnerTemporaryStore, WrittenObjects},
14    storage::{MarkerValue, ObjectKey},
15    transaction::{TransactionDataAPI, VerifiedTransaction},
16};
17
18/// TransactionOutputs
19pub struct TransactionOutputs {
20    pub transaction: Arc<VerifiedTransaction>,
21    pub effects: TransactionEffects,
22    pub events: TransactionEvents,
23
24    pub markers: Vec<(ObjectKey, MarkerValue)>,
25    pub wrapped: Vec<ObjectKey>,
26    pub deleted: Vec<ObjectKey>,
27    pub live_object_markers_to_delete: Vec<ObjectRef>,
28    pub new_live_object_markers_to_init: Vec<ObjectRef>,
29    pub written: WrittenObjects,
30}
31
32impl TransactionOutputs {
33    // Convert InnerTemporaryStore + Effects into the exact set of updates to the
34    // store
35    pub fn build_transaction_outputs(
36        transaction: VerifiedTransaction,
37        effects: TransactionEffects,
38        inner_temporary_store: InnerTemporaryStore,
39    ) -> TransactionOutputs {
40        let InnerTemporaryStore {
41            input_objects,
42            mutable_inputs,
43            written,
44            events,
45            loaded_runtime_objects: _,
46            binary_config: _,
47            runtime_packages_loaded_from_db: _,
48            lamport_version,
49        } = inner_temporary_store;
50
51        let tx_digest = *transaction.digest();
52
53        let deleted: HashMap<_, _> = effects.all_tombstones().into_iter().collect();
54
55        // Get the actual set of objects that have been received -- any received
56        // object will show up in the modified-at set.
57        let modified_at: HashSet<_> = effects.modified_at_versions().into_iter().collect();
58        let possible_to_receive = transaction.transaction_data().receiving_objects();
59        let received_objects = possible_to_receive
60            .iter()
61            .cloned()
62            .filter(|obj_ref| modified_at.contains(&(obj_ref.0, obj_ref.1)));
63
64        // We record any received or deleted objects since they could be pruned, and
65        // smear shared object deletions in the marker table. For deleted
66        // entries in the marker table we need to make sure we don't
67        // accidentally overwrite entries.
68        let markers: Vec<_> = {
69            let received = received_objects
70                .clone()
71                .map(|objref| (ObjectKey::from(objref), MarkerValue::Received));
72
73            let deleted = deleted.into_iter().map(|(object_id, version)| {
74                let object_key = ObjectKey(object_id, version);
75                if input_objects
76                    .get(&object_id)
77                    .is_some_and(|object| object.is_shared())
78                {
79                    (object_key, MarkerValue::SharedDeleted(tx_digest))
80                } else {
81                    (object_key, MarkerValue::OwnedDeleted)
82                }
83            });
84
85            // We "smear" shared deleted objects in the marker table to allow for proper
86            // sequencing of transactions that are submitted after the deletion
87            // of the shared object. NB: that we do _not_ smear shared objects
88            // that were taken immutably in the transaction.
89            let smeared_objects = effects.deleted_mutably_accessed_shared_objects();
90            let shared_smears = smeared_objects.into_iter().map(move |object_id| {
91                (
92                    ObjectKey(object_id, lamport_version),
93                    MarkerValue::SharedDeleted(tx_digest),
94                )
95            });
96
97            received.chain(deleted).chain(shared_smears).collect()
98        };
99
100        let live_object_markers_to_delete: Vec<_> = mutable_inputs
101            .into_iter()
102            .filter_map(|(id, ((version, digest), owner))| {
103                owner.is_address_owned().then_some((id, version, digest))
104            })
105            .chain(received_objects)
106            .collect();
107
108        let new_live_object_markers_to_init: Vec<_> = written
109            .values()
110            .filter_map(|new_object| {
111                if new_object.is_address_owned() {
112                    Some(new_object.compute_object_reference())
113                } else {
114                    None
115                }
116            })
117            .collect();
118
119        let deleted = effects
120            .deleted()
121            .into_iter()
122            .chain(effects.unwrapped_then_deleted())
123            .map(ObjectKey::from)
124            .collect();
125
126        let wrapped = effects.wrapped().into_iter().map(ObjectKey::from).collect();
127
128        TransactionOutputs {
129            transaction: Arc::new(transaction),
130            effects,
131            events,
132            markers,
133            wrapped,
134            deleted,
135            live_object_markers_to_delete,
136            new_live_object_markers_to_init,
137            written,
138        }
139    }
140}