1use crate::accumulator_event::AccumulatorEvent;
5use crate::base_types::{
6 EpochId, ObjectID, ObjectRef, SequenceNumber, SuiAddress, TransactionDigest, random_object_ref,
7};
8use crate::digests::{ObjectDigest, TransactionEventsDigest};
9use crate::effects::{InputConsensusObject, TransactionEffectsAPI, UnchangedConsensusKind};
10use crate::execution_status::{
11 ExecutionErrorKind, ExecutionFailure, ExecutionStatus, MoveLocation,
12};
13use crate::gas::GasCostSummary;
14use crate::object::Owner;
15use serde::{Deserialize, Serialize};
16use std::collections::{BTreeMap, HashSet};
17use std::fmt::{Display, Formatter, Write};
18
19use super::object_change::AccumulatorWriteV1;
20use super::{IDOperation, ObjectChange};
21
22#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)]
24pub struct TransactionEffectsV1 {
25 status: ExecutionStatus,
27 executed_epoch: EpochId,
29 gas_used: GasCostSummary,
30 modified_at_versions: Vec<(ObjectID, SequenceNumber)>,
33 shared_objects: Vec<ObjectRef>,
35 transaction_digest: TransactionDigest,
37
38 created: Vec<(ObjectRef, Owner)>,
42 mutated: Vec<(ObjectRef, Owner)>,
44 unwrapped: Vec<(ObjectRef, Owner)>,
48 deleted: Vec<ObjectRef>,
50 unwrapped_then_deleted: Vec<ObjectRef>,
52 wrapped: Vec<ObjectRef>,
54 gas_object: (ObjectRef, Owner),
57 events_digest: Option<TransactionEventsDigest>,
60 dependencies: Vec<TransactionDigest>,
62}
63
64impl TransactionEffectsV1 {
65 pub fn new(
66 status: ExecutionStatus,
67 executed_epoch: EpochId,
68 gas_used: GasCostSummary,
69 modified_at_versions: Vec<(ObjectID, SequenceNumber)>,
70 shared_objects: Vec<ObjectRef>,
71 transaction_digest: TransactionDigest,
72 created: Vec<(ObjectRef, Owner)>,
73 mutated: Vec<(ObjectRef, Owner)>,
74 unwrapped: Vec<(ObjectRef, Owner)>,
75 deleted: Vec<ObjectRef>,
76 unwrapped_then_deleted: Vec<ObjectRef>,
77 wrapped: Vec<ObjectRef>,
78 gas_object: (ObjectRef, Owner),
79 events_digest: Option<TransactionEventsDigest>,
80 dependencies: Vec<TransactionDigest>,
81 ) -> Self {
82 Self {
83 status,
84 executed_epoch,
85 gas_used,
86 modified_at_versions,
87 shared_objects,
88 transaction_digest,
89 created,
90 mutated,
91 unwrapped,
92 deleted,
93 unwrapped_then_deleted,
94 wrapped,
95 gas_object,
96 events_digest,
97 dependencies,
98 }
99 }
100
101 pub fn modified_at_versions(&self) -> &[(ObjectID, SequenceNumber)] {
102 &self.modified_at_versions
103 }
104
105 pub fn mutated(&self) -> &[(ObjectRef, Owner)] {
106 &self.mutated
107 }
108
109 pub fn created(&self) -> &[(ObjectRef, Owner)] {
110 &self.created
111 }
112
113 pub fn unwrapped(&self) -> &[(ObjectRef, Owner)] {
114 &self.unwrapped
115 }
116
117 pub fn deleted(&self) -> &[ObjectRef] {
118 &self.deleted
119 }
120
121 pub fn wrapped(&self) -> &[ObjectRef] {
122 &self.wrapped
123 }
124
125 pub fn shared_objects(&self) -> &[ObjectRef] {
126 &self.shared_objects
127 }
128}
129
130impl TransactionEffectsAPI for TransactionEffectsV1 {
131 fn status(&self) -> &ExecutionStatus {
132 &self.status
133 }
134
135 fn into_status(self) -> ExecutionStatus {
136 self.status
137 }
138
139 fn executed_epoch(&self) -> EpochId {
140 self.executed_epoch
141 }
142
143 fn modified_at_versions(&self) -> Vec<(ObjectID, SequenceNumber)> {
144 self.modified_at_versions
145 .iter()
146 .filter(|(object_id, _)| {
150 !self
151 .unwrapped_then_deleted
152 .iter()
153 .any(|(deleted_id, _, _)| deleted_id == object_id)
154 })
155 .cloned()
156 .collect()
157 }
158
159 fn move_abort(&self) -> Option<(MoveLocation, u64)> {
160 let ExecutionStatus::Failure(ExecutionFailure {
161 error: ExecutionErrorKind::MoveAbort(move_location, code),
162 ..
163 }) = self.status()
164 else {
165 return None;
166 };
167 Some((move_location.clone(), *code))
168 }
169
170 fn lamport_version(&self) -> SequenceNumber {
171 SequenceNumber::lamport_increment(self.modified_at_versions.iter().map(|(_, v)| *v))
172 }
173
174 fn old_object_metadata(&self) -> Vec<(ObjectRef, Owner)> {
175 unimplemented!("Only supposed by v2 and above");
176 }
177
178 fn input_consensus_objects(&self) -> Vec<InputConsensusObject> {
179 let modified: HashSet<_> = self.modified_at_versions.iter().map(|(r, _)| r).collect();
180 self.shared_objects
181 .iter()
182 .map(|r| {
183 if modified.contains(&r.0) {
184 InputConsensusObject::Mutate(*r)
185 } else {
186 InputConsensusObject::ReadOnly(*r)
187 }
188 })
189 .collect()
190 }
191
192 fn created(&self) -> Vec<(ObjectRef, Owner)> {
193 self.created.clone()
194 }
195
196 fn mutated(&self) -> Vec<(ObjectRef, Owner)> {
197 self.mutated.clone()
198 }
199
200 fn unwrapped(&self) -> Vec<(ObjectRef, Owner)> {
201 self.unwrapped.clone()
202 }
203
204 fn deleted(&self) -> Vec<ObjectRef> {
205 self.deleted.clone()
206 }
207
208 fn unwrapped_then_deleted(&self) -> Vec<ObjectRef> {
209 self.unwrapped_then_deleted.clone()
210 }
211
212 fn wrapped(&self) -> Vec<ObjectRef> {
213 self.wrapped.clone()
214 }
215
216 fn transferred_from_consensus(&self) -> Vec<ObjectRef> {
217 vec![]
219 }
220
221 fn transferred_to_consensus(&self) -> Vec<ObjectRef> {
222 vec![]
224 }
225
226 fn consensus_owner_changed(&self) -> Vec<ObjectRef> {
227 vec![]
229 }
230
231 fn object_changes(&self) -> Vec<ObjectChange> {
232 let modified_at: BTreeMap<_, _> = self.modified_at_versions.iter().copied().collect();
233
234 let created = self.created.iter().map(|((id, v, d), _)| ObjectChange {
235 id: *id,
236 input_version: None,
237 input_digest: None,
238 output_version: Some(*v),
239 output_digest: Some(*d),
240 id_operation: IDOperation::Created,
241 });
242
243 let mutated = self.mutated.iter().map(|((id, v, d), _)| ObjectChange {
244 id: *id,
245 input_version: modified_at.get(id).copied(),
246 input_digest: None,
247 output_version: Some(*v),
248 output_digest: Some(*d),
249 id_operation: IDOperation::None,
250 });
251
252 let unwrapped = self.unwrapped.iter().map(|((id, v, d), _)| ObjectChange {
253 id: *id,
254 input_version: None,
255 input_digest: None,
256 output_version: Some(*v),
257 output_digest: Some(*d),
258 id_operation: IDOperation::None,
259 });
260
261 let deleted = self.deleted.iter().map(|(id, _, _)| ObjectChange {
262 id: *id,
263 input_version: modified_at.get(id).copied(),
264 input_digest: None,
265 output_version: None,
266 output_digest: None,
267 id_operation: IDOperation::Deleted,
268 });
269
270 let unwrapped_then_deleted =
271 self.unwrapped_then_deleted
272 .iter()
273 .map(|(id, _, _)| ObjectChange {
274 id: *id,
275 input_version: None,
276 input_digest: None,
277 output_version: None,
278 output_digest: None,
279 id_operation: IDOperation::Deleted,
280 });
281
282 let wrapped = self.wrapped.iter().map(|(id, _, _)| ObjectChange {
283 id: *id,
284 input_version: modified_at.get(id).copied(),
285 input_digest: None,
286 output_version: None,
287 output_digest: None,
288 id_operation: IDOperation::None,
289 });
290
291 created
292 .chain(mutated)
293 .chain(unwrapped)
294 .chain(deleted)
295 .chain(unwrapped_then_deleted)
296 .chain(wrapped)
297 .collect()
298 }
299
300 fn published_packages(&self) -> Vec<ObjectID> {
301 unimplemented!("not supported in v1");
302 }
303
304 fn written(&self) -> Vec<ObjectRef> {
305 unimplemented!("TransactionEffectsV1::written() never called in V1");
306 }
307
308 fn accumulator_events(&self) -> Vec<AccumulatorEvent> {
309 vec![]
311 }
312
313 fn gas_object(&self) -> (ObjectRef, Owner) {
314 self.gas_object.clone()
315 }
316 fn events_digest(&self) -> Option<&TransactionEventsDigest> {
317 self.events_digest.as_ref()
318 }
319
320 fn dependencies(&self) -> &[TransactionDigest] {
321 &self.dependencies
322 }
323
324 fn transaction_digest(&self) -> &TransactionDigest {
325 &self.transaction_digest
326 }
327
328 fn gas_cost_summary(&self) -> &GasCostSummary {
329 &self.gas_used
330 }
331
332 fn unchanged_consensus_objects(&self) -> Vec<(ObjectID, UnchangedConsensusKind)> {
333 self.input_consensus_objects()
334 .iter()
335 .filter_map(|o| match o {
336 InputConsensusObject::ReadOnly(oref) => Some((
338 oref.0,
339 UnchangedConsensusKind::ReadOnlyRoot((oref.1, oref.2)),
340 )),
341 _ => None,
342 })
343 .collect()
344 }
345
346 fn accumulator_updates(&self) -> Vec<(ObjectID, AccumulatorWriteV1)> {
347 vec![]
348 }
349
350 fn status_mut_for_testing(&mut self) -> &mut ExecutionStatus {
351 &mut self.status
352 }
353
354 fn gas_cost_summary_mut_for_testing(&mut self) -> &mut GasCostSummary {
355 &mut self.gas_used
356 }
357
358 fn transaction_digest_mut_for_testing(&mut self) -> &mut TransactionDigest {
359 &mut self.transaction_digest
360 }
361
362 fn dependencies_mut_for_testing(&mut self) -> &mut Vec<TransactionDigest> {
363 &mut self.dependencies
364 }
365
366 fn unsafe_add_input_consensus_object_for_testing(&mut self, kind: InputConsensusObject) {
367 match kind {
368 InputConsensusObject::Mutate(obj_ref) => {
369 self.shared_objects.push(obj_ref);
370 self.modified_at_versions.push((obj_ref.0, obj_ref.1));
371 }
372 InputConsensusObject::ReadOnly(obj_ref) => {
373 self.shared_objects.push(obj_ref);
374 }
375 InputConsensusObject::ReadConsensusStreamEnded(id, version)
376 | InputConsensusObject::MutateConsensusStreamEnded(id, version) => {
377 self.shared_objects
378 .push((id, version, ObjectDigest::OBJECT_DIGEST_DELETED));
379 }
380 InputConsensusObject::Cancelled(..) => {
381 panic!("Transaction cancellation is not supported in effect v1");
382 }
383 }
384 }
385
386 fn unsafe_add_deleted_live_object_for_testing(&mut self, object: ObjectRef) {
387 self.modified_at_versions.push((object.0, object.1));
388 }
389
390 fn unsafe_add_object_tombstone_for_testing(&mut self, object: ObjectRef) {
391 self.deleted.push(object);
392 }
393}
394
395impl Display for TransactionEffectsV1 {
396 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
397 let mut writer = String::new();
398 writeln!(writer, "Status : {:?}", self.status)?;
399 if !self.created.is_empty() {
400 writeln!(writer, "Created Objects:")?;
401 for ((id, _, _), owner) in &self.created {
402 writeln!(writer, " - ID: {} , Owner: {}", id, owner)?;
403 }
404 }
405 if !self.mutated.is_empty() {
406 writeln!(writer, "Mutated Objects:")?;
407 for ((id, _, _), owner) in &self.mutated {
408 writeln!(writer, " - ID: {} , Owner: {}", id, owner)?;
409 }
410 }
411 if !self.deleted.is_empty() {
412 writeln!(writer, "Deleted Objects:")?;
413 for (id, _, _) in &self.deleted {
414 writeln!(writer, " - ID: {}", id)?;
415 }
416 }
417 if !self.wrapped.is_empty() {
418 writeln!(writer, "Wrapped Objects:")?;
419 for (id, _, _) in &self.wrapped {
420 writeln!(writer, " - ID: {}", id)?;
421 }
422 }
423 if !self.unwrapped.is_empty() {
424 writeln!(writer, "Unwrapped Objects:")?;
425 for ((id, _, _), owner) in &self.unwrapped {
426 writeln!(writer, " - ID: {} , Owner: {}", id, owner)?;
427 }
428 }
429 write!(f, "{}", writer)
430 }
431}
432
433impl Default for TransactionEffectsV1 {
434 fn default() -> Self {
435 TransactionEffectsV1 {
436 status: ExecutionStatus::Success,
437 executed_epoch: 0,
438 gas_used: GasCostSummary {
439 computation_cost: 0,
440 storage_cost: 0,
441 storage_rebate: 0,
442 non_refundable_storage_fee: 0,
443 },
444 modified_at_versions: Vec::new(),
445 shared_objects: Vec::new(),
446 transaction_digest: TransactionDigest::random(),
447 created: Vec::new(),
448 mutated: Vec::new(),
449 unwrapped: Vec::new(),
450 deleted: Vec::new(),
451 unwrapped_then_deleted: Vec::new(),
452 wrapped: Vec::new(),
453 gas_object: (
454 random_object_ref(),
455 Owner::AddressOwner(SuiAddress::default()),
456 ),
457 events_digest: None,
458 dependencies: Vec::new(),
459 }
460 }
461}