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