1use std::collections::{BTreeMap, BTreeSet};
6
7use iota_sdk_types::{
8 ExecutionStatus, ObjectDigest, ObjectId, ObjectReference, Owner, TransactionEventsDigest,
9 Version, gas::GasCostSummary,
10};
11
12use crate::{
13 effects::{
14 EffectsObjectChange, IDOperation, ObjectIn, ObjectOut, TransactionEffects,
15 TransactionEffectsExt,
16 },
17 execution::SharedInput,
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, Version>,
28 events_digest: Option<TransactionEventsDigest>,
29 created_objects: Vec<(ObjectId, Owner)>,
30 mutated_objects: Vec<(ObjectId, Version, Owner)>,
32 deleted_objects: Vec<(ObjectId, Version)>,
34 wrapped_objects: Vec<(ObjectId, Version)>,
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(mut self, versions: BTreeMap<ObjectId, Version>) -> 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, Version, 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, Version)>,
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, Version)>,
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(ObjectReference::new(*id, *version, ObjectDigest::MIN))
129 })
130 .collect();
131 let epoch = 0;
132 let sender = self.transaction.transaction().sender();
133 let changed_objects = self
135 .transaction
136 .transaction()
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 object_id: oref.object_id,
151 input_state: ObjectIn::Data {
152 version: oref.version,
153 digest: oref.digest,
154 owner: Owner::Address(sender),
155 },
156 output_state: ObjectOut::ObjectWrite {
157 digest: ObjectDigest::MAX,
159 owner: Owner::Address(sender),
160 },
161 id_operation: IDOperation::None,
162 },
163 ))
164 }
165 InputObjectKind::MovePackage(_) => None,
166 InputObjectKind::SharedMoveObject {
167 id,
168 initial_shared_version,
169 mutable,
170 } => mutable.then_some((
171 *id,
172 EffectsObjectChange {
173 object_id: *id,
174 input_state: ObjectIn::Data {
175 version: *self
176 .shared_input_versions
177 .get(id)
178 .unwrap_or(initial_shared_version),
179 digest: ObjectDigest::MIN,
180 owner: Owner::Shared(*initial_shared_version),
181 },
182 output_state: ObjectOut::ObjectWrite {
183 digest: ObjectDigest::MAX,
185 owner: Owner::Shared(*initial_shared_version),
186 },
187 id_operation: IDOperation::None,
188 },
189 )),
190 })
191 .chain(self.created_objects.into_iter().map(|(id, owner)| {
192 (
193 id,
194 EffectsObjectChange {
195 object_id: id,
196 input_state: ObjectIn::Missing,
197 output_state: ObjectOut::ObjectWrite {
198 digest: ObjectDigest::random(),
199 owner,
200 },
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 object_id: id,
213 input_state: ObjectIn::Data {
214 version,
215 digest: ObjectDigest::random(),
216 owner: Owner::Address(sender),
217 },
218 output_state: ObjectOut::ObjectWrite {
219 digest: ObjectDigest::random(),
220 owner,
221 },
222 id_operation: IDOperation::None,
223 },
224 )
225 }),
226 )
227 .chain(self.deleted_objects.into_iter().map(|(id, version)| {
228 (
229 id,
230 EffectsObjectChange {
231 object_id: id,
232 input_state: ObjectIn::Data {
233 version,
234 digest: ObjectDigest::random(),
235 owner: Owner::Address(sender),
236 },
237 output_state: ObjectOut::Missing,
238 id_operation: IDOperation::Deleted,
239 },
240 )
241 }))
242 .chain(self.wrapped_objects.into_iter().map(|(id, version)| {
243 (
244 id,
245 EffectsObjectChange {
246 object_id: id,
247 input_state: ObjectIn::Data {
248 version,
249 digest: ObjectDigest::random(),
250 owner: Owner::Address(sender),
251 },
252 output_state: ObjectOut::Missing,
253 id_operation: IDOperation::None,
254 },
255 )
256 }))
257 .chain(self.unwrapped_objects.into_iter().map(|(id, owner)| {
258 (
259 id,
260 EffectsObjectChange {
261 object_id: id,
262 input_state: ObjectIn::Missing,
263 output_state: ObjectOut::ObjectWrite {
264 digest: ObjectDigest::random(),
265 owner,
266 },
267 id_operation: IDOperation::None,
268 },
269 )
270 }))
271 .collect();
272 let gas_object_id = self.transaction.transaction().gas()[0].object_id;
273 let event_digest = self.events_digest;
274 let dependencies = vec![];
275
276 TransactionEffects::new_from_execution_v1(
277 status,
278 epoch,
279 GasCostSummary::default(),
280 shared_objects,
281 BTreeSet::new(),
282 self.transaction.digest(),
283 lamport_version,
284 changed_objects,
285 Some(gas_object_id),
286 event_digest,
287 dependencies,
288 )
289 }
290
291 fn get_lamport_version(&self) -> Version {
292 Version::lamport_increment(
293 self.transaction
294 .transaction()
295 .input_objects()
296 .unwrap()
297 .iter()
298 .filter_map(|kind| kind.version())
299 .chain(
300 self.transaction
301 .transaction()
302 .receiving_objects()
303 .iter()
304 .map(|oref| oref.version),
305 )
306 .chain(self.shared_input_versions.values().copied())
307 .chain(self.mutated_objects.iter().map(|(_, v, _)| *v))
308 .chain(self.deleted_objects.iter().map(|(_, v)| *v))
309 .chain(self.wrapped_objects.iter().map(|(_, v)| *v)),
310 )
311 .unwrap()
312 }
313}