1use std::collections::{BTreeMap, BTreeSet};
6
7use effects_v1::TransactionEffectsV1;
8pub use effects_v1::UnchangedSharedKind;
9use enum_dispatch::enum_dispatch;
10use iota_sdk_types::crypto::{Intent, IntentScope};
11pub use object_change::{EffectsObjectChange, ObjectIn, ObjectOut};
12use serde::{Deserialize, Serialize};
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 all_affected_objects(&self) -> Vec<ObjectRef> {
220 self.created()
221 .into_iter()
222 .map(|(r, _)| r)
223 .chain(self.mutated().into_iter().map(|(r, _)| r))
224 .chain(self.unwrapped().into_iter().map(|(r, _)| r))
225 .chain(
226 self.input_shared_objects()
227 .into_iter()
228 .map(|r| r.object_ref()),
229 )
230 .chain(self.deleted())
231 .chain(self.unwrapped_then_deleted())
232 .chain(self.wrapped())
233 .collect()
234 }
235
236 pub fn summary_for_debug(&self) -> TransactionEffectsDebugSummary {
237 TransactionEffectsDebugSummary {
238 bcs_size: bcs::serialized_size(self).unwrap(),
239 status: self.status().clone(),
240 gas_used: self.gas_cost_summary().clone(),
241 transaction_digest: *self.transaction_digest(),
242 created_object_count: self.created().len(),
243 mutated_object_count: self.mutated().len(),
244 unwrapped_object_count: self.unwrapped().len(),
245 deleted_object_count: self.deleted().len(),
246 wrapped_object_count: self.wrapped().len(),
247 dependency_count: self.dependencies().len(),
248 }
249 }
250}
251
252#[derive(Eq, PartialEq, Clone, Debug)]
253pub enum InputSharedObject {
254 Mutate(ObjectRef),
255 ReadOnly(ObjectRef),
256 ReadDeleted(ObjectID, SequenceNumber),
257 MutateDeleted(ObjectID, SequenceNumber),
258 Cancelled(ObjectID, SequenceNumber),
259}
260
261impl InputSharedObject {
262 pub fn id_and_version(&self) -> (ObjectID, SequenceNumber) {
263 let oref = self.object_ref();
264 (oref.0, oref.1)
265 }
266
267 pub fn object_ref(&self) -> ObjectRef {
268 match self {
269 InputSharedObject::Mutate(oref) | InputSharedObject::ReadOnly(oref) => *oref,
270 InputSharedObject::ReadDeleted(id, version)
271 | InputSharedObject::MutateDeleted(id, version) => {
272 (*id, *version, ObjectDigest::OBJECT_DIGEST_DELETED)
273 }
274 InputSharedObject::Cancelled(id, version) => {
275 (*id, *version, ObjectDigest::OBJECT_DIGEST_CANCELLED)
276 }
277 }
278 }
279}
280
281#[enum_dispatch]
282pub trait TransactionEffectsAPI {
283 fn status(&self) -> &ExecutionStatus;
284 fn into_status(self) -> ExecutionStatus;
285 fn executed_epoch(&self) -> EpochId;
286 fn modified_at_versions(&self) -> Vec<(ObjectID, SequenceNumber)>;
287
288 fn lamport_version(&self) -> SequenceNumber;
290
291 fn old_object_metadata(&self) -> Vec<(ObjectRef, Owner)>;
296 fn input_shared_objects(&self) -> Vec<InputSharedObject>;
304 fn created(&self) -> Vec<(ObjectRef, Owner)>;
305 fn mutated(&self) -> Vec<(ObjectRef, Owner)>;
306 fn unwrapped(&self) -> Vec<(ObjectRef, Owner)>;
307 fn deleted(&self) -> Vec<ObjectRef>;
308 fn unwrapped_then_deleted(&self) -> Vec<ObjectRef>;
309 fn wrapped(&self) -> Vec<ObjectRef>;
310
311 fn object_changes(&self) -> Vec<ObjectChange>;
312
313 fn gas_object(&self) -> (ObjectRef, Owner);
317
318 fn events_digest(&self) -> Option<&TransactionEventsDigest>;
319 fn dependencies(&self) -> &[TransactionDigest];
320
321 fn transaction_digest(&self) -> &TransactionDigest;
322
323 fn gas_cost_summary(&self) -> &GasCostSummary;
324
325 fn deleted_mutably_accessed_shared_objects(&self) -> Vec<ObjectID> {
326 self.input_shared_objects()
327 .into_iter()
328 .filter_map(|kind| match kind {
329 InputSharedObject::MutateDeleted(id, _) => Some(id),
330 InputSharedObject::Mutate(..)
331 | InputSharedObject::ReadOnly(..)
332 | InputSharedObject::ReadDeleted(..)
333 | InputSharedObject::Cancelled(..) => None,
334 })
335 .collect()
336 }
337
338 fn unchanged_shared_objects(&self) -> Vec<(ObjectID, UnchangedSharedKind)>;
341
342 fn status_mut_for_testing(&mut self) -> &mut ExecutionStatus;
346 fn gas_cost_summary_mut_for_testing(&mut self) -> &mut GasCostSummary;
347 fn transaction_digest_mut_for_testing(&mut self) -> &mut TransactionDigest;
348 fn dependencies_mut_for_testing(&mut self) -> &mut Vec<TransactionDigest>;
349 fn unsafe_add_input_shared_object_for_testing(&mut self, kind: InputSharedObject);
350
351 fn unsafe_add_deleted_live_object_for_testing(&mut self, obj_ref: ObjectRef);
353
354 fn unsafe_add_object_tombstone_for_testing(&mut self, obj_ref: ObjectRef);
356}
357
358#[derive(Clone)]
359pub struct ObjectChange {
360 pub id: ObjectID,
361 pub input_version: Option<SequenceNumber>,
362 pub input_digest: Option<ObjectDigest>,
363 pub output_version: Option<SequenceNumber>,
364 pub output_digest: Option<ObjectDigest>,
365 pub id_operation: IDOperation,
366}
367
368#[derive(Eq, PartialEq, Copy, Clone, Debug, Serialize, Deserialize)]
369pub enum IDOperation {
370 None,
371 Created,
372 Deleted,
373}
374
375#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, Default)]
376pub struct TransactionEvents {
377 pub data: Vec<Event>,
378}
379
380impl TransactionEvents {
381 pub fn digest(&self) -> TransactionEventsDigest {
382 TransactionEventsDigest::new(default_hash(self))
383 }
384}
385
386#[derive(Debug)]
387pub struct TransactionEffectsDebugSummary {
388 pub bcs_size: usize,
390 pub status: ExecutionStatus,
391 pub gas_used: GasCostSummary,
392 pub transaction_digest: TransactionDigest,
393 pub created_object_count: usize,
394 pub mutated_object_count: usize,
395 pub unwrapped_object_count: usize,
396 pub deleted_object_count: usize,
397 pub wrapped_object_count: usize,
398 pub dependency_count: usize,
399 }
401
402pub type TransactionEffectsEnvelope<S> = Envelope<TransactionEffects, S>;
403pub type UnsignedTransactionEffects = TransactionEffectsEnvelope<EmptySignInfo>;
404pub type SignedTransactionEffects = TransactionEffectsEnvelope<AuthoritySignInfo>;
405pub type CertifiedTransactionEffects = TransactionEffectsEnvelope<AuthorityStrongQuorumSignInfo>;
406
407pub type TrustedSignedTransactionEffects = TrustedEnvelope<TransactionEffects, AuthoritySignInfo>;
408pub type VerifiedTransactionEffectsEnvelope<S> = VerifiedEnvelope<TransactionEffects, S>;
409pub type VerifiedSignedTransactionEffects = VerifiedTransactionEffectsEnvelope<AuthoritySignInfo>;
410pub type VerifiedCertifiedTransactionEffects =
411 VerifiedTransactionEffectsEnvelope<AuthorityStrongQuorumSignInfo>;
412
413impl CertifiedTransactionEffects {
414 pub fn verify_authority_signatures(&self, committee: &Committee) -> IotaResult {
415 self.auth_sig().verify_secure(
416 self.data(),
417 Intent::iota_app(IntentScope::TransactionEffects),
418 committee,
419 )
420 }
421
422 pub fn verify(self, committee: &Committee) -> IotaResult<VerifiedCertifiedTransactionEffects> {
423 self.verify_authority_signatures(committee)?;
424 Ok(VerifiedCertifiedTransactionEffects::new_from_verified(self))
425 }
426}