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