iota_types/
full_checkpoint_content.rs1use std::collections::BTreeMap;
6
7use iota_sdk_types::{
8 ObjectId, ObjectReference, TransactionEffects, TransactionEvents, TransactionKind,
9 checkpoint::CheckpointContents,
10};
11use serde::{Deserialize, Serialize};
12use tap::Pipe;
13
14use crate::{
15 base_types::ExecutionData,
16 effects::{TransactionEffectsAPI, TransactionEffectsExt},
17 iota_system_state::{IotaSystemStateTrait, get_iota_system_state},
18 messages_checkpoint::CertifiedCheckpointSummary,
19 object::Object,
20 storage::{BackingPackageStore, EpochInfo, error::Error as StorageError},
21 transaction::{TransactionDataAPI, TransactionEnvelope},
22};
23
24#[derive(Clone, Debug, Serialize, Deserialize)]
25pub struct CheckpointData {
26 pub checkpoint_summary: CertifiedCheckpointSummary,
27 pub checkpoint_contents: CheckpointContents,
28 pub transactions: Vec<CheckpointTransaction>,
29}
30
31impl CheckpointData {
32 pub fn latest_live_output_objects(&self) -> Vec<&Object> {
35 let mut latest_live_objects = BTreeMap::new();
36 for tx in self.transactions.iter() {
37 for obj in tx.output_objects.iter() {
38 latest_live_objects.insert(obj.id(), obj);
39 }
40 for obj_ref in tx.removed_object_refs_post_version() {
41 latest_live_objects.remove(&obj_ref.object_id);
42 }
43 }
44 latest_live_objects.into_values().collect()
45 }
46
47 pub fn eventually_removed_object_refs_post_version(&self) -> Vec<ObjectReference> {
50 let mut eventually_removed_object_refs = BTreeMap::new();
51 for tx in self.transactions.iter() {
52 for obj_ref in tx.removed_object_refs_post_version() {
53 eventually_removed_object_refs.insert(obj_ref.object_id, obj_ref);
54 }
55 for obj in tx.output_objects.iter() {
56 eventually_removed_object_refs.remove(&(obj.id()));
57 }
58 }
59 eventually_removed_object_refs.into_values().collect()
60 }
61
62 pub fn all_objects(&self) -> Vec<&Object> {
63 self.transactions
64 .iter()
65 .flat_map(|tx| &tx.input_objects)
66 .chain(self.transactions.iter().flat_map(|tx| &tx.output_objects))
67 .collect()
68 }
69
70 pub fn end_of_epoch_transaction(&self) -> Option<&CheckpointTransaction> {
75 self.checkpoint_summary.end_of_epoch_data.as_ref()?;
77 let transaction = self.transactions.last()?;
80 transaction
81 .transaction
82 .transaction()
83 .is_end_of_epoch_tx()
84 .then_some(transaction)
85 }
86
87 pub fn epoch_info(
93 &self,
94 ) -> Result<Option<(EpochInfo, Option<TransactionEvents>)>, StorageError> {
95 if self.checkpoint_summary.end_of_epoch_data.is_none()
97 && self.checkpoint_summary.sequence_number != 0
98 {
99 return Ok(None);
100 }
101
102 let (start_checkpoint, transaction) = if self.checkpoint_summary.sequence_number != 0 {
103 let Some(transaction) = self.end_of_epoch_transaction() else {
104 return Err(StorageError::custom(format!(
105 "Failed to get end of epoch transaction in checkpoint {} with EndOfEpochData",
106 self.checkpoint_summary.sequence_number,
107 )));
108 };
109 (self.checkpoint_summary.sequence_number + 1, transaction)
110 } else {
111 let Some(transaction) = self.transactions.iter().find(|tx| {
113 matches!(
114 tx.transaction.transaction().kind(),
115 TransactionKind::Genesis(_)
116 )
117 }) else {
118 return Err(StorageError::custom(format!(
119 "Failed to get genesis transaction in checkpoint {}",
120 self.checkpoint_summary.sequence_number,
121 )));
122 };
123 (0, transaction)
124 };
125
126 let system_state =
127 get_iota_system_state(&transaction.output_objects.as_slice()).map_err(|e| {
128 StorageError::custom(format!(
129 "Failed to find system state object output from end of epoch or genesis transaction: {e}"
130 ))
131 })?;
132
133 Ok(Some((
134 EpochInfo {
135 epoch: system_state.epoch(),
136 protocol_version: system_state.protocol_version(),
137 start_timestamp_ms: system_state.epoch_start_timestamp_ms(),
138 end_timestamp_ms: None,
139 start_checkpoint,
140 end_checkpoint: None,
141 reference_gas_price: system_state.reference_gas_price(),
142 system_state,
143 },
144 transaction.events.clone(),
145 )))
146 }
147}
148
149#[derive(Clone, Debug, Serialize, Deserialize)]
150pub struct CheckpointTransaction {
151 pub transaction: TransactionEnvelope,
153 pub effects: TransactionEffects,
155 pub events: Option<TransactionEvents>,
157 pub input_objects: Vec<Object>,
160 pub output_objects: Vec<Object>,
163}
164
165impl CheckpointTransaction {
166 pub fn removed_objects_pre_version(&self) -> impl Iterator<Item = &Object> {
168 self.effects
172 .all_removed_objects()
173 .into_iter() .map(|(object_ref, _)| {
175 self.input_objects
176 .iter()
177 .find(|o| o.id() == object_ref.object_id)
178 .expect("all removed objects should show up in input objects")
179 })
180 }
181
182 pub fn removed_object_refs_post_version(&self) -> impl Iterator<Item = ObjectReference> {
183 let deleted = self.effects.deleted().into_iter();
184 let wrapped = self.effects.wrapped().into_iter();
185 let unwrapped_then_deleted = self.effects.unwrapped_then_deleted().into_iter();
186 deleted.chain(wrapped).chain(unwrapped_then_deleted)
187 }
188
189 pub fn changed_objects(&self) -> impl Iterator<Item = (&Object, Option<&Object>)> {
190 self.effects
191 .all_changed_objects()
192 .into_iter()
193 .map(|(object_ref, ..)| {
194 let object = self
195 .output_objects
196 .iter()
197 .find(|o| o.id() == object_ref.object_id)
198 .expect("changed objects should show up in output objects");
199
200 let old_object = self
201 .input_objects
202 .iter()
203 .find(|o| o.id() == object_ref.object_id);
204
205 (object, old_object)
206 })
207 }
208
209 pub fn created_objects(&self) -> impl Iterator<Item = &Object> {
210 self.effects
212 .created()
213 .into_iter()
214 .map(|(object_ref, _)| {
216 self.output_objects
217 .iter()
218 .find(|o| o.id() == object_ref.object_id && o.version() == object_ref.version)
219 .expect("created objects should show up in output objects")
220 })
221 }
222
223 pub fn execution_data(&self) -> ExecutionData {
224 ExecutionData {
225 transaction: self.transaction.clone(),
226 effects: self.effects.clone(),
227 }
228 }
229}
230
231impl BackingPackageStore for CheckpointData {
232 fn get_package_object(
233 &self,
234 package_id: &ObjectId,
235 ) -> crate::error::IotaResult<Option<crate::storage::PackageObject>> {
236 self.transactions
237 .iter()
238 .flat_map(|transaction| transaction.output_objects.iter())
239 .find(|object| object.is_package() && &object.id() == package_id)
240 .cloned()
241 .map(crate::storage::PackageObject::new)
242 .pipe(Ok)
243 }
244}