sui_types/
full_checkpoint_content.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use std::collections::BTreeMap;
5
6use crate::base_types::{ExecutionData, ObjectRef};
7use crate::effects::{TransactionEffects, TransactionEffectsAPI, TransactionEvents};
8use crate::messages_checkpoint::{CertifiedCheckpointSummary, CheckpointContents};
9use crate::object::Object;
10use crate::signature::GenericSignature;
11use crate::storage::ObjectKey;
12use crate::storage::error::Error as StorageError;
13use crate::storage::{BackingPackageStore, EpochInfo};
14use crate::sui_system_state::SuiSystemStateTrait;
15use crate::sui_system_state::get_sui_system_state;
16use crate::transaction::{Transaction, TransactionData, TransactionDataAPI, TransactionKind};
17use serde::{Deserialize, Serialize};
18use tap::Pipe;
19
20#[derive(Clone, Debug, Serialize, Deserialize)]
21pub struct CheckpointData {
22    pub checkpoint_summary: CertifiedCheckpointSummary,
23    pub checkpoint_contents: CheckpointContents,
24    pub transactions: Vec<CheckpointTransaction>,
25}
26
27impl CheckpointData {
28    // returns the latest versions of the output objects that still exist at the end of the checkpoint
29    pub fn latest_live_output_objects(&self) -> Vec<&Object> {
30        let mut latest_live_objects = BTreeMap::new();
31        for tx in self.transactions.iter() {
32            for obj in tx.output_objects.iter() {
33                latest_live_objects.insert(obj.id(), obj);
34            }
35            for obj_ref in tx.removed_object_refs_post_version() {
36                latest_live_objects.remove(&(obj_ref.0));
37            }
38        }
39        latest_live_objects.into_values().collect()
40    }
41
42    // returns the object refs that are eventually deleted or wrapped in the current checkpoint
43    pub fn eventually_removed_object_refs_post_version(&self) -> Vec<ObjectRef> {
44        let mut eventually_removed_object_refs = BTreeMap::new();
45        for tx in self.transactions.iter() {
46            for obj_ref in tx.removed_object_refs_post_version() {
47                eventually_removed_object_refs.insert(obj_ref.0, obj_ref);
48            }
49            for obj in tx.output_objects.iter() {
50                eventually_removed_object_refs.remove(&(obj.id()));
51            }
52        }
53        eventually_removed_object_refs.into_values().collect()
54    }
55
56    pub fn all_objects(&self) -> Vec<&Object> {
57        self.transactions
58            .iter()
59            .flat_map(|tx| &tx.input_objects)
60            .chain(self.transactions.iter().flat_map(|tx| &tx.output_objects))
61            .collect()
62    }
63
64    pub fn epoch_info(&self) -> Result<Option<EpochInfo>, StorageError> {
65        if self.checkpoint_summary.end_of_epoch_data.is_none()
66            && self.checkpoint_summary.sequence_number != 0
67        {
68            return Ok(None);
69        }
70        let (start_checkpoint, transaction) = if self.checkpoint_summary.sequence_number == 0 {
71            (0, &self.transactions[0])
72        } else {
73            let Some(transaction) = self.transactions.iter().find(|tx| {
74                matches!(
75                    tx.transaction.intent_message().value.kind(),
76                    TransactionKind::ChangeEpoch(_) | TransactionKind::EndOfEpochTransaction(_)
77                )
78            }) else {
79                return Err(StorageError::custom(format!(
80                    "Failed to get end of epoch transaction in checkpoint {} with EndOfEpochData",
81                    self.checkpoint_summary.sequence_number,
82                )));
83            };
84            (self.checkpoint_summary.sequence_number + 1, transaction)
85        };
86        let system_state =
87            get_sui_system_state(&transaction.output_objects.as_slice()).map_err(|e| {
88                StorageError::custom(format!(
89                    "Failed to find system state object output from end of epoch transaction: {e}"
90                ))
91            })?;
92        Ok(Some(EpochInfo {
93            epoch: system_state.epoch(),
94            protocol_version: Some(system_state.protocol_version()),
95            start_timestamp_ms: Some(system_state.epoch_start_timestamp_ms()),
96            end_timestamp_ms: None,
97            start_checkpoint: Some(start_checkpoint),
98            end_checkpoint: None,
99            reference_gas_price: Some(system_state.reference_gas_price()),
100            system_state: Some(system_state),
101        }))
102    }
103}
104
105#[derive(Clone, Debug, Serialize, Deserialize)]
106pub struct CheckpointTransaction {
107    /// The input Transaction
108    pub transaction: Transaction,
109    /// The effects produced by executing this transaction
110    pub effects: TransactionEffects,
111    /// The events, if any, emitted by this transactions during execution
112    pub events: Option<TransactionEvents>,
113    /// The state of all inputs to this transaction as they were prior to execution.
114    pub input_objects: Vec<Object>,
115    /// The state of all output objects created or mutated or unwrapped by this transaction.
116    pub output_objects: Vec<Object>,
117}
118
119impl CheckpointTransaction {
120    // provide an iterator over all deleted or wrapped objects in this transaction
121    pub fn removed_objects_pre_version(&self) -> impl Iterator<Item = &Object> {
122        // Since each object ID can only show up once in the input_objects, we can just use the
123        // ids of deleted and wrapped objects to lookup the object in the input_objects.
124        self.effects
125            .all_removed_objects()
126            .into_iter() // Use id and version to lookup in input Objects
127            .map(|((id, _, _), _)| {
128                self.input_objects
129                    .iter()
130                    .find(|o| o.id() == id)
131                    .expect("all removed objects should show up in input objects")
132            })
133    }
134
135    pub fn removed_object_refs_post_version(&self) -> impl Iterator<Item = ObjectRef> {
136        let deleted = self.effects.deleted().into_iter();
137        let wrapped = self.effects.wrapped().into_iter();
138        let unwrapped_then_deleted = self.effects.unwrapped_then_deleted().into_iter();
139        deleted.chain(wrapped).chain(unwrapped_then_deleted)
140    }
141
142    pub fn changed_objects(&self) -> impl Iterator<Item = (&Object, Option<&Object>)> {
143        self.effects
144            .all_changed_objects()
145            .into_iter()
146            .map(|((id, _, _), ..)| {
147                let object = self
148                    .output_objects
149                    .iter()
150                    .find(|o| o.id() == id)
151                    .expect("changed objects should show up in output objects");
152
153                let old_object = self.input_objects.iter().find(|o| o.id() == id);
154
155                (object, old_object)
156            })
157    }
158
159    pub fn created_objects(&self) -> impl Iterator<Item = &Object> {
160        // Iterator over (ObjectId, version) for created objects
161        self.effects
162            .created()
163            .into_iter()
164            // Lookup Objects in output Objects as well as old versions for mutated objects
165            .map(|((id, version, _), _)| {
166                self.output_objects
167                    .iter()
168                    .find(|o| o.id() == id && o.version() == version)
169                    .expect("created objects should show up in output objects")
170            })
171    }
172
173    pub fn execution_data(&self) -> ExecutionData {
174        ExecutionData {
175            transaction: self.transaction.clone(),
176            effects: self.effects.clone(),
177        }
178    }
179}
180
181impl BackingPackageStore for CheckpointData {
182    fn get_package_object(
183        &self,
184        package_id: &crate::base_types::ObjectID,
185    ) -> crate::error::SuiResult<Option<crate::storage::PackageObject>> {
186        self.transactions
187            .iter()
188            .flat_map(|transaction| transaction.output_objects.iter())
189            .find(|object| object.is_package() && &object.id() == package_id)
190            .cloned()
191            .map(crate::storage::PackageObject::new)
192            .pipe(Ok)
193    }
194}
195
196// Never remove these asserts!
197// These data structures are meant to be used in-memory, for structures that can be persisted in
198// storage you should look at the protobuf versions.
199static_assertions::assert_not_impl_any!(Checkpoint: serde::Serialize, serde::de::DeserializeOwned);
200static_assertions::assert_not_impl_any!(ExecutedTransaction: serde::Serialize, serde::de::DeserializeOwned);
201static_assertions::assert_not_impl_any!(ObjectSet: serde::Serialize, serde::de::DeserializeOwned);
202
203#[derive(Clone, Debug)]
204pub struct Checkpoint {
205    pub summary: CertifiedCheckpointSummary,
206    pub contents: CheckpointContents,
207    pub transactions: Vec<ExecutedTransaction>,
208    pub object_set: ObjectSet,
209}
210
211#[derive(Clone, Debug)]
212pub struct ExecutedTransaction {
213    /// The input Transaction
214    pub transaction: TransactionData,
215    pub signatures: Vec<GenericSignature>,
216    /// The effects produced by executing this transaction
217    pub effects: TransactionEffects,
218    /// The events, if any, emitted by this transactions during execution
219    pub events: Option<TransactionEvents>,
220    pub unchanged_loaded_runtime_objects: Vec<ObjectKey>,
221}
222
223#[derive(Default, Clone, Debug)]
224pub struct ObjectSet(BTreeMap<ObjectKey, Object>);
225
226impl ObjectSet {
227    pub fn get(&self, key: &ObjectKey) -> Option<&Object> {
228        self.0.get(key)
229    }
230
231    pub fn insert(&mut self, object: Object) {
232        self.0
233            .insert(ObjectKey(object.id(), object.version()), object);
234    }
235
236    pub fn iter(&self) -> impl Iterator<Item = &Object> {
237        self.0.values()
238    }
239}
240
241impl From<Checkpoint> for CheckpointData {
242    fn from(value: Checkpoint) -> Self {
243        let transactions = value
244            .transactions
245            .into_iter()
246            .map(|tx| {
247                let input_objects = tx
248                    .effects
249                    .modified_at_versions()
250                    .into_iter()
251                    .filter_map(|(object_id, version)| {
252                        value
253                            .object_set
254                            .get(&ObjectKey(object_id, version))
255                            .cloned()
256                    })
257                    .collect::<Vec<_>>();
258                let output_objects = tx
259                    .effects
260                    .all_changed_objects()
261                    .into_iter()
262                    .filter_map(|(object_ref, _owner, _kind)| {
263                        value.object_set.get(&object_ref.into()).cloned()
264                    })
265                    .collect::<Vec<_>>();
266
267                CheckpointTransaction {
268                    transaction: Transaction::from_generic_sig_data(tx.transaction, tx.signatures),
269                    effects: tx.effects,
270                    events: tx.events,
271                    input_objects,
272                    output_objects,
273                }
274            })
275            .collect();
276        Self {
277            checkpoint_summary: value.summary,
278            checkpoint_contents: value.contents,
279            transactions,
280        }
281    }
282}
283
284// Lossy conversion
285impl From<CheckpointData> for Checkpoint {
286    fn from(value: CheckpointData) -> Self {
287        let mut object_set = ObjectSet::default();
288        let transactions = value
289            .transactions
290            .into_iter()
291            .map(|tx| {
292                for o in tx
293                    .input_objects
294                    .into_iter()
295                    .chain(tx.output_objects.into_iter())
296                {
297                    object_set.insert(o);
298                }
299
300                let sender_signed = tx.transaction.into_data().into_inner();
301
302                ExecutedTransaction {
303                    transaction: sender_signed.intent_message.value,
304                    signatures: sender_signed.tx_signatures,
305                    effects: tx.effects,
306                    events: tx.events,
307
308                    // lossy
309                    unchanged_loaded_runtime_objects: Vec::new(),
310                }
311            })
312            .collect();
313        Self {
314            summary: value.checkpoint_summary,
315            contents: value.checkpoint_contents,
316            transactions,
317            object_set,
318        }
319    }
320}