1use 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 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
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
284impl 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 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}