sui_types/effects/
test_effects_builder.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use crate::accumulator_event::AccumulatorEvent;
5use crate::base_types::{ObjectID, SequenceNumber};
6use crate::digests::{ObjectDigest, TransactionEventsDigest};
7use crate::effects::{EffectsObjectChange, IDOperation, ObjectIn, ObjectOut, TransactionEffects};
8use crate::execution::SharedInput;
9use crate::execution_status::ExecutionStatus;
10use crate::gas::GasCostSummary;
11use crate::message_envelope::Message;
12use crate::object::Owner;
13use crate::transaction::{
14    InputObjectKind, SenderSignedData, SharedObjectMutability, TransactionDataAPI,
15};
16use std::collections::{BTreeMap, BTreeSet};
17
18#[derive(Clone)]
19pub struct TestEffectsBuilder {
20    transaction: SenderSignedData,
21    /// Override the execution status if provided.
22    status: Option<ExecutionStatus>,
23    /// Provide the assigned versions for all shared objects.
24    shared_input_versions: BTreeMap<ObjectID, SequenceNumber>,
25    events_digest: Option<TransactionEventsDigest>,
26    created_objects: Vec<(ObjectID, Owner)>,
27    /// Objects that are mutated: (ID, old version, new owner).
28    mutated_objects: Vec<(ObjectID, SequenceNumber, Owner)>,
29    /// Objects that are deleted: (ID, old version).
30    deleted_objects: Vec<(ObjectID, SequenceNumber)>,
31    /// Objects that are wrapped: (ID, old version).
32    wrapped_objects: Vec<(ObjectID, SequenceNumber)>,
33    /// Objects that are unwrapped: (ID, new owner).
34    unwrapped_objects: Vec<(ObjectID, Owner)>,
35    /// Immutable objects that are read.
36    frozen_objects: BTreeSet<ObjectID>,
37    /// Accumulator events.
38    accumulator_events: Vec<AccumulatorEvent>,
39}
40
41impl TestEffectsBuilder {
42    pub fn new(transaction: &SenderSignedData) -> Self {
43        Self {
44            transaction: transaction.clone(),
45            status: None,
46            shared_input_versions: BTreeMap::new(),
47            events_digest: None,
48            created_objects: vec![],
49            mutated_objects: vec![],
50            deleted_objects: vec![],
51            wrapped_objects: vec![],
52            unwrapped_objects: vec![],
53            frozen_objects: BTreeSet::new(),
54            accumulator_events: vec![],
55        }
56    }
57
58    pub fn with_status(mut self, status: ExecutionStatus) -> Self {
59        self.status = Some(status);
60        self
61    }
62
63    pub fn with_shared_input_versions(
64        mut self,
65        versions: BTreeMap<ObjectID, SequenceNumber>,
66    ) -> Self {
67        assert!(self.shared_input_versions.is_empty());
68        self.shared_input_versions = versions;
69        self
70    }
71
72    pub fn with_events_digest(mut self, digest: TransactionEventsDigest) -> Self {
73        self.events_digest = Some(digest);
74        self
75    }
76
77    pub fn with_created_objects(
78        mut self,
79        objects: impl IntoIterator<Item = (ObjectID, Owner)>,
80    ) -> Self {
81        self.created_objects.extend(objects);
82        self
83    }
84
85    pub fn with_mutated_objects(
86        mut self,
87        // Object ID, old version, and new owner.
88        objects: impl IntoIterator<Item = (ObjectID, SequenceNumber, Owner)>,
89    ) -> Self {
90        self.mutated_objects.extend(objects);
91        self
92    }
93
94    pub fn with_wrapped_objects(
95        mut self,
96        objects: impl IntoIterator<Item = (ObjectID, SequenceNumber)>,
97    ) -> Self {
98        self.wrapped_objects.extend(objects);
99        self
100    }
101
102    pub fn with_unwrapped_objects(
103        mut self,
104        objects: impl IntoIterator<Item = (ObjectID, Owner)>,
105    ) -> Self {
106        self.unwrapped_objects.extend(objects);
107        self
108    }
109
110    pub fn with_deleted_objects(
111        mut self,
112        objects: impl IntoIterator<Item = (ObjectID, SequenceNumber)>,
113    ) -> Self {
114        self.deleted_objects.extend(objects);
115        self
116    }
117
118    pub fn with_frozen_objects(mut self, objects: impl IntoIterator<Item = ObjectID>) -> Self {
119        self.frozen_objects.extend(objects);
120        self
121    }
122
123    pub fn with_accumulator_events(
124        mut self,
125        events: impl IntoIterator<Item = AccumulatorEvent>,
126    ) -> Self {
127        self.accumulator_events.extend(events);
128        self
129    }
130
131    pub fn build(self) -> TransactionEffects {
132        let lamport_version = self.get_lamport_version();
133        let status = self.status.unwrap_or(ExecutionStatus::Success);
134        // TODO: This does not yet support deleted shared objects.
135        let shared_objects = self
136            .shared_input_versions
137            .iter()
138            .map(|(id, version)| SharedInput::Existing((*id, *version, ObjectDigest::MIN)))
139            .collect();
140        let executed_epoch = 0;
141        let sender = self.transaction.transaction_data().sender();
142        // TODO: Include receiving objects in the object changes as well.
143        let changed_objects = self
144            .transaction
145            .transaction_data()
146            .input_objects()
147            .unwrap()
148            .iter()
149            .filter_map(|kind| match kind {
150                InputObjectKind::ImmOrOwnedMoveObject((id, _, _))
151                    if self.frozen_objects.contains(id) =>
152                {
153                    None
154                }
155                InputObjectKind::ImmOrOwnedMoveObject(oref) => Some((
156                    oref.0,
157                    EffectsObjectChange {
158                        input_state: ObjectIn::Exist((
159                            (oref.1, oref.2),
160                            Owner::AddressOwner(sender),
161                        )),
162                        output_state: ObjectOut::ObjectWrite((
163                            // Digest must change with a mutation.
164                            ObjectDigest::MAX,
165                            Owner::AddressOwner(sender),
166                        )),
167                        id_operation: IDOperation::None,
168                    },
169                )),
170                InputObjectKind::MovePackage(_) => None,
171                InputObjectKind::SharedMoveObject {
172                    id,
173                    initial_shared_version,
174                    mutability,
175                } => {
176                    let mutable = match mutability {
177                        SharedObjectMutability::Mutable => true,
178                        SharedObjectMutability::Immutable => false,
179                        SharedObjectMutability::NonExclusiveWrite => todo!(),
180                    };
181                    mutable.then_some((
182                        *id,
183                        EffectsObjectChange {
184                            input_state: ObjectIn::Exist((
185                                (
186                                    *self
187                                        .shared_input_versions
188                                        .get(id)
189                                        .unwrap_or(initial_shared_version),
190                                    ObjectDigest::MIN,
191                                ),
192                                Owner::Shared {
193                                    initial_shared_version: *initial_shared_version,
194                                },
195                            )),
196                            output_state: ObjectOut::ObjectWrite((
197                                // Digest must change with a mutation.
198                                ObjectDigest::MAX,
199                                Owner::Shared {
200                                    initial_shared_version: *initial_shared_version,
201                                },
202                            )),
203                            id_operation: IDOperation::None,
204                        },
205                    ))
206                }
207            })
208            .chain(self.created_objects.into_iter().map(|(id, owner)| {
209                (
210                    id,
211                    EffectsObjectChange {
212                        input_state: ObjectIn::NotExist,
213                        output_state: ObjectOut::ObjectWrite((ObjectDigest::random(), owner)),
214                        id_operation: IDOperation::Created,
215                    },
216                )
217            }))
218            .chain(
219                self.mutated_objects
220                    .into_iter()
221                    .map(|(id, version, owner)| {
222                        (
223                            id,
224                            EffectsObjectChange {
225                                input_state: ObjectIn::Exist((
226                                    (version, ObjectDigest::random()),
227                                    Owner::AddressOwner(sender),
228                                )),
229                                output_state: ObjectOut::ObjectWrite((
230                                    ObjectDigest::random(),
231                                    owner,
232                                )),
233                                id_operation: IDOperation::None,
234                            },
235                        )
236                    }),
237            )
238            .chain(self.deleted_objects.into_iter().map(|(id, version)| {
239                (
240                    id,
241                    EffectsObjectChange {
242                        input_state: ObjectIn::Exist((
243                            (version, ObjectDigest::random()),
244                            Owner::AddressOwner(sender),
245                        )),
246                        output_state: ObjectOut::NotExist,
247                        id_operation: IDOperation::Deleted,
248                    },
249                )
250            }))
251            .chain(self.wrapped_objects.into_iter().map(|(id, version)| {
252                (
253                    id,
254                    EffectsObjectChange {
255                        input_state: ObjectIn::Exist((
256                            (version, ObjectDigest::random()),
257                            Owner::AddressOwner(sender),
258                        )),
259                        output_state: ObjectOut::NotExist,
260                        id_operation: IDOperation::None,
261                    },
262                )
263            }))
264            .chain(self.unwrapped_objects.into_iter().map(|(id, owner)| {
265                (
266                    id,
267                    EffectsObjectChange {
268                        input_state: ObjectIn::NotExist,
269                        output_state: ObjectOut::ObjectWrite((ObjectDigest::random(), owner)),
270                        id_operation: IDOperation::None,
271                    },
272                )
273            }))
274            .chain(self.accumulator_events.into_iter().map(|event| {
275                (
276                    *event.accumulator_obj.inner(),
277                    EffectsObjectChange {
278                        input_state: ObjectIn::NotExist,
279                        output_state: ObjectOut::AccumulatorWriteV1(event.write),
280                        id_operation: IDOperation::None,
281                    },
282                )
283            }))
284            .collect();
285        let gas_object_id = self.transaction.transaction_data().gas()[0].0;
286        let event_digest = self.events_digest;
287        let dependencies = vec![];
288        TransactionEffects::new_from_execution_v2(
289            status,
290            executed_epoch,
291            GasCostSummary::default(),
292            shared_objects,
293            BTreeSet::new(),
294            self.transaction.digest(),
295            lamport_version,
296            changed_objects,
297            Some(gas_object_id),
298            event_digest,
299            dependencies,
300        )
301    }
302
303    fn get_lamport_version(&self) -> SequenceNumber {
304        SequenceNumber::lamport_increment(
305            self.transaction
306                .transaction_data()
307                .input_objects()
308                .unwrap()
309                .iter()
310                .filter_map(|kind| kind.version())
311                .chain(
312                    self.transaction
313                        .transaction_data()
314                        .receiving_objects()
315                        .iter()
316                        .map(|oref| oref.1),
317                )
318                .chain(self.shared_input_versions.values().copied())
319                .chain(self.mutated_objects.iter().map(|(_, v, _)| *v))
320                .chain(self.deleted_objects.iter().map(|(_, v)| *v))
321                .chain(self.wrapped_objects.iter().map(|(_, v)| *v)),
322        )
323    }
324}