1use std::collections::{BTreeMap, BTreeSet};
6
7use effects_v1::TransactionEffectsV1;
8pub use effects_v1::UnchangedSharedKind;
9use enum_dispatch::enum_dispatch;
10pub use object_change::{EffectsObjectChange, ObjectIn, ObjectOut};
11use serde::{Deserialize, Serialize};
12use shared_crypto::intent::{Intent, IntentScope};
13pub use test_effects_builder::TestEffectsBuilder;
14
15use crate::{
16 base_types::{ExecutionDigests, ObjectID, ObjectRef, SequenceNumber},
17 committee::{Committee, EpochId},
18 crypto::{
19 AuthoritySignInfo, AuthoritySignInfoTrait, AuthorityStrongQuorumSignInfo, EmptySignInfo,
20 default_hash,
21 },
22 digests::{ObjectDigest, TransactionDigest, TransactionEffectsDigest, TransactionEventsDigest},
23 error::IotaResult,
24 event::Event,
25 execution::SharedInput,
26 execution_status::ExecutionStatus,
27 gas::GasCostSummary,
28 message_envelope::{Envelope, Message, TrustedEnvelope, VerifiedEnvelope},
29 object::Owner,
30 storage::WriteKind,
31};
32
33pub(crate) mod effects_v1;
34mod object_change;
35mod test_effects_builder;
36
37pub const APPROX_SIZE_OF_OBJECT_REF: usize = 80;
41pub const APPROX_SIZE_OF_EXECUTION_STATUS: usize = 120;
43pub const APPROX_SIZE_OF_EPOCH_ID: usize = 10;
45pub const APPROX_SIZE_OF_GAS_COST_SUMMARY: usize = 50;
47pub const APPROX_SIZE_OF_OPT_TX_EVENTS_DIGEST: usize = 40;
49pub const APPROX_SIZE_OF_TX_DIGEST: usize = 40;
51pub const APPROX_SIZE_OF_OWNER: usize = 48;
53
54#[enum_dispatch(TransactionEffectsAPI)]
56#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)]
57pub enum TransactionEffects {
58 V1(TransactionEffectsV1),
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::V1(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 shared_objects: Vec<SharedInput>,
90 loaded_per_epoch_config_objects: BTreeSet<ObjectID>,
91 transaction_digest: TransactionDigest,
92 lamport_version: SequenceNumber,
93 changed_objects: BTreeMap<ObjectID, EffectsObjectChange>,
94 gas_object: Option<ObjectID>,
95 events_digest: Option<TransactionEventsDigest>,
96 dependencies: Vec<TransactionDigest>,
97 ) -> Self {
98 Self::V1(TransactionEffectsV1::new(
99 status,
100 executed_epoch,
101 gas_used,
102 shared_objects,
103 loaded_per_epoch_config_objects,
104 transaction_digest,
105 lamport_version,
106 changed_objects,
107 gas_object,
108 events_digest,
109 dependencies,
110 ))
111 }
112
113 pub fn execution_digests(&self) -> ExecutionDigests {
114 ExecutionDigests {
115 transaction: *self.transaction_digest(),
116 effects: self.digest(),
117 }
118 }
119
120 pub fn estimate_effects_size_upperbound_v1(
121 num_writes: usize,
122 num_modifies: usize,
123 num_deps: usize,
124 ) -> usize {
125 let fixed_sizes = APPROX_SIZE_OF_EXECUTION_STATUS
126 + APPROX_SIZE_OF_EPOCH_ID
127 + APPROX_SIZE_OF_GAS_COST_SUMMARY
128 + APPROX_SIZE_OF_OPT_TX_EVENTS_DIGEST;
129
130 let approx_change_entry_size = 1_000
132 + (APPROX_SIZE_OF_OWNER + APPROX_SIZE_OF_OBJECT_REF) * num_writes
133 + (APPROX_SIZE_OF_OWNER + APPROX_SIZE_OF_OBJECT_REF) * num_modifies;
134
135 let deps_size = 1_000 + APPROX_SIZE_OF_TX_DIGEST * num_deps;
136
137 fixed_sizes + approx_change_entry_size + deps_size
138 }
139
140 pub fn all_changed_objects(&self) -> Vec<(ObjectRef, Owner, WriteKind)> {
145 self.mutated()
146 .into_iter()
147 .map(|(r, o)| (r, o, WriteKind::Mutate))
148 .chain(
149 self.created()
150 .into_iter()
151 .map(|(r, o)| (r, o, WriteKind::Create)),
152 )
153 .chain(
154 self.unwrapped()
155 .into_iter()
156 .map(|(r, o)| (r, o, WriteKind::Unwrap)),
157 )
158 .collect()
159 }
160
161 pub fn all_removed_objects(&self) -> Vec<(ObjectRef, ObjectRemoveKind)> {
166 self.deleted()
167 .iter()
168 .map(|obj_ref| (*obj_ref, ObjectRemoveKind::Delete))
169 .chain(
170 self.wrapped()
171 .iter()
172 .map(|obj_ref| (*obj_ref, ObjectRemoveKind::Wrap)),
173 )
174 .collect()
175 }
176
177 pub fn all_tombstones(&self) -> Vec<(ObjectID, SequenceNumber)> {
180 self.deleted()
181 .into_iter()
182 .chain(self.unwrapped_then_deleted())
183 .chain(self.wrapped())
184 .map(|obj_ref| (obj_ref.0, obj_ref.1))
185 .collect()
186 }
187
188 pub fn mutated_excluding_gas(&self) -> Vec<(ObjectRef, Owner)> {
190 self.mutated()
191 .into_iter()
192 .filter(|o| o != &self.gas_object())
193 .collect()
194 }
195
196 pub fn summary_for_debug(&self) -> TransactionEffectsDebugSummary {
197 TransactionEffectsDebugSummary {
198 bcs_size: bcs::serialized_size(self).unwrap(),
199 status: self.status().clone(),
200 gas_used: self.gas_cost_summary().clone(),
201 transaction_digest: *self.transaction_digest(),
202 created_object_count: self.created().len(),
203 mutated_object_count: self.mutated().len(),
204 unwrapped_object_count: self.unwrapped().len(),
205 deleted_object_count: self.deleted().len(),
206 wrapped_object_count: self.wrapped().len(),
207 dependency_count: self.dependencies().len(),
208 }
209 }
210}
211
212#[derive(Eq, PartialEq, Clone, Debug)]
213pub enum InputSharedObject {
214 Mutate(ObjectRef),
215 ReadOnly(ObjectRef),
216 ReadDeleted(ObjectID, SequenceNumber),
217 MutateDeleted(ObjectID, SequenceNumber),
218 Cancelled(ObjectID, SequenceNumber),
219}
220
221impl InputSharedObject {
222 pub fn id_and_version(&self) -> (ObjectID, SequenceNumber) {
223 let oref = self.object_ref();
224 (oref.0, oref.1)
225 }
226
227 pub fn object_ref(&self) -> ObjectRef {
228 match self {
229 InputSharedObject::Mutate(oref) | InputSharedObject::ReadOnly(oref) => *oref,
230 InputSharedObject::ReadDeleted(id, version)
231 | InputSharedObject::MutateDeleted(id, version) => {
232 (*id, *version, ObjectDigest::OBJECT_DIGEST_DELETED)
233 }
234 InputSharedObject::Cancelled(id, version) => {
235 (*id, *version, ObjectDigest::OBJECT_DIGEST_CANCELLED)
236 }
237 }
238 }
239}
240
241#[enum_dispatch]
242pub trait TransactionEffectsAPI {
243 fn status(&self) -> &ExecutionStatus;
244 fn into_status(self) -> ExecutionStatus;
245 fn executed_epoch(&self) -> EpochId;
246 fn modified_at_versions(&self) -> Vec<(ObjectID, SequenceNumber)>;
247
248 fn lamport_version(&self) -> SequenceNumber;
250
251 fn old_object_metadata(&self) -> Vec<(ObjectRef, Owner)>;
256 fn input_shared_objects(&self) -> Vec<InputSharedObject>;
264 fn created(&self) -> Vec<(ObjectRef, Owner)>;
265 fn mutated(&self) -> Vec<(ObjectRef, Owner)>;
266 fn unwrapped(&self) -> Vec<(ObjectRef, Owner)>;
267 fn deleted(&self) -> Vec<ObjectRef>;
268 fn unwrapped_then_deleted(&self) -> Vec<ObjectRef>;
269 fn wrapped(&self) -> Vec<ObjectRef>;
270
271 fn object_changes(&self) -> Vec<ObjectChange>;
272
273 fn gas_object(&self) -> (ObjectRef, Owner);
277
278 fn events_digest(&self) -> Option<&TransactionEventsDigest>;
279 fn dependencies(&self) -> &[TransactionDigest];
280
281 fn transaction_digest(&self) -> &TransactionDigest;
282
283 fn gas_cost_summary(&self) -> &GasCostSummary;
284
285 fn deleted_mutably_accessed_shared_objects(&self) -> Vec<ObjectID> {
286 self.input_shared_objects()
287 .into_iter()
288 .filter_map(|kind| match kind {
289 InputSharedObject::MutateDeleted(id, _) => Some(id),
290 InputSharedObject::Mutate(..)
291 | InputSharedObject::ReadOnly(..)
292 | InputSharedObject::ReadDeleted(..)
293 | InputSharedObject::Cancelled(..) => None,
294 })
295 .collect()
296 }
297
298 fn unchanged_shared_objects(&self) -> Vec<(ObjectID, UnchangedSharedKind)>;
301
302 fn status_mut_for_testing(&mut self) -> &mut ExecutionStatus;
306 fn gas_cost_summary_mut_for_testing(&mut self) -> &mut GasCostSummary;
307 fn transaction_digest_mut_for_testing(&mut self) -> &mut TransactionDigest;
308 fn dependencies_mut_for_testing(&mut self) -> &mut Vec<TransactionDigest>;
309 fn unsafe_add_input_shared_object_for_testing(&mut self, kind: InputSharedObject);
310
311 fn unsafe_add_deleted_live_object_for_testing(&mut self, obj_ref: ObjectRef);
313
314 fn unsafe_add_object_tombstone_for_testing(&mut self, obj_ref: ObjectRef);
316}
317
318#[derive(Clone)]
319pub struct ObjectChange {
320 pub id: ObjectID,
321 pub input_version: Option<SequenceNumber>,
322 pub input_digest: Option<ObjectDigest>,
323 pub output_version: Option<SequenceNumber>,
324 pub output_digest: Option<ObjectDigest>,
325 pub id_operation: IDOperation,
326}
327
328#[derive(Eq, PartialEq, Copy, Clone, Debug, Serialize, Deserialize)]
329pub enum IDOperation {
330 None,
331 Created,
332 Deleted,
333}
334
335#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, Default)]
336pub struct TransactionEvents {
337 pub data: Vec<Event>,
338}
339
340impl TransactionEvents {
341 pub fn digest(&self) -> TransactionEventsDigest {
342 TransactionEventsDigest::new(default_hash(self))
343 }
344}
345
346#[derive(Debug)]
347pub struct TransactionEffectsDebugSummary {
348 pub bcs_size: usize,
350 pub status: ExecutionStatus,
351 pub gas_used: GasCostSummary,
352 pub transaction_digest: TransactionDigest,
353 pub created_object_count: usize,
354 pub mutated_object_count: usize,
355 pub unwrapped_object_count: usize,
356 pub deleted_object_count: usize,
357 pub wrapped_object_count: usize,
358 pub dependency_count: usize,
359 }
361
362pub type TransactionEffectsEnvelope<S> = Envelope<TransactionEffects, S>;
363pub type UnsignedTransactionEffects = TransactionEffectsEnvelope<EmptySignInfo>;
364pub type SignedTransactionEffects = TransactionEffectsEnvelope<AuthoritySignInfo>;
365pub type CertifiedTransactionEffects = TransactionEffectsEnvelope<AuthorityStrongQuorumSignInfo>;
366
367pub type TrustedSignedTransactionEffects = TrustedEnvelope<TransactionEffects, AuthoritySignInfo>;
368pub type VerifiedTransactionEffectsEnvelope<S> = VerifiedEnvelope<TransactionEffects, S>;
369pub type VerifiedSignedTransactionEffects = VerifiedTransactionEffectsEnvelope<AuthoritySignInfo>;
370pub type VerifiedCertifiedTransactionEffects =
371 VerifiedTransactionEffectsEnvelope<AuthorityStrongQuorumSignInfo>;
372
373impl CertifiedTransactionEffects {
374 pub fn verify_authority_signatures(&self, committee: &Committee) -> IotaResult {
375 self.auth_sig().verify_secure(
376 self.data(),
377 Intent::iota_app(IntentScope::TransactionEffects),
378 committee,
379 )
380 }
381
382 pub fn verify(self, committee: &Committee) -> IotaResult<VerifiedCertifiedTransactionEffects> {
383 self.verify_authority_signatures(committee)?;
384 Ok(VerifiedCertifiedTransactionEffects::new_from_verified(self))
385 }
386}