iota_types/
full_checkpoint_content.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use std::collections::{BTreeMap, HashSet};
6
7use serde::{Deserialize, Serialize};
8use tap::Pipe;
9
10use crate::{
11    base_types::{ObjectID, ObjectRef},
12    effects::{
13        IDOperation, ObjectIn, ObjectOut, TransactionEffects, TransactionEffectsAPI,
14        TransactionEvents,
15    },
16    messages_checkpoint::{CertifiedCheckpointSummary, CheckpointContents},
17    object::Object,
18    storage::BackingPackageStore,
19    transaction::Transaction,
20};
21
22#[derive(Clone, Debug, Serialize, Deserialize)]
23pub struct CheckpointData {
24    pub checkpoint_summary: CertifiedCheckpointSummary,
25    pub checkpoint_contents: CheckpointContents,
26    pub transactions: Vec<CheckpointTransaction>,
27}
28
29impl CheckpointData {
30    // returns the latest versions of the output objects that still exist at the end
31    // of the checkpoint
32    pub fn latest_live_output_objects(&self) -> Vec<&Object> {
33        let mut latest_live_objects = BTreeMap::new();
34        for tx in self.transactions.iter() {
35            for obj in tx.output_objects.iter() {
36                latest_live_objects.insert(obj.id(), obj);
37            }
38            for obj_ref in tx.removed_object_refs_post_version() {
39                latest_live_objects.remove(&(obj_ref.0));
40            }
41        }
42        latest_live_objects.into_values().collect()
43    }
44
45    // returns the object refs that are eventually deleted or wrapped in the current
46    // checkpoint
47    pub fn eventually_removed_object_refs_post_version(&self) -> Vec<ObjectRef> {
48        let mut eventually_removed_object_refs = BTreeMap::new();
49        for tx in self.transactions.iter() {
50            for obj_ref in tx.removed_object_refs_post_version() {
51                eventually_removed_object_refs.insert(obj_ref.0, obj_ref);
52            }
53            for obj in tx.output_objects.iter() {
54                eventually_removed_object_refs.remove(&(obj.id()));
55            }
56        }
57        eventually_removed_object_refs.into_values().collect()
58    }
59
60    /// Returns all objects that are used as input to the transactions in the
61    /// checkpoint, and already exist prior to the checkpoint.
62    pub fn checkpoint_input_objects(&self) -> BTreeMap<ObjectID, &Object> {
63        let mut output_objects_seen = HashSet::new();
64        let mut checkpoint_input_objects = BTreeMap::new();
65        for tx in self.transactions.iter() {
66            for obj in tx.input_objects.iter() {
67                let id = obj.id();
68                if output_objects_seen.contains(&id) || checkpoint_input_objects.contains_key(&id) {
69                    continue;
70                }
71                checkpoint_input_objects.insert(id, obj);
72            }
73            for obj in tx.output_objects.iter() {
74                // We want to track input objects that are not output objects
75                // in the previous transactions.
76                output_objects_seen.insert(obj.id());
77            }
78        }
79        checkpoint_input_objects
80    }
81
82    pub fn all_objects(&self) -> Vec<&Object> {
83        self.transactions
84            .iter()
85            .flat_map(|tx| &tx.input_objects)
86            .chain(self.transactions.iter().flat_map(|tx| &tx.output_objects))
87            .collect()
88    }
89}
90
91#[derive(Clone, Debug, Serialize, Deserialize)]
92pub struct CheckpointTransaction {
93    /// The input Transaction
94    pub transaction: Transaction,
95    /// The effects produced by executing this transaction
96    pub effects: TransactionEffects,
97    /// The events, if any, emitted by this transaction during execution
98    pub events: Option<TransactionEvents>,
99    /// The state of all inputs to this transaction as they were prior to
100    /// execution.
101    pub input_objects: Vec<Object>,
102    /// The state of all output objects created or mutated or unwrapped by this
103    /// transaction.
104    pub output_objects: Vec<Object>,
105}
106
107impl CheckpointTransaction {
108    // provide an iterator over all deleted or wrapped objects in this transaction
109    pub fn removed_objects_pre_version(&self) -> impl Iterator<Item = &Object> {
110        // Iterator over id and versions for all deleted or wrapped objects
111        match &self.effects {
112            TransactionEffects::V1(v1) => {
113                v1.changed_objects().iter().filter_map(|(id, change)| {
114                    match (
115                        &change.input_state,
116                        &change.output_state,
117                        &change.id_operation,
118                    ) {
119                        // Deleted Objects
120                        (
121                            ObjectIn::Exist(((version, _d), _o)),
122                            ObjectOut::NotExist,
123                            IDOperation::Deleted,
124                        ) => Some((id, version)),
125
126                        // Wrapped Objects
127                        (
128                            ObjectIn::Exist(((version, _), _)),
129                            ObjectOut::NotExist,
130                            IDOperation::None,
131                        ) => Some((id, version)),
132                        _ => None,
133                    }
134                })
135            }
136        }
137        // Use id and version to lookup in input Objects
138        .map(|(id, version)| {
139            self.input_objects
140                .iter()
141                .find(|o| &o.id() == id && &o.version() == version)
142                .expect("all removed objects should show up in input objects")
143        })
144    }
145
146    pub fn removed_object_refs_post_version(&self) -> impl Iterator<Item = ObjectRef> {
147        let deleted = self.effects.deleted().into_iter();
148        let wrapped = self.effects.wrapped().into_iter();
149        let unwrapped_then_deleted = self.effects.unwrapped_then_deleted().into_iter();
150        deleted.chain(wrapped).chain(unwrapped_then_deleted)
151    }
152
153    pub fn changed_objects(&self) -> impl Iterator<Item = (&Object, Option<&Object>)> {
154        // Iterator over ((ObjectId, new version), Option<old version>)
155        match &self.effects {
156            TransactionEffects::V1(v1) => {
157                v1.changed_objects().iter().filter_map(|(id, change)| {
158                    match (
159                        &change.input_state,
160                        &change.output_state,
161                        &change.id_operation,
162                    ) {
163                        // Created Objects
164                        (ObjectIn::NotExist, ObjectOut::ObjectWrite(_), IDOperation::Created) => {
165                            Some(((id, &v1.lamport_version), None))
166                        }
167                        (
168                            ObjectIn::NotExist,
169                            ObjectOut::PackageWrite((version, _)),
170                            IDOperation::Created,
171                        ) => Some(((id, version), None)),
172
173                        // Unwrapped Objects
174                        (ObjectIn::NotExist, ObjectOut::ObjectWrite(_), IDOperation::None) => {
175                            Some(((id, &v1.lamport_version), None))
176                        }
177
178                        // Mutated Objects
179                        (ObjectIn::Exist(((old_version, _), _)), ObjectOut::ObjectWrite(_), _) => {
180                            Some(((id, &v1.lamport_version), Some(old_version)))
181                        }
182                        (
183                            ObjectIn::Exist(((old_version, _), _)),
184                            ObjectOut::PackageWrite((version, _)),
185                            _,
186                        ) => Some(((id, version), Some(old_version))),
187
188                        _ => None,
189                    }
190                })
191            }
192        }
193        // Lookup Objects in output Objects as well as old versions for mutated objects
194        .map(|((id, version), old_version)| {
195            let object = self
196                .output_objects
197                .iter()
198                .find(|o| &o.id() == id && &o.version() == version)
199                .expect("changed objects should show up in output objects");
200
201            let old_object = old_version.map(|old_version| {
202                self.input_objects
203                    .iter()
204                    .find(|o| &o.id() == id && &o.version() == old_version)
205                    .expect("mutated objects should have a previous version in input objects")
206            });
207
208            (object, old_object)
209        })
210    }
211
212    pub fn created_objects(&self) -> impl Iterator<Item = &Object> {
213        // Iterator over (ObjectId, version) for created objects
214        match &self.effects {
215            TransactionEffects::V1(v1) => {
216                v1.changed_objects().iter().filter_map(|(id, change)| {
217                    match (
218                        &change.input_state,
219                        &change.output_state,
220                        &change.id_operation,
221                    ) {
222                        // Created Objects
223                        (ObjectIn::NotExist, ObjectOut::ObjectWrite(_), IDOperation::Created) => {
224                            Some((id, &v1.lamport_version))
225                        }
226                        (
227                            ObjectIn::NotExist,
228                            ObjectOut::PackageWrite((version, _)),
229                            IDOperation::Created,
230                        ) => Some((id, version)),
231
232                        _ => None,
233                    }
234                })
235            }
236        }
237        // Lookup Objects in output Objects as well as old versions for mutated objects
238        .map(|(id, version)| {
239            self.output_objects
240                .iter()
241                .find(|o| &o.id() == id && &o.version() == version)
242                .expect("created objects should show up in output objects")
243        })
244    }
245}
246
247impl BackingPackageStore for CheckpointData {
248    fn get_package_object(
249        &self,
250        package_id: &crate::base_types::ObjectID,
251    ) -> crate::error::IotaResult<Option<crate::storage::PackageObject>> {
252        self.transactions
253            .iter()
254            .flat_map(|transaction| transaction.output_objects.iter())
255            .find(|object| object.is_package() && &object.id() == package_id)
256            .cloned()
257            .map(crate::storage::PackageObject::new)
258            .pipe(Ok)
259    }
260}