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;
14use tracing::instrument;
15
16use crate::{
17 base_types::{ExecutionDigests, ObjectID, ObjectRef, SequenceNumber},
18 committee::{Committee, EpochId},
19 crypto::{
20 AuthoritySignInfo, AuthoritySignInfoTrait, AuthorityStrongQuorumSignInfo, EmptySignInfo,
21 default_hash,
22 },
23 digests::{ObjectDigest, TransactionDigest, TransactionEffectsDigest, TransactionEventsDigest},
24 error::IotaResult,
25 event::Event,
26 execution::SharedInput,
27 execution_status::ExecutionStatus,
28 gas::GasCostSummary,
29 message_envelope::{Envelope, Message, TrustedEnvelope, VerifiedEnvelope},
30 object::Owner,
31 storage::WriteKind,
32};
33
34pub(crate) mod effects_v1;
35mod object_change;
36mod test_effects_builder;
37
38pub const APPROX_SIZE_OF_OBJECT_REF: usize = 80;
42pub const APPROX_SIZE_OF_EXECUTION_STATUS: usize = 120;
44pub const APPROX_SIZE_OF_EPOCH_ID: usize = 10;
46pub const APPROX_SIZE_OF_GAS_COST_SUMMARY: usize = 50;
48pub const APPROX_SIZE_OF_OPT_TX_EVENTS_DIGEST: usize = 40;
50pub const APPROX_SIZE_OF_TX_DIGEST: usize = 40;
52pub const APPROX_SIZE_OF_OWNER: usize = 48;
54
55#[enum_dispatch(TransactionEffectsAPI)]
57#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)]
58pub enum TransactionEffects {
59 V1(TransactionEffectsV1),
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::V1(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 shared_objects: Vec<SharedInput>,
91 loaded_per_epoch_config_objects: BTreeSet<ObjectID>,
92 transaction_digest: TransactionDigest,
93 lamport_version: SequenceNumber,
94 changed_objects: BTreeMap<ObjectID, EffectsObjectChange>,
95 gas_object: Option<ObjectID>,
96 events_digest: Option<TransactionEventsDigest>,
97 dependencies: Vec<TransactionDigest>,
98 ) -> Self {
99 Self::V1(TransactionEffectsV1::new(
100 status,
101 executed_epoch,
102 gas_used,
103 shared_objects,
104 loaded_per_epoch_config_objects,
105 transaction_digest,
106 lamport_version,
107 changed_objects,
108 gas_object,
109 events_digest,
110 dependencies,
111 ))
112 }
113
114 pub fn execution_digests(&self) -> ExecutionDigests {
115 ExecutionDigests {
116 transaction: *self.transaction_digest(),
117 effects: self.digest(),
118 }
119 }
120
121 pub fn estimate_effects_size_upperbound_v1(
122 num_writes: usize,
123 num_modifies: usize,
124 num_deps: usize,
125 ) -> usize {
126 let fixed_sizes = APPROX_SIZE_OF_EXECUTION_STATUS
127 + APPROX_SIZE_OF_EPOCH_ID
128 + APPROX_SIZE_OF_GAS_COST_SUMMARY
129 + APPROX_SIZE_OF_OPT_TX_EVENTS_DIGEST;
130
131 let approx_change_entry_size = 1_000
133 + (APPROX_SIZE_OF_OWNER + APPROX_SIZE_OF_OBJECT_REF) * num_writes
134 + (APPROX_SIZE_OF_OWNER + APPROX_SIZE_OF_OBJECT_REF) * num_modifies;
135
136 let deps_size = 1_000 + APPROX_SIZE_OF_TX_DIGEST * num_deps;
137
138 fixed_sizes + approx_change_entry_size + deps_size
139 }
140
141 pub fn all_changed_objects(&self) -> Vec<(ObjectRef, Owner, WriteKind)> {
146 self.mutated()
147 .into_iter()
148 .map(|(r, o)| (r, o, WriteKind::Mutate))
149 .chain(
150 self.created()
151 .into_iter()
152 .map(|(r, o)| (r, o, WriteKind::Create)),
153 )
154 .chain(
155 self.unwrapped()
156 .into_iter()
157 .map(|(r, o)| (r, o, WriteKind::Unwrap)),
158 )
159 .collect()
160 }
161
162 pub fn all_removed_objects(&self) -> Vec<(ObjectRef, ObjectRemoveKind)> {
167 self.deleted()
168 .iter()
169 .map(|obj_ref| (*obj_ref, ObjectRemoveKind::Delete))
170 .chain(
171 self.wrapped()
172 .iter()
173 .map(|obj_ref| (*obj_ref, ObjectRemoveKind::Wrap)),
174 )
175 .collect()
176 }
177
178 pub fn all_tombstones(&self) -> Vec<(ObjectID, SequenceNumber)> {
181 self.deleted()
182 .into_iter()
183 .chain(self.unwrapped_then_deleted())
184 .chain(self.wrapped())
185 .map(|obj_ref| (obj_ref.0, obj_ref.1))
186 .collect()
187 }
188
189 pub fn created_then_wrapped_objects(&self) -> Vec<(ObjectID, SequenceNumber)> {
191 self.object_changes()
195 .into_iter()
196 .filter_map(|change| {
197 if change.input_digest.is_none()
198 && change.output_digest.is_none()
199 && change.id_operation == IDOperation::Created
200 {
201 Some((change.id, change.output_version.unwrap_or_default()))
202 } else {
203 None
204 }
205 })
206 .collect::<Vec<_>>()
207 }
208
209 pub fn mutated_excluding_gas(&self) -> Vec<(ObjectRef, Owner)> {
211 self.mutated()
212 .into_iter()
213 .filter(|o| o != &self.gas_object())
214 .collect()
215 }
216
217 pub fn all_affected_objects(&self) -> Vec<ObjectRef> {
221 self.created()
222 .into_iter()
223 .map(|(r, _)| r)
224 .chain(self.mutated().into_iter().map(|(r, _)| r))
225 .chain(self.unwrapped().into_iter().map(|(r, _)| r))
226 .chain(
227 self.input_shared_objects()
228 .into_iter()
229 .map(|r| r.object_ref()),
230 )
231 .chain(self.deleted())
232 .chain(self.unwrapped_then_deleted())
233 .chain(self.wrapped())
234 .collect()
235 }
236
237 pub fn summary_for_debug(&self) -> TransactionEffectsDebugSummary {
238 TransactionEffectsDebugSummary {
239 bcs_size: bcs::serialized_size(self).unwrap(),
240 status: self.status().clone(),
241 gas_used: self.gas_cost_summary().clone(),
242 transaction_digest: *self.transaction_digest(),
243 created_object_count: self.created().len(),
244 mutated_object_count: self.mutated().len(),
245 unwrapped_object_count: self.unwrapped().len(),
246 deleted_object_count: self.deleted().len(),
247 wrapped_object_count: self.wrapped().len(),
248 dependency_count: self.dependencies().len(),
249 }
250 }
251}
252
253#[derive(Eq, PartialEq, Clone, Debug)]
254pub enum InputSharedObject {
255 Mutate(ObjectRef),
256 ReadOnly(ObjectRef),
257 ReadDeleted(ObjectID, SequenceNumber),
258 MutateDeleted(ObjectID, SequenceNumber),
259 Cancelled(ObjectID, SequenceNumber),
260}
261
262impl InputSharedObject {
263 pub fn id_and_version(&self) -> (ObjectID, SequenceNumber) {
264 let oref = self.object_ref();
265 (oref.0, oref.1)
266 }
267
268 pub fn object_ref(&self) -> ObjectRef {
269 match self {
270 InputSharedObject::Mutate(oref) | InputSharedObject::ReadOnly(oref) => *oref,
271 InputSharedObject::ReadDeleted(id, version)
272 | InputSharedObject::MutateDeleted(id, version) => {
273 (*id, *version, ObjectDigest::OBJECT_DIGEST_DELETED)
274 }
275 InputSharedObject::Cancelled(id, version) => {
276 (*id, *version, ObjectDigest::OBJECT_DIGEST_CANCELLED)
277 }
278 }
279 }
280}
281
282#[enum_dispatch]
283pub trait TransactionEffectsAPI {
284 fn status(&self) -> &ExecutionStatus;
285 fn into_status(self) -> ExecutionStatus;
286 fn executed_epoch(&self) -> EpochId;
287 fn modified_at_versions(&self) -> Vec<(ObjectID, SequenceNumber)>;
288
289 fn lamport_version(&self) -> SequenceNumber;
291
292 fn old_object_metadata(&self) -> Vec<(ObjectRef, Owner)>;
297 fn input_shared_objects(&self) -> Vec<InputSharedObject>;
305 fn created(&self) -> Vec<(ObjectRef, Owner)>;
306 fn mutated(&self) -> Vec<(ObjectRef, Owner)>;
307 fn unwrapped(&self) -> Vec<(ObjectRef, Owner)>;
308 fn deleted(&self) -> Vec<ObjectRef>;
309 fn unwrapped_then_deleted(&self) -> Vec<ObjectRef>;
310 fn wrapped(&self) -> Vec<ObjectRef>;
311
312 fn object_changes(&self) -> Vec<ObjectChange>;
313
314 fn gas_object(&self) -> (ObjectRef, Owner);
318
319 fn events_digest(&self) -> Option<&TransactionEventsDigest>;
320 fn dependencies(&self) -> &[TransactionDigest];
321
322 fn transaction_digest(&self) -> &TransactionDigest;
323
324 fn gas_cost_summary(&self) -> &GasCostSummary;
325
326 fn deleted_mutably_accessed_shared_objects(&self) -> Vec<ObjectID> {
327 self.input_shared_objects()
328 .into_iter()
329 .filter_map(|kind| match kind {
330 InputSharedObject::MutateDeleted(id, _) => Some(id),
331 InputSharedObject::Mutate(..)
332 | InputSharedObject::ReadOnly(..)
333 | InputSharedObject::ReadDeleted(..)
334 | InputSharedObject::Cancelled(..) => None,
335 })
336 .collect()
337 }
338
339 fn unchanged_shared_objects(&self) -> Vec<(ObjectID, UnchangedSharedKind)>;
342
343 fn status_mut_for_testing(&mut self) -> &mut ExecutionStatus;
347 fn gas_cost_summary_mut_for_testing(&mut self) -> &mut GasCostSummary;
348 fn transaction_digest_mut_for_testing(&mut self) -> &mut TransactionDigest;
349 fn dependencies_mut_for_testing(&mut self) -> &mut Vec<TransactionDigest>;
350 fn unsafe_add_input_shared_object_for_testing(&mut self, kind: InputSharedObject);
351
352 fn unsafe_add_deleted_live_object_for_testing(&mut self, obj_ref: ObjectRef);
354
355 fn unsafe_add_object_tombstone_for_testing(&mut self, obj_ref: ObjectRef);
357}
358
359#[derive(Clone)]
360pub struct ObjectChange {
361 pub id: ObjectID,
362 pub input_version: Option<SequenceNumber>,
363 pub input_digest: Option<ObjectDigest>,
364 pub output_version: Option<SequenceNumber>,
365 pub output_digest: Option<ObjectDigest>,
366 pub id_operation: IDOperation,
367}
368
369#[derive(Eq, PartialEq, Copy, Clone, Debug, Serialize, Deserialize)]
370pub enum IDOperation {
371 None,
372 Created,
373 Deleted,
374}
375
376#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, Default)]
377pub struct TransactionEvents {
378 pub data: Vec<Event>,
379}
380
381impl TransactionEvents {
382 pub fn digest(&self) -> TransactionEventsDigest {
383 TransactionEventsDigest::new(default_hash(self))
384 }
385}
386
387#[derive(Debug)]
388pub struct TransactionEffectsDebugSummary {
389 pub bcs_size: usize,
391 pub status: ExecutionStatus,
392 pub gas_used: GasCostSummary,
393 pub transaction_digest: TransactionDigest,
394 pub created_object_count: usize,
395 pub mutated_object_count: usize,
396 pub unwrapped_object_count: usize,
397 pub deleted_object_count: usize,
398 pub wrapped_object_count: usize,
399 pub dependency_count: usize,
400 }
402
403pub type TransactionEffectsEnvelope<S> = Envelope<TransactionEffects, S>;
404pub type UnsignedTransactionEffects = TransactionEffectsEnvelope<EmptySignInfo>;
405pub type SignedTransactionEffects = TransactionEffectsEnvelope<AuthoritySignInfo>;
406pub type CertifiedTransactionEffects = TransactionEffectsEnvelope<AuthorityStrongQuorumSignInfo>;
407
408pub type TrustedSignedTransactionEffects = TrustedEnvelope<TransactionEffects, AuthoritySignInfo>;
409pub type VerifiedTransactionEffectsEnvelope<S> = VerifiedEnvelope<TransactionEffects, S>;
410pub type VerifiedSignedTransactionEffects = VerifiedTransactionEffectsEnvelope<AuthoritySignInfo>;
411pub type VerifiedCertifiedTransactionEffects =
412 VerifiedTransactionEffectsEnvelope<AuthorityStrongQuorumSignInfo>;
413
414impl CertifiedTransactionEffects {
415 #[instrument(level = "trace", skip_all)]
416 pub fn verify_authority_signatures(&self, committee: &Committee) -> IotaResult {
417 self.auth_sig().verify_secure(
418 self.data(),
419 Intent::iota_app(IntentScope::TransactionEffects),
420 committee,
421 )
422 }
423
424 #[instrument(level = "trace", skip_all)]
425 pub fn verify(self, committee: &Committee) -> IotaResult<VerifiedCertifiedTransactionEffects> {
426 self.verify_authority_signatures(committee)?;
427 Ok(VerifiedCertifiedTransactionEffects::new_from_verified(self))
428 }
429}