1pub use self::effects_v2::TransactionEffectsV2;
5use crate::accumulator_event::AccumulatorEvent;
6use crate::base_types::{ExecutionDigests, ObjectID, ObjectRef, SequenceNumber};
7use crate::committee::EpochId;
8use crate::crypto::{AuthoritySignInfo, EmptySignInfo, default_hash};
9use crate::digests::{
10 ObjectDigest, TransactionDigest, TransactionEffectsDigest, TransactionEventsDigest,
11};
12use crate::event::Event;
13use crate::execution::SharedInput;
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;
52
53#[enum_dispatch(TransactionEffectsAPI)]
55#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)]
56#[allow(clippy::large_enum_variant)]
57pub enum TransactionEffects {
58 V1(TransactionEffectsV1),
59 V2(TransactionEffectsV2),
60}
61
62impl Message for TransactionEffects {
63 type DigestType = TransactionEffectsDigest;
64 const SCOPE: IntentScope = IntentScope::TransactionEffects;
65
66 fn digest(&self) -> Self::DigestType {
67 TransactionEffectsDigest::new(default_hash(self))
68 }
69}
70
71impl Default for TransactionEffects {
73 fn default() -> Self {
74 TransactionEffects::V2(Default::default())
75 }
76}
77
78pub enum ObjectRemoveKind {
79 Delete,
80 Wrap,
81}
82
83impl TransactionEffects {
84 pub fn new_from_execution_v1(
87 status: ExecutionStatus,
88 executed_epoch: EpochId,
89 gas_used: GasCostSummary,
90 modified_at_versions: Vec<(ObjectID, SequenceNumber)>,
91 shared_objects: Vec<ObjectRef>,
92 transaction_digest: TransactionDigest,
93 created: Vec<(ObjectRef, Owner)>,
94 mutated: Vec<(ObjectRef, Owner)>,
95 unwrapped: Vec<(ObjectRef, Owner)>,
96 deleted: Vec<ObjectRef>,
97 unwrapped_then_deleted: Vec<ObjectRef>,
98 wrapped: Vec<ObjectRef>,
99 gas_object: (ObjectRef, Owner),
100 events_digest: Option<TransactionEventsDigest>,
101 dependencies: Vec<TransactionDigest>,
102 ) -> Self {
103 Self::V1(TransactionEffectsV1::new(
104 status,
105 executed_epoch,
106 gas_used,
107 modified_at_versions,
108 shared_objects,
109 transaction_digest,
110 created,
111 mutated,
112 unwrapped,
113 deleted,
114 unwrapped_then_deleted,
115 wrapped,
116 gas_object,
117 events_digest,
118 dependencies,
119 ))
120 }
121
122 pub fn new_from_execution_v2(
125 status: ExecutionStatus,
126 executed_epoch: EpochId,
127 gas_used: GasCostSummary,
128 shared_objects: Vec<SharedInput>,
129 loaded_per_epoch_config_objects: BTreeSet<ObjectID>,
130 transaction_digest: TransactionDigest,
131 lamport_version: SequenceNumber,
132 changed_objects: BTreeMap<ObjectID, EffectsObjectChange>,
133 gas_object: Option<ObjectID>,
134 events_digest: Option<TransactionEventsDigest>,
135 dependencies: Vec<TransactionDigest>,
136 ) -> Self {
137 Self::V2(TransactionEffectsV2::new(
138 status,
139 executed_epoch,
140 gas_used,
141 shared_objects,
142 loaded_per_epoch_config_objects,
143 transaction_digest,
144 lamport_version,
145 changed_objects,
146 gas_object,
147 events_digest,
148 dependencies,
149 ))
150 }
151
152 pub fn execution_digests(&self) -> ExecutionDigests {
153 ExecutionDigests {
154 transaction: *self.transaction_digest(),
155 effects: self.digest(),
156 }
157 }
158
159 pub fn estimate_effects_size_upperbound_v1(
160 num_writes: usize,
161 num_mutables: usize,
162 num_deletes: usize,
163 num_deps: usize,
164 ) -> usize {
165 let fixed_sizes = APPROX_SIZE_OF_EXECUTION_STATUS
166 + APPROX_SIZE_OF_EPOCH_ID
167 + APPROX_SIZE_OF_GAS_COST_SUMMARY
168 + APPROX_SIZE_OF_OPT_TX_EVENTS_DIGEST;
169
170 let approx_change_entry_size = 1_000
174 + (APPROX_SIZE_OF_OWNER + APPROX_SIZE_OF_OBJECT_REF) * num_writes
175 + (APPROX_SIZE_OF_OBJECT_REF * num_mutables)
176 + (APPROX_SIZE_OF_OBJECT_REF * num_deletes);
177
178 let deps_size = 1_000 + APPROX_SIZE_OF_TX_DIGEST * num_deps;
179
180 fixed_sizes + approx_change_entry_size + deps_size
181 }
182
183 pub fn estimate_effects_size_upperbound_v2(
184 num_writes: usize,
185 num_modifies: usize,
186 num_deps: usize,
187 ) -> usize {
188 let fixed_sizes = APPROX_SIZE_OF_EXECUTION_STATUS
189 + APPROX_SIZE_OF_EPOCH_ID
190 + APPROX_SIZE_OF_GAS_COST_SUMMARY
191 + APPROX_SIZE_OF_OPT_TX_EVENTS_DIGEST;
192
193 let approx_change_entry_size = 1_000
195 + (APPROX_SIZE_OF_OWNER + APPROX_SIZE_OF_OBJECT_REF) * num_writes
196 + (APPROX_SIZE_OF_OWNER + APPROX_SIZE_OF_OBJECT_REF) * num_modifies;
197
198 let deps_size = 1_000 + APPROX_SIZE_OF_TX_DIGEST * num_deps;
199
200 fixed_sizes + approx_change_entry_size + deps_size
201 }
202
203 pub fn all_changed_objects(&self) -> Vec<(ObjectRef, Owner, WriteKind)> {
208 self.mutated()
209 .into_iter()
210 .map(|(r, o)| (r, o, WriteKind::Mutate))
211 .chain(
212 self.created()
213 .into_iter()
214 .map(|(r, o)| (r, o, WriteKind::Create)),
215 )
216 .chain(
217 self.unwrapped()
218 .into_iter()
219 .map(|(r, o)| (r, o, WriteKind::Unwrap)),
220 )
221 .collect()
222 }
223
224 pub fn all_removed_objects(&self) -> Vec<(ObjectRef, ObjectRemoveKind)> {
228 self.deleted()
229 .iter()
230 .map(|obj_ref| (*obj_ref, ObjectRemoveKind::Delete))
231 .chain(
232 self.wrapped()
233 .iter()
234 .map(|obj_ref| (*obj_ref, ObjectRemoveKind::Wrap)),
235 )
236 .collect()
237 }
238
239 pub fn all_tombstones(&self) -> Vec<(ObjectID, SequenceNumber)> {
242 self.deleted()
243 .into_iter()
244 .chain(self.unwrapped_then_deleted())
245 .chain(self.wrapped())
246 .map(|obj_ref| (obj_ref.0, obj_ref.1))
247 .collect()
248 }
249
250 pub fn mutated_excluding_gas(&self) -> Vec<(ObjectRef, Owner)> {
252 let gas_id = self.gas_object().map(|(oref, _)| oref.0);
253 self.mutated()
254 .into_iter()
255 .filter(|o| Some(o.0.0) != gas_id)
256 .collect()
257 }
258
259 pub fn summary_for_debug(&self) -> TransactionEffectsDebugSummary {
260 TransactionEffectsDebugSummary {
261 bcs_size: bcs::serialized_size(self).unwrap(),
262 status: self.status().clone(),
263 gas_used: self.gas_cost_summary().clone(),
264 transaction_digest: *self.transaction_digest(),
265 created_object_count: self.created().len(),
266 mutated_object_count: self.mutated().len(),
267 unwrapped_object_count: self.unwrapped().len(),
268 deleted_object_count: self.deleted().len(),
269 wrapped_object_count: self.wrapped().len(),
270 dependency_count: self.dependencies().len(),
271 }
272 }
273}
274
275#[derive(Eq, PartialEq, Clone, Debug)]
276pub enum InputConsensusObject {
277 Mutate(ObjectRef),
278 ReadOnly(ObjectRef),
279 ReadConsensusStreamEnded(ObjectID, SequenceNumber),
280 MutateConsensusStreamEnded(ObjectID, SequenceNumber),
281 Cancelled(ObjectID, SequenceNumber),
282}
283
284impl InputConsensusObject {
285 pub fn id_and_version(&self) -> (ObjectID, SequenceNumber) {
286 match self {
287 InputConsensusObject::Mutate(oref) | InputConsensusObject::ReadOnly(oref) => {
288 (oref.0, oref.1)
289 }
290 InputConsensusObject::ReadConsensusStreamEnded(id, version)
291 | InputConsensusObject::MutateConsensusStreamEnded(id, version) => (*id, *version),
292 InputConsensusObject::Cancelled(id, version) => (*id, *version),
293 }
294 }
295
296 #[deprecated]
299 pub fn object_ref(&self) -> ObjectRef {
300 match self {
301 InputConsensusObject::Mutate(oref) | InputConsensusObject::ReadOnly(oref) => *oref,
302 InputConsensusObject::ReadConsensusStreamEnded(id, version)
303 | InputConsensusObject::MutateConsensusStreamEnded(id, version) => {
304 (*id, *version, ObjectDigest::OBJECT_DIGEST_DELETED)
305 }
306 InputConsensusObject::Cancelled(id, version) => {
307 (*id, *version, ObjectDigest::OBJECT_DIGEST_CANCELLED)
308 }
309 }
310 }
311}
312
313#[enum_dispatch]
314pub trait TransactionEffectsAPI {
315 fn status(&self) -> &ExecutionStatus;
316 fn into_status(self) -> ExecutionStatus;
317 fn executed_epoch(&self) -> EpochId;
318 fn modified_at_versions(&self) -> Vec<(ObjectID, SequenceNumber)>;
319 fn move_abort(&self) -> Option<(MoveLocation, u64)>;
320
321 fn lamport_version(&self) -> SequenceNumber;
323
324 fn old_object_metadata(&self) -> Vec<(ObjectRef, Owner)>;
329 fn input_consensus_objects(&self) -> Vec<InputConsensusObject>;
335 fn created(&self) -> Vec<(ObjectRef, Owner)>;
336 fn mutated(&self) -> Vec<(ObjectRef, Owner)>;
337 fn unwrapped(&self) -> Vec<(ObjectRef, Owner)>;
338 fn deleted(&self) -> Vec<ObjectRef>;
339 fn unwrapped_then_deleted(&self) -> Vec<ObjectRef>;
340 fn wrapped(&self) -> Vec<ObjectRef>;
341 fn transferred_from_consensus(&self) -> Vec<ObjectRef>;
342 fn transferred_to_consensus(&self) -> Vec<ObjectRef>;
343 fn consensus_owner_changed(&self) -> Vec<ObjectRef>;
344
345 fn object_changes(&self) -> Vec<ObjectChange>;
346 fn published_packages(&self) -> Vec<ObjectID>;
347
348 fn written(&self) -> Vec<ObjectRef>;
351
352 fn accumulator_events(&self) -> Vec<AccumulatorEvent>;
353
354 fn gas_object(&self) -> Option<(ObjectRef, Owner)>;
358
359 fn events_digest(&self) -> Option<&TransactionEventsDigest>;
360 fn dependencies(&self) -> &[TransactionDigest];
361
362 fn transaction_digest(&self) -> &TransactionDigest;
363
364 fn gas_cost_summary(&self) -> &GasCostSummary;
365
366 fn stream_ended_mutably_accessed_consensus_objects(&self) -> Vec<ObjectID> {
367 self.input_consensus_objects()
368 .into_iter()
369 .filter_map(|kind| match kind {
370 InputConsensusObject::MutateConsensusStreamEnded(id, _) => Some(id),
371 InputConsensusObject::Mutate(..)
372 | InputConsensusObject::ReadOnly(..)
373 | InputConsensusObject::ReadConsensusStreamEnded(..)
374 | InputConsensusObject::Cancelled(..) => None,
375 })
376 .collect()
377 }
378
379 fn unchanged_consensus_objects(&self) -> Vec<(ObjectID, UnchangedConsensusKind)>;
381
382 fn accumulator_updates(&self) -> Vec<(ObjectID, AccumulatorWriteV1)>;
384
385 fn status_mut_for_testing(&mut self) -> &mut ExecutionStatus;
388 fn gas_cost_summary_mut_for_testing(&mut self) -> &mut GasCostSummary;
389 fn transaction_digest_mut_for_testing(&mut self) -> &mut TransactionDigest;
390 fn dependencies_mut_for_testing(&mut self) -> &mut Vec<TransactionDigest>;
391 fn unsafe_add_input_consensus_object_for_testing(&mut self, kind: InputConsensusObject);
392
393 fn unsafe_add_deleted_live_object_for_testing(&mut self, obj_ref: ObjectRef);
395
396 fn unsafe_add_object_tombstone_for_testing(&mut self, obj_ref: ObjectRef);
398}
399
400#[derive(Clone, Debug)]
401pub struct ObjectChange {
402 pub id: ObjectID,
403 pub input_version: Option<SequenceNumber>,
404 pub input_digest: Option<ObjectDigest>,
405 pub output_version: Option<SequenceNumber>,
406 pub output_digest: Option<ObjectDigest>,
407 pub id_operation: IDOperation,
408}
409
410#[derive(Eq, PartialEq, Copy, Clone, Debug, Serialize, Deserialize)]
411pub enum IDOperation {
412 None,
413 Created,
414 Deleted,
415}
416
417#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, Default)]
418pub struct TransactionEvents {
419 pub data: Vec<Event>,
420}
421
422impl TransactionEvents {
423 pub fn digest(&self) -> TransactionEventsDigest {
424 TransactionEventsDigest::new(default_hash(self))
425 }
426}
427
428#[derive(Debug)]
429pub struct TransactionEffectsDebugSummary {
430 pub bcs_size: usize,
432 pub status: ExecutionStatus,
433 pub gas_used: GasCostSummary,
434 pub transaction_digest: TransactionDigest,
435 pub created_object_count: usize,
436 pub mutated_object_count: usize,
437 pub unwrapped_object_count: usize,
438 pub deleted_object_count: usize,
439 pub wrapped_object_count: usize,
440 pub dependency_count: usize,
441 }
443
444pub type TransactionEffectsEnvelope<S> = Envelope<TransactionEffects, S>;
445pub type UnsignedTransactionEffects = TransactionEffectsEnvelope<EmptySignInfo>;
446pub type SignedTransactionEffects = TransactionEffectsEnvelope<AuthoritySignInfo>;
447
448pub type TrustedSignedTransactionEffects = TrustedEnvelope<TransactionEffects, AuthoritySignInfo>;
449pub type VerifiedTransactionEffectsEnvelope<S> = VerifiedEnvelope<TransactionEffects, S>;
450pub type VerifiedSignedTransactionEffects = VerifiedTransactionEffectsEnvelope<AuthoritySignInfo>;
451
452#[cfg(test)]
453#[path = "../unit_tests/effects_tests.rs"]
454mod effects_tests;