1use crate::accumulator_event::AccumulatorEvent;
5use crate::base_types::{
6 EpochId, ObjectID, ObjectRef, SequenceNumber, SuiAddress, TransactionDigest, random_object_ref,
7};
8use crate::digests::{ObjectDigest, TransactionEventsDigest};
9use crate::effects::{InputConsensusObject, TransactionEffectsAPI, UnchangedConsensusKind};
10use crate::execution_status::{ExecutionFailureStatus, ExecutionStatus, MoveLocation};
11use crate::gas::GasCostSummary;
12use crate::object::Owner;
13use serde::{Deserialize, Serialize};
14use std::collections::{BTreeMap, HashSet};
15use std::fmt::{Display, Formatter, Write};
16
17use super::object_change::AccumulatorWriteV1;
18use super::{IDOperation, ObjectChange};
19
20#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)]
22pub struct TransactionEffectsV1 {
23 status: ExecutionStatus,
25 executed_epoch: EpochId,
27 gas_used: GasCostSummary,
28 modified_at_versions: Vec<(ObjectID, SequenceNumber)>,
31 shared_objects: Vec<ObjectRef>,
33 transaction_digest: TransactionDigest,
35
36 created: Vec<(ObjectRef, Owner)>,
40 mutated: Vec<(ObjectRef, Owner)>,
42 unwrapped: Vec<(ObjectRef, Owner)>,
46 deleted: Vec<ObjectRef>,
48 unwrapped_then_deleted: Vec<ObjectRef>,
50 wrapped: Vec<ObjectRef>,
52 gas_object: (ObjectRef, Owner),
55 events_digest: Option<TransactionEventsDigest>,
58 dependencies: Vec<TransactionDigest>,
60}
61
62impl TransactionEffectsV1 {
63 pub fn new(
64 status: ExecutionStatus,
65 executed_epoch: EpochId,
66 gas_used: GasCostSummary,
67 modified_at_versions: Vec<(ObjectID, SequenceNumber)>,
68 shared_objects: Vec<ObjectRef>,
69 transaction_digest: TransactionDigest,
70 created: Vec<(ObjectRef, Owner)>,
71 mutated: Vec<(ObjectRef, Owner)>,
72 unwrapped: Vec<(ObjectRef, Owner)>,
73 deleted: Vec<ObjectRef>,
74 unwrapped_then_deleted: Vec<ObjectRef>,
75 wrapped: Vec<ObjectRef>,
76 gas_object: (ObjectRef, Owner),
77 events_digest: Option<TransactionEventsDigest>,
78 dependencies: Vec<TransactionDigest>,
79 ) -> Self {
80 Self {
81 status,
82 executed_epoch,
83 gas_used,
84 modified_at_versions,
85 shared_objects,
86 transaction_digest,
87 created,
88 mutated,
89 unwrapped,
90 deleted,
91 unwrapped_then_deleted,
92 wrapped,
93 gas_object,
94 events_digest,
95 dependencies,
96 }
97 }
98
99 pub fn modified_at_versions(&self) -> &[(ObjectID, SequenceNumber)] {
100 &self.modified_at_versions
101 }
102
103 pub fn mutated(&self) -> &[(ObjectRef, Owner)] {
104 &self.mutated
105 }
106
107 pub fn created(&self) -> &[(ObjectRef, Owner)] {
108 &self.created
109 }
110
111 pub fn unwrapped(&self) -> &[(ObjectRef, Owner)] {
112 &self.unwrapped
113 }
114
115 pub fn deleted(&self) -> &[ObjectRef] {
116 &self.deleted
117 }
118
119 pub fn wrapped(&self) -> &[ObjectRef] {
120 &self.wrapped
121 }
122
123 pub fn shared_objects(&self) -> &[ObjectRef] {
124 &self.shared_objects
125 }
126}
127
128impl TransactionEffectsAPI for TransactionEffectsV1 {
129 fn status(&self) -> &ExecutionStatus {
130 &self.status
131 }
132
133 fn into_status(self) -> ExecutionStatus {
134 self.status
135 }
136
137 fn executed_epoch(&self) -> EpochId {
138 self.executed_epoch
139 }
140
141 fn modified_at_versions(&self) -> Vec<(ObjectID, SequenceNumber)> {
142 self.modified_at_versions
143 .iter()
144 .filter(|(object_id, _)| {
148 !self
149 .unwrapped_then_deleted
150 .iter()
151 .any(|(deleted_id, _, _)| deleted_id == object_id)
152 })
153 .cloned()
154 .collect()
155 }
156
157 fn move_abort(&self) -> Option<(MoveLocation, u64)> {
158 let ExecutionStatus::Failure {
159 error: ExecutionFailureStatus::MoveAbort(move_location, code),
160 ..
161 } = self.status()
162 else {
163 return None;
164 };
165 Some((move_location.clone(), *code))
166 }
167
168 fn lamport_version(&self) -> SequenceNumber {
169 SequenceNumber::lamport_increment(self.modified_at_versions.iter().map(|(_, v)| *v))
170 }
171
172 fn old_object_metadata(&self) -> Vec<(ObjectRef, Owner)> {
173 unimplemented!("Only supposed by v2 and above");
174 }
175
176 fn input_consensus_objects(&self) -> Vec<InputConsensusObject> {
177 let modified: HashSet<_> = self.modified_at_versions.iter().map(|(r, _)| r).collect();
178 self.shared_objects
179 .iter()
180 .map(|r| {
181 if modified.contains(&r.0) {
182 InputConsensusObject::Mutate(*r)
183 } else {
184 InputConsensusObject::ReadOnly(*r)
185 }
186 })
187 .collect()
188 }
189
190 fn created(&self) -> Vec<(ObjectRef, Owner)> {
191 self.created.clone()
192 }
193
194 fn mutated(&self) -> Vec<(ObjectRef, Owner)> {
195 self.mutated.clone()
196 }
197
198 fn unwrapped(&self) -> Vec<(ObjectRef, Owner)> {
199 self.unwrapped.clone()
200 }
201
202 fn deleted(&self) -> Vec<ObjectRef> {
203 self.deleted.clone()
204 }
205
206 fn unwrapped_then_deleted(&self) -> Vec<ObjectRef> {
207 self.unwrapped_then_deleted.clone()
208 }
209
210 fn wrapped(&self) -> Vec<ObjectRef> {
211 self.wrapped.clone()
212 }
213
214 fn transferred_from_consensus(&self) -> Vec<ObjectRef> {
215 vec![]
217 }
218
219 fn transferred_to_consensus(&self) -> Vec<ObjectRef> {
220 vec![]
222 }
223
224 fn consensus_owner_changed(&self) -> Vec<ObjectRef> {
225 vec![]
227 }
228
229 fn object_changes(&self) -> Vec<ObjectChange> {
230 let modified_at: BTreeMap<_, _> = self.modified_at_versions.iter().copied().collect();
231
232 let created = self.created.iter().map(|((id, v, d), _)| ObjectChange {
233 id: *id,
234 input_version: None,
235 input_digest: None,
236 output_version: Some(*v),
237 output_digest: Some(*d),
238 id_operation: IDOperation::Created,
239 });
240
241 let mutated = self.mutated.iter().map(|((id, v, d), _)| ObjectChange {
242 id: *id,
243 input_version: modified_at.get(id).copied(),
244 input_digest: None,
245 output_version: Some(*v),
246 output_digest: Some(*d),
247 id_operation: IDOperation::None,
248 });
249
250 let unwrapped = self.unwrapped.iter().map(|((id, v, d), _)| ObjectChange {
251 id: *id,
252 input_version: None,
253 input_digest: None,
254 output_version: Some(*v),
255 output_digest: Some(*d),
256 id_operation: IDOperation::None,
257 });
258
259 let deleted = self.deleted.iter().map(|(id, _, _)| ObjectChange {
260 id: *id,
261 input_version: modified_at.get(id).copied(),
262 input_digest: None,
263 output_version: None,
264 output_digest: None,
265 id_operation: IDOperation::Deleted,
266 });
267
268 let unwrapped_then_deleted =
269 self.unwrapped_then_deleted
270 .iter()
271 .map(|(id, _, _)| ObjectChange {
272 id: *id,
273 input_version: None,
274 input_digest: None,
275 output_version: None,
276 output_digest: None,
277 id_operation: IDOperation::Deleted,
278 });
279
280 let wrapped = self.wrapped.iter().map(|(id, _, _)| ObjectChange {
281 id: *id,
282 input_version: modified_at.get(id).copied(),
283 input_digest: None,
284 output_version: None,
285 output_digest: None,
286 id_operation: IDOperation::None,
287 });
288
289 created
290 .chain(mutated)
291 .chain(unwrapped)
292 .chain(deleted)
293 .chain(unwrapped_then_deleted)
294 .chain(wrapped)
295 .collect()
296 }
297
298 fn written(&self) -> Vec<ObjectRef> {
299 unimplemented!("TransactionEffectsV1::written() never called in V1");
300 }
301
302 fn accumulator_events(&self) -> Vec<AccumulatorEvent> {
303 vec![]
305 }
306
307 fn gas_object(&self) -> (ObjectRef, Owner) {
308 self.gas_object.clone()
309 }
310 fn events_digest(&self) -> Option<&TransactionEventsDigest> {
311 self.events_digest.as_ref()
312 }
313
314 fn dependencies(&self) -> &[TransactionDigest] {
315 &self.dependencies
316 }
317
318 fn transaction_digest(&self) -> &TransactionDigest {
319 &self.transaction_digest
320 }
321
322 fn gas_cost_summary(&self) -> &GasCostSummary {
323 &self.gas_used
324 }
325
326 fn unchanged_consensus_objects(&self) -> Vec<(ObjectID, UnchangedConsensusKind)> {
327 self.input_consensus_objects()
328 .iter()
329 .filter_map(|o| match o {
330 InputConsensusObject::ReadOnly(oref) => Some((
332 oref.0,
333 UnchangedConsensusKind::ReadOnlyRoot((oref.1, oref.2)),
334 )),
335 _ => None,
336 })
337 .collect()
338 }
339
340 fn accumulator_updates(&self) -> Vec<(ObjectID, AccumulatorWriteV1)> {
341 vec![]
342 }
343
344 fn status_mut_for_testing(&mut self) -> &mut ExecutionStatus {
345 &mut self.status
346 }
347
348 fn gas_cost_summary_mut_for_testing(&mut self) -> &mut GasCostSummary {
349 &mut self.gas_used
350 }
351
352 fn transaction_digest_mut_for_testing(&mut self) -> &mut TransactionDigest {
353 &mut self.transaction_digest
354 }
355
356 fn dependencies_mut_for_testing(&mut self) -> &mut Vec<TransactionDigest> {
357 &mut self.dependencies
358 }
359
360 fn unsafe_add_input_consensus_object_for_testing(&mut self, kind: InputConsensusObject) {
361 match kind {
362 InputConsensusObject::Mutate(obj_ref) => {
363 self.shared_objects.push(obj_ref);
364 self.modified_at_versions.push((obj_ref.0, obj_ref.1));
365 }
366 InputConsensusObject::ReadOnly(obj_ref) => {
367 self.shared_objects.push(obj_ref);
368 }
369 InputConsensusObject::ReadConsensusStreamEnded(id, version)
370 | InputConsensusObject::MutateConsensusStreamEnded(id, version) => {
371 self.shared_objects
372 .push((id, version, ObjectDigest::OBJECT_DIGEST_DELETED));
373 }
374 InputConsensusObject::Cancelled(..) => {
375 panic!("Transaction cancellation is not supported in effect v1");
376 }
377 }
378 }
379
380 fn unsafe_add_deleted_live_object_for_testing(&mut self, object: ObjectRef) {
381 self.modified_at_versions.push((object.0, object.1));
382 }
383
384 fn unsafe_add_object_tombstone_for_testing(&mut self, object: ObjectRef) {
385 self.deleted.push(object);
386 }
387}
388
389impl Display for TransactionEffectsV1 {
390 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
391 let mut writer = String::new();
392 writeln!(writer, "Status : {:?}", self.status)?;
393 if !self.created.is_empty() {
394 writeln!(writer, "Created Objects:")?;
395 for ((id, _, _), owner) in &self.created {
396 writeln!(writer, " - ID: {} , Owner: {}", id, owner)?;
397 }
398 }
399 if !self.mutated.is_empty() {
400 writeln!(writer, "Mutated Objects:")?;
401 for ((id, _, _), owner) in &self.mutated {
402 writeln!(writer, " - ID: {} , Owner: {}", id, owner)?;
403 }
404 }
405 if !self.deleted.is_empty() {
406 writeln!(writer, "Deleted Objects:")?;
407 for (id, _, _) in &self.deleted {
408 writeln!(writer, " - ID: {}", id)?;
409 }
410 }
411 if !self.wrapped.is_empty() {
412 writeln!(writer, "Wrapped Objects:")?;
413 for (id, _, _) in &self.wrapped {
414 writeln!(writer, " - ID: {}", id)?;
415 }
416 }
417 if !self.unwrapped.is_empty() {
418 writeln!(writer, "Unwrapped Objects:")?;
419 for ((id, _, _), owner) in &self.unwrapped {
420 writeln!(writer, " - ID: {} , Owner: {}", id, owner)?;
421 }
422 }
423 write!(f, "{}", writer)
424 }
425}
426
427impl Default for TransactionEffectsV1 {
428 fn default() -> Self {
429 TransactionEffectsV1 {
430 status: ExecutionStatus::Success,
431 executed_epoch: 0,
432 gas_used: GasCostSummary {
433 computation_cost: 0,
434 storage_cost: 0,
435 storage_rebate: 0,
436 non_refundable_storage_fee: 0,
437 },
438 modified_at_versions: Vec::new(),
439 shared_objects: Vec::new(),
440 transaction_digest: TransactionDigest::random(),
441 created: Vec::new(),
442 mutated: Vec::new(),
443 unwrapped: Vec::new(),
444 deleted: Vec::new(),
445 unwrapped_then_deleted: Vec::new(),
446 wrapped: Vec::new(),
447 gas_object: (
448 random_object_ref(),
449 Owner::AddressOwner(SuiAddress::default()),
450 ),
451 events_digest: None,
452 dependencies: Vec::new(),
453 }
454 }
455}