1pub use self::effects_v2::TransactionEffectsV2;
5use crate::TypeTag;
6use crate::accumulator_event::AccumulatorEvent;
7use crate::base_types::{ExecutionDigests, ObjectID, ObjectRef, SequenceNumber};
8use crate::committee::EpochId;
9use crate::crypto::{AuthoritySignInfo, EmptySignInfo, default_hash};
10use crate::digests::{
11 ObjectDigest, TransactionDigest, TransactionEffectsDigest, TransactionEventsDigest,
12};
13use crate::event::Event;
14use crate::execution_status::{ExecutionStatus, MoveLocation};
15use crate::gas::GasCostSummary;
16use crate::message_envelope::{Envelope, Message, TrustedEnvelope, VerifiedEnvelope};
17use crate::object::Owner;
18use crate::storage::WriteKind;
19pub use effects_v1::TransactionEffectsV1;
20pub use effects_v2::UnchangedConsensusKind;
21use enum_dispatch::enum_dispatch;
22pub use object_change::{
23 AccumulatorAddress, AccumulatorOperation, AccumulatorValue, AccumulatorWriteV1,
24 EffectsObjectChange, ObjectIn, ObjectOut,
25};
26use serde::{Deserialize, Serialize};
27use shared_crypto::intent::IntentScope;
28use std::collections::{BTreeMap, BTreeSet};
29pub use test_effects_builder::TestEffectsBuilder;
30
31mod effects_v1;
32mod effects_v2;
33mod object_change;
34mod test_effects_builder;
35
36pub const APPROX_SIZE_OF_OBJECT_REF: usize = 80;
40pub const APPROX_SIZE_OF_EXECUTION_STATUS: usize = 120;
42pub const APPROX_SIZE_OF_EPOCH_ID: usize = 10;
44pub const APPROX_SIZE_OF_GAS_COST_SUMMARY: usize = 40;
46pub const APPROX_SIZE_OF_OPT_TX_EVENTS_DIGEST: usize = 40;
48pub const APPROX_SIZE_OF_TX_DIGEST: usize = 40;
50pub const APPROX_SIZE_OF_OWNER: usize = 48;
52pub const APPROX_SIZE_OF_ACCUMULATOR_WRITE: usize = 96;
55pub const APPROX_SIZE_OF_ACCUMULATOR_EVENT_DIGEST: usize = 48;
57pub const APPROX_SIZE_OF_ID_ONLY_CHANGE: usize = 40;
60pub const APPROX_SIZE_OF_UNCHANGED_CONSENSUS_OBJECT: usize = 80;
62
63#[enum_dispatch(TransactionEffectsAPI)]
65#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)]
66#[allow(clippy::large_enum_variant)]
67pub enum TransactionEffects {
68 V1(TransactionEffectsV1),
69 V2(TransactionEffectsV2),
70}
71
72impl Message for TransactionEffects {
73 type DigestType = TransactionEffectsDigest;
74 const SCOPE: IntentScope = IntentScope::TransactionEffects;
75
76 fn digest(&self) -> Self::DigestType {
77 TransactionEffectsDigest::new(default_hash(self))
78 }
79}
80
81impl Default for TransactionEffects {
83 fn default() -> Self {
84 TransactionEffects::V2(Default::default())
85 }
86}
87
88pub enum ObjectRemoveKind {
89 Delete,
90 Wrap,
91}
92
93impl TransactionEffects {
94 pub fn new_from_execution_v1(
97 status: ExecutionStatus,
98 executed_epoch: EpochId,
99 gas_used: GasCostSummary,
100 modified_at_versions: Vec<(ObjectID, SequenceNumber)>,
101 shared_objects: Vec<ObjectRef>,
102 transaction_digest: TransactionDigest,
103 created: Vec<(ObjectRef, Owner)>,
104 mutated: Vec<(ObjectRef, Owner)>,
105 unwrapped: Vec<(ObjectRef, Owner)>,
106 deleted: Vec<ObjectRef>,
107 unwrapped_then_deleted: Vec<ObjectRef>,
108 wrapped: Vec<ObjectRef>,
109 gas_object: (ObjectRef, Owner),
110 events_digest: Option<TransactionEventsDigest>,
111 dependencies: Vec<TransactionDigest>,
112 ) -> Self {
113 Self::V1(TransactionEffectsV1::new(
114 status,
115 executed_epoch,
116 gas_used,
117 modified_at_versions,
118 shared_objects,
119 transaction_digest,
120 created,
121 mutated,
122 unwrapped,
123 deleted,
124 unwrapped_then_deleted,
125 wrapped,
126 gas_object,
127 events_digest,
128 dependencies,
129 ))
130 }
131
132 pub fn new_from_execution_v2(
135 status: ExecutionStatus,
136 executed_epoch: EpochId,
137 gas_used: GasCostSummary,
138 unchanged_consensus_objects: Vec<(ObjectID, UnchangedConsensusKind)>,
139 transaction_digest: TransactionDigest,
140 lamport_version: SequenceNumber,
141 changed_objects: BTreeMap<ObjectID, EffectsObjectChange>,
142 gas_object: Option<ObjectID>,
143 events_digest: Option<TransactionEventsDigest>,
144 dependencies: Vec<TransactionDigest>,
145 ) -> Self {
146 Self::V2(TransactionEffectsV2::new(
147 status,
148 executed_epoch,
149 gas_used,
150 unchanged_consensus_objects,
151 transaction_digest,
152 lamport_version,
153 changed_objects,
154 gas_object,
155 events_digest,
156 dependencies,
157 ))
158 }
159
160 pub fn execution_digests(&self) -> ExecutionDigests {
161 ExecutionDigests {
162 transaction: *self.transaction_digest(),
163 effects: self.digest(),
164 }
165 }
166
167 pub fn estimate_effects_size_upperbound_v1(
168 num_writes: usize,
169 num_mutables: usize,
170 num_deletes: usize,
171 num_deps: usize,
172 ) -> usize {
173 let fixed_sizes = APPROX_SIZE_OF_EXECUTION_STATUS
174 + APPROX_SIZE_OF_EPOCH_ID
175 + APPROX_SIZE_OF_GAS_COST_SUMMARY
176 + APPROX_SIZE_OF_OPT_TX_EVENTS_DIGEST;
177
178 let approx_change_entry_size = 1_000
182 + (APPROX_SIZE_OF_OWNER + APPROX_SIZE_OF_OBJECT_REF) * num_writes
183 + (APPROX_SIZE_OF_OBJECT_REF * num_mutables)
184 + (APPROX_SIZE_OF_OBJECT_REF * num_deletes);
185
186 let deps_size = 1_000 + APPROX_SIZE_OF_TX_DIGEST * num_deps;
187
188 fixed_sizes + approx_change_entry_size + deps_size
189 }
190
191 pub fn estimate_effects_size_upperbound_v2(
194 num_writes: usize,
195 num_modifies: usize,
196 num_deps: usize,
197 num_id_only_changes: usize,
198 num_unchanged_consensus_objects: usize,
199 accumulator_events: &[AccumulatorEvent],
200 complete_effects_size_estimate: bool,
201 ) -> usize {
202 let fixed_sizes = APPROX_SIZE_OF_EXECUTION_STATUS
203 + APPROX_SIZE_OF_EPOCH_ID
204 + APPROX_SIZE_OF_GAS_COST_SUMMARY
205 + APPROX_SIZE_OF_OPT_TX_EVENTS_DIGEST;
206
207 let approx_change_entry_size = 1_000
209 + (APPROX_SIZE_OF_OWNER + APPROX_SIZE_OF_OBJECT_REF) * num_writes
210 + (APPROX_SIZE_OF_OWNER + APPROX_SIZE_OF_OBJECT_REF) * num_modifies;
211
212 let deps_size = 1_000 + APPROX_SIZE_OF_TX_DIGEST * num_deps;
213
214 let mut size = fixed_sizes + approx_change_entry_size + deps_size;
215
216 if complete_effects_size_estimate {
217 let id_only_changes_size =
218 num_id_only_changes.saturating_mul(APPROX_SIZE_OF_ID_ONLY_CHANGE);
219 let unchanged_consensus_objects_size = num_unchanged_consensus_objects
220 .saturating_mul(APPROX_SIZE_OF_UNCHANGED_CONSENSUS_OBJECT);
221 let accumulator_writes_size =
222 Self::estimate_accumulator_effects_size_upperbound(accumulator_events);
223
224 size = size
225 .saturating_add(id_only_changes_size)
226 .saturating_add(unchanged_consensus_objects_size)
227 .saturating_add(accumulator_writes_size);
228 }
229
230 size
231 }
232
233 fn estimate_accumulator_effects_size_upperbound(
236 accumulator_events: &[AccumulatorEvent],
237 ) -> usize {
238 let mut seen = BTreeSet::new();
239 let mut size: usize = 0;
240 for event in accumulator_events {
241 if seen.insert(event.accumulator_obj) {
242 size = size.saturating_add(Self::estimate_accumulator_write_size_upperbound(
243 &event.write.address.ty,
244 ));
245 }
246 let digests_size = match &event.write.value {
247 AccumulatorValue::Integer(_) | AccumulatorValue::IntegerTuple(_, _) => 0,
248 AccumulatorValue::EventDigest(digests) => digests
249 .len()
250 .saturating_mul(APPROX_SIZE_OF_ACCUMULATOR_EVENT_DIGEST),
251 };
252 size = size.saturating_add(digests_size);
253 }
254 size
255 }
256
257 pub fn estimate_accumulator_write_size_upperbound(ty: &TypeTag) -> usize {
259 bcs::serialized_size(ty)
260 .unwrap_or(usize::MAX)
261 .saturating_add(APPROX_SIZE_OF_ACCUMULATOR_WRITE)
262 }
263
264 pub fn all_changed_objects(&self) -> Vec<(ObjectRef, Owner, WriteKind)> {
269 self.mutated()
270 .into_iter()
271 .map(|(r, o)| (r, o, WriteKind::Mutate))
272 .chain(
273 self.created()
274 .into_iter()
275 .map(|(r, o)| (r, o, WriteKind::Create)),
276 )
277 .chain(
278 self.unwrapped()
279 .into_iter()
280 .map(|(r, o)| (r, o, WriteKind::Unwrap)),
281 )
282 .collect()
283 }
284
285 pub fn all_removed_objects(&self) -> Vec<(ObjectRef, ObjectRemoveKind)> {
289 self.deleted()
290 .iter()
291 .map(|obj_ref| (*obj_ref, ObjectRemoveKind::Delete))
292 .chain(
293 self.wrapped()
294 .iter()
295 .map(|obj_ref| (*obj_ref, ObjectRemoveKind::Wrap)),
296 )
297 .collect()
298 }
299
300 pub fn all_tombstones(&self) -> Vec<(ObjectID, SequenceNumber)> {
303 self.deleted()
304 .into_iter()
305 .chain(self.unwrapped_then_deleted())
306 .chain(self.wrapped())
307 .map(|obj_ref| (obj_ref.0, obj_ref.1))
308 .collect()
309 }
310
311 pub fn mutated_excluding_gas(&self) -> Vec<(ObjectRef, Owner)> {
313 let gas_id = self.gas_object().map(|(oref, _)| oref.0);
314 self.mutated()
315 .into_iter()
316 .filter(|o| Some(o.0.0) != gas_id)
317 .collect()
318 }
319
320 pub fn summary_for_debug(&self) -> TransactionEffectsDebugSummary {
321 TransactionEffectsDebugSummary {
322 bcs_size: bcs::serialized_size(self).unwrap(),
323 status: self.status().clone(),
324 gas_used: self.gas_cost_summary().clone(),
325 transaction_digest: *self.transaction_digest(),
326 created_object_count: self.created().len(),
327 mutated_object_count: self.mutated().len(),
328 unwrapped_object_count: self.unwrapped().len(),
329 deleted_object_count: self.deleted().len(),
330 wrapped_object_count: self.wrapped().len(),
331 dependency_count: self.dependencies().len(),
332 }
333 }
334}
335
336#[derive(Eq, PartialEq, Clone, Debug)]
337pub enum InputConsensusObject {
338 Mutate(ObjectRef),
339 ReadOnly(ObjectRef),
340 ReadConsensusStreamEnded(ObjectID, SequenceNumber),
341 MutateConsensusStreamEnded(ObjectID, SequenceNumber),
342 Cancelled(ObjectID, SequenceNumber),
343}
344
345impl InputConsensusObject {
346 pub fn id_and_version(&self) -> (ObjectID, SequenceNumber) {
347 match self {
348 InputConsensusObject::Mutate(oref) | InputConsensusObject::ReadOnly(oref) => {
349 (oref.0, oref.1)
350 }
351 InputConsensusObject::ReadConsensusStreamEnded(id, version)
352 | InputConsensusObject::MutateConsensusStreamEnded(id, version) => (*id, *version),
353 InputConsensusObject::Cancelled(id, version) => (*id, *version),
354 }
355 }
356
357 #[deprecated]
360 pub fn object_ref(&self) -> ObjectRef {
361 match self {
362 InputConsensusObject::Mutate(oref) | InputConsensusObject::ReadOnly(oref) => *oref,
363 InputConsensusObject::ReadConsensusStreamEnded(id, version)
364 | InputConsensusObject::MutateConsensusStreamEnded(id, version) => {
365 (*id, *version, ObjectDigest::OBJECT_DIGEST_DELETED)
366 }
367 InputConsensusObject::Cancelled(id, version) => {
368 (*id, *version, ObjectDigest::OBJECT_DIGEST_CANCELLED)
369 }
370 }
371 }
372}
373
374#[enum_dispatch]
375pub trait TransactionEffectsAPI {
376 fn status(&self) -> &ExecutionStatus;
377 fn into_status(self) -> ExecutionStatus;
378 fn executed_epoch(&self) -> EpochId;
379 fn modified_at_versions(&self) -> Vec<(ObjectID, SequenceNumber)>;
380 fn move_abort(&self) -> Option<(MoveLocation, u64)>;
381
382 fn lamport_version(&self) -> SequenceNumber;
384
385 fn old_object_metadata(&self) -> Vec<(ObjectRef, Owner)>;
390 fn accessed_consensus_objects(&self) -> Vec<InputConsensusObject>;
402 fn created(&self) -> Vec<(ObjectRef, Owner)>;
403 fn mutated(&self) -> Vec<(ObjectRef, Owner)>;
404 fn unwrapped(&self) -> Vec<(ObjectRef, Owner)>;
405 fn deleted(&self) -> Vec<ObjectRef>;
406 fn unwrapped_then_deleted(&self) -> Vec<ObjectRef>;
407 fn wrapped(&self) -> Vec<ObjectRef>;
408 fn transferred_from_consensus(&self) -> Vec<ObjectRef>;
409 fn transferred_to_consensus(&self) -> Vec<ObjectRef>;
410 fn consensus_owner_changed(&self) -> Vec<ObjectRef>;
411
412 fn object_changes(&self) -> Vec<ObjectChange>;
413 fn published_packages(&self) -> Vec<ObjectID>;
414
415 fn written(&self) -> Vec<ObjectRef>;
418
419 fn accumulator_events(&self) -> Vec<AccumulatorEvent>;
420
421 fn gas_object(&self) -> Option<(ObjectRef, Owner)>;
425
426 fn events_digest(&self) -> Option<&TransactionEventsDigest>;
427 fn dependencies(&self) -> &[TransactionDigest];
428
429 fn transaction_digest(&self) -> &TransactionDigest;
430
431 fn gas_cost_summary(&self) -> &GasCostSummary;
432
433 fn stream_ended_mutably_accessed_consensus_objects(&self) -> Vec<ObjectID> {
434 self.accessed_consensus_objects()
435 .into_iter()
436 .filter_map(|kind| match kind {
437 InputConsensusObject::MutateConsensusStreamEnded(id, _) => Some(id),
438 InputConsensusObject::Mutate(..)
439 | InputConsensusObject::ReadOnly(..)
440 | InputConsensusObject::ReadConsensusStreamEnded(..)
441 | InputConsensusObject::Cancelled(..) => None,
442 })
443 .collect()
444 }
445
446 fn unchanged_consensus_objects(&self) -> Vec<(ObjectID, UnchangedConsensusKind)>;
448
449 fn accumulator_updates(&self) -> Vec<(ObjectID, AccumulatorWriteV1)>;
451
452 fn status_mut_for_testing(&mut self) -> &mut ExecutionStatus;
455 fn gas_cost_summary_mut_for_testing(&mut self) -> &mut GasCostSummary;
456 fn transaction_digest_mut_for_testing(&mut self) -> &mut TransactionDigest;
457 fn dependencies_mut_for_testing(&mut self) -> &mut Vec<TransactionDigest>;
458 fn unsafe_add_input_consensus_object_for_testing(&mut self, kind: InputConsensusObject);
459
460 fn unsafe_add_deleted_live_object_for_testing(&mut self, obj_ref: ObjectRef);
462
463 fn unsafe_add_object_tombstone_for_testing(&mut self, obj_ref: ObjectRef);
465}
466
467#[derive(Clone, Debug)]
468pub struct ObjectChange {
469 pub id: ObjectID,
470 pub input_version: Option<SequenceNumber>,
471 pub input_digest: Option<ObjectDigest>,
472 pub output_version: Option<SequenceNumber>,
473 pub output_digest: Option<ObjectDigest>,
474 pub id_operation: IDOperation,
475}
476
477#[derive(Eq, PartialEq, Copy, Clone, Debug, Serialize, Deserialize)]
478pub enum IDOperation {
479 None,
480 Created,
481 Deleted,
482}
483
484#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, Default)]
485pub struct TransactionEvents {
486 pub data: Vec<Event>,
487}
488
489impl TransactionEvents {
490 pub fn digest(&self) -> TransactionEventsDigest {
491 TransactionEventsDigest::new(default_hash(self))
492 }
493}
494
495#[derive(Debug)]
496pub struct TransactionEffectsDebugSummary {
497 pub bcs_size: usize,
499 pub status: ExecutionStatus,
500 pub gas_used: GasCostSummary,
501 pub transaction_digest: TransactionDigest,
502 pub created_object_count: usize,
503 pub mutated_object_count: usize,
504 pub unwrapped_object_count: usize,
505 pub deleted_object_count: usize,
506 pub wrapped_object_count: usize,
507 pub dependency_count: usize,
508 }
510
511pub type TransactionEffectsEnvelope<S> = Envelope<TransactionEffects, S>;
512pub type UnsignedTransactionEffects = TransactionEffectsEnvelope<EmptySignInfo>;
513pub type SignedTransactionEffects = TransactionEffectsEnvelope<AuthoritySignInfo>;
514
515pub type TrustedSignedTransactionEffects = TrustedEnvelope<TransactionEffects, AuthoritySignInfo>;
516pub type VerifiedTransactionEffectsEnvelope<S> = VerifiedEnvelope<TransactionEffects, S>;
517pub type VerifiedSignedTransactionEffects = VerifiedTransactionEffectsEnvelope<AuthoritySignInfo>;
518
519#[cfg(test)]
520#[path = "../unit_tests/effects_tests.rs"]
521mod effects_tests;