Skip to main content

iota_types/effects/
mod.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use std::collections::{BTreeMap, BTreeSet};
6
7use iota_sdk_types::{
8    Address, EpochId, ExecutionStatus, GasCostSummary, InputSharedObject, IntentScope,
9    ObjectChange, ObjectDigest, ObjectId, ObjectReference, ObjectRemoveKind, ObjectVersion,
10    OwnedObjectReference, Owner, TransactionDigest, TransactionEffectsDigest,
11    TransactionEventsDigest, UnchangedSharedKind, UnchangedSharedObject, Version, WriteKind,
12    crypto::Intent,
13    effects::{
14        ChangedObject, IdOperation, ObjectIn, ObjectOut, TransactionEffects, TransactionEffectsV1,
15    },
16};
17pub use test_effects_builder::TestEffectsBuilder;
18use tracing::instrument;
19
20use crate::{
21    base_types::ExecutionDigests,
22    committee::Committee,
23    crypto::{
24        AuthoritySignInfo, AuthoritySignInfoTrait, AuthorityStrongQuorumSignInfo, EmptySignInfo,
25        default_hash,
26    },
27    error::IotaResult,
28    execution::SharedInput,
29    message_envelope::{Envelope, Message, TrustedEnvelope, VerifiedEnvelope},
30    object::OBJECT_START_VERSION,
31};
32
33mod test_effects_builder;
34mod v1;
35
36// Since `std::mem::size_of` may not be stable across platforms, we use rough
37// constants We need these for estimating effects sizes
38// Approximate size of `ObjectReference` type in bytes
39pub const APPROX_SIZE_OF_OBJECT_REF: usize = 80;
40// Approximate size of `ExecutionStatus` type in bytes
41pub const APPROX_SIZE_OF_EXECUTION_STATUS: usize = 144;
42// Approximate size of `EpochId` type in bytes
43pub const APPROX_SIZE_OF_EPOCH_ID: usize = 10;
44// Approximate size of `GasCostSummary` type in bytes
45pub const APPROX_SIZE_OF_GAS_COST_SUMMARY: usize = 50;
46// Approximate size of `Option<TransactionEventsDigest>` type in bytes
47pub const APPROX_SIZE_OF_OPT_TX_EVENTS_DIGEST: usize = 40;
48// Approximate size of `TransactionDigest` type in bytes
49pub const APPROX_SIZE_OF_TX_DIGEST: usize = 40;
50// Approximate size of `Owner` type in bytes
51pub const APPROX_SIZE_OF_OWNER: usize = 48;
52
53impl Message for TransactionEffects {
54    type DigestType = TransactionEffectsDigest;
55    const SCOPE: IntentScope = IntentScope::TransactionEffects;
56
57    fn digest(&self) -> Self::DigestType {
58        TransactionEffectsDigest::new(default_hash(self))
59    }
60}
61
62mod transaction_effects_api {
63    pub trait Sealed {}
64    impl Sealed for super::TransactionEffects {}
65    impl Sealed for super::TransactionEffectsV1 {}
66}
67
68/// Version-agnostic accessors for [`TransactionEffects`].
69///
70/// Sealed; implemented for the enum and each version struct. The enum impl
71/// dispatches to the active variant.
72pub trait TransactionEffectsAPI: transaction_effects_api::Sealed {
73    /// Return the status of the transaction.
74    fn status(&self) -> &ExecutionStatus;
75
76    /// Consume `self` and return the owned status of the transaction.
77    fn into_status(self) -> ExecutionStatus;
78
79    /// Return the epoch in which this transaction was executed.
80    fn epoch(&self) -> EpochId;
81
82    /// Return the `(ObjectId, Version)` pair, at their pre-execution version,
83    /// of every object that existed in the store before this transaction
84    /// and was modified by it (mutated, wrapped, or deleted).
85    fn modified_at_versions(&self) -> Vec<ObjectVersion>;
86
87    /// The version assigned to all output objects (apart from packages).
88    fn lamport_version(&self) -> Version;
89
90    /// Metadata of objects prior to modification. This includes any object that
91    /// exists in the store prior to this transaction and is modified in
92    /// this transaction. It includes objects that are mutated, wrapped and
93    /// deleted.
94    fn old_object_metadata(&self) -> Vec<OwnedObjectReference>;
95
96    /// Returns the list of sequenced shared objects used in the input.
97    /// This is needed in effects because in transaction we only have object ID
98    /// for shared objects. Their version and digest can only be figured out
99    /// after sequencing. Also provides the use kind to indicate whether the
100    /// object was mutated or read-only. It does not include per epoch
101    /// config objects since they do not require sequencing. TODO: Rename
102    /// this function to indicate sequencing requirement.
103    fn input_shared_objects(&self) -> Vec<InputSharedObject>;
104
105    /// Objects (Move objects and packages) newly created by this transaction,
106    /// paired with their owner. Excludes objects that were created and then
107    /// wrapped within the same transaction.
108    fn created(&self) -> Vec<OwnedObjectReference>;
109
110    /// Objects that existed before this transaction and whose contents were
111    /// updated by it (in-place mutations and system package upgrades),
112    /// reported at their post-execution `(ObjectReference, Owner)`.
113    fn mutated(&self) -> Vec<OwnedObjectReference>;
114
115    /// Objects that were wrapped inside another object before this transaction
116    /// and have been promoted back to top-level objects in the store by it.
117    fn unwrapped(&self) -> Vec<OwnedObjectReference>;
118
119    /// Objects that existed before this transaction and were deleted by it.
120    /// References use the post-execution version and the
121    /// [`ObjectDigest::OBJECT_DELETED`] tombstone digest.
122    fn deleted(&self) -> Vec<ObjectReference>;
123
124    /// Objects that were unwrapped and then deleted within this same
125    /// transaction (i.e. did not exist as top-level objects either before
126    /// or after). References use the post-execution version and the
127    /// [`ObjectDigest::OBJECT_DELETED`] tombstone digest.
128    fn unwrapped_then_deleted(&self) -> Vec<ObjectReference>;
129
130    /// Objects that existed as top-level objects before this transaction and
131    /// have been wrapped inside another object by it (i.e. no longer visible
132    /// in the object store as top-level). References use the post-execution
133    /// version and the [`ObjectDigest::OBJECT_WRAPPED`] tombstone
134    /// digest.
135    fn wrapped(&self) -> Vec<ObjectReference>;
136
137    /// Returns a flattened view of every object change recorded in these
138    /// effects: for each touched object, the input and output version/digest
139    /// (when present) together with the [`IdOperation`] describing whether
140    /// the ID was created, deleted, or unchanged.
141    fn object_changes(&self) -> Vec<ObjectChange>;
142
143    /// Returns the post-execution reference and owner of the gas object.
144    // TODO: We should consider having this function to return Option.
145    // When the gas object is not available (i.e. system transaction), we currently
146    // return dummy object ref and owner. This is not ideal.
147    fn gas_object(&self) -> OwnedObjectReference;
148
149    /// Digest of the events emitted by this transaction, or `None` if it
150    /// emitted no events.
151    fn events_digest(&self) -> Option<&TransactionEventsDigest>;
152
153    /// Digests of the transactions this one depends on, i.e. transactions
154    /// that must be executed before this one for its inputs to be available.
155    fn dependencies(&self) -> &[TransactionDigest];
156
157    /// Digest of the transaction that produced these effects.
158    fn transaction_digest(&self) -> &TransactionDigest;
159
160    /// Return the gas cost summary of the transaction.
161    fn gas_cost_summary(&self) -> &GasCostSummary;
162
163    /// IDs of shared objects that were declared as mutable inputs by the
164    /// transaction but had already been deleted at the time of execution.
165    fn deleted_mutably_accessed_shared_objects(&self) -> Vec<ObjectId> {
166        self.input_shared_objects()
167            .into_iter()
168            .filter_map(|kind| match kind {
169                InputSharedObject::MutateDeleted(object) => Some(object.object_id),
170                InputSharedObject::Mutate(..)
171                | InputSharedObject::ReadOnly(..)
172                | InputSharedObject::ReadDeleted(..)
173                | InputSharedObject::Canceled(..) => None,
174            })
175            .collect()
176    }
177
178    /// Returns all root shared objects (i.e. not child object) that are
179    /// read-only in the transaction.
180    fn unchanged_shared_objects(&self) -> Vec<(ObjectId, UnchangedSharedKind)>;
181}
182
183/// Test-only mutators for [`TransactionEffects`] that bypass the normal
184/// invariants. Not for production use.
185pub trait TransactionEffectsAPIForTesting: TransactionEffectsAPI {
186    // All of these should be #[cfg(test)], but they are used by tests in other
187    // crates.
188
189    /// Returns a mutable reference to the gas cost summary, for tests.
190    fn gas_cost_summary_mut_for_testing(&mut self) -> &mut GasCostSummary;
191
192    /// Returns a mutable reference to the dependency list, for tests.
193    fn dependencies_mut_for_testing(&mut self) -> &mut Vec<TransactionDigest>;
194
195    /// Records a shared object these effects read without changing, for tests.
196    /// Unsafe: makes no attempt to keep the effects self-consistent.
197    fn unsafe_add_read_only_shared_object_for_testing(&mut self, object: ObjectReference);
198
199    /// Records a shared object these effects took mutably, for tests. Unsafe:
200    /// makes no attempt to keep the effects self-consistent.
201    fn unsafe_add_mutated_shared_object_for_testing(&mut self, object: ObjectReference);
202
203    /// Records an entry that represents the pre-execution version of a still
204    /// live object, without validating consistency with the rest of the
205    /// effects. For tests only.
206    fn unsafe_add_deleted_live_object_for_testing(&mut self, object_ref: ObjectReference);
207
208    /// Records a tombstone entry for a deleted object, without validating
209    /// consistency with the rest of the effects. For tests only.
210    fn unsafe_add_object_tombstone_for_testing(&mut self, object_ref: ObjectReference);
211}
212
213mod transaction_effects_ext {
214    pub trait Sealed {}
215    impl Sealed for super::TransactionEffects {}
216}
217
218/// The version-selecting constructor and aggregating queries for the
219/// [`TransactionEffects`] enum. Sealed; implemented only for the enum.
220pub trait TransactionEffectsExt: transaction_effects_ext::Sealed {
221    /// Build effects from the results of executing a transaction under the
222    /// V1 protocol shape.
223    fn new_from_execution_v1(
224        status: ExecutionStatus,
225        epoch: EpochId,
226        gas_cost_summary: GasCostSummary,
227        shared_objects: Vec<SharedInput>,
228        loaded_per_epoch_config_objects: BTreeSet<ObjectId>,
229        transaction_digest: TransactionDigest,
230        lamport_version: Version,
231        changed_objects: BTreeMap<ObjectId, ChangedObject>,
232        gas_object: Option<ObjectId>,
233        events_digest: Option<TransactionEventsDigest>,
234        dependencies: Vec<TransactionDigest>,
235    ) -> Self;
236
237    /// Returns the `(transaction_digest, effects_digest)` pair identifying
238    /// this execution.
239    fn execution_digests(&self) -> ExecutionDigests;
240
241    /// Return an iterator that iterates through all changed objects, including
242    /// mutated, created and unwrapped objects. In other words, all objects
243    /// that still exist in the object state after this transaction.
244    /// It doesn't include deleted/wrapped objects.
245    fn all_changed_objects(&self) -> Vec<(OwnedObjectReference, WriteKind)>;
246
247    /// Return all objects that existed in the state prior to the transaction
248    /// but no longer exist in the state after the transaction.
249    /// It includes deleted and wrapped objects, but does not include
250    /// unwrapped_then_deleted objects.
251    fn all_removed_objects(&self) -> Vec<(ObjectReference, ObjectRemoveKind)>;
252
253    /// Returns all objects that will become a tombstone after this transaction.
254    /// This includes deleted, unwrapped_then_deleted and wrapped objects.
255    fn all_tombstones(&self) -> Vec<(ObjectId, Version)>;
256
257    /// Returns all objects that were created + wrapped in the same transaction.
258    fn created_then_wrapped_objects(&self) -> Vec<(ObjectId, Version)>;
259
260    /// Return an iterator of mutated objects, but excluding the gas object.
261    fn mutated_excluding_gas(&self) -> Vec<OwnedObjectReference>;
262
263    /// Returns all affected objects in this transaction effects.
264    /// Affected objects include created, mutated, unwrapped, deleted,
265    /// unwrapped_then_deleted, wrapped and input shared objects.
266    fn all_affected_objects(&self) -> Vec<ObjectReference>;
267
268    /// Returns a condensed [`TransactionEffectsDebugSummary`] suitable for
269    /// logging and inspection.
270    fn summary_for_debug(&self) -> TransactionEffectsDebugSummary;
271
272    /// Upper-bound estimate of the serialized size in bytes of effects with
273    /// the given number of writes, modifies, and dependencies under the V1
274    /// protocol shape.
275    fn estimate_size_upperbound_v1(
276        num_writes: usize,
277        num_modifies: usize,
278        num_deps: usize,
279    ) -> usize {
280        let fixed_sizes = APPROX_SIZE_OF_EXECUTION_STATUS
281            + APPROX_SIZE_OF_EPOCH_ID
282            + APPROX_SIZE_OF_GAS_COST_SUMMARY
283            + APPROX_SIZE_OF_OPT_TX_EVENTS_DIGEST;
284
285        // We store object ref and owner for both old objects and new objects.
286        let approx_change_entry_size = 1_000
287            + (APPROX_SIZE_OF_OWNER + APPROX_SIZE_OF_OBJECT_REF) * num_writes
288            + (APPROX_SIZE_OF_OWNER + APPROX_SIZE_OF_OBJECT_REF) * num_modifies;
289
290        let deps_size = 1_000 + APPROX_SIZE_OF_TX_DIGEST * num_deps;
291
292        fixed_sizes + approx_change_entry_size + deps_size
293    }
294}
295
296/// Test-only counterpart to [`TransactionEffectsExt`]. Not for production use.
297pub trait TransactionEffectsExtForTesting: transaction_effects_ext::Sealed {
298    // All of these should be #[cfg(test)], but they are used by tests in other
299    // crates.
300
301    /// Build empty V1 effects for `transaction_digest`: success status, no
302    /// object changes, and no gas object. For tests that need a placeholder
303    /// whose effects content is irrelevant, e.g. system transactions.
304    fn new_empty_v1_for_testing(transaction_digest: TransactionDigest) -> Self;
305}
306
307// The version these effects are, which is where everything about them is read
308// from. A new variant has to be handled in one place: here.
309macro_rules! effects_version {
310    ($self:ident) => {
311        match $self {
312            TransactionEffects::V1(v1) => &**v1,
313            _ => unimplemented!(
314                "a new TransactionEffects enum variant was added and needs to be handled"
315            ),
316        }
317    };
318    (mut $self:ident) => {
319        match $self {
320            TransactionEffects::V1(v1) => &mut **v1,
321            _ => unimplemented!(
322                "a new TransactionEffects enum variant was added and needs to be handled"
323            ),
324        }
325    };
326    (into $self:ident) => {
327        match $self {
328            TransactionEffects::V1(v1) => *v1,
329            _ => unimplemented!(
330                "a new TransactionEffects enum variant was added and needs to be handled"
331            ),
332        }
333    };
334}
335
336impl TransactionEffectsAPI for TransactionEffects {
337    fn status(&self) -> &ExecutionStatus {
338        &effects_version!(self).status
339    }
340
341    fn into_status(self) -> ExecutionStatus {
342        effects_version!(into self).status
343    }
344
345    fn epoch(&self) -> EpochId {
346        effects_version!(self).epoch
347    }
348
349    fn modified_at_versions(&self) -> Vec<ObjectVersion> {
350        effects_version!(self).modified_at_versions()
351    }
352
353    fn lamport_version(&self) -> Version {
354        effects_version!(self).lamport_version
355    }
356
357    fn old_object_metadata(&self) -> Vec<OwnedObjectReference> {
358        effects_version!(self).old_object_metadata()
359    }
360
361    fn input_shared_objects(&self) -> Vec<InputSharedObject> {
362        effects_version!(self).input_shared_objects()
363    }
364
365    fn created(&self) -> Vec<OwnedObjectReference> {
366        effects_version!(self).created()
367    }
368
369    fn mutated(&self) -> Vec<OwnedObjectReference> {
370        effects_version!(self).mutated()
371    }
372
373    fn unwrapped(&self) -> Vec<OwnedObjectReference> {
374        effects_version!(self).unwrapped()
375    }
376
377    fn deleted(&self) -> Vec<ObjectReference> {
378        effects_version!(self).deleted()
379    }
380
381    fn unwrapped_then_deleted(&self) -> Vec<ObjectReference> {
382        effects_version!(self).unwrapped_then_deleted()
383    }
384
385    fn wrapped(&self) -> Vec<ObjectReference> {
386        effects_version!(self).wrapped()
387    }
388
389    fn object_changes(&self) -> Vec<ObjectChange> {
390        effects_version!(self).object_changes()
391    }
392
393    fn gas_object(&self) -> OwnedObjectReference {
394        // A system transaction pays no gas, so its effects name no gas object;
395        // this reports the dummy reference callers here have always been given
396        // for that case.
397        effects_version!(self).gas_object().unwrap_or_else(|| {
398            OwnedObjectReference::new(
399                ObjectReference::new(ObjectId::ZERO, Version::default(), ObjectDigest::MIN),
400                Owner::Address(Address::ZERO),
401            )
402        })
403    }
404
405    fn events_digest(&self) -> Option<&TransactionEventsDigest> {
406        effects_version!(self).events_digest.as_ref()
407    }
408
409    fn dependencies(&self) -> &[TransactionDigest] {
410        &effects_version!(self).dependencies
411    }
412
413    fn transaction_digest(&self) -> &TransactionDigest {
414        &effects_version!(self).transaction_digest
415    }
416
417    fn gas_cost_summary(&self) -> &GasCostSummary {
418        &effects_version!(self).gas_cost_summary
419    }
420
421    fn unchanged_shared_objects(&self) -> Vec<(ObjectId, UnchangedSharedKind)> {
422        effects_version!(self)
423            .unchanged_shared_objects
424            .iter()
425            .map(|unchanged| (unchanged.object_id, unchanged.kind.clone()))
426            .collect()
427    }
428}
429
430impl TransactionEffectsAPIForTesting for TransactionEffects {
431    fn gas_cost_summary_mut_for_testing(&mut self) -> &mut GasCostSummary {
432        &mut effects_version!(mut self).gas_cost_summary
433    }
434
435    fn dependencies_mut_for_testing(&mut self) -> &mut Vec<TransactionDigest> {
436        &mut effects_version!(mut self).dependencies
437    }
438
439    fn unsafe_add_read_only_shared_object_for_testing(&mut self, object: ObjectReference) {
440        let ObjectReference {
441            object_id,
442            version,
443            digest,
444        } = object;
445        effects_version!(mut self)
446            .unchanged_shared_objects
447            .push(UnchangedSharedObject {
448                object_id,
449                kind: UnchangedSharedKind::ReadOnlyRoot { version, digest },
450            })
451    }
452
453    fn unsafe_add_mutated_shared_object_for_testing(&mut self, object: ObjectReference) {
454        let ObjectReference {
455            object_id,
456            version,
457            digest,
458        } = object;
459        effects_version!(mut self)
460            .changed_objects
461            .push(ChangedObject {
462                object_id,
463                input_state: ObjectIn::Data {
464                    version,
465                    digest,
466                    owner: Owner::Shared(OBJECT_START_VERSION),
467                },
468                output_state: ObjectOut::ObjectWrite {
469                    digest,
470                    owner: Owner::Shared(version),
471                },
472                id_operation: IdOperation::None,
473            })
474    }
475
476    fn unsafe_add_deleted_live_object_for_testing(&mut self, object_ref: ObjectReference) {
477        let v1 = effects_version!(mut self);
478        let (object_id, version, digest) = object_ref.into_parts();
479        v1.changed_objects.push(ChangedObject {
480            object_id,
481            input_state: ObjectIn::Data {
482                version,
483                digest,
484                owner: Owner::Address(Address::ZERO),
485            },
486            output_state: ObjectOut::ObjectWrite {
487                digest,
488                owner: Owner::Address(Address::ZERO),
489            },
490            id_operation: IdOperation::None,
491        })
492    }
493
494    fn unsafe_add_object_tombstone_for_testing(&mut self, object_ref: ObjectReference) {
495        let v1 = effects_version!(mut self);
496        let (object_id, version, digest) = object_ref.into_parts();
497        v1.changed_objects.push(ChangedObject {
498            object_id,
499            input_state: ObjectIn::Data {
500                version,
501                digest,
502                owner: Owner::Address(Address::ZERO),
503            },
504            output_state: ObjectOut::Missing,
505            id_operation: IdOperation::Deleted,
506        })
507    }
508}
509
510impl TransactionEffectsExt for TransactionEffects {
511    fn new_from_execution_v1(
512        status: ExecutionStatus,
513        epoch: EpochId,
514        gas_cost_summary: GasCostSummary,
515        shared_objects: Vec<SharedInput>,
516        loaded_per_epoch_config_objects: BTreeSet<ObjectId>,
517        transaction_digest: TransactionDigest,
518        lamport_version: Version,
519        changed_objects: BTreeMap<ObjectId, ChangedObject>,
520        gas_object: Option<ObjectId>,
521        events_digest: Option<TransactionEventsDigest>,
522        dependencies: Vec<TransactionDigest>,
523    ) -> Self {
524        TransactionEffects::V1(Box::new(v1::new_from_execution(
525            status,
526            epoch,
527            gas_cost_summary,
528            shared_objects,
529            loaded_per_epoch_config_objects,
530            transaction_digest,
531            lamport_version,
532            changed_objects,
533            gas_object,
534            events_digest,
535            dependencies,
536        )))
537    }
538
539    fn execution_digests(&self) -> ExecutionDigests {
540        ExecutionDigests {
541            transaction: *self.transaction_digest(),
542            effects: self.digest(),
543        }
544    }
545
546    fn all_changed_objects(&self) -> Vec<(OwnedObjectReference, WriteKind)> {
547        effects_version!(self).all_changed_objects()
548    }
549
550    fn all_removed_objects(&self) -> Vec<(ObjectReference, ObjectRemoveKind)> {
551        effects_version!(self).all_removed_objects()
552    }
553
554    fn all_tombstones(&self) -> Vec<(ObjectId, Version)> {
555        self.deleted()
556            .into_iter()
557            .chain(self.unwrapped_then_deleted())
558            .chain(self.wrapped())
559            .map(|obj_ref| (obj_ref.object_id, obj_ref.version))
560            .collect()
561    }
562
563    fn created_then_wrapped_objects(&self) -> Vec<(ObjectId, Version)> {
564        // Filter `ObjectChange` where:
565        // - `input_digest` and `output_digest` are `None`, and
566        // - `id_operation` is `Created`.
567        self.object_changes()
568            .into_iter()
569            .filter_map(|change| {
570                if change.input_digest.is_none()
571                    && change.output_digest.is_none()
572                    && change.id_operation == IdOperation::Created
573                {
574                    Some((change.object_id, change.output_version.unwrap_or_default()))
575                } else {
576                    None
577                }
578            })
579            .collect::<Vec<_>>()
580    }
581
582    fn mutated_excluding_gas(&self) -> Vec<OwnedObjectReference> {
583        let gas = self.gas_object();
584        self.mutated()
585            .into_iter()
586            .filter(|mutated| *mutated != gas)
587            .collect()
588    }
589
590    fn all_affected_objects(&self) -> Vec<ObjectReference> {
591        let reference = |owned: OwnedObjectReference| owned.reference;
592        self.created()
593            .into_iter()
594            .map(reference)
595            .chain(self.mutated().into_iter().map(reference))
596            .chain(self.unwrapped().into_iter().map(reference))
597            .chain(
598                self.input_shared_objects()
599                    .into_iter()
600                    .map(|shared| shared.object_reference()),
601            )
602            .chain(self.deleted())
603            .chain(self.unwrapped_then_deleted())
604            .chain(self.wrapped())
605            .collect()
606    }
607
608    fn summary_for_debug(&self) -> TransactionEffectsDebugSummary {
609        TransactionEffectsDebugSummary {
610            bcs_size: bcs::serialized_size(self).unwrap(),
611            status: self.status().clone(),
612            gas_cost_summary: self.gas_cost_summary().clone(),
613            transaction_digest: *self.transaction_digest(),
614            created_object_count: self.created().len(),
615            mutated_object_count: self.mutated().len(),
616            unwrapped_object_count: self.unwrapped().len(),
617            deleted_object_count: self.deleted().len(),
618            wrapped_object_count: self.wrapped().len(),
619            dependency_count: self.dependencies().len(),
620        }
621    }
622}
623
624impl TransactionEffectsExtForTesting for TransactionEffects {
625    fn new_empty_v1_for_testing(transaction_digest: TransactionDigest) -> Self {
626        Self::new_from_execution_v1(
627            ExecutionStatus::Success,
628            0,
629            GasCostSummary::default(),
630            vec![],
631            BTreeSet::new(),
632            transaction_digest,
633            Version::default(),
634            BTreeMap::new(),
635            None,
636            None,
637            vec![],
638        )
639    }
640}
641
642#[derive(Debug)]
643pub struct TransactionEffectsDebugSummary {
644    /// Size of bcs serialized bytes of the effects.
645    pub bcs_size: usize,
646    pub status: ExecutionStatus,
647    pub gas_cost_summary: GasCostSummary,
648    pub transaction_digest: TransactionDigest,
649    pub created_object_count: usize,
650    pub mutated_object_count: usize,
651    pub unwrapped_object_count: usize,
652    pub deleted_object_count: usize,
653    pub wrapped_object_count: usize,
654    pub dependency_count: usize,
655    // TODO: Add deleted_and_unwrapped_object_count and event digest.
656}
657
658pub type TransactionEffectsEnvelope<S> = Envelope<TransactionEffects, S>;
659pub type UnsignedTransactionEffects = TransactionEffectsEnvelope<EmptySignInfo>;
660pub type SignedTransactionEffects = TransactionEffectsEnvelope<AuthoritySignInfo>;
661pub type CertifiedTransactionEffects = TransactionEffectsEnvelope<AuthorityStrongQuorumSignInfo>;
662
663pub type TrustedSignedTransactionEffects = TrustedEnvelope<TransactionEffects, AuthoritySignInfo>;
664pub type VerifiedTransactionEffectsEnvelope<S> = VerifiedEnvelope<TransactionEffects, S>;
665pub type VerifiedSignedTransactionEffects = VerifiedTransactionEffectsEnvelope<AuthoritySignInfo>;
666pub type VerifiedCertifiedTransactionEffects =
667    VerifiedTransactionEffectsEnvelope<AuthorityStrongQuorumSignInfo>;
668
669impl CertifiedTransactionEffects {
670    #[instrument(level = "trace", skip_all)]
671    pub fn verify_authority_signatures(&self, committee: &Committee) -> IotaResult {
672        self.auth_sig().verify_secure(
673            self.data(),
674            Intent::iota_app(IntentScope::TransactionEffects),
675            committee,
676        )
677    }
678
679    #[instrument(level = "trace", skip_all)]
680    pub fn verify(self, committee: &Committee) -> IotaResult<VerifiedCertifiedTransactionEffects> {
681        self.verify_authority_signatures(committee)?;
682        Ok(VerifiedCertifiedTransactionEffects::new_from_verified(self))
683    }
684}
685#[cfg(test)]
686mod tests {
687    use super::*;
688
689    /// `<TransactionEffects as Message>::digest` and the SDK's inherent
690    /// `TransactionEffects::digest` are defined independently in two crates.
691    /// They must agree: `Envelope<TransactionEffects, _>` resolves digests via
692    /// the trait, while direct call sites resolve to the inherent. Silent
693    /// divergence would split-brain storage and consensus digests.
694    #[test]
695    fn message_trait_and_effects_digest_match() {
696        let effects = TransactionEffects::new_empty_v1_for_testing(TransactionDigest::default());
697        let message_digest = <TransactionEffects as Message>::digest(&effects);
698        let effects_digest = effects.digest();
699        assert_eq!(message_digest, effects_digest);
700    }
701}