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            .into_iter()
61            .filter(|obj_ref| modified_at.contains(&(obj_ref.0, obj_ref.1)));
62
63        // We record any received or deleted objects since they could be pruned, and
64        // smear shared object deletions in the marker table. For deleted
65        // entries in the marker table we need to make sure we don't
66        // accidentally overwrite entries.
67        let markers: Vec<_> = {
68            let received = received_objects
69                .clone()
70                .map(|objref| (ObjectKey::from(objref), MarkerValue::Received));
71
72            let deleted = deleted.into_iter().map(|(object_id, version)| {
73                let object_key = ObjectKey(object_id, version);
74                if input_objects
75                    .get(&object_id)
76                    .is_some_and(|object| object.is_shared())
77                {
78                    (object_key, MarkerValue::SharedDeleted(tx_digest))
79                } else {
80                    (object_key, MarkerValue::OwnedDeleted)
81                }
82            });
83
84            // We "smear" shared deleted objects in the marker table to allow for proper
85            // sequencing of transactions that are submitted after the deletion
86            // of the shared object. NB: that we do _not_ smear shared objects
87            // that were taken immutably in the transaction.
88            let smeared_objects = effects.deleted_mutably_accessed_shared_objects();
89            let shared_smears = smeared_objects.into_iter().map(move |object_id| {
90                (
91                    ObjectKey(object_id, lamport_version),
92                    MarkerValue::SharedDeleted(tx_digest),
93                )
94            });
95
96            received.chain(deleted).chain(shared_smears).collect()
97        };
98
99        let live_object_markers_to_delete: Vec<_> = mutable_inputs
100            .into_iter()
101            .filter_map(|(id, ((version, digest), owner))| {
102                owner.is_address_owned().then_some((id, version, digest))
103            })
104            .chain(received_objects)
105            .collect();
106
107        let new_live_object_markers_to_init: Vec<_> = written
108            .values()
109            .filter_map(|new_object| {
110                if new_object.is_address_owned() {
111                    Some(new_object.compute_object_reference())
112                } else {
113                    None
114                }
115            })
116            .collect();
117
118        let deleted = effects
119            .deleted()
120            .into_iter()
121            .chain(effects.unwrapped_then_deleted())
122            .map(ObjectKey::from)
123            .collect();
124
125        let wrapped = effects.wrapped().into_iter().map(ObjectKey::from).collect();
126
127        TransactionOutputs {
128            transaction: Arc::new(transaction),
129            effects,
130            events,
131            markers,
132            wrapped,
133            deleted,
134            live_object_markers_to_delete,
135            new_live_object_markers_to_init,
136            written,
137        }
138    }
139}