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