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 created_then_wrapped_objects(&self) -> Vec<(ObjectID, SequenceNumber)> {
190 self.object_changes()
194 .into_iter()
195 .filter_map(|change| {
196 if change.input_digest.is_none()
197 && change.output_digest.is_none()
198 && change.id_operation == IDOperation::Created
199 {
200 Some((change.id, change.output_version.unwrap_or_default()))
201 } else {
202 None
203 }
204 })
205 .collect::<Vec<_>>()
206 }
207
208 pub fn mutated_excluding_gas(&self) -> Vec<(ObjectRef, Owner)> {
210 self.mutated()
211 .into_iter()
212 .filter(|o| o != &self.gas_object())
213 .collect()
214 }
215
216 pub fn summary_for_debug(&self) -> TransactionEffectsDebugSummary {
217 TransactionEffectsDebugSummary {
218 bcs_size: bcs::serialized_size(self).unwrap(),
219 status: self.status().clone(),
220 gas_used: self.gas_cost_summary().clone(),
221 transaction_digest: *self.transaction_digest(),
222 created_object_count: self.created().len(),
223 mutated_object_count: self.mutated().len(),
224 unwrapped_object_count: self.unwrapped().len(),
225 deleted_object_count: self.deleted().len(),
226 wrapped_object_count: self.wrapped().len(),
227 dependency_count: self.dependencies().len(),
228 }
229 }
230}
231
232#[derive(Eq, PartialEq, Clone, Debug)]
233pub enum InputSharedObject {
234 Mutate(ObjectRef),
235 ReadOnly(ObjectRef),
236 ReadDeleted(ObjectID, SequenceNumber),
237 MutateDeleted(ObjectID, SequenceNumber),
238 Cancelled(ObjectID, SequenceNumber),
239}
240
241impl InputSharedObject {
242 pub fn id_and_version(&self) -> (ObjectID, SequenceNumber) {
243 let oref = self.object_ref();
244 (oref.0, oref.1)
245 }
246
247 pub fn object_ref(&self) -> ObjectRef {
248 match self {
249 InputSharedObject::Mutate(oref) | InputSharedObject::ReadOnly(oref) => *oref,
250 InputSharedObject::ReadDeleted(id, version)
251 | InputSharedObject::MutateDeleted(id, version) => {
252 (*id, *version, ObjectDigest::OBJECT_DIGEST_DELETED)
253 }
254 InputSharedObject::Cancelled(id, version) => {
255 (*id, *version, ObjectDigest::OBJECT_DIGEST_CANCELLED)
256 }
257 }
258 }
259}
260
261#[enum_dispatch]
262pub trait TransactionEffectsAPI {
263 fn status(&self) -> &ExecutionStatus;
264 fn into_status(self) -> ExecutionStatus;
265 fn executed_epoch(&self) -> EpochId;
266 fn modified_at_versions(&self) -> Vec<(ObjectID, SequenceNumber)>;
267
268 fn lamport_version(&self) -> SequenceNumber;
270
271 fn old_object_metadata(&self) -> Vec<(ObjectRef, Owner)>;
276 fn input_shared_objects(&self) -> Vec<InputSharedObject>;
284 fn created(&self) -> Vec<(ObjectRef, Owner)>;
285 fn mutated(&self) -> Vec<(ObjectRef, Owner)>;
286 fn unwrapped(&self) -> Vec<(ObjectRef, Owner)>;
287 fn deleted(&self) -> Vec<ObjectRef>;
288 fn unwrapped_then_deleted(&self) -> Vec<ObjectRef>;
289 fn wrapped(&self) -> Vec<ObjectRef>;
290
291 fn object_changes(&self) -> Vec<ObjectChange>;
292
293 fn gas_object(&self) -> (ObjectRef, Owner);
297
298 fn events_digest(&self) -> Option<&TransactionEventsDigest>;
299 fn dependencies(&self) -> &[TransactionDigest];
300
301 fn transaction_digest(&self) -> &TransactionDigest;
302
303 fn gas_cost_summary(&self) -> &GasCostSummary;
304
305 fn deleted_mutably_accessed_shared_objects(&self) -> Vec<ObjectID> {
306 self.input_shared_objects()
307 .into_iter()
308 .filter_map(|kind| match kind {
309 InputSharedObject::MutateDeleted(id, _) => Some(id),
310 InputSharedObject::Mutate(..)
311 | InputSharedObject::ReadOnly(..)
312 | InputSharedObject::ReadDeleted(..)
313 | InputSharedObject::Cancelled(..) => None,
314 })
315 .collect()
316 }
317
318 fn unchanged_shared_objects(&self) -> Vec<(ObjectID, UnchangedSharedKind)>;
321
322 fn status_mut_for_testing(&mut self) -> &mut ExecutionStatus;
326 fn gas_cost_summary_mut_for_testing(&mut self) -> &mut GasCostSummary;
327 fn transaction_digest_mut_for_testing(&mut self) -> &mut TransactionDigest;
328 fn dependencies_mut_for_testing(&mut self) -> &mut Vec<TransactionDigest>;
329 fn unsafe_add_input_shared_object_for_testing(&mut self, kind: InputSharedObject);
330
331 fn unsafe_add_deleted_live_object_for_testing(&mut self, obj_ref: ObjectRef);
333
334 fn unsafe_add_object_tombstone_for_testing(&mut self, obj_ref: ObjectRef);
336}
337
338#[derive(Clone)]
339pub struct ObjectChange {
340 pub id: ObjectID,
341 pub input_version: Option<SequenceNumber>,
342 pub input_digest: Option<ObjectDigest>,
343 pub output_version: Option<SequenceNumber>,
344 pub output_digest: Option<ObjectDigest>,
345 pub id_operation: IDOperation,
346}
347
348#[derive(Eq, PartialEq, Copy, Clone, Debug, Serialize, Deserialize)]
349pub enum IDOperation {
350 None,
351 Created,
352 Deleted,
353}
354
355#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, Default)]
356pub struct TransactionEvents {
357 pub data: Vec<Event>,
358}
359
360impl TransactionEvents {
361 pub fn digest(&self) -> TransactionEventsDigest {
362 TransactionEventsDigest::new(default_hash(self))
363 }
364}
365
366#[derive(Debug)]
367pub struct TransactionEffectsDebugSummary {
368 pub bcs_size: usize,
370 pub status: ExecutionStatus,
371 pub gas_used: GasCostSummary,
372 pub transaction_digest: TransactionDigest,
373 pub created_object_count: usize,
374 pub mutated_object_count: usize,
375 pub unwrapped_object_count: usize,
376 pub deleted_object_count: usize,
377 pub wrapped_object_count: usize,
378 pub dependency_count: usize,
379 }
381
382pub type TransactionEffectsEnvelope<S> = Envelope<TransactionEffects, S>;
383pub type UnsignedTransactionEffects = TransactionEffectsEnvelope<EmptySignInfo>;
384pub type SignedTransactionEffects = TransactionEffectsEnvelope<AuthoritySignInfo>;
385pub type CertifiedTransactionEffects = TransactionEffectsEnvelope<AuthorityStrongQuorumSignInfo>;
386
387pub type TrustedSignedTransactionEffects = TrustedEnvelope<TransactionEffects, AuthoritySignInfo>;
388pub type VerifiedTransactionEffectsEnvelope<S> = VerifiedEnvelope<TransactionEffects, S>;
389pub type VerifiedSignedTransactionEffects = VerifiedTransactionEffectsEnvelope<AuthoritySignInfo>;
390pub type VerifiedCertifiedTransactionEffects =
391 VerifiedTransactionEffectsEnvelope<AuthorityStrongQuorumSignInfo>;
392
393impl CertifiedTransactionEffects {
394 pub fn verify_authority_signatures(&self, committee: &Committee) -> IotaResult {
395 self.auth_sig().verify_secure(
396 self.data(),
397 Intent::iota_app(IntentScope::TransactionEffects),
398 committee,
399 )
400 }
401
402 pub fn verify(self, committee: &Committee) -> IotaResult<VerifiedCertifiedTransactionEffects> {
403 self.verify_authority_signatures(committee)?;
404 Ok(VerifiedCertifiedTransactionEffects::new_from_verified(self))
405 }
406}