1use crate::{
5 base_types::{SuiAddress, VersionDigest},
6 digests::{Digest, ObjectDigest},
7 object::{Object, Owner},
8};
9use move_core_types::language_storage::TypeTag;
10use nonempty::NonEmpty;
11use serde::{Deserialize, Serialize};
12
13#[cfg(test)]
14use nonempty::nonempty;
15
16use super::IDOperation;
17
18#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)]
19pub struct EffectsObjectChange {
20 pub(crate) input_state: ObjectIn,
24 pub(crate) output_state: ObjectOut,
26
27 pub(crate) id_operation: IDOperation,
31}
32
33impl EffectsObjectChange {
34 pub fn new(
35 modified_at: Option<(VersionDigest, Owner)>,
36 written: Option<&Object>,
37 id_created: bool,
38 id_deleted: bool,
39 ) -> Self {
40 debug_assert!(
41 !id_created || !id_deleted,
42 "Object ID can't be created and deleted at the same time."
43 );
44 Self {
45 input_state: modified_at.map_or(ObjectIn::NotExist, ObjectIn::Exist),
46 output_state: written.map_or(ObjectOut::NotExist, |o| {
47 if o.is_package() {
48 ObjectOut::PackageWrite((o.version(), o.digest()))
49 } else {
50 ObjectOut::ObjectWrite((o.digest(), o.owner.clone()))
51 }
52 }),
53 id_operation: if id_created {
54 IDOperation::Created
55 } else if id_deleted {
56 IDOperation::Deleted
57 } else {
58 IDOperation::None
59 },
60 }
61 }
62
63 pub fn new_from_accumulator_write(write: AccumulatorWriteV1) -> Self {
64 Self {
65 input_state: ObjectIn::NotExist,
66 output_state: ObjectOut::AccumulatorWriteV1(write),
67 id_operation: IDOperation::None,
68 }
69 }
70}
71
72#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)]
76pub enum ObjectIn {
77 NotExist,
78 Exist((VersionDigest, Owner)),
80}
81
82#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)]
83pub enum AccumulatorOperation {
84 Merge,
86 Split,
88}
89
90#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)]
91pub enum AccumulatorValue {
92 Integer(u64),
93 IntegerTuple(u64, u64),
94 EventDigest(NonEmpty<(u64 , Digest)>),
95}
96
97#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)]
100pub struct AccumulatorAddress {
101 pub address: SuiAddress,
102 pub ty: TypeTag,
103}
104
105impl AccumulatorAddress {
106 pub fn new(address: SuiAddress, ty: TypeTag) -> Self {
107 Self { address, ty }
108 }
109}
110
111#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)]
112pub struct AccumulatorWriteV1 {
113 pub address: AccumulatorAddress,
114 pub operation: AccumulatorOperation,
116 pub value: AccumulatorValue,
118}
119
120impl AccumulatorWriteV1 {
121 pub fn merge(mut writes: Vec<Self>) -> Self {
122 if writes.len() == 1 {
123 return writes.pop().unwrap();
124 }
125
126 let address = writes[0].address.clone();
127
128 if mysten_common::in_test_configuration() {
129 for write in &writes[1..] {
130 if write.address.address != address.address {
131 mysten_common::debug_fatal!(
132 "All writes must have the same accumulator address: {} != {}",
133 write.address.address,
134 address.address
135 );
136 }
137 if write.address.ty != address.ty {
138 mysten_common::debug_fatal!(
139 "All writes must have the same accumulator type: {:?} != {:?}",
140 write.address.ty,
141 address.ty
142 );
143 }
144 }
145 }
146 let (merged_value, net_operation) = match &writes[0].value {
147 AccumulatorValue::Integer(_) => {
148 let (merge_amount, split_amount) =
149 writes.iter().fold((0u64, 0u64), |(merge, split), w| {
150 if let AccumulatorValue::Integer(v) = w.value {
151 match w.operation {
152 AccumulatorOperation::Merge => (
153 merge.checked_add(v).expect("validated in object runtime"),
154 split,
155 ),
156 AccumulatorOperation::Split => (
157 merge,
158 split.checked_add(v).expect("validated in object runtime"),
159 ),
160 }
161 } else {
162 mysten_common::fatal!(
163 "mismatched accumulator value types for same object"
164 );
165 }
166 });
167 let (amount, operation) = if merge_amount >= split_amount {
168 (merge_amount - split_amount, AccumulatorOperation::Merge)
169 } else {
170 (split_amount - merge_amount, AccumulatorOperation::Split)
171 };
172 (AccumulatorValue::Integer(amount), operation)
173 }
174 AccumulatorValue::IntegerTuple(_, _) => {
175 todo!("IntegerTuple netting-out logic not yet implemented")
176 }
177 AccumulatorValue::EventDigest(first_digests) => {
178 let mut event_digests = first_digests.clone();
179 for write in &writes[1..] {
180 if let AccumulatorValue::EventDigest(digests) = &write.value {
181 event_digests.extend(digests.iter().copied());
182 } else {
183 mysten_common::fatal!("mismatched accumulator value types for same object");
184 }
185 }
186 (
187 AccumulatorValue::EventDigest(event_digests),
188 AccumulatorOperation::Merge,
189 )
190 }
191 };
192 AccumulatorWriteV1 {
193 address,
194 operation: net_operation,
195 value: merged_value,
196 }
197 }
198
199 pub fn get_fund_withdraw_amount(&self) -> Option<u128> {
200 if let (&AccumulatorOperation::Split, &AccumulatorValue::Integer(amount)) =
201 (&self.operation, &self.value)
202 {
203 Some(amount as u128)
204 } else {
205 None
206 }
207 }
208}
209
210#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)]
211pub enum ObjectOut {
212 NotExist,
214 ObjectWrite((ObjectDigest, Owner)),
216 PackageWrite(VersionDigest),
219 AccumulatorWriteV1(AccumulatorWriteV1),
221}
222
223#[cfg(test)]
224mod tests {
225 use super::*;
226 use crate::base_types::SuiAddress;
227
228 fn test_accumulator_address() -> AccumulatorAddress {
229 AccumulatorAddress::new(
230 SuiAddress::random_for_testing_only(),
231 "0x2::balance::Balance<0x2::sui::SUI>".parse().unwrap(),
232 )
233 }
234
235 #[test]
236 fn test_merge_single_item() {
237 let addr = test_accumulator_address();
238 let write = AccumulatorWriteV1 {
239 address: addr.clone(),
240 operation: AccumulatorOperation::Merge,
241 value: AccumulatorValue::Integer(100),
242 };
243
244 let result = AccumulatorWriteV1::merge(vec![write.clone()]);
245 assert_eq!(result, write);
246 }
247
248 #[test]
249 fn test_merge_positive_balance() {
250 let addr = test_accumulator_address();
251 let writes = vec![
252 AccumulatorWriteV1 {
253 address: addr.clone(),
254 operation: AccumulatorOperation::Merge,
255 value: AccumulatorValue::Integer(100),
256 },
257 AccumulatorWriteV1 {
258 address: addr.clone(),
259 operation: AccumulatorOperation::Merge,
260 value: AccumulatorValue::Integer(50),
261 },
262 AccumulatorWriteV1 {
263 address: addr.clone(),
264 operation: AccumulatorOperation::Split,
265 value: AccumulatorValue::Integer(30),
266 },
267 ];
268
269 let result = AccumulatorWriteV1::merge(writes);
270 assert_eq!(result.operation, AccumulatorOperation::Merge);
271 assert_eq!(result.value, AccumulatorValue::Integer(120));
272 }
273
274 #[test]
275 fn test_merge_negative_balance() {
276 let addr = test_accumulator_address();
277 let writes = vec![
278 AccumulatorWriteV1 {
279 address: addr.clone(),
280 operation: AccumulatorOperation::Merge,
281 value: AccumulatorValue::Integer(50),
282 },
283 AccumulatorWriteV1 {
284 address: addr.clone(),
285 operation: AccumulatorOperation::Split,
286 value: AccumulatorValue::Integer(100),
287 },
288 AccumulatorWriteV1 {
289 address: addr.clone(),
290 operation: AccumulatorOperation::Split,
291 value: AccumulatorValue::Integer(30),
292 },
293 ];
294
295 let result = AccumulatorWriteV1::merge(writes);
296 assert_eq!(result.operation, AccumulatorOperation::Split);
297 assert_eq!(result.value, AccumulatorValue::Integer(80));
298 }
299
300 #[test]
301 fn test_merge_zero_balance() {
302 let addr = test_accumulator_address();
303 let writes = vec![
304 AccumulatorWriteV1 {
305 address: addr.clone(),
306 operation: AccumulatorOperation::Merge,
307 value: AccumulatorValue::Integer(100),
308 },
309 AccumulatorWriteV1 {
310 address: addr.clone(),
311 operation: AccumulatorOperation::Split,
312 value: AccumulatorValue::Integer(100),
313 },
314 ];
315
316 let result = AccumulatorWriteV1::merge(writes);
317 assert_eq!(result.operation, AccumulatorOperation::Merge);
318 assert_eq!(result.value, AccumulatorValue::Integer(0));
319 }
320
321 #[test]
322 fn test_merge_all_merges() {
323 let addr = test_accumulator_address();
324 let writes = vec![
325 AccumulatorWriteV1 {
326 address: addr.clone(),
327 operation: AccumulatorOperation::Merge,
328 value: AccumulatorValue::Integer(100),
329 },
330 AccumulatorWriteV1 {
331 address: addr.clone(),
332 operation: AccumulatorOperation::Merge,
333 value: AccumulatorValue::Integer(200),
334 },
335 AccumulatorWriteV1 {
336 address: addr.clone(),
337 operation: AccumulatorOperation::Merge,
338 value: AccumulatorValue::Integer(300),
339 },
340 ];
341
342 let result = AccumulatorWriteV1::merge(writes);
343 assert_eq!(result.operation, AccumulatorOperation::Merge);
344 assert_eq!(result.value, AccumulatorValue::Integer(600));
345 }
346
347 #[test]
348 fn test_merge_all_splits() {
349 let addr = test_accumulator_address();
350 let writes = vec![
351 AccumulatorWriteV1 {
352 address: addr.clone(),
353 operation: AccumulatorOperation::Split,
354 value: AccumulatorValue::Integer(100),
355 },
356 AccumulatorWriteV1 {
357 address: addr.clone(),
358 operation: AccumulatorOperation::Split,
359 value: AccumulatorValue::Integer(200),
360 },
361 AccumulatorWriteV1 {
362 address: addr.clone(),
363 operation: AccumulatorOperation::Split,
364 value: AccumulatorValue::Integer(50),
365 },
366 ];
367
368 let result = AccumulatorWriteV1::merge(writes);
369 assert_eq!(result.operation, AccumulatorOperation::Split);
370 assert_eq!(result.value, AccumulatorValue::Integer(350));
371 }
372
373 #[test]
374 fn test_merge_event_digests_single() {
375 let addr = test_accumulator_address();
376 let digest1 = Digest::random();
377 let digest2 = Digest::random();
378
379 let write = AccumulatorWriteV1 {
380 address: addr.clone(),
381 operation: AccumulatorOperation::Merge,
382 value: AccumulatorValue::EventDigest(nonempty![(0, digest1), (1, digest2)]),
383 };
384
385 let result = AccumulatorWriteV1::merge(vec![write.clone()]);
386 assert_eq!(result, write);
387 }
388
389 #[test]
390 fn test_merge_event_digests_multiple() {
391 let addr = test_accumulator_address();
392 let digest1 = Digest::random();
393 let digest2 = Digest::random();
394 let digest3 = Digest::random();
395 let digest4 = Digest::random();
396
397 let writes = vec![
398 AccumulatorWriteV1 {
399 address: addr.clone(),
400 operation: AccumulatorOperation::Merge,
401 value: AccumulatorValue::EventDigest(nonempty![(0, digest1), (1, digest2)]),
402 },
403 AccumulatorWriteV1 {
404 address: addr.clone(),
405 operation: AccumulatorOperation::Merge,
406 value: AccumulatorValue::EventDigest(nonempty![(2, digest3)]),
407 },
408 AccumulatorWriteV1 {
409 address: addr.clone(),
410 operation: AccumulatorOperation::Merge,
411 value: AccumulatorValue::EventDigest(nonempty![(3, digest4)]),
412 },
413 ];
414
415 let result = AccumulatorWriteV1::merge(writes);
416 assert_eq!(result.operation, AccumulatorOperation::Merge);
417 if let AccumulatorValue::EventDigest(digests) = result.value {
418 assert_eq!(digests.len(), 4);
419 assert_eq!(digests[0], (0, digest1));
420 assert_eq!(digests[1], (1, digest2));
421 assert_eq!(digests[2], (2, digest3));
422 assert_eq!(digests[3], (3, digest4));
423 } else {
424 panic!("Expected EventDigest value");
425 }
426 }
427
428 #[test]
429 #[should_panic(expected = "All writes must have the same accumulator address")]
430 fn test_merge_mismatched_addresses() {
431 let addr1 = test_accumulator_address();
432 let addr2 = test_accumulator_address();
433
434 let writes = vec![
435 AccumulatorWriteV1 {
436 address: addr1,
437 operation: AccumulatorOperation::Merge,
438 value: AccumulatorValue::Integer(100),
439 },
440 AccumulatorWriteV1 {
441 address: addr2,
442 operation: AccumulatorOperation::Merge,
443 value: AccumulatorValue::Integer(50),
444 },
445 ];
446
447 AccumulatorWriteV1::merge(writes);
448 }
449
450 #[test]
451 #[should_panic(expected = "All writes must have the same accumulator type")]
452 fn test_merge_mismatched_types() {
453 let addr = SuiAddress::random_for_testing_only();
454 let addr1 = AccumulatorAddress::new(
455 addr,
456 "0x2::balance::Balance<0x2::sui::SUI>".parse().unwrap(),
457 );
458 let addr2 = AccumulatorAddress::new(
459 addr,
460 "0x2::accumulator_settlement::EventStreamHead"
461 .parse()
462 .unwrap(),
463 );
464
465 let writes = vec![
466 AccumulatorWriteV1 {
467 address: addr1,
468 operation: AccumulatorOperation::Merge,
469 value: AccumulatorValue::Integer(100),
470 },
471 AccumulatorWriteV1 {
472 address: addr2,
473 operation: AccumulatorOperation::Merge,
474 value: AccumulatorValue::Integer(50),
475 },
476 ];
477
478 AccumulatorWriteV1::merge(writes);
479 }
480}