1use std::collections::BTreeMap;
5
6use crate::base_types::{ExecutionData, ObjectID, 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 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 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 pub transaction: Transaction,
109 pub effects: TransactionEffects,
111 pub events: Option<TransactionEvents>,
113 pub input_objects: Vec<Object>,
115 pub output_objects: Vec<Object>,
117}
118
119impl CheckpointTransaction {
120 pub fn removed_objects_pre_version(&self) -> impl Iterator<Item = &Object> {
122 self.effects
125 .all_removed_objects()
126 .into_iter() .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 self.effects
162 .created()
163 .into_iter()
164 .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
196static_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 pub transaction: TransactionData,
215 pub signatures: Vec<GenericSignature>,
216 pub effects: TransactionEffects,
218 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 pub fn len(&self) -> usize {
241 self.0.len()
242 }
243
244 pub fn is_empty(&self) -> bool {
245 self.0.is_empty()
246 }
247}
248
249impl Checkpoint {
250 pub fn latest_live_output_objects(&self) -> BTreeMap<ObjectID, Object> {
251 let mut latest_live_output_objects = BTreeMap::new();
252 for tx in self.transactions.iter() {
253 for obj in tx.output_objects(&self.object_set) {
254 latest_live_output_objects.insert(obj.id(), obj.clone());
255 }
256 for obj_ref in tx
257 .effects
258 .deleted()
259 .into_iter()
260 .chain(tx.effects.wrapped().into_iter())
261 .chain(tx.effects.unwrapped_then_deleted().into_iter())
262 {
263 latest_live_output_objects.remove(&obj_ref.0);
264 }
265 }
266 latest_live_output_objects
267 }
268
269 pub fn eventually_removed_object_refs_post_version(&self) -> Vec<ObjectRef> {
270 let mut eventually_removed_object_refs = BTreeMap::new();
271 for tx in self.transactions.iter() {
272 for obj_ref in tx
273 .effects
274 .deleted()
275 .into_iter()
276 .chain(tx.effects.wrapped().into_iter())
277 .chain(tx.effects.unwrapped_then_deleted().into_iter())
278 {
279 eventually_removed_object_refs.insert(obj_ref.0, obj_ref);
280 }
281 for obj in tx.output_objects(&self.object_set) {
282 eventually_removed_object_refs.remove(&obj.id());
283 }
284 }
285 eventually_removed_object_refs.into_values().collect()
286 }
287}
288
289impl ExecutedTransaction {
290 pub fn input_objects<'a>(
291 &self,
292 object_set: &'a ObjectSet,
293 ) -> impl Iterator<Item = &'a Object> + 'a {
294 self.effects
295 .object_changes()
296 .into_iter()
297 .filter_map(move |change| {
298 change
299 .input_version
300 .and_then(|version| object_set.get(&ObjectKey(change.id, version)))
301 })
302 }
303
304 pub fn output_objects<'a>(
305 &self,
306 object_set: &'a ObjectSet,
307 ) -> impl Iterator<Item = &'a Object> + 'a {
308 self.effects
309 .object_changes()
310 .into_iter()
311 .filter_map(move |change| {
312 change
313 .output_version
314 .and_then(|version| object_set.get(&ObjectKey(change.id, version)))
315 })
316 }
317}
318
319impl From<Checkpoint> for CheckpointData {
320 fn from(value: Checkpoint) -> Self {
321 let transactions = value
322 .transactions
323 .into_iter()
324 .map(|tx| {
325 let input_objects = tx
326 .effects
327 .modified_at_versions()
328 .into_iter()
329 .filter_map(|(object_id, version)| {
330 value
331 .object_set
332 .get(&ObjectKey(object_id, version))
333 .cloned()
334 })
335 .collect::<Vec<_>>();
336 let output_objects = tx
337 .effects
338 .all_changed_objects()
339 .into_iter()
340 .filter_map(|(object_ref, _owner, _kind)| {
341 value.object_set.get(&object_ref.into()).cloned()
342 })
343 .collect::<Vec<_>>();
344
345 CheckpointTransaction {
346 transaction: Transaction::from_generic_sig_data(tx.transaction, tx.signatures),
347 effects: tx.effects,
348 events: tx.events,
349 input_objects,
350 output_objects,
351 }
352 })
353 .collect();
354 Self {
355 checkpoint_summary: value.summary,
356 checkpoint_contents: value.contents,
357 transactions,
358 }
359 }
360}
361
362impl From<CheckpointData> for Checkpoint {
364 fn from(value: CheckpointData) -> Self {
365 let mut object_set = ObjectSet::default();
366 let transactions = value
367 .transactions
368 .into_iter()
369 .map(|tx| {
370 for o in tx
371 .input_objects
372 .into_iter()
373 .chain(tx.output_objects.into_iter())
374 {
375 object_set.insert(o);
376 }
377
378 let sender_signed = tx.transaction.into_data().into_inner();
379
380 ExecutedTransaction {
381 transaction: sender_signed.intent_message.value,
382 signatures: sender_signed.tx_signatures,
383 effects: tx.effects,
384 events: tx.events,
385
386 unchanged_loaded_runtime_objects: Vec::new(),
388 }
389 })
390 .collect();
391 Self {
392 summary: value.checkpoint_summary,
393 contents: value.checkpoint_contents,
394 transactions,
395 object_set,
396 }
397 }
398}