1use std::collections::{BTreeMap, BTreeSet};
6
7use crate::{
8 base_types::{ObjectID, ObjectRef, SequenceNumber},
9 digests::{ObjectDigest, TransactionEventsDigest},
10 effects::{EffectsObjectChange, IDOperation, ObjectIn, ObjectOut, TransactionEffects},
11 execution::SharedInput,
12 execution_status::ExecutionStatus,
13 gas::GasCostSummary,
14 message_envelope::Message,
15 object::Owner,
16 transaction::{InputObjectKind, SenderSignedData, TransactionDataAPI},
17};
18
19pub struct TestEffectsBuilder {
20 transaction: SenderSignedData,
21 status: Option<ExecutionStatus>,
23 shared_input_versions: BTreeMap<ObjectID, SequenceNumber>,
25 events_digest: Option<TransactionEventsDigest>,
26 created_objects: Vec<(ObjectID, Owner)>,
27 mutated_objects: Vec<(ObjectID, SequenceNumber, Owner)>,
29 deleted_objects: Vec<(ObjectID, SequenceNumber)>,
31 wrapped_objects: Vec<(ObjectID, SequenceNumber)>,
33 unwrapped_objects: Vec<(ObjectID, Owner)>,
35 frozen_objects: BTreeSet<ObjectID>,
37}
38
39impl TestEffectsBuilder {
40 pub fn new(transaction: &SenderSignedData) -> Self {
41 Self {
42 transaction: transaction.clone(),
43 status: None,
44 shared_input_versions: BTreeMap::new(),
45 events_digest: None,
46 created_objects: vec![],
47 mutated_objects: vec![],
48 deleted_objects: vec![],
49 wrapped_objects: vec![],
50 unwrapped_objects: vec![],
51 frozen_objects: BTreeSet::new(),
52 }
53 }
54
55 pub fn with_status(mut self, status: ExecutionStatus) -> Self {
56 self.status = Some(status);
57 self
58 }
59
60 pub fn with_shared_input_versions(
61 mut self,
62 versions: BTreeMap<ObjectID, SequenceNumber>,
63 ) -> Self {
64 assert!(self.shared_input_versions.is_empty());
65 self.shared_input_versions = versions;
66 self
67 }
68
69 pub fn with_events_digest(mut self, digest: TransactionEventsDigest) -> Self {
70 self.events_digest = Some(digest);
71 self
72 }
73
74 pub fn with_created_objects(
75 mut self,
76 objects: impl IntoIterator<Item = (ObjectID, Owner)>,
77 ) -> Self {
78 self.created_objects.extend(objects);
79 self
80 }
81
82 pub fn with_mutated_objects(
83 mut self,
84 objects: impl IntoIterator<Item = (ObjectID, SequenceNumber, Owner)>,
86 ) -> Self {
87 self.mutated_objects.extend(objects);
88 self
89 }
90
91 pub fn with_wrapped_objects(
92 mut self,
93 objects: impl IntoIterator<Item = (ObjectID, SequenceNumber)>,
94 ) -> Self {
95 self.wrapped_objects.extend(objects);
96 self
97 }
98
99 pub fn with_unwrapped_objects(
100 mut self,
101 objects: impl IntoIterator<Item = (ObjectID, Owner)>,
102 ) -> Self {
103 self.unwrapped_objects.extend(objects);
104 self
105 }
106
107 pub fn with_deleted_objects(
108 mut self,
109 objects: impl IntoIterator<Item = (ObjectID, SequenceNumber)>,
110 ) -> Self {
111 self.deleted_objects.extend(objects);
112 self
113 }
114
115 pub fn with_frozen_objects(mut self, objects: impl IntoIterator<Item = ObjectID>) -> Self {
116 self.frozen_objects.extend(objects);
117 self
118 }
119
120 pub fn build(self) -> TransactionEffects {
121 let lamport_version = self.get_lamport_version();
122 let status = self.status.unwrap_or(ExecutionStatus::Success);
123 let shared_objects = self
125 .shared_input_versions
126 .iter()
127 .map(|(id, version)| {
128 SharedInput::Existing(ObjectRef::new(*id, *version, ObjectDigest::MIN))
129 })
130 .collect();
131 let executed_epoch = 0;
132 let sender = self.transaction.transaction_data().sender();
133 let changed_objects = self
135 .transaction
136 .transaction_data()
137 .input_objects()
138 .unwrap()
139 .iter()
140 .filter_map(|kind| match kind {
141 InputObjectKind::ImmOrOwnedMoveObject(object_ref)
142 if self.frozen_objects.contains(&object_ref.object_id) =>
143 {
144 None
145 }
146 InputObjectKind::ImmOrOwnedMoveObject(oref) => {
147 Some((
148 oref.object_id,
149 EffectsObjectChange {
150 input_state: ObjectIn::Exist((
151 (oref.version, oref.digest),
152 Owner::Address(sender),
153 )),
154 output_state: ObjectOut::ObjectWrite((
155 ObjectDigest::MAX,
157 Owner::Address(sender),
158 )),
159 id_operation: IDOperation::None,
160 },
161 ))
162 }
163 InputObjectKind::MovePackage(_) => None,
164 InputObjectKind::SharedMoveObject {
165 id,
166 initial_shared_version,
167 mutable,
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(*initial_shared_version),
180 )),
181 output_state: ObjectOut::ObjectWrite((
182 ObjectDigest::MAX,
184 Owner::Shared(*initial_shared_version),
185 )),
186 id_operation: IDOperation::None,
187 },
188 )),
189 })
190 .chain(self.created_objects.into_iter().map(|(id, owner)| {
191 (
192 id,
193 EffectsObjectChange {
194 input_state: ObjectIn::NotExist,
195 output_state: ObjectOut::ObjectWrite((ObjectDigest::random(), owner)),
196 id_operation: IDOperation::Created,
197 },
198 )
199 }))
200 .chain(
201 self.mutated_objects
202 .into_iter()
203 .map(|(id, version, owner)| {
204 (
205 id,
206 EffectsObjectChange {
207 input_state: ObjectIn::Exist((
208 (version, ObjectDigest::random()),
209 Owner::Address(sender),
210 )),
211 output_state: ObjectOut::ObjectWrite((
212 ObjectDigest::random(),
213 owner,
214 )),
215 id_operation: IDOperation::None,
216 },
217 )
218 }),
219 )
220 .chain(self.deleted_objects.into_iter().map(|(id, version)| {
221 (
222 id,
223 EffectsObjectChange {
224 input_state: ObjectIn::Exist((
225 (version, ObjectDigest::random()),
226 Owner::Address(sender),
227 )),
228 output_state: ObjectOut::NotExist,
229 id_operation: IDOperation::Deleted,
230 },
231 )
232 }))
233 .chain(self.wrapped_objects.into_iter().map(|(id, version)| {
234 (
235 id,
236 EffectsObjectChange {
237 input_state: ObjectIn::Exist((
238 (version, ObjectDigest::random()),
239 Owner::Address(sender),
240 )),
241 output_state: ObjectOut::NotExist,
242 id_operation: IDOperation::None,
243 },
244 )
245 }))
246 .chain(self.unwrapped_objects.into_iter().map(|(id, owner)| {
247 (
248 id,
249 EffectsObjectChange {
250 input_state: ObjectIn::NotExist,
251 output_state: ObjectOut::ObjectWrite((ObjectDigest::random(), owner)),
252 id_operation: IDOperation::None,
253 },
254 )
255 }))
256 .collect();
257 let gas_object_id = self.transaction.transaction_data().gas()[0].object_id;
258 let event_digest = self.events_digest;
259 let dependencies = vec![];
260 TransactionEffects::new_from_execution_v1(
261 status,
262 executed_epoch,
263 GasCostSummary::default(),
264 shared_objects,
265 BTreeSet::new(),
266 self.transaction.digest(),
267 lamport_version,
268 changed_objects,
269 Some(gas_object_id),
270 event_digest,
271 dependencies,
272 )
273 }
274
275 fn get_lamport_version(&self) -> SequenceNumber {
276 SequenceNumber::lamport_increment(
277 self.transaction
278 .transaction_data()
279 .input_objects()
280 .unwrap()
281 .iter()
282 .filter_map(|kind| kind.version())
283 .chain(
284 self.transaction
285 .transaction_data()
286 .receiving_objects()
287 .iter()
288 .map(|oref| oref.version),
289 )
290 .chain(self.shared_input_versions.values().copied())
291 .chain(self.mutated_objects.iter().map(|(_, v, _)| *v))
292 .chain(self.deleted_objects.iter().map(|(_, v)| *v))
293 .chain(self.wrapped_objects.iter().map(|(_, v)| *v)),
294 )
295 .unwrap()
296 }
297}