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