1use super::object_change::{AccumulatorWriteV1, ObjectIn, ObjectOut};
5use super::{EffectsObjectChange, IDOperation, ObjectChange};
6use crate::accumulator_event::AccumulatorEvent;
7use crate::accumulator_root::AccumulatorObjId;
8use crate::base_types::{
9 EpochId, ObjectDigest, ObjectID, ObjectRef, SequenceNumber, SuiAddress, TransactionDigest,
10 VersionDigest,
11};
12use crate::digests::{EffectsAuxDataDigest, TransactionEventsDigest};
13use crate::effects::{InputConsensusObject, TransactionEffectsAPI};
14use crate::execution::SharedInput;
15use crate::execution_status::{
16 ExecutionErrorKind, ExecutionFailure, ExecutionStatus, MoveLocation,
17};
18use crate::gas::GasCostSummary;
19#[cfg(debug_assertions)]
20use crate::is_system_package;
21use crate::object::{OBJECT_START_VERSION, Owner};
22use crate::transaction::SharedObjectMutability;
23use serde::{Deserialize, Serialize};
24#[cfg(debug_assertions)]
25use std::collections::HashSet;
26use std::collections::{BTreeMap, BTreeSet};
27
28#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)]
30pub struct TransactionEffectsV2 {
31 pub(crate) status: ExecutionStatus,
33 pub(crate) executed_epoch: EpochId,
35 pub(crate) gas_used: GasCostSummary,
36 pub(crate) transaction_digest: TransactionDigest,
38 pub(crate) gas_object_index: Option<u32>,
42 pub(crate) events_digest: Option<TransactionEventsDigest>,
45 pub(crate) dependencies: Vec<TransactionDigest>,
48
49 pub(crate) lamport_version: SequenceNumber,
51 pub(crate) changed_objects: Vec<(ObjectID, EffectsObjectChange)>,
59 pub(crate) unchanged_consensus_objects: Vec<(ObjectID, UnchangedConsensusKind)>,
64 pub(crate) aux_data_digest: Option<EffectsAuxDataDigest>,
68}
69
70impl TransactionEffectsAPI for TransactionEffectsV2 {
71 fn status(&self) -> &ExecutionStatus {
72 &self.status
73 }
74
75 fn into_status(self) -> ExecutionStatus {
76 self.status
77 }
78
79 fn executed_epoch(&self) -> EpochId {
80 self.executed_epoch
81 }
82
83 fn modified_at_versions(&self) -> Vec<(ObjectID, SequenceNumber)> {
84 self.changed_objects
85 .iter()
86 .filter_map(|(id, change)| {
87 if let ObjectIn::Exist(((version, _digest), _owner)) = &change.input_state {
88 Some((*id, *version))
89 } else {
90 None
91 }
92 })
93 .collect()
94 }
95
96 fn move_abort(&self) -> Option<(MoveLocation, u64)> {
97 let ExecutionStatus::Failure(ExecutionFailure {
98 error: ExecutionErrorKind::MoveAbort(move_location, code),
99 ..
100 }) = self.status()
101 else {
102 return None;
103 };
104 Some((move_location.clone(), *code))
105 }
106
107 fn lamport_version(&self) -> SequenceNumber {
108 self.lamport_version
109 }
110
111 fn old_object_metadata(&self) -> Vec<(ObjectRef, Owner)> {
112 self.changed_objects
113 .iter()
114 .filter_map(|(id, change)| {
115 if let ObjectIn::Exist(((version, digest), owner)) = &change.input_state {
116 Some(((*id, *version, *digest), owner.clone()))
117 } else {
118 None
119 }
120 })
121 .collect()
122 }
123
124 fn accessed_consensus_objects(&self) -> Vec<InputConsensusObject> {
125 self.changed_objects
126 .iter()
127 .filter_map(|(id, change)| match &change.input_state {
128 ObjectIn::Exist(((version, digest), owner)) if owner.is_consensus() => {
129 Some(InputConsensusObject::Mutate((*id, *version, *digest)))
130 }
131 _ => None,
132 })
133 .chain(
134 self.unchanged_consensus_objects
135 .iter()
136 .filter_map(|(id, change_kind)| match change_kind {
137 UnchangedConsensusKind::ReadOnlyRoot((version, digest)) => {
138 Some(InputConsensusObject::ReadOnly((*id, *version, *digest)))
139 }
140 UnchangedConsensusKind::MutateConsensusStreamEnded(seqno) => Some(
141 InputConsensusObject::MutateConsensusStreamEnded(*id, *seqno),
142 ),
143 UnchangedConsensusKind::ReadConsensusStreamEnded(seqno) => {
144 Some(InputConsensusObject::ReadConsensusStreamEnded(*id, *seqno))
145 }
146 UnchangedConsensusKind::Cancelled(seqno) => {
147 Some(InputConsensusObject::Cancelled(*id, *seqno))
148 }
149 UnchangedConsensusKind::PerEpochConfig => None,
153 }),
154 )
155 .collect()
156 }
157
158 fn created(&self) -> Vec<(ObjectRef, Owner)> {
159 self.changed_objects
160 .iter()
161 .filter_map(|(id, change)| {
162 match (
163 &change.input_state,
164 &change.output_state,
165 &change.id_operation,
166 ) {
167 (
168 ObjectIn::NotExist,
169 ObjectOut::ObjectWrite((digest, owner)),
170 IDOperation::Created,
171 ) => Some(((*id, self.lamport_version, *digest), owner.clone())),
172 (
173 ObjectIn::NotExist,
174 ObjectOut::PackageWrite((version, digest)),
175 IDOperation::Created,
176 ) => Some(((*id, *version, *digest), Owner::Immutable)),
177 _ => None,
178 }
179 })
180 .collect()
181 }
182
183 fn mutated(&self) -> Vec<(ObjectRef, Owner)> {
184 self.changed_objects
185 .iter()
186 .filter_map(
187 |(id, change)| match (&change.input_state, &change.output_state) {
188 (ObjectIn::Exist(_), ObjectOut::ObjectWrite((digest, owner))) => {
189 Some(((*id, self.lamport_version, *digest), owner.clone()))
190 }
191 (ObjectIn::Exist(_), ObjectOut::PackageWrite((version, digest))) => {
192 Some(((*id, *version, *digest), Owner::Immutable))
193 }
194 _ => None,
195 },
196 )
197 .collect()
198 }
199
200 fn unwrapped(&self) -> Vec<(ObjectRef, Owner)> {
201 self.changed_objects
202 .iter()
203 .filter_map(|(id, change)| {
204 match (
205 &change.input_state,
206 &change.output_state,
207 &change.id_operation,
208 ) {
209 (
210 ObjectIn::NotExist,
211 ObjectOut::ObjectWrite((digest, owner)),
212 IDOperation::None,
213 ) => Some(((*id, self.lamport_version, *digest), owner.clone())),
214 _ => None,
215 }
216 })
217 .collect()
218 }
219
220 fn deleted(&self) -> Vec<ObjectRef> {
221 self.changed_objects
222 .iter()
223 .filter_map(|(id, change)| {
224 match (
225 &change.input_state,
226 &change.output_state,
227 &change.id_operation,
228 ) {
229 (ObjectIn::Exist(_), ObjectOut::NotExist, IDOperation::Deleted) => Some((
230 *id,
231 self.lamport_version,
232 ObjectDigest::OBJECT_DIGEST_DELETED,
233 )),
234 _ => None,
235 }
236 })
237 .collect()
238 }
239
240 fn unwrapped_then_deleted(&self) -> Vec<ObjectRef> {
241 self.changed_objects
242 .iter()
243 .filter_map(|(id, change)| {
244 match (
245 &change.input_state,
246 &change.output_state,
247 &change.id_operation,
248 ) {
249 (ObjectIn::NotExist, ObjectOut::NotExist, IDOperation::Deleted) => Some((
250 *id,
251 self.lamport_version,
252 ObjectDigest::OBJECT_DIGEST_DELETED,
253 )),
254 _ => None,
255 }
256 })
257 .collect()
258 }
259
260 fn wrapped(&self) -> Vec<ObjectRef> {
261 self.changed_objects
262 .iter()
263 .filter_map(|(id, change)| {
264 match (
265 &change.input_state,
266 &change.output_state,
267 &change.id_operation,
268 ) {
269 (ObjectIn::Exist(_), ObjectOut::NotExist, IDOperation::None) => Some((
270 *id,
271 self.lamport_version,
272 ObjectDigest::OBJECT_DIGEST_WRAPPED,
273 )),
274 _ => None,
275 }
276 })
277 .collect()
278 }
279
280 fn written(&self) -> Vec<ObjectRef> {
281 self.changed_objects
282 .iter()
283 .filter_map(
284 |(id, change)| match (&change.output_state, &change.id_operation) {
285 (ObjectOut::NotExist, IDOperation::Deleted) => Some((
286 *id,
287 self.lamport_version,
288 ObjectDigest::OBJECT_DIGEST_DELETED,
289 )),
290 (ObjectOut::NotExist, IDOperation::None) => Some((
291 *id,
292 self.lamport_version,
293 ObjectDigest::OBJECT_DIGEST_WRAPPED,
294 )),
295 (ObjectOut::ObjectWrite((d, _)), _) => Some((*id, self.lamport_version, *d)),
296 (ObjectOut::PackageWrite(vd), _) => Some((*id, vd.0, vd.1)),
297 (ObjectOut::AccumulatorWriteV1(_), _) => None,
298 _ => None,
299 },
300 )
301 .collect()
302 }
303
304 fn transferred_from_consensus(&self) -> Vec<ObjectRef> {
305 self.changed_objects
306 .iter()
307 .filter_map(|(id, change)| {
308 match (
309 &change.input_state,
310 &change.output_state,
311 &change.id_operation,
312 ) {
313 (
314 ObjectIn::Exist((_, Owner::ConsensusAddressOwner { .. })),
315 ObjectOut::ObjectWrite((
316 object_digest,
317 Owner::AddressOwner(_) | Owner::ObjectOwner(_) | Owner::Immutable,
318 )),
319 IDOperation::None,
320 ) => Some((*id, self.lamport_version, *object_digest)),
321 _ => None,
322 }
323 })
324 .collect()
325 }
326
327 fn transferred_to_consensus(&self) -> Vec<ObjectRef> {
328 self.changed_objects
329 .iter()
330 .filter_map(|(id, change)| {
331 match (
332 &change.input_state,
333 &change.output_state,
334 &change.id_operation,
335 ) {
336 (
337 ObjectIn::Exist((_, Owner::AddressOwner(_) | Owner::ObjectOwner(_))),
338 ObjectOut::ObjectWrite((
339 object_digest,
340 Owner::ConsensusAddressOwner { .. },
341 )),
342 IDOperation::None,
343 ) => Some((*id, self.lamport_version, *object_digest)),
344 _ => None,
345 }
346 })
347 .collect()
348 }
349
350 fn consensus_owner_changed(&self) -> Vec<ObjectRef> {
351 self.changed_objects
352 .iter()
353 .filter_map(|(id, change)| {
354 match (
355 &change.input_state,
356 &change.output_state,
357 &change.id_operation,
358 ) {
359 (
360 ObjectIn::Exist((
361 _,
362 Owner::ConsensusAddressOwner {
363 owner: old_owner, ..
364 },
365 )),
366 ObjectOut::ObjectWrite((
367 object_digest,
368 Owner::ConsensusAddressOwner {
369 owner: new_owner, ..
370 },
371 )),
372 IDOperation::None,
373 ) if old_owner != new_owner => {
374 Some((*id, self.lamport_version, *object_digest))
375 }
376 _ => None,
377 }
378 })
379 .collect()
380 }
381
382 fn object_changes(&self) -> Vec<ObjectChange> {
383 self.changed_objects
384 .iter()
385 .filter_map(|(id, change)| {
386 let input_version_digest = match &change.input_state {
387 ObjectIn::NotExist => None,
388 ObjectIn::Exist((vd, _)) => Some(*vd),
389 };
390
391 let output_version_digest = match &change.output_state {
392 ObjectOut::NotExist => None,
393 ObjectOut::ObjectWrite((d, _)) => Some((self.lamport_version, *d)),
394 ObjectOut::PackageWrite(vd) => Some(*vd),
395 ObjectOut::AccumulatorWriteV1(_) => {
396 return None;
397 }
398 };
399
400 Some(ObjectChange {
401 id: *id,
402
403 input_version: input_version_digest.map(|k| k.0),
404 input_digest: input_version_digest.map(|k| k.1),
405
406 output_version: output_version_digest.map(|k| k.0),
407 output_digest: output_version_digest.map(|k| k.1),
408
409 id_operation: change.id_operation,
410 })
411 })
412 .collect()
413 }
414
415 fn published_packages(&self) -> Vec<ObjectID> {
416 self.changed_objects
417 .iter()
418 .filter_map(|(id, change)| {
419 if matches!(&change.output_state, ObjectOut::PackageWrite(_)) {
420 Some(*id)
421 } else {
422 None
423 }
424 })
425 .collect()
426 }
427
428 fn accumulator_events(&self) -> Vec<AccumulatorEvent> {
429 self.changed_objects
430 .iter()
431 .filter_map(|(id, change)| match &change.output_state {
432 ObjectOut::AccumulatorWriteV1(write) => Some(AccumulatorEvent::new(
433 AccumulatorObjId::new_unchecked(*id),
434 write.clone(),
435 )),
436 _ => None,
437 })
438 .collect()
439 }
440
441 fn gas_object(&self) -> Option<(ObjectRef, Owner)> {
442 self.gas_object_index.map(|index| {
443 let entry = &self.changed_objects[index as usize];
444 match &entry.1.output_state {
445 ObjectOut::ObjectWrite((digest, owner)) => {
446 ((entry.0, self.lamport_version, *digest), owner.clone())
447 }
448 ObjectOut::NotExist => {
449 (
452 (
453 entry.0,
454 self.lamport_version,
455 ObjectDigest::OBJECT_DIGEST_DELETED,
456 ),
457 Owner::AddressOwner(SuiAddress::default()),
458 )
459 }
460 _ => panic!("Gas object must be an ObjectWrite or Deleted in changed_objects"),
461 }
462 })
463 }
464
465 fn events_digest(&self) -> Option<&TransactionEventsDigest> {
466 self.events_digest.as_ref()
467 }
468
469 fn dependencies(&self) -> &[TransactionDigest] {
470 &self.dependencies
471 }
472
473 fn transaction_digest(&self) -> &TransactionDigest {
474 &self.transaction_digest
475 }
476
477 fn gas_cost_summary(&self) -> &GasCostSummary {
478 &self.gas_used
479 }
480
481 fn unchanged_consensus_objects(&self) -> Vec<(ObjectID, UnchangedConsensusKind)> {
482 self.unchanged_consensus_objects.clone()
483 }
484
485 fn accumulator_updates(&self) -> Vec<(ObjectID, AccumulatorWriteV1)> {
486 self.changed_objects
487 .iter()
488 .filter_map(|(id, change)| match &change.output_state {
489 ObjectOut::AccumulatorWriteV1(update) => Some((*id, update.clone())),
490 _ => None,
491 })
492 .collect()
493 }
494
495 fn status_mut_for_testing(&mut self) -> &mut ExecutionStatus {
496 &mut self.status
497 }
498
499 fn gas_cost_summary_mut_for_testing(&mut self) -> &mut GasCostSummary {
500 &mut self.gas_used
501 }
502
503 fn transaction_digest_mut_for_testing(&mut self) -> &mut TransactionDigest {
504 &mut self.transaction_digest
505 }
506
507 fn dependencies_mut_for_testing(&mut self) -> &mut Vec<TransactionDigest> {
508 &mut self.dependencies
509 }
510
511 fn unsafe_add_input_consensus_object_for_testing(&mut self, kind: InputConsensusObject) {
512 match kind {
513 InputConsensusObject::Mutate(obj_ref) => self.changed_objects.push((
514 obj_ref.0,
515 EffectsObjectChange {
516 input_state: ObjectIn::Exist((
517 (obj_ref.1, obj_ref.2),
518 Owner::Shared {
519 initial_shared_version: OBJECT_START_VERSION,
520 },
521 )),
522 output_state: ObjectOut::ObjectWrite((
523 obj_ref.2,
524 Owner::Shared {
525 initial_shared_version: obj_ref.1,
526 },
527 )),
528 id_operation: IDOperation::None,
529 },
530 )),
531 InputConsensusObject::ReadOnly(obj_ref) => self.unchanged_consensus_objects.push((
532 obj_ref.0,
533 UnchangedConsensusKind::ReadOnlyRoot((obj_ref.1, obj_ref.2)),
534 )),
535 InputConsensusObject::ReadConsensusStreamEnded(obj_id, seqno) => {
536 self.unchanged_consensus_objects.push((
537 obj_id,
538 UnchangedConsensusKind::ReadConsensusStreamEnded(seqno),
539 ))
540 }
541 InputConsensusObject::MutateConsensusStreamEnded(obj_id, seqno) => {
542 self.unchanged_consensus_objects.push((
543 obj_id,
544 UnchangedConsensusKind::MutateConsensusStreamEnded(seqno),
545 ))
546 }
547 InputConsensusObject::Cancelled(obj_id, seqno) => self
548 .unchanged_consensus_objects
549 .push((obj_id, UnchangedConsensusKind::Cancelled(seqno))),
550 }
551 }
552
553 fn unsafe_add_deleted_live_object_for_testing(&mut self, obj_ref: ObjectRef) {
554 self.changed_objects.push((
555 obj_ref.0,
556 EffectsObjectChange {
557 input_state: ObjectIn::Exist((
558 (obj_ref.1, obj_ref.2),
559 Owner::AddressOwner(SuiAddress::default()),
560 )),
561 output_state: ObjectOut::ObjectWrite((
562 obj_ref.2,
563 Owner::AddressOwner(SuiAddress::default()),
564 )),
565 id_operation: IDOperation::None,
566 },
567 ))
568 }
569
570 fn unsafe_add_object_tombstone_for_testing(&mut self, obj_ref: ObjectRef) {
571 self.changed_objects.push((
572 obj_ref.0,
573 EffectsObjectChange {
574 input_state: ObjectIn::Exist((
575 (obj_ref.1, obj_ref.2),
576 Owner::AddressOwner(SuiAddress::default()),
577 )),
578 output_state: ObjectOut::NotExist,
579 id_operation: IDOperation::Deleted,
580 },
581 ))
582 }
583}
584
585impl TransactionEffectsV2 {
586 pub fn compute_unchanged_consensus_objects(
590 shared_objects: Vec<SharedInput>,
591 loaded_per_epoch_config_objects: BTreeSet<ObjectID>,
592 changed_objects: &BTreeMap<ObjectID, EffectsObjectChange>,
593 loaded_system_objects: BTreeMap<ObjectID, VersionDigest>,
594 ) -> Vec<(ObjectID, UnchangedConsensusKind)> {
595 let mut unchanged_consensus_objects: Vec<_> = shared_objects
596 .into_iter()
597 .filter_map(|shared_input| match shared_input {
598 SharedInput::Existing((id, version, digest)) => {
599 if changed_objects.contains_key(&id) {
600 None
601 } else {
602 Some((id, UnchangedConsensusKind::ReadOnlyRoot((version, digest))))
603 }
604 }
605 SharedInput::ConsensusStreamEnded((id, version, mutability, _)) => {
606 debug_assert!(!changed_objects.contains_key(&id));
607 match mutability {
608 SharedObjectMutability::Mutable => Some((
609 id,
610 UnchangedConsensusKind::MutateConsensusStreamEnded(version),
611 )),
612 SharedObjectMutability::Immutable => Some((
613 id,
614 UnchangedConsensusKind::ReadConsensusStreamEnded(version),
615 )),
616 SharedObjectMutability::NonExclusiveWrite => Some((
619 id,
620 UnchangedConsensusKind::MutateConsensusStreamEnded(version),
621 )),
622 }
623 }
624 SharedInput::Cancelled((id, version)) => {
625 debug_assert!(!changed_objects.contains_key(&id));
626 Some((id, UnchangedConsensusKind::Cancelled(version)))
627 }
628 })
629 .chain(
630 loaded_per_epoch_config_objects
631 .into_iter()
632 .map(|id| (id, UnchangedConsensusKind::PerEpochConfig)),
633 )
634 .collect();
635
636 let already_recorded: BTreeMap<ObjectID, Option<VersionDigest>> = changed_objects
644 .iter()
645 .map(|(id, change)| {
646 let recorded = match &change.input_state {
647 ObjectIn::Exist((version_digest, _)) => Some(*version_digest),
648 _ => None,
649 };
650 (*id, recorded)
651 })
652 .chain(unchanged_consensus_objects.iter().map(|(id, kind)| {
653 let recorded = match kind {
654 UnchangedConsensusKind::ReadOnlyRoot(version_digest) => Some(*version_digest),
655 _ => None,
656 };
657 (*id, recorded)
658 }))
659 .collect();
660 for (id, version_digest) in loaded_system_objects {
661 match already_recorded.get(&id) {
662 None => {
663 unchanged_consensus_objects
664 .push((id, UnchangedConsensusKind::ReadOnlyRoot(version_digest)));
665 }
666 Some(recorded) => {
667 if *recorded != Some(version_digest) {
673 mysten_common::debug_fatal!(
674 "system object {id} read at version {version_digest:?} but its \
675 effects entry records {recorded:?}"
676 );
677 }
678 }
679 }
680 }
681 unchanged_consensus_objects
682 }
683
684 pub fn new(
685 status: ExecutionStatus,
686 executed_epoch: EpochId,
687 gas_used: GasCostSummary,
688 unchanged_consensus_objects: Vec<(ObjectID, UnchangedConsensusKind)>,
689 transaction_digest: TransactionDigest,
690 lamport_version: SequenceNumber,
691 changed_objects: BTreeMap<ObjectID, EffectsObjectChange>,
692 gas_object: Option<ObjectID>,
693 events_digest: Option<TransactionEventsDigest>,
694 dependencies: Vec<TransactionDigest>,
695 ) -> Self {
696 let changed_objects: Vec<_> = changed_objects.into_iter().collect();
697
698 let gas_object_index = gas_object.map(|gas_id| {
699 changed_objects
700 .iter()
701 .position(|(id, _)| id == &gas_id)
702 .unwrap() as u32
703 });
704
705 let result = Self {
706 status,
707 executed_epoch,
708 gas_used,
709 transaction_digest,
710 lamport_version,
711 changed_objects,
712 unchanged_consensus_objects,
713 gas_object_index,
714 events_digest,
715 dependencies,
716 aux_data_digest: None,
717 };
718 #[cfg(debug_assertions)]
719 result.check_invariant();
720
721 result
722 }
723
724 #[cfg(debug_assertions)]
727 fn check_invariant(&self) {
728 let mut unique_ids = HashSet::new();
729 for (id, change) in &self.changed_objects {
730 assert!(unique_ids.insert(*id));
731 match (
732 &change.input_state,
733 &change.output_state,
734 &change.id_operation,
735 ) {
736 (ObjectIn::NotExist, ObjectOut::NotExist, IDOperation::Created) => {
737 }
739 (ObjectIn::NotExist, ObjectOut::NotExist, IDOperation::Deleted) => {
740 }
742 (ObjectIn::NotExist, ObjectOut::ObjectWrite((_, owner)), IDOperation::None) => {
743 assert!(!owner.is_shared());
746 }
747 (ObjectIn::NotExist, ObjectOut::ObjectWrite(..), IDOperation::Created) => {
748 }
750 (ObjectIn::NotExist, ObjectOut::PackageWrite(_), IDOperation::Created) => {
751 }
753 (
754 ObjectIn::Exist(((old_version, _), old_owner)),
755 ObjectOut::NotExist,
756 IDOperation::None,
757 ) => {
758 assert!(old_version.value() < self.lamport_version.value());
760 assert!(
761 !old_owner.is_shared() && !old_owner.is_immutable(),
762 "Cannot wrap shared or immutable object"
763 );
764 }
765 (
766 ObjectIn::Exist(((old_version, _), old_owner)),
767 ObjectOut::NotExist,
768 IDOperation::Deleted,
769 ) => {
770 assert!(old_version.value() < self.lamport_version.value());
772 assert!(!old_owner.is_immutable(), "Cannot delete immutable object");
773 }
774 (
775 ObjectIn::Exist(((old_version, old_digest), old_owner)),
776 ObjectOut::ObjectWrite((new_digest, new_owner)),
777 IDOperation::None,
778 ) => {
779 assert!(old_version.value() < self.lamport_version.value());
781 assert_ne!(old_digest, new_digest);
782 assert!(!old_owner.is_immutable(), "Cannot mutate immutable object");
783 if old_owner.is_shared() {
784 assert!(new_owner.is_shared(), "Cannot un-share an object");
785 } else {
786 assert!(!new_owner.is_shared(), "Cannot share an existing object");
787 }
788 }
789 (
790 ObjectIn::Exist(((old_version, old_digest), old_owner)),
791 ObjectOut::PackageWrite((new_version, new_digest)),
792 IDOperation::None,
793 ) => {
794 assert!(
796 old_owner.is_immutable() && is_system_package(*id),
797 "Must be a system package"
798 );
799 assert_eq!(old_version.value() + 1, new_version.value());
800 assert_ne!(old_digest, new_digest);
801 }
802 (ObjectIn::NotExist, ObjectOut::AccumulatorWriteV1(_), IDOperation::None) => {
803 }
805 _ => {
806 panic!("Impossible object change: {:?}, {:?}", id, change);
807 }
808 }
809 }
810 if let Some((_, owner)) = self.gas_object() {
812 assert!(matches!(owner, Owner::AddressOwner(_)));
813 }
814
815 for (id, _) in &self.unchanged_consensus_objects {
816 assert!(
817 unique_ids.insert(*id),
818 "Duplicate object id: {:?}\n{:#?}",
819 id,
820 self
821 );
822 }
823 }
824}
825
826impl Default for TransactionEffectsV2 {
827 fn default() -> Self {
828 Self {
829 status: ExecutionStatus::Success,
830 executed_epoch: 0,
831 gas_used: GasCostSummary::default(),
832 transaction_digest: TransactionDigest::default(),
833 lamport_version: SequenceNumber::default(),
834 changed_objects: vec![],
835 unchanged_consensus_objects: vec![],
836 gas_object_index: None,
837 events_digest: None,
838 dependencies: vec![],
839 aux_data_digest: None,
840 }
841 }
842}
843
844#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)]
845pub enum UnchangedConsensusKind {
846 ReadOnlyRoot(VersionDigest),
849 MutateConsensusStreamEnded(SequenceNumber),
851 ReadConsensusStreamEnded(SequenceNumber),
853 Cancelled(SequenceNumber),
855 PerEpochConfig,
857}