iota_types/
full_checkpoint_content.rs1use std::collections::BTreeMap;
6
7use serde::{Deserialize, Serialize};
8use tap::Pipe;
9
10use crate::{
11 base_types::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 all_objects(&self) -> Vec<&Object> {
59 self.transactions
60 .iter()
61 .flat_map(|tx| &tx.input_objects)
62 .chain(self.transactions.iter().flat_map(|tx| &tx.output_objects))
63 .collect()
64 }
65
66 pub fn epoch_info(&self) -> Result<Option<EpochInfo>, StorageError> {
67 if self.checkpoint_summary.end_of_epoch_data.is_none()
69 && self.checkpoint_summary.sequence_number != 0
70 {
71 return Ok(None);
72 }
73
74 let (start_checkpoint, transaction) = if self.checkpoint_summary.sequence_number != 0 {
75 let Some(transaction) = self.transactions.iter().find(|tx| {
76 matches!(
77 tx.transaction.intent_message().value.kind(),
78 TransactionKind::EndOfEpochTransaction(_)
79 )
80 }) else {
81 return Err(StorageError::custom(format!(
82 "Failed to get end of epoch transaction in checkpoint {} with EndOfEpochData",
83 self.checkpoint_summary.sequence_number,
84 )));
85 };
86 (self.checkpoint_summary.sequence_number + 1, transaction)
87 } else {
88 let Some(transaction) = self.transactions.iter().find(|tx| {
90 matches!(
91 tx.transaction.intent_message().value.kind(),
92 TransactionKind::Genesis(_)
93 )
94 }) else {
95 return Err(StorageError::custom(format!(
96 "Failed to get genesis transaction in checkpoint {}",
97 self.checkpoint_summary.sequence_number,
98 )));
99 };
100 (0, transaction)
101 };
102
103 let system_state =
104 get_iota_system_state(&transaction.output_objects.as_slice()).map_err(|e| {
105 StorageError::custom(format!(
106 "Failed to find system state object output from end of epoch or genesis transaction: {e}"
107 ))
108 })?;
109
110 Ok(Some(EpochInfo {
111 epoch: system_state.epoch(),
112 protocol_version: system_state.protocol_version(),
113 start_timestamp_ms: system_state.epoch_start_timestamp_ms(),
114 end_timestamp_ms: None,
115 start_checkpoint,
116 end_checkpoint: None,
117 reference_gas_price: system_state.reference_gas_price(),
118 system_state,
119 }))
120 }
121}
122
123#[derive(Clone, Debug, Serialize, Deserialize)]
124pub struct CheckpointTransaction {
125 pub transaction: Transaction,
127 pub effects: TransactionEffects,
129 pub events: Option<TransactionEvents>,
131 pub input_objects: Vec<Object>,
134 pub output_objects: Vec<Object>,
137}
138
139impl CheckpointTransaction {
140 pub fn removed_objects_pre_version(&self) -> impl Iterator<Item = &Object> {
142 self.effects
146 .all_removed_objects()
147 .into_iter() .map(|((id, _, _), _)| {
149 self.input_objects
150 .iter()
151 .find(|o| o.id() == id)
152 .expect("all removed objects should show up in input objects")
153 })
154 }
155
156 pub fn removed_object_refs_post_version(&self) -> impl Iterator<Item = ObjectRef> {
157 let deleted = self.effects.deleted().into_iter();
158 let wrapped = self.effects.wrapped().into_iter();
159 let unwrapped_then_deleted = self.effects.unwrapped_then_deleted().into_iter();
160 deleted.chain(wrapped).chain(unwrapped_then_deleted)
161 }
162
163 pub fn changed_objects(&self) -> impl Iterator<Item = (&Object, Option<&Object>)> {
164 self.effects
165 .all_changed_objects()
166 .into_iter()
167 .map(|((id, _, _), ..)| {
168 let object = self
169 .output_objects
170 .iter()
171 .find(|o| o.id() == id)
172 .expect("changed objects should show up in output objects");
173
174 let old_object = self.input_objects.iter().find(|o| o.id() == id);
175
176 (object, old_object)
177 })
178 }
179
180 pub fn created_objects(&self) -> impl Iterator<Item = &Object> {
181 self.effects
183 .created()
184 .into_iter()
185 .map(|((id, version, _), _)| {
187 self.output_objects
188 .iter()
189 .find(|o| o.id() == id && o.version() == version)
190 .expect("created objects should show up in output objects")
191 })
192 }
193}
194
195impl BackingPackageStore for CheckpointData {
196 fn get_package_object(
197 &self,
198 package_id: &crate::base_types::ObjectID,
199 ) -> crate::error::IotaResult<Option<crate::storage::PackageObject>> {
200 self.transactions
201 .iter()
202 .flat_map(|transaction| transaction.output_objects.iter())
203 .find(|object| object.is_package() && &object.id() == package_id)
204 .cloned()
205 .map(crate::storage::PackageObject::new)
206 .pipe(Ok)
207 }
208}