sui_types/effects/
test_effects_builder.rs

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