1use std::collections::{BTreeMap, BTreeSet};
6
7use iota_sdk_types::{ExecutionStatus, ObjectId, Owner};
8
9use crate::{
10 base_types::{ObjectRef, SequenceNumber},
11 digests::{ObjectDigest, TransactionEventsDigest},
12 effects::{
13 EffectsObjectChange, IDOperation, ObjectIn, ObjectOut, TransactionEffects,
14 TransactionEffectsExt,
15 },
16 execution::SharedInput,
17 gas::GasCostSummary,
18 message_envelope::Message,
19 transaction::{InputObjectKind, SenderSignedData, TransactionDataAPI},
20};
21
22pub struct TestEffectsBuilder {
23 transaction: SenderSignedData,
24 status: Option<ExecutionStatus>,
26 shared_input_versions: BTreeMap<ObjectId, SequenceNumber>,
28 events_digest: Option<TransactionEventsDigest>,
29 created_objects: Vec<(ObjectId, Owner)>,
30 mutated_objects: Vec<(ObjectId, SequenceNumber, Owner)>,
32 deleted_objects: Vec<(ObjectId, SequenceNumber)>,
34 wrapped_objects: Vec<(ObjectId, SequenceNumber)>,
36 unwrapped_objects: Vec<(ObjectId, Owner)>,
38 frozen_objects: BTreeSet<ObjectId>,
40}
41
42impl TestEffectsBuilder {
43 pub fn new(transaction: &SenderSignedData) -> Self {
44 Self {
45 transaction: transaction.clone(),
46 status: None,
47 shared_input_versions: BTreeMap::new(),
48 events_digest: None,
49 created_objects: vec![],
50 mutated_objects: vec![],
51 deleted_objects: vec![],
52 wrapped_objects: vec![],
53 unwrapped_objects: vec![],
54 frozen_objects: BTreeSet::new(),
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 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 build(self) -> TransactionEffects {
124 let lamport_version = self.get_lamport_version();
125 let status = self.status.unwrap_or(ExecutionStatus::Success);
126 let shared_objects = self
128 .shared_input_versions
129 .iter()
130 .map(|(id, version)| {
131 SharedInput::Existing(ObjectRef::new(*id, *version, ObjectDigest::MIN))
132 })
133 .collect();
134 let epoch = 0;
135 let sender = self.transaction.transaction_data().sender();
136 let changed_objects = self
138 .transaction
139 .transaction_data()
140 .input_objects()
141 .unwrap()
142 .iter()
143 .filter_map(|kind| match kind {
144 InputObjectKind::ImmOrOwnedMoveObject(object_ref)
145 if self.frozen_objects.contains(&object_ref.object_id) =>
146 {
147 None
148 }
149 InputObjectKind::ImmOrOwnedMoveObject(oref) => {
150 Some((
151 oref.object_id,
152 EffectsObjectChange {
153 object_id: oref.object_id,
154 input_state: ObjectIn::Data {
155 version: oref.version,
156 digest: oref.digest,
157 owner: Owner::Address(sender),
158 },
159 output_state: ObjectOut::ObjectWrite {
160 digest: ObjectDigest::MAX,
162 owner: Owner::Address(sender),
163 },
164 id_operation: IDOperation::None,
165 },
166 ))
167 }
168 InputObjectKind::MovePackage(_) => None,
169 InputObjectKind::SharedMoveObject {
170 id,
171 initial_shared_version,
172 mutable,
173 } => mutable.then_some((
174 *id,
175 EffectsObjectChange {
176 object_id: *id,
177 input_state: ObjectIn::Data {
178 version: *self
179 .shared_input_versions
180 .get(id)
181 .unwrap_or(initial_shared_version),
182 digest: ObjectDigest::MIN,
183 owner: Owner::Shared(*initial_shared_version),
184 },
185 output_state: ObjectOut::ObjectWrite {
186 digest: ObjectDigest::MAX,
188 owner: Owner::Shared(*initial_shared_version),
189 },
190 id_operation: IDOperation::None,
191 },
192 )),
193 })
194 .chain(self.created_objects.into_iter().map(|(id, owner)| {
195 (
196 id,
197 EffectsObjectChange {
198 object_id: id,
199 input_state: ObjectIn::Missing,
200 output_state: ObjectOut::ObjectWrite {
201 digest: ObjectDigest::random(),
202 owner,
203 },
204 id_operation: IDOperation::Created,
205 },
206 )
207 }))
208 .chain(
209 self.mutated_objects
210 .into_iter()
211 .map(|(id, version, owner)| {
212 (
213 id,
214 EffectsObjectChange {
215 object_id: id,
216 input_state: ObjectIn::Data {
217 version,
218 digest: ObjectDigest::random(),
219 owner: Owner::Address(sender),
220 },
221 output_state: ObjectOut::ObjectWrite {
222 digest: ObjectDigest::random(),
223 owner,
224 },
225 id_operation: IDOperation::None,
226 },
227 )
228 }),
229 )
230 .chain(self.deleted_objects.into_iter().map(|(id, version)| {
231 (
232 id,
233 EffectsObjectChange {
234 object_id: id,
235 input_state: ObjectIn::Data {
236 version,
237 digest: ObjectDigest::random(),
238 owner: Owner::Address(sender),
239 },
240 output_state: ObjectOut::Missing,
241 id_operation: IDOperation::Deleted,
242 },
243 )
244 }))
245 .chain(self.wrapped_objects.into_iter().map(|(id, version)| {
246 (
247 id,
248 EffectsObjectChange {
249 object_id: id,
250 input_state: ObjectIn::Data {
251 version,
252 digest: ObjectDigest::random(),
253 owner: Owner::Address(sender),
254 },
255 output_state: ObjectOut::Missing,
256 id_operation: IDOperation::None,
257 },
258 )
259 }))
260 .chain(self.unwrapped_objects.into_iter().map(|(id, owner)| {
261 (
262 id,
263 EffectsObjectChange {
264 object_id: id,
265 input_state: ObjectIn::Missing,
266 output_state: ObjectOut::ObjectWrite {
267 digest: ObjectDigest::random(),
268 owner,
269 },
270 id_operation: IDOperation::None,
271 },
272 )
273 }))
274 .collect();
275 let gas_object_id = self.transaction.transaction_data().gas()[0].object_id;
276 let event_digest = self.events_digest;
277 let dependencies = vec![];
278
279 TransactionEffects::new_from_execution_v1(
280 status,
281 epoch,
282 GasCostSummary::default(),
283 shared_objects,
284 BTreeSet::new(),
285 self.transaction.digest(),
286 lamport_version,
287 changed_objects,
288 Some(gas_object_id),
289 event_digest,
290 dependencies,
291 )
292 }
293
294 fn get_lamport_version(&self) -> SequenceNumber {
295 SequenceNumber::lamport_increment(
296 self.transaction
297 .transaction_data()
298 .input_objects()
299 .unwrap()
300 .iter()
301 .filter_map(|kind| kind.version())
302 .chain(
303 self.transaction
304 .transaction_data()
305 .receiving_objects()
306 .iter()
307 .map(|oref| oref.version),
308 )
309 .chain(self.shared_input_versions.values().copied())
310 .chain(self.mutated_objects.iter().map(|(_, v, _)| *v))
311 .chain(self.deleted_objects.iter().map(|(_, v)| *v))
312 .chain(self.wrapped_objects.iter().map(|(_, v)| *v)),
313 )
314 .unwrap()
315 }
316}