1use std::collections::{BTreeMap, BTreeSet};
6
7use crate::{
8 base_types::{ObjectID, 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)| SharedInput::Existing((*id, *version, ObjectDigest::MIN)))
128 .collect();
129 let executed_epoch = 0;
130 let sender = self.transaction.transaction_data().sender();
131 let changed_objects = self
133 .transaction
134 .transaction_data()
135 .input_objects()
136 .unwrap()
137 .iter()
138 .filter_map(|kind| match kind {
139 InputObjectKind::ImmOrOwnedMoveObject((id, _, _))
140 if self.frozen_objects.contains(id) =>
141 {
142 None
143 }
144 InputObjectKind::ImmOrOwnedMoveObject(oref) => {
145 Some((
146 oref.0,
147 EffectsObjectChange {
148 input_state: ObjectIn::Exist((
149 (oref.1, oref.2),
150 Owner::AddressOwner(sender),
151 )),
152 output_state: ObjectOut::ObjectWrite((
153 ObjectDigest::MAX,
155 Owner::AddressOwner(sender),
156 )),
157 id_operation: IDOperation::None,
158 },
159 ))
160 }
161 InputObjectKind::MovePackage(_) => None,
162 InputObjectKind::SharedMoveObject {
163 id,
164 initial_shared_version,
165 mutable,
166 } => mutable.then_some((
167 *id,
168 EffectsObjectChange {
169 input_state: ObjectIn::Exist((
170 (
171 *self
172 .shared_input_versions
173 .get(id)
174 .unwrap_or(initial_shared_version),
175 ObjectDigest::MIN,
176 ),
177 Owner::Shared {
178 initial_shared_version: *initial_shared_version,
179 },
180 )),
181 output_state: ObjectOut::ObjectWrite((
182 ObjectDigest::MAX,
184 Owner::Shared {
185 initial_shared_version: *initial_shared_version,
186 },
187 )),
188 id_operation: IDOperation::None,
189 },
190 )),
191 })
192 .chain(self.created_objects.into_iter().map(|(id, owner)| {
193 (
194 id,
195 EffectsObjectChange {
196 input_state: ObjectIn::NotExist,
197 output_state: ObjectOut::ObjectWrite((ObjectDigest::random(), owner)),
198 id_operation: IDOperation::Created,
199 },
200 )
201 }))
202 .chain(
203 self.mutated_objects
204 .into_iter()
205 .map(|(id, version, owner)| {
206 (
207 id,
208 EffectsObjectChange {
209 input_state: ObjectIn::Exist((
210 (version, ObjectDigest::random()),
211 Owner::AddressOwner(sender),
212 )),
213 output_state: ObjectOut::ObjectWrite((
214 ObjectDigest::random(),
215 owner,
216 )),
217 id_operation: IDOperation::None,
218 },
219 )
220 }),
221 )
222 .chain(self.deleted_objects.into_iter().map(|(id, version)| {
223 (
224 id,
225 EffectsObjectChange {
226 input_state: ObjectIn::Exist((
227 (version, ObjectDigest::random()),
228 Owner::AddressOwner(sender),
229 )),
230 output_state: ObjectOut::NotExist,
231 id_operation: IDOperation::Deleted,
232 },
233 )
234 }))
235 .chain(self.wrapped_objects.into_iter().map(|(id, version)| {
236 (
237 id,
238 EffectsObjectChange {
239 input_state: ObjectIn::Exist((
240 (version, ObjectDigest::random()),
241 Owner::AddressOwner(sender),
242 )),
243 output_state: ObjectOut::NotExist,
244 id_operation: IDOperation::None,
245 },
246 )
247 }))
248 .chain(self.unwrapped_objects.into_iter().map(|(id, owner)| {
249 (
250 id,
251 EffectsObjectChange {
252 input_state: ObjectIn::NotExist,
253 output_state: ObjectOut::ObjectWrite((ObjectDigest::random(), owner)),
254 id_operation: IDOperation::None,
255 },
256 )
257 }))
258 .collect();
259 let gas_object_id = self.transaction.transaction_data().gas()[0].0;
260 let event_digest = self.events_digest;
261 let dependencies = vec![];
262 TransactionEffects::new_from_execution_v1(
263 status,
264 executed_epoch,
265 GasCostSummary::default(),
266 shared_objects,
267 BTreeSet::new(),
268 self.transaction.digest(),
269 lamport_version,
270 changed_objects,
271 Some(gas_object_id),
272 event_digest,
273 dependencies,
274 )
275 }
276
277 fn get_lamport_version(&self) -> SequenceNumber {
278 SequenceNumber::lamport_increment(
279 self.transaction
280 .transaction_data()
281 .input_objects()
282 .unwrap()
283 .iter()
284 .filter_map(|kind| kind.version())
285 .chain(
286 self.transaction
287 .transaction_data()
288 .receiving_objects()
289 .iter()
290 .map(|oref| oref.1),
291 )
292 .chain(self.shared_input_versions.values().copied())
293 .chain(self.mutated_objects.iter().map(|(_, v, _)| *v))
294 .chain(self.deleted_objects.iter().map(|(_, v)| *v))
295 .chain(self.wrapped_objects.iter().map(|(_, v)| *v)),
296 )
297 }
298}