1use std::collections::{BTreeMap, HashSet};
6
7use serde::{Deserialize, Serialize};
8use tap::Pipe;
9
10use crate::{
11 base_types::{ObjectID, ObjectRef},
12 effects::{
13 IDOperation, ObjectIn, ObjectOut, TransactionEffects, TransactionEffectsAPI,
14 TransactionEvents,
15 },
16 messages_checkpoint::{CertifiedCheckpointSummary, CheckpointContents},
17 object::Object,
18 storage::BackingPackageStore,
19 transaction::Transaction,
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.0));
40 }
41 }
42 latest_live_objects.into_values().collect()
43 }
44
45 pub fn eventually_removed_object_refs_post_version(&self) -> Vec<ObjectRef> {
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.0, 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 checkpoint_input_objects(&self) -> BTreeMap<ObjectID, &Object> {
63 let mut output_objects_seen = HashSet::new();
64 let mut checkpoint_input_objects = BTreeMap::new();
65 for tx in self.transactions.iter() {
66 for obj in tx.input_objects.iter() {
67 let id = obj.id();
68 if output_objects_seen.contains(&id) || checkpoint_input_objects.contains_key(&id) {
69 continue;
70 }
71 checkpoint_input_objects.insert(id, obj);
72 }
73 for obj in tx.output_objects.iter() {
74 output_objects_seen.insert(obj.id());
77 }
78 }
79 checkpoint_input_objects
80 }
81
82 pub fn all_objects(&self) -> Vec<&Object> {
83 self.transactions
84 .iter()
85 .flat_map(|tx| &tx.input_objects)
86 .chain(self.transactions.iter().flat_map(|tx| &tx.output_objects))
87 .collect()
88 }
89}
90
91#[derive(Clone, Debug, Serialize, Deserialize)]
92pub struct CheckpointTransaction {
93 pub transaction: Transaction,
95 pub effects: TransactionEffects,
97 pub events: Option<TransactionEvents>,
99 pub input_objects: Vec<Object>,
102 pub output_objects: Vec<Object>,
105}
106
107impl CheckpointTransaction {
108 pub fn removed_objects_pre_version(&self) -> impl Iterator<Item = &Object> {
110 match &self.effects {
112 TransactionEffects::V1(v1) => {
113 v1.changed_objects().iter().filter_map(|(id, change)| {
114 match (
115 &change.input_state,
116 &change.output_state,
117 &change.id_operation,
118 ) {
119 (
121 ObjectIn::Exist(((version, _d), _o)),
122 ObjectOut::NotExist,
123 IDOperation::Deleted,
124 ) => Some((id, version)),
125
126 (
128 ObjectIn::Exist(((version, _), _)),
129 ObjectOut::NotExist,
130 IDOperation::None,
131 ) => Some((id, version)),
132 _ => None,
133 }
134 })
135 }
136 }
137 .map(|(id, version)| {
139 self.input_objects
140 .iter()
141 .find(|o| &o.id() == id && &o.version() == version)
142 .expect("all removed objects should show up in input objects")
143 })
144 }
145
146 pub fn removed_object_refs_post_version(&self) -> impl Iterator<Item = ObjectRef> {
147 let deleted = self.effects.deleted().into_iter();
148 let wrapped = self.effects.wrapped().into_iter();
149 let unwrapped_then_deleted = self.effects.unwrapped_then_deleted().into_iter();
150 deleted.chain(wrapped).chain(unwrapped_then_deleted)
151 }
152
153 pub fn changed_objects(&self) -> impl Iterator<Item = (&Object, Option<&Object>)> {
154 match &self.effects {
156 TransactionEffects::V1(v1) => {
157 v1.changed_objects().iter().filter_map(|(id, change)| {
158 match (
159 &change.input_state,
160 &change.output_state,
161 &change.id_operation,
162 ) {
163 (ObjectIn::NotExist, ObjectOut::ObjectWrite(_), IDOperation::Created) => {
165 Some(((id, &v1.lamport_version), None))
166 }
167 (
168 ObjectIn::NotExist,
169 ObjectOut::PackageWrite((version, _)),
170 IDOperation::Created,
171 ) => Some(((id, version), None)),
172
173 (ObjectIn::NotExist, ObjectOut::ObjectWrite(_), IDOperation::None) => {
175 Some(((id, &v1.lamport_version), None))
176 }
177
178 (ObjectIn::Exist(((old_version, _), _)), ObjectOut::ObjectWrite(_), _) => {
180 Some(((id, &v1.lamport_version), Some(old_version)))
181 }
182 (
183 ObjectIn::Exist(((old_version, _), _)),
184 ObjectOut::PackageWrite((version, _)),
185 _,
186 ) => Some(((id, version), Some(old_version))),
187
188 _ => None,
189 }
190 })
191 }
192 }
193 .map(|((id, version), old_version)| {
195 let object = self
196 .output_objects
197 .iter()
198 .find(|o| &o.id() == id && &o.version() == version)
199 .expect("changed objects should show up in output objects");
200
201 let old_object = old_version.map(|old_version| {
202 self.input_objects
203 .iter()
204 .find(|o| &o.id() == id && &o.version() == old_version)
205 .expect("mutated objects should have a previous version in input objects")
206 });
207
208 (object, old_object)
209 })
210 }
211
212 pub fn created_objects(&self) -> impl Iterator<Item = &Object> {
213 match &self.effects {
215 TransactionEffects::V1(v1) => {
216 v1.changed_objects().iter().filter_map(|(id, change)| {
217 match (
218 &change.input_state,
219 &change.output_state,
220 &change.id_operation,
221 ) {
222 (ObjectIn::NotExist, ObjectOut::ObjectWrite(_), IDOperation::Created) => {
224 Some((id, &v1.lamport_version))
225 }
226 (
227 ObjectIn::NotExist,
228 ObjectOut::PackageWrite((version, _)),
229 IDOperation::Created,
230 ) => Some((id, version)),
231
232 _ => None,
233 }
234 })
235 }
236 }
237 .map(|(id, version)| {
239 self.output_objects
240 .iter()
241 .find(|o| &o.id() == id && &o.version() == version)
242 .expect("created objects should show up in output objects")
243 })
244 }
245}
246
247impl BackingPackageStore for CheckpointData {
248 fn get_package_object(
249 &self,
250 package_id: &crate::base_types::ObjectID,
251 ) -> crate::error::IotaResult<Option<crate::storage::PackageObject>> {
252 self.transactions
253 .iter()
254 .flat_map(|transaction| transaction.output_objects.iter())
255 .find(|object| object.is_package() && &object.id() == package_id)
256 .cloned()
257 .map(crate::storage::PackageObject::new)
258 .pipe(Ok)
259 }
260}