iota_types/
full_checkpoint_content.rs1use std::collections::BTreeMap;
6
7use iota_sdk_types::{ObjectId, ObjectReference, TransactionKind};
8use serde::{Deserialize, Serialize};
9use tap::Pipe;
10
11use crate::{
12 effects::{
13 TransactionEffects, TransactionEffectsAPI, TransactionEffectsExt, TransactionEvents,
14 },
15 iota_system_state::{IotaSystemStateTrait, get_iota_system_state},
16 messages_checkpoint::{CertifiedCheckpointSummary, CheckpointContents},
17 object::Object,
18 storage::{BackingPackageStore, EpochInfo, error::Error as StorageError},
19 transaction::{Transaction, TransactionDataAPI},
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 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.object_id);
40 }
41 }
42 latest_live_objects.into_values().collect()
43 }
44
45 pub fn eventually_removed_object_refs_post_version(&self) -> Vec<ObjectReference> {
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.object_id, 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 all_objects(&self) -> Vec<&Object> {
61 self.transactions
62 .iter()
63 .flat_map(|tx| &tx.input_objects)
64 .chain(self.transactions.iter().flat_map(|tx| &tx.output_objects))
65 .collect()
66 }
67
68 pub fn end_of_epoch_transaction(&self) -> Option<&CheckpointTransaction> {
73 self.checkpoint_summary.end_of_epoch_data.as_ref()?;
75 let transaction = self.transactions.last()?;
78 transaction
79 .transaction
80 .intent_message()
81 .value
82 .is_end_of_epoch_tx()
83 .then_some(transaction)
84 }
85
86 pub fn epoch_info(
92 &self,
93 ) -> Result<Option<(EpochInfo, Option<TransactionEvents>)>, StorageError> {
94 if self.checkpoint_summary.end_of_epoch_data.is_none()
96 && self.checkpoint_summary.sequence_number != 0
97 {
98 return Ok(None);
99 }
100
101 let (start_checkpoint, transaction) = if self.checkpoint_summary.sequence_number != 0 {
102 let Some(transaction) = self.end_of_epoch_transaction() else {
103 return Err(StorageError::custom(format!(
104 "Failed to get end of epoch transaction in checkpoint {} with EndOfEpochData",
105 self.checkpoint_summary.sequence_number,
106 )));
107 };
108 (self.checkpoint_summary.sequence_number + 1, transaction)
109 } else {
110 let Some(transaction) = self.transactions.iter().find(|tx| {
112 matches!(
113 tx.transaction.intent_message().value.kind(),
114 TransactionKind::Genesis(_)
115 )
116 }) else {
117 return Err(StorageError::custom(format!(
118 "Failed to get genesis transaction in checkpoint {}",
119 self.checkpoint_summary.sequence_number,
120 )));
121 };
122 (0, transaction)
123 };
124
125 let system_state =
126 get_iota_system_state(&transaction.output_objects.as_slice()).map_err(|e| {
127 StorageError::custom(format!(
128 "Failed to find system state object output from end of epoch or genesis transaction: {e}"
129 ))
130 })?;
131
132 Ok(Some((
133 EpochInfo {
134 epoch: system_state.epoch(),
135 protocol_version: system_state.protocol_version(),
136 start_timestamp_ms: system_state.epoch_start_timestamp_ms(),
137 end_timestamp_ms: None,
138 start_checkpoint,
139 end_checkpoint: None,
140 reference_gas_price: system_state.reference_gas_price(),
141 system_state,
142 },
143 transaction.events.clone(),
144 )))
145 }
146}
147
148#[derive(Clone, Debug, Serialize, Deserialize)]
149pub struct CheckpointTransaction {
150 pub transaction: Transaction,
152 pub effects: TransactionEffects,
154 pub events: Option<TransactionEvents>,
156 pub input_objects: Vec<Object>,
159 pub output_objects: Vec<Object>,
162}
163
164impl CheckpointTransaction {
165 pub fn removed_objects_pre_version(&self) -> impl Iterator<Item = &Object> {
167 self.effects
171 .all_removed_objects()
172 .into_iter() .map(|(object_ref, _)| {
174 self.input_objects
175 .iter()
176 .find(|o| o.id() == object_ref.object_id)
177 .expect("all removed objects should show up in input objects")
178 })
179 }
180
181 pub fn removed_object_refs_post_version(&self) -> impl Iterator<Item = ObjectReference> {
182 let deleted = self.effects.deleted().into_iter();
183 let wrapped = self.effects.wrapped().into_iter();
184 let unwrapped_then_deleted = self.effects.unwrapped_then_deleted().into_iter();
185 deleted.chain(wrapped).chain(unwrapped_then_deleted)
186 }
187
188 pub fn changed_objects(&self) -> impl Iterator<Item = (&Object, Option<&Object>)> {
189 self.effects
190 .all_changed_objects()
191 .into_iter()
192 .map(|(object_ref, ..)| {
193 let object = self
194 .output_objects
195 .iter()
196 .find(|o| o.id() == object_ref.object_id)
197 .expect("changed objects should show up in output objects");
198
199 let old_object = self
200 .input_objects
201 .iter()
202 .find(|o| o.id() == object_ref.object_id);
203
204 (object, old_object)
205 })
206 }
207
208 pub fn created_objects(&self) -> impl Iterator<Item = &Object> {
209 self.effects
211 .created()
212 .into_iter()
213 .map(|(object_ref, _)| {
215 self.output_objects
216 .iter()
217 .find(|o| o.id() == object_ref.object_id && o.version() == object_ref.version)
218 .expect("created objects should show up in output objects")
219 })
220 }
221}
222
223impl BackingPackageStore for CheckpointData {
224 fn get_package_object(
225 &self,
226 package_id: &ObjectId,
227 ) -> crate::error::IotaResult<Option<crate::storage::PackageObject>> {
228 self.transactions
229 .iter()
230 .flat_map(|transaction| transaction.output_objects.iter())
231 .find(|object| object.is_package() && &object.id() == package_id)
232 .cloned()
233 .map(crate::storage::PackageObject::new)
234 .pipe(Ok)
235 }
236}