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    ObjectDigest, ObjectId, ObjectReference, ObjectRemoveKind, ObjectVersion, OwnedObjectReference,
10    Owner, TransactionDigest, TransactionEffectsDigest, TransactionEventsDigest,
11    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 the post-execution reference and owner of the gas object.
138    // TODO: We should consider having this function to return Option.
139    // When the gas object is not available (i.e. system transaction), we currently
140    // return dummy object ref and owner. This is not ideal.
141    fn gas_object(&self) -> OwnedObjectReference;
142
143    /// Digest of the events emitted by this transaction, or `None` if it
144    /// emitted no events.
145    fn events_digest(&self) -> Option<&TransactionEventsDigest>;
146
147    /// Digests of the transactions this one depends on, i.e. transactions
148    /// that must be executed before this one for its inputs to be available.
149    fn dependencies(&self) -> &[TransactionDigest];
150
151    /// Digest of the transaction that produced these effects.
152    fn transaction_digest(&self) -> &TransactionDigest;
153
154    /// Return the gas cost summary of the transaction.
155    fn gas_cost_summary(&self) -> &GasCostSummary;
156
157    /// IDs of shared objects that were declared as mutable inputs by the
158    /// transaction but had already been deleted at the time of execution.
159    fn deleted_mutably_accessed_shared_objects(&self) -> Vec<ObjectId> {
160        self.input_shared_objects()
161            .into_iter()
162            .filter_map(|kind| match kind {
163                InputSharedObject::MutateDeleted(object) => Some(*object.object_id()),
164                InputSharedObject::Mutate(..)
165                | InputSharedObject::ReadOnly(..)
166                | InputSharedObject::ReadDeleted(..)
167                | InputSharedObject::Canceled(..) => None,
168                _ => unimplemented!(
169                    "a new InputSharedObject enum variant was added and needs to be handled"
170                ),
171            })
172            .collect()
173    }
174
175    /// Returns all root shared objects (i.e. not child object) that are
176    /// read-only in the transaction.
177    fn unchanged_shared_objects(&self) -> Vec<(ObjectId, UnchangedSharedKind)>;
178}
179
180/// Test-only mutators for [`TransactionEffects`] that bypass the normal
181/// invariants. Not for production use.
182pub trait TransactionEffectsAPIForTesting: TransactionEffectsAPI {
183    // All of these should be #[cfg(test)], but they are used by tests in other
184    // crates.
185
186    /// Returns a mutable reference to the gas cost summary, for tests.
187    fn gas_cost_summary_mut_for_testing(&mut self) -> &mut GasCostSummary;
188
189    /// Returns a mutable reference to the dependency list, for tests.
190    fn dependencies_mut_for_testing(&mut self) -> &mut Vec<TransactionDigest>;
191
192    /// Records a shared object these effects read without changing, for tests.
193    /// Unsafe: makes no attempt to keep the effects self-consistent.
194    fn unsafe_add_read_only_shared_object_for_testing(&mut self, object: ObjectReference);
195
196    /// Records a shared object these effects took mutably, for tests. Unsafe:
197    /// makes no attempt to keep the effects self-consistent.
198    fn unsafe_add_mutated_shared_object_for_testing(&mut self, object: ObjectReference);
199
200    /// Records an entry that represents the pre-execution version of a still
201    /// live object, without validating consistency with the rest of the
202    /// effects. For tests only.
203    fn unsafe_add_deleted_live_object_for_testing(&mut self, object_ref: ObjectReference);
204
205    /// Records a tombstone entry for a deleted object, without validating
206    /// consistency with the rest of the effects. For tests only.
207    fn unsafe_add_object_tombstone_for_testing(&mut self, object_ref: ObjectReference);
208}
209
210mod transaction_effects_ext {
211    pub trait Sealed {}
212    impl Sealed for super::TransactionEffects {}
213}
214
215/// The version-selecting constructor and aggregating queries for the
216/// [`TransactionEffects`] enum. Sealed; implemented only for the enum.
217pub trait TransactionEffectsExt: transaction_effects_ext::Sealed {
218    /// Build effects from the results of executing a transaction under the
219    /// V1 protocol shape.
220    fn new_from_execution_v1(
221        status: ExecutionStatus,
222        epoch: EpochId,
223        gas_cost_summary: GasCostSummary,
224        shared_objects: Vec<SharedInput>,
225        loaded_per_epoch_config_objects: BTreeSet<ObjectId>,
226        transaction_digest: TransactionDigest,
227        lamport_version: Version,
228        changed_objects: BTreeMap<ObjectId, ChangedObject>,
229        gas_object: Option<ObjectId>,
230        events_digest: Option<TransactionEventsDigest>,
231        dependencies: Vec<TransactionDigest>,
232    ) -> Self;
233
234    /// Returns the `(transaction_digest, effects_digest)` pair identifying
235    /// this execution.
236    fn execution_digests(&self) -> ExecutionDigests;
237
238    /// Return an iterator that iterates through all changed objects, including
239    /// mutated, created and unwrapped objects. In other words, all objects
240    /// that still exist in the object state after this transaction.
241    /// It doesn't include deleted/wrapped objects.
242    fn all_changed_objects(&self) -> Vec<(OwnedObjectReference, WriteKind)>;
243
244    /// Return all objects that existed in the state prior to the transaction
245    /// but no longer exist in the state after the transaction.
246    /// It includes deleted and wrapped objects, but does not include
247    /// unwrapped_then_deleted objects.
248    fn all_removed_objects(&self) -> Vec<(ObjectReference, ObjectRemoveKind)>;
249
250    /// Returns all objects that will become a tombstone after this transaction.
251    /// This includes deleted, unwrapped_then_deleted and wrapped objects.
252    fn all_tombstones(&self) -> Vec<(ObjectId, Version)>;
253
254    /// Returns all objects that were created + wrapped in the same transaction.
255    /// Such an object leaves no version behind: it is absent from the store on
256    /// both sides of the transaction.
257    fn created_then_wrapped_objects(&self) -> Vec<ObjectId>;
258
259    /// Return an iterator of mutated objects, but excluding the gas object.
260    fn mutated_excluding_gas(&self) -> Vec<OwnedObjectReference>;
261
262    /// Returns all affected objects in this transaction effects.
263    /// Affected objects include created, mutated, unwrapped, deleted,
264    /// unwrapped_then_deleted, wrapped and input shared objects.
265    fn all_affected_objects(&self) -> Vec<ObjectReference>;
266
267    /// Returns a condensed [`TransactionEffectsDebugSummary`] suitable for
268    /// logging and inspection.
269    fn summary_for_debug(&self) -> TransactionEffectsDebugSummary;
270
271    /// Upper-bound estimate of the serialized size in bytes of effects with
272    /// the given number of writes, modifies, and dependencies under the V1
273    /// protocol shape.
274    fn estimate_size_upperbound_v1(
275        num_writes: usize,
276        num_modifies: usize,
277        num_deps: usize,
278    ) -> usize {
279        let fixed_sizes = APPROX_SIZE_OF_EXECUTION_STATUS
280            + APPROX_SIZE_OF_EPOCH_ID
281            + APPROX_SIZE_OF_GAS_COST_SUMMARY
282            + APPROX_SIZE_OF_OPT_TX_EVENTS_DIGEST;
283
284        // We store object ref and owner for both old objects and new objects.
285        let approx_change_entry_size = 1_000
286            + (APPROX_SIZE_OF_OWNER + APPROX_SIZE_OF_OBJECT_REF) * num_writes
287            + (APPROX_SIZE_OF_OWNER + APPROX_SIZE_OF_OBJECT_REF) * num_modifies;
288
289        let deps_size = 1_000 + APPROX_SIZE_OF_TX_DIGEST * num_deps;
290
291        fixed_sizes + approx_change_entry_size + deps_size
292    }
293}
294
295/// Test-only counterpart to [`TransactionEffectsExt`]. Not for production use.
296pub trait TransactionEffectsExtForTesting: transaction_effects_ext::Sealed {
297    // All of these should be #[cfg(test)], but they are used by tests in other
298    // crates.
299
300    /// Build empty V1 effects for `transaction_digest`: success status, no
301    /// object changes, and no gas object. For tests that need a placeholder
302    /// whose effects content is irrelevant, e.g. system transactions.
303    fn new_empty_v1_for_testing(transaction_digest: TransactionDigest) -> Self;
304}
305
306// The version these effects are, which is where everything about them is read
307// from. A new variant has to be handled in one place: here.
308macro_rules! effects_version {
309    ($self:ident) => {
310        match $self {
311            TransactionEffects::V1(v1) => &**v1,
312            _ => unimplemented!(
313                "a new TransactionEffects enum variant was added and needs to be handled"
314            ),
315        }
316    };
317    (mut $self:ident) => {
318        match $self {
319            TransactionEffects::V1(v1) => &mut **v1,
320            _ => unimplemented!(
321                "a new TransactionEffects enum variant was added and needs to be handled"
322            ),
323        }
324    };
325    (into $self:ident) => {
326        match $self {
327            TransactionEffects::V1(v1) => *v1,
328            _ => unimplemented!(
329                "a new TransactionEffects enum variant was added and needs to be handled"
330            ),
331        }
332    };
333}
334
335impl TransactionEffectsAPI for TransactionEffects {
336    fn status(&self) -> &ExecutionStatus {
337        &effects_version!(self).status
338    }
339
340    fn into_status(self) -> ExecutionStatus {
341        effects_version!(into self).status
342    }
343
344    fn epoch(&self) -> EpochId {
345        effects_version!(self).epoch
346    }
347
348    fn modified_at_versions(&self) -> Vec<ObjectVersion> {
349        effects_version!(self).modified_at_versions()
350    }
351
352    fn lamport_version(&self) -> Version {
353        effects_version!(self).lamport_version
354    }
355
356    fn old_object_metadata(&self) -> Vec<OwnedObjectReference> {
357        effects_version!(self).old_object_metadata()
358    }
359
360    fn input_shared_objects(&self) -> Vec<InputSharedObject> {
361        effects_version!(self).input_shared_objects()
362    }
363
364    fn created(&self) -> Vec<OwnedObjectReference> {
365        effects_version!(self).created()
366    }
367
368    fn mutated(&self) -> Vec<OwnedObjectReference> {
369        effects_version!(self).mutated()
370    }
371
372    fn unwrapped(&self) -> Vec<OwnedObjectReference> {
373        effects_version!(self).unwrapped()
374    }
375
376    fn deleted(&self) -> Vec<ObjectReference> {
377        effects_version!(self).deleted()
378    }
379
380    fn unwrapped_then_deleted(&self) -> Vec<ObjectReference> {
381        effects_version!(self).unwrapped_then_deleted()
382    }
383
384    fn wrapped(&self) -> Vec<ObjectReference> {
385        effects_version!(self).wrapped()
386    }
387
388    fn gas_object(&self) -> OwnedObjectReference {
389        // A system transaction pays no gas, so its effects name no gas object;
390        // this reports the dummy reference callers here have always been given
391        // for that case.
392        effects_version!(self).gas_object().unwrap_or_else(|| {
393            OwnedObjectReference::new(
394                ObjectReference::new(ObjectId::ZERO, Version::default(), ObjectDigest::MIN),
395                Owner::Address(Address::ZERO),
396            )
397        })
398    }
399
400    fn events_digest(&self) -> Option<&TransactionEventsDigest> {
401        effects_version!(self).events_digest.as_ref()
402    }
403
404    fn dependencies(&self) -> &[TransactionDigest] {
405        &effects_version!(self).dependencies
406    }
407
408    fn transaction_digest(&self) -> &TransactionDigest {
409        &effects_version!(self).transaction_digest
410    }
411
412    fn gas_cost_summary(&self) -> &GasCostSummary {
413        &effects_version!(self).gas_cost_summary
414    }
415
416    fn unchanged_shared_objects(&self) -> Vec<(ObjectId, UnchangedSharedKind)> {
417        effects_version!(self)
418            .unchanged_shared_objects
419            .iter()
420            .map(|unchanged| (unchanged.object_id, unchanged.kind.clone()))
421            .collect()
422    }
423}
424
425impl TransactionEffectsAPIForTesting for TransactionEffects {
426    fn gas_cost_summary_mut_for_testing(&mut self) -> &mut GasCostSummary {
427        &mut effects_version!(mut self).gas_cost_summary
428    }
429
430    fn dependencies_mut_for_testing(&mut self) -> &mut Vec<TransactionDigest> {
431        &mut effects_version!(mut self).dependencies
432    }
433
434    fn unsafe_add_read_only_shared_object_for_testing(&mut self, object: ObjectReference) {
435        let ObjectReference {
436            object_id,
437            version,
438            digest,
439        } = object;
440        effects_version!(mut self)
441            .unchanged_shared_objects
442            .push(UnchangedSharedObject {
443                object_id,
444                kind: UnchangedSharedKind::ReadOnlyRoot { version, digest },
445            })
446    }
447
448    fn unsafe_add_mutated_shared_object_for_testing(&mut self, object: ObjectReference) {
449        let ObjectReference {
450            object_id,
451            version,
452            digest,
453        } = object;
454        effects_version!(mut self)
455            .changed_objects
456            .push(ChangedObject {
457                object_id,
458                input_state: ObjectIn::Data {
459                    version,
460                    digest,
461                    owner: Owner::Shared(OBJECT_START_VERSION),
462                },
463                output_state: ObjectOut::ObjectWrite {
464                    digest,
465                    owner: Owner::Shared(version),
466                },
467                id_operation: IdOperation::None,
468            })
469    }
470
471    fn unsafe_add_deleted_live_object_for_testing(&mut self, object_ref: ObjectReference) {
472        let v1 = effects_version!(mut self);
473        let (object_id, version, digest) = object_ref.into_parts();
474        v1.changed_objects.push(ChangedObject {
475            object_id,
476            input_state: ObjectIn::Data {
477                version,
478                digest,
479                owner: Owner::Address(Address::ZERO),
480            },
481            output_state: ObjectOut::ObjectWrite {
482                digest,
483                owner: Owner::Address(Address::ZERO),
484            },
485            id_operation: IdOperation::None,
486        })
487    }
488
489    fn unsafe_add_object_tombstone_for_testing(&mut self, object_ref: ObjectReference) {
490        let v1 = effects_version!(mut self);
491        let (object_id, version, digest) = object_ref.into_parts();
492        v1.changed_objects.push(ChangedObject {
493            object_id,
494            input_state: ObjectIn::Data {
495                version,
496                digest,
497                owner: Owner::Address(Address::ZERO),
498            },
499            output_state: ObjectOut::Missing,
500            id_operation: IdOperation::Deleted,
501        })
502    }
503}
504
505impl TransactionEffectsExt for TransactionEffects {
506    fn new_from_execution_v1(
507        status: ExecutionStatus,
508        epoch: EpochId,
509        gas_cost_summary: GasCostSummary,
510        shared_objects: Vec<SharedInput>,
511        loaded_per_epoch_config_objects: BTreeSet<ObjectId>,
512        transaction_digest: TransactionDigest,
513        lamport_version: Version,
514        changed_objects: BTreeMap<ObjectId, ChangedObject>,
515        gas_object: Option<ObjectId>,
516        events_digest: Option<TransactionEventsDigest>,
517        dependencies: Vec<TransactionDigest>,
518    ) -> Self {
519        TransactionEffects::V1(Box::new(v1::new_from_execution(
520            status,
521            epoch,
522            gas_cost_summary,
523            shared_objects,
524            loaded_per_epoch_config_objects,
525            transaction_digest,
526            lamport_version,
527            changed_objects,
528            gas_object,
529            events_digest,
530            dependencies,
531        )))
532    }
533
534    fn execution_digests(&self) -> ExecutionDigests {
535        ExecutionDigests {
536            transaction: *self.transaction_digest(),
537            effects: self.digest(),
538        }
539    }
540
541    fn all_changed_objects(&self) -> Vec<(OwnedObjectReference, WriteKind)> {
542        effects_version!(self).all_changed_objects()
543    }
544
545    fn all_removed_objects(&self) -> Vec<(ObjectReference, ObjectRemoveKind)> {
546        effects_version!(self).all_removed_objects()
547    }
548
549    fn all_tombstones(&self) -> Vec<(ObjectId, Version)> {
550        self.deleted()
551            .into_iter()
552            .chain(self.unwrapped_then_deleted())
553            .chain(self.wrapped())
554            .map(|obj_ref| (obj_ref.object_id, obj_ref.version))
555            .collect()
556    }
557
558    fn created_then_wrapped_objects(&self) -> Vec<ObjectId> {
559        effects_version!(self)
560            .changed_objects
561            .iter()
562            .filter(|changed| {
563                matches!(
564                    (
565                        &changed.input_state,
566                        &changed.output_state,
567                        &changed.id_operation
568                    ),
569                    (ObjectIn::Missing, ObjectOut::Missing, IdOperation::Created)
570                )
571            })
572            .map(|changed| changed.object_id)
573            .collect()
574    }
575
576    fn mutated_excluding_gas(&self) -> Vec<OwnedObjectReference> {
577        let gas = self.gas_object();
578        self.mutated()
579            .into_iter()
580            .filter(|mutated| *mutated != gas)
581            .collect()
582    }
583
584    fn all_affected_objects(&self) -> Vec<ObjectReference> {
585        let reference = |owned: OwnedObjectReference| *owned.reference();
586        self.created()
587            .into_iter()
588            .map(reference)
589            .chain(self.mutated().into_iter().map(reference))
590            .chain(self.unwrapped().into_iter().map(reference))
591            .chain(
592                self.input_shared_objects()
593                    .into_iter()
594                    .map(|shared| shared.object_reference()),
595            )
596            .chain(self.deleted())
597            .chain(self.unwrapped_then_deleted())
598            .chain(self.wrapped())
599            .collect()
600    }
601
602    fn summary_for_debug(&self) -> TransactionEffectsDebugSummary {
603        TransactionEffectsDebugSummary {
604            bcs_size: bcs::serialized_size(self).unwrap(),
605            status: self.status().clone(),
606            gas_cost_summary: self.gas_cost_summary().clone(),
607            transaction_digest: *self.transaction_digest(),
608            created_object_count: self.created().len(),
609            mutated_object_count: self.mutated().len(),
610            unwrapped_object_count: self.unwrapped().len(),
611            deleted_object_count: self.deleted().len(),
612            wrapped_object_count: self.wrapped().len(),
613            dependency_count: self.dependencies().len(),
614        }
615    }
616}
617
618impl TransactionEffectsExtForTesting for TransactionEffects {
619    fn new_empty_v1_for_testing(transaction_digest: TransactionDigest) -> Self {
620        Self::new_from_execution_v1(
621            ExecutionStatus::Success,
622            0,
623            GasCostSummary::default(),
624            vec![],
625            BTreeSet::new(),
626            transaction_digest,
627            Version::default(),
628            BTreeMap::new(),
629            None,
630            None,
631            vec![],
632        )
633    }
634}
635
636#[derive(Debug)]
637pub struct TransactionEffectsDebugSummary {
638    /// Size of bcs serialized bytes of the effects.
639    pub bcs_size: usize,
640    pub status: ExecutionStatus,
641    pub gas_cost_summary: GasCostSummary,
642    pub transaction_digest: TransactionDigest,
643    pub created_object_count: usize,
644    pub mutated_object_count: usize,
645    pub unwrapped_object_count: usize,
646    pub deleted_object_count: usize,
647    pub wrapped_object_count: usize,
648    pub dependency_count: usize,
649    // TODO: Add deleted_and_unwrapped_object_count and event digest.
650}
651
652pub type TransactionEffectsEnvelope<S> = Envelope<TransactionEffects, S>;
653pub type UnsignedTransactionEffects = TransactionEffectsEnvelope<EmptySignInfo>;
654pub type SignedTransactionEffects = TransactionEffectsEnvelope<AuthoritySignInfo>;
655pub type CertifiedTransactionEffects = TransactionEffectsEnvelope<AuthorityStrongQuorumSignInfo>;
656
657pub type TrustedSignedTransactionEffects = TrustedEnvelope<TransactionEffects, AuthoritySignInfo>;
658pub type VerifiedTransactionEffectsEnvelope<S> = VerifiedEnvelope<TransactionEffects, S>;
659pub type VerifiedSignedTransactionEffects = VerifiedTransactionEffectsEnvelope<AuthoritySignInfo>;
660pub type VerifiedCertifiedTransactionEffects =
661    VerifiedTransactionEffectsEnvelope<AuthorityStrongQuorumSignInfo>;
662
663impl CertifiedTransactionEffects {
664    #[instrument(level = "trace", skip_all)]
665    pub fn verify_authority_signatures(&self, committee: &Committee) -> IotaResult {
666        self.auth_sig().verify_secure(
667            self.data(),
668            Intent::iota_app(IntentScope::TransactionEffects),
669            committee,
670        )
671    }
672
673    #[instrument(level = "trace", skip_all)]
674    pub fn verify(self, committee: &Committee) -> IotaResult<VerifiedCertifiedTransactionEffects> {
675        self.verify_authority_signatures(committee)?;
676        Ok(VerifiedCertifiedTransactionEffects::new_from_verified(self))
677    }
678}
679#[cfg(test)]
680mod tests {
681    use super::*;
682
683    /// `<TransactionEffects as Message>::digest` and the SDK's inherent
684    /// `TransactionEffects::digest` are defined independently in two crates.
685    /// They must agree: `Envelope<TransactionEffects, _>` resolves digests via
686    /// the trait, while direct call sites resolve to the inherent. Silent
687    /// divergence would split-brain storage and consensus digests.
688    #[test]
689    fn message_trait_and_effects_digest_match() {
690        let effects = TransactionEffects::new_empty_v1_for_testing(TransactionDigest::default());
691        let message_digest = <TransactionEffects as Message>::digest(&effects);
692        let effects_digest = effects.digest();
693        assert_eq!(message_digest, effects_digest);
694    }
695}