1pub use self::effects_v2::TransactionEffectsV2;
5use crate::accumulator_event::AccumulatorEvent;
6use crate::base_types::{ExecutionDigests, ObjectID, ObjectRef, SequenceNumber};
7use crate::committee::{Committee, EpochId};
8use crate::crypto::{
9 AuthoritySignInfo, AuthoritySignInfoTrait, AuthorityStrongQuorumSignInfo, EmptySignInfo,
10 default_hash,
11};
12use crate::digests::{
13 ObjectDigest, TransactionDigest, TransactionEffectsDigest, TransactionEventsDigest,
14};
15use crate::error::SuiResult;
16use crate::event::Event;
17use crate::execution::SharedInput;
18use crate::execution_status::{ExecutionStatus, MoveLocation};
19use crate::gas::GasCostSummary;
20use crate::message_envelope::{Envelope, Message, TrustedEnvelope, VerifiedEnvelope};
21use crate::object::Owner;
22use crate::storage::WriteKind;
23pub use effects_v1::TransactionEffectsV1;
24pub use effects_v2::UnchangedConsensusKind;
25use enum_dispatch::enum_dispatch;
26pub use object_change::{
27 AccumulatorAddress, AccumulatorOperation, AccumulatorValue, AccumulatorWriteV1,
28 EffectsObjectChange, ObjectIn, ObjectOut,
29};
30use serde::{Deserialize, Serialize};
31use shared_crypto::intent::{Intent, IntentScope};
32use std::collections::{BTreeMap, BTreeSet};
33pub use test_effects_builder::TestEffectsBuilder;
34
35mod effects_v1;
36mod effects_v2;
37mod object_change;
38mod test_effects_builder;
39
40pub const APPROX_SIZE_OF_OBJECT_REF: usize = 80;
44pub const APPROX_SIZE_OF_EXECUTION_STATUS: usize = 120;
46pub const APPROX_SIZE_OF_EPOCH_ID: usize = 10;
48pub const APPROX_SIZE_OF_GAS_COST_SUMMARY: usize = 40;
50pub const APPROX_SIZE_OF_OPT_TX_EVENTS_DIGEST: usize = 40;
52pub const APPROX_SIZE_OF_TX_DIGEST: usize = 40;
54pub const APPROX_SIZE_OF_OWNER: usize = 48;
56
57#[enum_dispatch(TransactionEffectsAPI)]
59#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)]
60#[allow(clippy::large_enum_variant)]
61pub enum TransactionEffects {
62 V1(TransactionEffectsV1),
63 V2(TransactionEffectsV2),
64}
65
66impl Message for TransactionEffects {
67 type DigestType = TransactionEffectsDigest;
68 const SCOPE: IntentScope = IntentScope::TransactionEffects;
69
70 fn digest(&self) -> Self::DigestType {
71 TransactionEffectsDigest::new(default_hash(self))
72 }
73}
74
75impl Default for TransactionEffects {
77 fn default() -> Self {
78 TransactionEffects::V2(Default::default())
79 }
80}
81
82pub enum ObjectRemoveKind {
83 Delete,
84 Wrap,
85}
86
87impl TransactionEffects {
88 pub fn new_from_execution_v1(
91 status: ExecutionStatus,
92 executed_epoch: EpochId,
93 gas_used: GasCostSummary,
94 modified_at_versions: Vec<(ObjectID, SequenceNumber)>,
95 shared_objects: Vec<ObjectRef>,
96 transaction_digest: TransactionDigest,
97 created: Vec<(ObjectRef, Owner)>,
98 mutated: Vec<(ObjectRef, Owner)>,
99 unwrapped: Vec<(ObjectRef, Owner)>,
100 deleted: Vec<ObjectRef>,
101 unwrapped_then_deleted: Vec<ObjectRef>,
102 wrapped: Vec<ObjectRef>,
103 gas_object: (ObjectRef, Owner),
104 events_digest: Option<TransactionEventsDigest>,
105 dependencies: Vec<TransactionDigest>,
106 ) -> Self {
107 Self::V1(TransactionEffectsV1::new(
108 status,
109 executed_epoch,
110 gas_used,
111 modified_at_versions,
112 shared_objects,
113 transaction_digest,
114 created,
115 mutated,
116 unwrapped,
117 deleted,
118 unwrapped_then_deleted,
119 wrapped,
120 gas_object,
121 events_digest,
122 dependencies,
123 ))
124 }
125
126 pub fn new_from_execution_v2(
129 status: ExecutionStatus,
130 executed_epoch: EpochId,
131 gas_used: GasCostSummary,
132 shared_objects: Vec<SharedInput>,
133 loaded_per_epoch_config_objects: BTreeSet<ObjectID>,
134 transaction_digest: TransactionDigest,
135 lamport_version: SequenceNumber,
136 changed_objects: BTreeMap<ObjectID, EffectsObjectChange>,
137 gas_object: Option<ObjectID>,
138 events_digest: Option<TransactionEventsDigest>,
139 dependencies: Vec<TransactionDigest>,
140 ) -> Self {
141 Self::V2(TransactionEffectsV2::new(
142 status,
143 executed_epoch,
144 gas_used,
145 shared_objects,
146 loaded_per_epoch_config_objects,
147 transaction_digest,
148 lamport_version,
149 changed_objects,
150 gas_object,
151 events_digest,
152 dependencies,
153 ))
154 }
155
156 pub fn execution_digests(&self) -> ExecutionDigests {
157 ExecutionDigests {
158 transaction: *self.transaction_digest(),
159 effects: self.digest(),
160 }
161 }
162
163 pub fn estimate_effects_size_upperbound_v1(
164 num_writes: usize,
165 num_mutables: usize,
166 num_deletes: usize,
167 num_deps: usize,
168 ) -> usize {
169 let fixed_sizes = APPROX_SIZE_OF_EXECUTION_STATUS
170 + APPROX_SIZE_OF_EPOCH_ID
171 + APPROX_SIZE_OF_GAS_COST_SUMMARY
172 + APPROX_SIZE_OF_OPT_TX_EVENTS_DIGEST;
173
174 let approx_change_entry_size = 1_000
178 + (APPROX_SIZE_OF_OWNER + APPROX_SIZE_OF_OBJECT_REF) * num_writes
179 + (APPROX_SIZE_OF_OBJECT_REF * num_mutables)
180 + (APPROX_SIZE_OF_OBJECT_REF * num_deletes);
181
182 let deps_size = 1_000 + APPROX_SIZE_OF_TX_DIGEST * num_deps;
183
184 fixed_sizes + approx_change_entry_size + deps_size
185 }
186
187 pub fn estimate_effects_size_upperbound_v2(
188 num_writes: usize,
189 num_modifies: usize,
190 num_deps: usize,
191 ) -> usize {
192 let fixed_sizes = APPROX_SIZE_OF_EXECUTION_STATUS
193 + APPROX_SIZE_OF_EPOCH_ID
194 + APPROX_SIZE_OF_GAS_COST_SUMMARY
195 + APPROX_SIZE_OF_OPT_TX_EVENTS_DIGEST;
196
197 let approx_change_entry_size = 1_000
199 + (APPROX_SIZE_OF_OWNER + APPROX_SIZE_OF_OBJECT_REF) * num_writes
200 + (APPROX_SIZE_OF_OWNER + APPROX_SIZE_OF_OBJECT_REF) * num_modifies;
201
202 let deps_size = 1_000 + APPROX_SIZE_OF_TX_DIGEST * num_deps;
203
204 fixed_sizes + approx_change_entry_size + deps_size
205 }
206
207 pub fn all_changed_objects(&self) -> Vec<(ObjectRef, Owner, WriteKind)> {
212 self.mutated()
213 .into_iter()
214 .map(|(r, o)| (r, o, WriteKind::Mutate))
215 .chain(
216 self.created()
217 .into_iter()
218 .map(|(r, o)| (r, o, WriteKind::Create)),
219 )
220 .chain(
221 self.unwrapped()
222 .into_iter()
223 .map(|(r, o)| (r, o, WriteKind::Unwrap)),
224 )
225 .collect()
226 }
227
228 pub fn all_removed_objects(&self) -> Vec<(ObjectRef, ObjectRemoveKind)> {
232 self.deleted()
233 .iter()
234 .map(|obj_ref| (*obj_ref, ObjectRemoveKind::Delete))
235 .chain(
236 self.wrapped()
237 .iter()
238 .map(|obj_ref| (*obj_ref, ObjectRemoveKind::Wrap)),
239 )
240 .collect()
241 }
242
243 pub fn all_tombstones(&self) -> Vec<(ObjectID, SequenceNumber)> {
246 self.deleted()
247 .into_iter()
248 .chain(self.unwrapped_then_deleted())
249 .chain(self.wrapped())
250 .map(|obj_ref| (obj_ref.0, obj_ref.1))
251 .collect()
252 }
253
254 pub fn mutated_excluding_gas(&self) -> Vec<(ObjectRef, Owner)> {
256 let gas_id = self.gas_object().map(|(oref, _)| oref.0);
257 self.mutated()
258 .into_iter()
259 .filter(|o| Some(o.0.0) != gas_id)
260 .collect()
261 }
262
263 pub fn summary_for_debug(&self) -> TransactionEffectsDebugSummary {
264 TransactionEffectsDebugSummary {
265 bcs_size: bcs::serialized_size(self).unwrap(),
266 status: self.status().clone(),
267 gas_used: self.gas_cost_summary().clone(),
268 transaction_digest: *self.transaction_digest(),
269 created_object_count: self.created().len(),
270 mutated_object_count: self.mutated().len(),
271 unwrapped_object_count: self.unwrapped().len(),
272 deleted_object_count: self.deleted().len(),
273 wrapped_object_count: self.wrapped().len(),
274 dependency_count: self.dependencies().len(),
275 }
276 }
277}
278
279#[derive(Eq, PartialEq, Clone, Debug)]
280pub enum InputConsensusObject {
281 Mutate(ObjectRef),
282 ReadOnly(ObjectRef),
283 ReadConsensusStreamEnded(ObjectID, SequenceNumber),
284 MutateConsensusStreamEnded(ObjectID, SequenceNumber),
285 Cancelled(ObjectID, SequenceNumber),
286}
287
288impl InputConsensusObject {
289 pub fn id_and_version(&self) -> (ObjectID, SequenceNumber) {
290 match self {
291 InputConsensusObject::Mutate(oref) | InputConsensusObject::ReadOnly(oref) => {
292 (oref.0, oref.1)
293 }
294 InputConsensusObject::ReadConsensusStreamEnded(id, version)
295 | InputConsensusObject::MutateConsensusStreamEnded(id, version) => (*id, *version),
296 InputConsensusObject::Cancelled(id, version) => (*id, *version),
297 }
298 }
299
300 #[deprecated]
303 pub fn object_ref(&self) -> ObjectRef {
304 match self {
305 InputConsensusObject::Mutate(oref) | InputConsensusObject::ReadOnly(oref) => *oref,
306 InputConsensusObject::ReadConsensusStreamEnded(id, version)
307 | InputConsensusObject::MutateConsensusStreamEnded(id, version) => {
308 (*id, *version, ObjectDigest::OBJECT_DIGEST_DELETED)
309 }
310 InputConsensusObject::Cancelled(id, version) => {
311 (*id, *version, ObjectDigest::OBJECT_DIGEST_CANCELLED)
312 }
313 }
314 }
315}
316
317#[enum_dispatch]
318pub trait TransactionEffectsAPI {
319 fn status(&self) -> &ExecutionStatus;
320 fn into_status(self) -> ExecutionStatus;
321 fn executed_epoch(&self) -> EpochId;
322 fn modified_at_versions(&self) -> Vec<(ObjectID, SequenceNumber)>;
323 fn move_abort(&self) -> Option<(MoveLocation, u64)>;
324
325 fn lamport_version(&self) -> SequenceNumber;
327
328 fn old_object_metadata(&self) -> Vec<(ObjectRef, Owner)>;
333 fn input_consensus_objects(&self) -> Vec<InputConsensusObject>;
339 fn created(&self) -> Vec<(ObjectRef, Owner)>;
340 fn mutated(&self) -> Vec<(ObjectRef, Owner)>;
341 fn unwrapped(&self) -> Vec<(ObjectRef, Owner)>;
342 fn deleted(&self) -> Vec<ObjectRef>;
343 fn unwrapped_then_deleted(&self) -> Vec<ObjectRef>;
344 fn wrapped(&self) -> Vec<ObjectRef>;
345 fn transferred_from_consensus(&self) -> Vec<ObjectRef>;
346 fn transferred_to_consensus(&self) -> Vec<ObjectRef>;
347 fn consensus_owner_changed(&self) -> Vec<ObjectRef>;
348
349 fn object_changes(&self) -> Vec<ObjectChange>;
350 fn published_packages(&self) -> Vec<ObjectID>;
351
352 fn written(&self) -> Vec<ObjectRef>;
355
356 fn accumulator_events(&self) -> Vec<AccumulatorEvent>;
357
358 fn gas_object(&self) -> Option<(ObjectRef, Owner)>;
362
363 fn events_digest(&self) -> Option<&TransactionEventsDigest>;
364 fn dependencies(&self) -> &[TransactionDigest];
365
366 fn transaction_digest(&self) -> &TransactionDigest;
367
368 fn gas_cost_summary(&self) -> &GasCostSummary;
369
370 fn stream_ended_mutably_accessed_consensus_objects(&self) -> Vec<ObjectID> {
371 self.input_consensus_objects()
372 .into_iter()
373 .filter_map(|kind| match kind {
374 InputConsensusObject::MutateConsensusStreamEnded(id, _) => Some(id),
375 InputConsensusObject::Mutate(..)
376 | InputConsensusObject::ReadOnly(..)
377 | InputConsensusObject::ReadConsensusStreamEnded(..)
378 | InputConsensusObject::Cancelled(..) => None,
379 })
380 .collect()
381 }
382
383 fn unchanged_consensus_objects(&self) -> Vec<(ObjectID, UnchangedConsensusKind)>;
385
386 fn accumulator_updates(&self) -> Vec<(ObjectID, AccumulatorWriteV1)>;
388
389 fn status_mut_for_testing(&mut self) -> &mut ExecutionStatus;
392 fn gas_cost_summary_mut_for_testing(&mut self) -> &mut GasCostSummary;
393 fn transaction_digest_mut_for_testing(&mut self) -> &mut TransactionDigest;
394 fn dependencies_mut_for_testing(&mut self) -> &mut Vec<TransactionDigest>;
395 fn unsafe_add_input_consensus_object_for_testing(&mut self, kind: InputConsensusObject);
396
397 fn unsafe_add_deleted_live_object_for_testing(&mut self, obj_ref: ObjectRef);
399
400 fn unsafe_add_object_tombstone_for_testing(&mut self, obj_ref: ObjectRef);
402}
403
404#[derive(Clone, Debug)]
405pub struct ObjectChange {
406 pub id: ObjectID,
407 pub input_version: Option<SequenceNumber>,
408 pub input_digest: Option<ObjectDigest>,
409 pub output_version: Option<SequenceNumber>,
410 pub output_digest: Option<ObjectDigest>,
411 pub id_operation: IDOperation,
412}
413
414#[derive(Eq, PartialEq, Copy, Clone, Debug, Serialize, Deserialize)]
415pub enum IDOperation {
416 None,
417 Created,
418 Deleted,
419}
420
421#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, Default)]
422pub struct TransactionEvents {
423 pub data: Vec<Event>,
424}
425
426impl TransactionEvents {
427 pub fn digest(&self) -> TransactionEventsDigest {
428 TransactionEventsDigest::new(default_hash(self))
429 }
430}
431
432#[derive(Debug)]
433pub struct TransactionEffectsDebugSummary {
434 pub bcs_size: usize,
436 pub status: ExecutionStatus,
437 pub gas_used: GasCostSummary,
438 pub transaction_digest: TransactionDigest,
439 pub created_object_count: usize,
440 pub mutated_object_count: usize,
441 pub unwrapped_object_count: usize,
442 pub deleted_object_count: usize,
443 pub wrapped_object_count: usize,
444 pub dependency_count: usize,
445 }
447
448pub type TransactionEffectsEnvelope<S> = Envelope<TransactionEffects, S>;
449pub type UnsignedTransactionEffects = TransactionEffectsEnvelope<EmptySignInfo>;
450pub type SignedTransactionEffects = TransactionEffectsEnvelope<AuthoritySignInfo>;
451pub type CertifiedTransactionEffects = TransactionEffectsEnvelope<AuthorityStrongQuorumSignInfo>;
452
453pub type TrustedSignedTransactionEffects = TrustedEnvelope<TransactionEffects, AuthoritySignInfo>;
454pub type VerifiedTransactionEffectsEnvelope<S> = VerifiedEnvelope<TransactionEffects, S>;
455pub type VerifiedSignedTransactionEffects = VerifiedTransactionEffectsEnvelope<AuthoritySignInfo>;
456pub type VerifiedCertifiedTransactionEffects =
457 VerifiedTransactionEffectsEnvelope<AuthorityStrongQuorumSignInfo>;
458
459impl CertifiedTransactionEffects {
460 pub fn verify_authority_signatures(&self, committee: &Committee) -> SuiResult {
461 self.auth_sig().verify_secure(
462 self.data(),
463 Intent::sui_app(IntentScope::TransactionEffects),
464 committee,
465 )
466 }
467
468 pub fn verify(self, committee: &Committee) -> SuiResult<VerifiedCertifiedTransactionEffects> {
469 self.verify_authority_signatures(committee)?;
470 Ok(VerifiedCertifiedTransactionEffects::new_from_verified(self))
471 }
472}
473
474#[cfg(test)]
475#[path = "../unit_tests/effects_tests.rs"]
476mod effects_tests;