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;
6
7use serde::{Deserialize, Serialize};
8use tap::Pipe;
9
10use crate::{
11    base_types::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    pub fn input_objects(&self) -> Vec<&Object> {
61        self.transactions
62            .iter()
63            .flat_map(|tx| &tx.input_objects)
64            .collect()
65    }
66
67    pub fn all_objects(&self) -> Vec<&Object> {
68        self.transactions
69            .iter()
70            .flat_map(|tx| &tx.input_objects)
71            .chain(self.transactions.iter().flat_map(|tx| &tx.output_objects))
72            .collect()
73    }
74}
75
76#[derive(Clone, Debug, Serialize, Deserialize)]
77pub struct CheckpointTransaction {
78    /// The input Transaction
79    pub transaction: Transaction,
80    /// The effects produced by executing this transaction
81    pub effects: TransactionEffects,
82    /// The events, if any, emitted by this transaction during execution
83    pub events: Option<TransactionEvents>,
84    /// The state of all inputs to this transaction as they were prior to
85    /// execution.
86    pub input_objects: Vec<Object>,
87    /// The state of all output objects created or mutated or unwrapped by this
88    /// transaction.
89    pub output_objects: Vec<Object>,
90}
91
92impl CheckpointTransaction {
93    // provide an iterator over all deleted or wrapped objects in this transaction
94    pub fn removed_objects_pre_version(&self) -> impl Iterator<Item = &Object> {
95        // Iterator over id and versions for all deleted or wrapped objects
96        match &self.effects {
97            TransactionEffects::V1(v1) => {
98                v1.changed_objects().iter().filter_map(|(id, change)| {
99                    match (
100                        &change.input_state,
101                        &change.output_state,
102                        &change.id_operation,
103                    ) {
104                        // Deleted Objects
105                        (
106                            ObjectIn::Exist(((version, _d), _o)),
107                            ObjectOut::NotExist,
108                            IDOperation::Deleted,
109                        ) => Some((id, version)),
110
111                        // Wrapped Objects
112                        (
113                            ObjectIn::Exist(((version, _), _)),
114                            ObjectOut::NotExist,
115                            IDOperation::None,
116                        ) => Some((id, version)),
117                        _ => None,
118                    }
119                })
120            }
121        }
122        // Use id and version to lookup in input Objects
123        .map(|(id, version)| {
124            self.input_objects
125                .iter()
126                .find(|o| &o.id() == id && &o.version() == version)
127                .expect("all removed objects should show up in input objects")
128        })
129    }
130
131    pub fn removed_object_refs_post_version(&self) -> impl Iterator<Item = ObjectRef> {
132        let deleted = self.effects.deleted().into_iter();
133        let wrapped = self.effects.wrapped().into_iter();
134        let unwrapped_then_deleted = self.effects.unwrapped_then_deleted().into_iter();
135        deleted.chain(wrapped).chain(unwrapped_then_deleted)
136    }
137
138    pub fn changed_objects(&self) -> impl Iterator<Item = (&Object, Option<&Object>)> {
139        // Iterator over ((ObjectId, new version), Option<old version>)
140        match &self.effects {
141            TransactionEffects::V1(v1) => {
142                v1.changed_objects().iter().filter_map(|(id, change)| {
143                    match (
144                        &change.input_state,
145                        &change.output_state,
146                        &change.id_operation,
147                    ) {
148                        // Created Objects
149                        (ObjectIn::NotExist, ObjectOut::ObjectWrite(_), IDOperation::Created) => {
150                            Some(((id, &v1.lamport_version), None))
151                        }
152                        (
153                            ObjectIn::NotExist,
154                            ObjectOut::PackageWrite((version, _)),
155                            IDOperation::Created,
156                        ) => Some(((id, version), None)),
157
158                        // Unwrapped Objects
159                        (ObjectIn::NotExist, ObjectOut::ObjectWrite(_), IDOperation::None) => {
160                            Some(((id, &v1.lamport_version), None))
161                        }
162
163                        // Mutated Objects
164                        (ObjectIn::Exist(((old_version, _), _)), ObjectOut::ObjectWrite(_), _) => {
165                            Some(((id, &v1.lamport_version), Some(old_version)))
166                        }
167                        (
168                            ObjectIn::Exist(((old_version, _), _)),
169                            ObjectOut::PackageWrite((version, _)),
170                            _,
171                        ) => Some(((id, version), Some(old_version))),
172
173                        _ => None,
174                    }
175                })
176            }
177        }
178        // Lookup Objects in output Objects as well as old versions for mutated objects
179        .map(|((id, version), old_version)| {
180            let object = self
181                .output_objects
182                .iter()
183                .find(|o| &o.id() == id && &o.version() == version)
184                .expect("changed objects should show up in output objects");
185
186            let old_object = old_version.map(|old_version| {
187                self.input_objects
188                    .iter()
189                    .find(|o| &o.id() == id && &o.version() == old_version)
190                    .expect("mutated objects should have a previous version in input objects")
191            });
192
193            (object, old_object)
194        })
195    }
196
197    pub fn created_objects(&self) -> impl Iterator<Item = &Object> {
198        // Iterator over (ObjectId, version) for created objects
199        match &self.effects {
200            TransactionEffects::V1(v1) => {
201                v1.changed_objects().iter().filter_map(|(id, change)| {
202                    match (
203                        &change.input_state,
204                        &change.output_state,
205                        &change.id_operation,
206                    ) {
207                        // Created Objects
208                        (ObjectIn::NotExist, ObjectOut::ObjectWrite(_), IDOperation::Created) => {
209                            Some((id, &v1.lamport_version))
210                        }
211                        (
212                            ObjectIn::NotExist,
213                            ObjectOut::PackageWrite((version, _)),
214                            IDOperation::Created,
215                        ) => Some((id, version)),
216
217                        _ => None,
218                    }
219                })
220            }
221        }
222        // Lookup Objects in output Objects as well as old versions for mutated objects
223        .map(|(id, version)| {
224            self.output_objects
225                .iter()
226                .find(|o| &o.id() == id && &o.version() == version)
227                .expect("created objects should show up in output objects")
228        })
229    }
230}
231
232impl BackingPackageStore for CheckpointData {
233    fn get_package_object(
234        &self,
235        package_id: &crate::base_types::ObjectID,
236    ) -> crate::error::IotaResult<Option<crate::storage::PackageObject>> {
237        self.transactions
238            .iter()
239            .flat_map(|transaction| transaction.output_objects.iter())
240            .find(|object| object.is_package() && &object.id() == package_id)
241            .cloned()
242            .map(crate::storage::PackageObject::new)
243            .pipe(Ok)
244    }
245}