1#[cfg(debug_assertions)]
6use std::collections::HashSet;
7use std::collections::{BTreeMap, BTreeSet};
8
9use serde::{Deserialize, Serialize};
10
11use super::{
12 EffectsObjectChange, IDOperation, ObjectChange,
13 object_change::{ObjectIn, ObjectOut},
14};
15#[cfg(debug_assertions)]
16use crate::is_system_package;
17use crate::{
18 base_types::{
19 EpochId, IotaAddress, ObjectDigest, ObjectID, ObjectRef, SequenceNumber, TransactionDigest,
20 VersionDigest,
21 },
22 digests::{EffectsAuxDataDigest, TransactionEventsDigest},
23 effects::{InputSharedObject, TransactionEffectsAPI},
24 execution::SharedInput,
25 execution_status::ExecutionStatus,
26 gas::GasCostSummary,
27 object::{OBJECT_START_VERSION, Owner},
28};
29
30#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)]
32pub struct TransactionEffectsV1 {
33 pub(crate) status: ExecutionStatus,
35 pub(crate) executed_epoch: EpochId,
37 pub(crate) gas_used: GasCostSummary,
38 pub(crate) transaction_digest: TransactionDigest,
40 pub(crate) gas_object_index: Option<u32>,
44 pub(crate) events_digest: Option<TransactionEventsDigest>,
47 pub(crate) dependencies: Vec<TransactionDigest>,
49
50 pub(crate) lamport_version: SequenceNumber,
52 pub(crate) changed_objects: Vec<(ObjectID, EffectsObjectChange)>,
54 pub(crate) unchanged_shared_objects: Vec<(ObjectID, UnchangedSharedKind)>,
60 pub(crate) aux_data_digest: Option<EffectsAuxDataDigest>,
65}
66
67impl TransactionEffectsAPI for TransactionEffectsV1 {
68 fn status(&self) -> &ExecutionStatus {
69 &self.status
70 }
71
72 fn into_status(self) -> ExecutionStatus {
73 self.status
74 }
75
76 fn executed_epoch(&self) -> EpochId {
77 self.executed_epoch
78 }
79
80 fn modified_at_versions(&self) -> Vec<(ObjectID, SequenceNumber)> {
81 self.changed_objects
82 .iter()
83 .filter_map(|(id, change)| {
84 if let ObjectIn::Exist(((version, _digest), _owner)) = &change.input_state {
85 Some((*id, *version))
86 } else {
87 None
88 }
89 })
90 .collect()
91 }
92
93 fn lamport_version(&self) -> SequenceNumber {
94 self.lamport_version
95 }
96
97 fn old_object_metadata(&self) -> Vec<(ObjectRef, Owner)> {
98 self.changed_objects
99 .iter()
100 .filter_map(|(id, change)| {
101 if let ObjectIn::Exist(((version, digest), owner)) = &change.input_state {
102 Some(((*id, *version, *digest), *owner))
103 } else {
104 None
105 }
106 })
107 .collect()
108 }
109
110 fn input_shared_objects(&self) -> Vec<InputSharedObject> {
111 self.changed_objects
112 .iter()
113 .filter_map(|(id, change)| match &change.input_state {
114 ObjectIn::Exist(((version, digest), Owner::Shared { .. })) => {
115 Some(InputSharedObject::Mutate((*id, *version, *digest)))
116 }
117 _ => None,
118 })
119 .chain(
120 self.unchanged_shared_objects
121 .iter()
122 .filter_map(|(id, change_kind)| match change_kind {
123 UnchangedSharedKind::ReadOnlyRoot((version, digest)) => {
124 Some(InputSharedObject::ReadOnly((*id, *version, *digest)))
125 }
126 UnchangedSharedKind::MutateDeleted(seqno) => {
127 Some(InputSharedObject::MutateDeleted(*id, *seqno))
128 }
129 UnchangedSharedKind::ReadDeleted(seqno) => {
130 Some(InputSharedObject::ReadDeleted(*id, *seqno))
131 }
132 UnchangedSharedKind::Cancelled(seqno) => {
133 Some(InputSharedObject::Cancelled(*id, *seqno))
134 }
135 UnchangedSharedKind::PerEpochConfig => None,
139 }),
140 )
141 .collect()
142 }
143
144 fn created(&self) -> Vec<(ObjectRef, Owner)> {
145 self.changed_objects
146 .iter()
147 .filter_map(|(id, change)| {
148 match (
149 &change.input_state,
150 &change.output_state,
151 &change.id_operation,
152 ) {
153 (
154 ObjectIn::NotExist,
155 ObjectOut::ObjectWrite((digest, owner)),
156 IDOperation::Created,
157 ) => Some(((*id, self.lamport_version, *digest), *owner)),
158 (
159 ObjectIn::NotExist,
160 ObjectOut::PackageWrite((version, digest)),
161 IDOperation::Created,
162 ) => Some(((*id, *version, *digest), Owner::Immutable)),
163 _ => None,
164 }
165 })
166 .collect()
167 }
168
169 fn mutated(&self) -> Vec<(ObjectRef, Owner)> {
170 self.changed_objects
171 .iter()
172 .filter_map(
173 |(id, change)| match (&change.input_state, &change.output_state) {
174 (ObjectIn::Exist(_), ObjectOut::ObjectWrite((digest, owner))) => {
175 Some(((*id, self.lamport_version, *digest), *owner))
176 }
177 (ObjectIn::Exist(_), ObjectOut::PackageWrite((version, digest))) => {
178 Some(((*id, *version, *digest), Owner::Immutable))
179 }
180 _ => None,
181 },
182 )
183 .collect()
184 }
185
186 fn unwrapped(&self) -> Vec<(ObjectRef, Owner)> {
187 self.changed_objects
188 .iter()
189 .filter_map(|(id, change)| {
190 match (
191 &change.input_state,
192 &change.output_state,
193 &change.id_operation,
194 ) {
195 (
196 ObjectIn::NotExist,
197 ObjectOut::ObjectWrite((digest, owner)),
198 IDOperation::None,
199 ) => Some(((*id, self.lamport_version, *digest), *owner)),
200 _ => None,
201 }
202 })
203 .collect()
204 }
205
206 fn deleted(&self) -> Vec<ObjectRef> {
207 self.changed_objects
208 .iter()
209 .filter_map(|(id, change)| {
210 match (
211 &change.input_state,
212 &change.output_state,
213 &change.id_operation,
214 ) {
215 (ObjectIn::Exist(_), ObjectOut::NotExist, IDOperation::Deleted) => Some((
216 *id,
217 self.lamport_version,
218 ObjectDigest::OBJECT_DIGEST_DELETED,
219 )),
220 _ => None,
221 }
222 })
223 .collect()
224 }
225
226 fn unwrapped_then_deleted(&self) -> Vec<ObjectRef> {
227 self.changed_objects
228 .iter()
229 .filter_map(|(id, change)| {
230 match (
231 &change.input_state,
232 &change.output_state,
233 &change.id_operation,
234 ) {
235 (ObjectIn::NotExist, ObjectOut::NotExist, IDOperation::Deleted) => Some((
236 *id,
237 self.lamport_version,
238 ObjectDigest::OBJECT_DIGEST_DELETED,
239 )),
240 _ => None,
241 }
242 })
243 .collect()
244 }
245
246 fn wrapped(&self) -> Vec<ObjectRef> {
247 self.changed_objects
248 .iter()
249 .filter_map(|(id, change)| {
250 match (
251 &change.input_state,
252 &change.output_state,
253 &change.id_operation,
254 ) {
255 (ObjectIn::Exist(_), ObjectOut::NotExist, IDOperation::None) => Some((
256 *id,
257 self.lamport_version,
258 ObjectDigest::OBJECT_DIGEST_WRAPPED,
259 )),
260 _ => None,
261 }
262 })
263 .collect()
264 }
265
266 fn object_changes(&self) -> Vec<ObjectChange> {
267 self.changed_objects
268 .iter()
269 .map(|(id, change)| {
270 let input_version_digest = match &change.input_state {
271 ObjectIn::NotExist => None,
272 ObjectIn::Exist((vd, _)) => Some(*vd),
273 };
274
275 let output_version_digest = match &change.output_state {
276 ObjectOut::NotExist => None,
277 ObjectOut::ObjectWrite((d, _)) => Some((self.lamport_version, *d)),
278 ObjectOut::PackageWrite(vd) => Some(*vd),
279 };
280
281 ObjectChange {
282 id: *id,
283
284 input_version: input_version_digest.map(|k| k.0),
285 input_digest: input_version_digest.map(|k| k.1),
286
287 output_version: output_version_digest.map(|k| k.0),
288 output_digest: output_version_digest.map(|k| k.1),
289
290 id_operation: change.id_operation,
291 }
292 })
293 .collect()
294 }
295
296 fn gas_object(&self) -> (ObjectRef, Owner) {
297 if let Some(gas_object_index) = self.gas_object_index {
298 let entry = &self.changed_objects[gas_object_index as usize];
299 match entry.1.output_state {
300 ObjectOut::ObjectWrite((digest, owner)) => {
301 ((entry.0, self.lamport_version, digest), owner)
302 }
303 _ => panic!("Gas object must be an ObjectWrite in changed_objects"),
304 }
305 } else {
306 (
307 (ObjectID::ZERO, SequenceNumber::default(), ObjectDigest::MIN),
308 Owner::AddressOwner(IotaAddress::default()),
309 )
310 }
311 }
312
313 fn events_digest(&self) -> Option<&TransactionEventsDigest> {
314 self.events_digest.as_ref()
315 }
316
317 fn dependencies(&self) -> &[TransactionDigest] {
318 &self.dependencies
319 }
320
321 fn transaction_digest(&self) -> &TransactionDigest {
322 &self.transaction_digest
323 }
324
325 fn gas_cost_summary(&self) -> &GasCostSummary {
326 &self.gas_used
327 }
328
329 fn unchanged_shared_objects(&self) -> Vec<(ObjectID, UnchangedSharedKind)> {
330 self.unchanged_shared_objects.clone()
331 }
332
333 fn status_mut_for_testing(&mut self) -> &mut ExecutionStatus {
334 &mut self.status
335 }
336
337 fn gas_cost_summary_mut_for_testing(&mut self) -> &mut GasCostSummary {
338 &mut self.gas_used
339 }
340
341 fn transaction_digest_mut_for_testing(&mut self) -> &mut TransactionDigest {
342 &mut self.transaction_digest
343 }
344
345 fn dependencies_mut_for_testing(&mut self) -> &mut Vec<TransactionDigest> {
346 &mut self.dependencies
347 }
348
349 fn unsafe_add_input_shared_object_for_testing(&mut self, kind: InputSharedObject) {
350 match kind {
351 InputSharedObject::Mutate(obj_ref) => self.changed_objects.push((
352 obj_ref.0,
353 EffectsObjectChange {
354 input_state: ObjectIn::Exist((
355 (obj_ref.1, obj_ref.2),
356 Owner::Shared {
357 initial_shared_version: OBJECT_START_VERSION,
358 },
359 )),
360 output_state: ObjectOut::ObjectWrite((
361 obj_ref.2,
362 Owner::Shared {
363 initial_shared_version: obj_ref.1,
364 },
365 )),
366 id_operation: IDOperation::None,
367 },
368 )),
369 InputSharedObject::ReadOnly(obj_ref) => self.unchanged_shared_objects.push((
370 obj_ref.0,
371 UnchangedSharedKind::ReadOnlyRoot((obj_ref.1, obj_ref.2)),
372 )),
373 InputSharedObject::ReadDeleted(obj_id, seqno) => self
374 .unchanged_shared_objects
375 .push((obj_id, UnchangedSharedKind::ReadDeleted(seqno))),
376 InputSharedObject::MutateDeleted(obj_id, seqno) => self
377 .unchanged_shared_objects
378 .push((obj_id, UnchangedSharedKind::MutateDeleted(seqno))),
379 InputSharedObject::Cancelled(obj_id, seqno) => self
380 .unchanged_shared_objects
381 .push((obj_id, UnchangedSharedKind::Cancelled(seqno))),
382 }
383 }
384
385 fn unsafe_add_deleted_live_object_for_testing(&mut self, obj_ref: ObjectRef) {
386 self.changed_objects.push((
387 obj_ref.0,
388 EffectsObjectChange {
389 input_state: ObjectIn::Exist((
390 (obj_ref.1, obj_ref.2),
391 Owner::AddressOwner(IotaAddress::default()),
392 )),
393 output_state: ObjectOut::ObjectWrite((
394 obj_ref.2,
395 Owner::AddressOwner(IotaAddress::default()),
396 )),
397 id_operation: IDOperation::None,
398 },
399 ))
400 }
401
402 fn unsafe_add_object_tombstone_for_testing(&mut self, obj_ref: ObjectRef) {
403 self.changed_objects.push((
404 obj_ref.0,
405 EffectsObjectChange {
406 input_state: ObjectIn::Exist((
407 (obj_ref.1, obj_ref.2),
408 Owner::AddressOwner(IotaAddress::default()),
409 )),
410 output_state: ObjectOut::NotExist,
411 id_operation: IDOperation::Deleted,
412 },
413 ))
414 }
415}
416
417impl TransactionEffectsV1 {
418 pub fn new(
419 status: ExecutionStatus,
420 executed_epoch: EpochId,
421 gas_used: GasCostSummary,
422 shared_objects: Vec<SharedInput>,
423 loaded_per_epoch_config_objects: BTreeSet<ObjectID>,
424 transaction_digest: TransactionDigest,
425 lamport_version: SequenceNumber,
426 changed_objects: BTreeMap<ObjectID, EffectsObjectChange>,
427 gas_object: Option<ObjectID>,
428 events_digest: Option<TransactionEventsDigest>,
429 dependencies: Vec<TransactionDigest>,
430 ) -> Self {
431 let unchanged_shared_objects = shared_objects
432 .into_iter()
433 .filter_map(|shared_input| match shared_input {
434 SharedInput::Existing((id, version, digest)) => {
435 if changed_objects.contains_key(&id) {
436 None
437 } else {
438 Some((id, UnchangedSharedKind::ReadOnlyRoot((version, digest))))
439 }
440 }
441 SharedInput::Deleted((id, version, mutable, _)) => {
442 debug_assert!(!changed_objects.contains_key(&id));
443 if mutable {
444 Some((id, UnchangedSharedKind::MutateDeleted(version)))
445 } else {
446 Some((id, UnchangedSharedKind::ReadDeleted(version)))
447 }
448 }
449 SharedInput::Cancelled((id, version)) => {
450 debug_assert!(!changed_objects.contains_key(&id));
451 Some((id, UnchangedSharedKind::Cancelled(version)))
452 }
453 })
454 .chain(
455 loaded_per_epoch_config_objects
456 .into_iter()
457 .map(|id| (id, UnchangedSharedKind::PerEpochConfig)),
458 )
459 .collect();
460 let changed_objects: Vec<_> = changed_objects.into_iter().collect();
461
462 let gas_object_index = gas_object.map(|gas_id| {
463 changed_objects
464 .iter()
465 .position(|(id, _)| id == &gas_id)
466 .unwrap() as u32
467 });
468
469 let result = Self {
470 status,
471 executed_epoch,
472 gas_used,
473 transaction_digest,
474 lamport_version,
475 changed_objects,
476 unchanged_shared_objects,
477 gas_object_index,
478 events_digest,
479 dependencies,
480 aux_data_digest: None,
481 };
482 #[cfg(debug_assertions)]
483 result.check_invariant();
484
485 result
486 }
487
488 #[cfg(debug_assertions)]
492 fn check_invariant(&self) {
493 let mut unique_ids = HashSet::new();
494 for (id, change) in &self.changed_objects {
495 assert!(unique_ids.insert(*id));
496 match (
497 &change.input_state,
498 &change.output_state,
499 &change.id_operation,
500 ) {
501 (ObjectIn::NotExist, ObjectOut::NotExist, IDOperation::Created) => {
502 }
504 (ObjectIn::NotExist, ObjectOut::NotExist, IDOperation::Deleted) => {
505 }
507 (ObjectIn::NotExist, ObjectOut::ObjectWrite((_, owner)), IDOperation::None) => {
508 assert!(!owner.is_shared());
511 }
512 (ObjectIn::NotExist, ObjectOut::ObjectWrite(..), IDOperation::Created) => {
513 }
515 (ObjectIn::NotExist, ObjectOut::PackageWrite(_), IDOperation::Created) => {
516 }
518 (
519 ObjectIn::Exist(((old_version, _), old_owner)),
520 ObjectOut::NotExist,
521 IDOperation::None,
522 ) => {
523 assert!(old_version.value() < self.lamport_version.value());
525 assert!(
526 !old_owner.is_shared() && !old_owner.is_immutable(),
527 "Cannot wrap shared or immutable object"
528 );
529 }
530 (
531 ObjectIn::Exist(((old_version, _), old_owner)),
532 ObjectOut::NotExist,
533 IDOperation::Deleted,
534 ) => {
535 assert!(old_version.value() < self.lamport_version.value());
537 assert!(!old_owner.is_immutable(), "Cannot delete immutable object");
538 }
539 (
540 ObjectIn::Exist(((old_version, old_digest), old_owner)),
541 ObjectOut::ObjectWrite((new_digest, new_owner)),
542 IDOperation::None,
543 ) => {
544 assert!(old_version.value() < self.lamport_version.value());
546 assert_ne!(old_digest, new_digest);
547 assert!(!old_owner.is_immutable(), "Cannot mutate immutable object");
548 if old_owner.is_shared() {
549 assert!(new_owner.is_shared(), "Cannot un-share an object");
550 } else {
551 assert!(!new_owner.is_shared(), "Cannot share an existing object");
552 }
553 }
554 (
555 ObjectIn::Exist(((old_version, old_digest), old_owner)),
556 ObjectOut::PackageWrite((new_version, new_digest)),
557 IDOperation::None,
558 ) => {
559 assert!(
561 old_owner.is_immutable() && is_system_package(*id),
562 "Must be a system package"
563 );
564 assert_eq!(old_version.value() + 1, new_version.value());
565 assert_ne!(old_digest, new_digest);
566 }
567 _ => {
568 panic!("Impossible object change: {:?}, {:?}", id, change);
569 }
570 }
571 }
572 let (_, owner) = self.gas_object();
574 assert!(matches!(owner, Owner::AddressOwner(_)));
575
576 for (id, _) in &self.unchanged_shared_objects {
577 assert!(
578 unique_ids.insert(*id),
579 "Duplicate object id: {:?}\n{:#?}",
580 id,
581 self
582 );
583 }
584 }
585
586 pub fn changed_objects(&self) -> &[(ObjectID, EffectsObjectChange)] {
587 &self.changed_objects
588 }
589
590 pub fn aux_data_digest(&self) -> Option<EffectsAuxDataDigest> {
591 self.aux_data_digest
592 }
593}
594
595impl Default for TransactionEffectsV1 {
596 fn default() -> Self {
597 Self {
598 status: ExecutionStatus::Success,
599 executed_epoch: 0,
600 gas_used: GasCostSummary::default(),
601 transaction_digest: TransactionDigest::default(),
602 lamport_version: SequenceNumber::default(),
603 changed_objects: vec![],
604 unchanged_shared_objects: vec![],
605 gas_object_index: None,
606 events_digest: None,
607 dependencies: vec![],
608 aux_data_digest: None,
609 }
610 }
611}
612
613#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)]
614pub enum UnchangedSharedKind {
615 ReadOnlyRoot(VersionDigest),
619 MutateDeleted(SequenceNumber),
621 ReadDeleted(SequenceNumber),
623 Cancelled(SequenceNumber),
626 PerEpochConfig,
629}