iota_types/
full_checkpoint_content.rs1use std::collections::{BTreeMap, HashSet};
6
7use serde::{Deserialize, Serialize};
8use tap::Pipe;
9
10use crate::{
11 base_types::{ObjectID, ObjectRef},
12 effects::{TransactionEffects, TransactionEffectsAPI, TransactionEvents},
13 iota_system_state::{IotaSystemStateTrait, get_iota_system_state},
14 messages_checkpoint::{CertifiedCheckpointSummary, CheckpointContents},
15 object::Object,
16 storage::{BackingPackageStore, EpochInfo, error::Error as StorageError},
17 transaction::{Transaction, TransactionDataAPI, TransactionKind},
18};
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> {
31 let mut latest_live_objects = BTreeMap::new();
32 for tx in self.transactions.iter() {
33 for obj in tx.output_objects.iter() {
34 latest_live_objects.insert(obj.id(), obj);
35 }
36 for obj_ref in tx.removed_object_refs_post_version() {
37 latest_live_objects.remove(&(obj_ref.0));
38 }
39 }
40 latest_live_objects.into_values().collect()
41 }
42
43 pub fn eventually_removed_object_refs_post_version(&self) -> Vec<ObjectRef> {
46 let mut eventually_removed_object_refs = BTreeMap::new();
47 for tx in self.transactions.iter() {
48 for obj_ref in tx.removed_object_refs_post_version() {
49 eventually_removed_object_refs.insert(obj_ref.0, obj_ref);
50 }
51 for obj in tx.output_objects.iter() {
52 eventually_removed_object_refs.remove(&(obj.id()));
53 }
54 }
55 eventually_removed_object_refs.into_values().collect()
56 }
57
58 pub fn checkpoint_input_objects(&self) -> BTreeMap<ObjectID, &Object> {
61 let mut output_objects_seen = HashSet::new();
62 let mut checkpoint_input_objects = BTreeMap::new();
63 for tx in self.transactions.iter() {
64 for obj in tx.input_objects.iter() {
65 let id = obj.id();
66 if output_objects_seen.contains(&id) || checkpoint_input_objects.contains_key(&id) {
67 continue;
68 }
69 checkpoint_input_objects.insert(id, obj);
70 }
71 for obj in tx.output_objects.iter() {
72 output_objects_seen.insert(obj.id());
75 }
76 }
77 checkpoint_input_objects
78 }
79
80 pub fn all_objects(&self) -> Vec<&Object> {
81 self.transactions
82 .iter()
83 .flat_map(|tx| &tx.input_objects)
84 .chain(self.transactions.iter().flat_map(|tx| &tx.output_objects))
85 .collect()
86 }
87
88 pub fn epoch_info(&self) -> Result<Option<EpochInfo>, StorageError> {
89 if self.checkpoint_summary.end_of_epoch_data.is_none()
91 && self.checkpoint_summary.sequence_number != 0
92 {
93 return Ok(None);
94 }
95
96 let (start_checkpoint, transaction) = if self.checkpoint_summary.sequence_number != 0 {
97 let Some(transaction) = self.transactions.iter().find(|tx| {
98 matches!(
99 tx.transaction.intent_message().value.kind(),
100 TransactionKind::EndOfEpochTransaction(_)
101 )
102 }) 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(EpochInfo {
133 epoch: system_state.epoch(),
134 protocol_version: system_state.protocol_version(),
135 start_timestamp_ms: system_state.epoch_start_timestamp_ms(),
136 end_timestamp_ms: None,
137 start_checkpoint,
138 end_checkpoint: None,
139 reference_gas_price: system_state.reference_gas_price(),
140 system_state,
141 }))
142 }
143}
144
145#[derive(Clone, Debug, Serialize, Deserialize)]
146pub struct CheckpointTransaction {
147 pub transaction: Transaction,
149 pub effects: TransactionEffects,
151 pub events: Option<TransactionEvents>,
153 pub input_objects: Vec<Object>,
156 pub output_objects: Vec<Object>,
159}
160
161impl CheckpointTransaction {
162 pub fn removed_objects_pre_version(&self) -> impl Iterator<Item = &Object> {
164 self.effects
168 .all_removed_objects()
169 .into_iter() .map(|((id, _, _), _)| {
171 self.input_objects
172 .iter()
173 .find(|o| o.id() == id)
174 .expect("all removed objects should show up in input objects")
175 })
176 }
177
178 pub fn removed_object_refs_post_version(&self) -> impl Iterator<Item = ObjectRef> {
179 let deleted = self.effects.deleted().into_iter();
180 let wrapped = self.effects.wrapped().into_iter();
181 let unwrapped_then_deleted = self.effects.unwrapped_then_deleted().into_iter();
182 deleted.chain(wrapped).chain(unwrapped_then_deleted)
183 }
184
185 pub fn changed_objects(&self) -> impl Iterator<Item = (&Object, Option<&Object>)> {
186 self.effects
187 .all_changed_objects()
188 .into_iter()
189 .map(|((id, _, _), ..)| {
190 let object = self
191 .output_objects
192 .iter()
193 .find(|o| o.id() == id)
194 .expect("changed objects should show up in output objects");
195
196 let old_object = self.input_objects.iter().find(|o| o.id() == id);
197
198 (object, old_object)
199 })
200 }
201
202 pub fn created_objects(&self) -> impl Iterator<Item = &Object> {
203 self.effects
205 .created()
206 .into_iter()
207 .map(|((id, version, _), _)| {
209 self.output_objects
210 .iter()
211 .find(|o| o.id() == id && o.version() == version)
212 .expect("created objects should show up in output objects")
213 })
214 }
215}
216
217impl BackingPackageStore for CheckpointData {
218 fn get_package_object(
219 &self,
220 package_id: &crate::base_types::ObjectID,
221 ) -> crate::error::IotaResult<Option<crate::storage::PackageObject>> {
222 self.transactions
223 .iter()
224 .flat_map(|transaction| transaction.output_objects.iter())
225 .find(|object| object.is_package() && &object.id() == package_id)
226 .cloned()
227 .map(crate::storage::PackageObject::new)
228 .pipe(Ok)
229 }
230}