1use move_core_types::{
5 ident_str,
6 language_storage::{StructTag, TypeTag},
7};
8use std::collections::{BTreeMap, BTreeSet, HashMap};
9use sui_protocol_config::{ProtocolConfig, ProtocolVersion};
10use sui_sdk_types::CheckpointTimestamp;
11use tap::Pipe;
12
13use crate::messages_checkpoint::CheckpointCommitment;
14use crate::{
15 base_types::{
16 dbg_addr, random_object_ref, ExecutionDigests, ObjectID, ObjectRef, SequenceNumber,
17 SuiAddress,
18 },
19 committee::Committee,
20 digests::TransactionDigest,
21 effects::{TestEffectsBuilder, TransactionEffectsAPI, TransactionEvents},
22 event::{Event, SystemEpochInfoEvent},
23 full_checkpoint_content::{CheckpointData, CheckpointTransaction},
24 gas_coin::GAS,
25 message_envelope::Message,
26 messages_checkpoint::{
27 CertifiedCheckpointSummary, CheckpointContents, CheckpointSummary, EndOfEpochData,
28 },
29 object::{MoveObject, Object, Owner, GAS_VALUE_FOR_TESTING},
30 programmable_transaction_builder::ProgrammableTransactionBuilder,
31 transaction::{
32 EndOfEpochTransactionKind, ObjectArg, SenderSignedData, SharedObjectMutability,
33 Transaction, TransactionData, TransactionKind,
34 },
35 SUI_SYSTEM_ADDRESS,
36};
37
38pub struct TestCheckpointDataBuilder {
50 live_objects: HashMap<ObjectID, Object>,
52 wrapped_objects: HashMap<ObjectID, Object>,
54 gas_map: HashMap<SuiAddress, ObjectID>,
58
59 checkpoint_builder: CheckpointBuilder,
62}
63
64struct CheckpointBuilder {
65 checkpoint: u64,
67 epoch: u64,
69 network_total_transactions: u64,
71 timestamp_ms: CheckpointTimestamp,
73 transactions: Vec<CheckpointTransaction>,
75 next_transaction: Option<TransactionBuilder>,
77}
78
79struct TransactionBuilder {
80 sender_idx: u8,
81 gas: ObjectRef,
82 move_calls: Vec<(ObjectID, &'static str, &'static str)>,
83 created_objects: BTreeMap<ObjectID, Object>,
84 mutated_objects: BTreeMap<ObjectID, Object>,
85 unwrapped_objects: BTreeSet<ObjectID>,
86 wrapped_objects: BTreeSet<ObjectID>,
87 deleted_objects: BTreeSet<ObjectID>,
88 frozen_objects: BTreeSet<ObjectRef>,
89 shared_inputs: BTreeMap<ObjectID, Shared>,
90 events: Option<Vec<Event>>,
91}
92
93struct Shared {
94 mutable: bool,
95 object: Object,
96}
97
98impl TransactionBuilder {
99 pub fn new(sender_idx: u8, gas: ObjectRef) -> Self {
100 Self {
101 sender_idx,
102 gas,
103 move_calls: vec![],
104 created_objects: BTreeMap::new(),
105 mutated_objects: BTreeMap::new(),
106 unwrapped_objects: BTreeSet::new(),
107 wrapped_objects: BTreeSet::new(),
108 deleted_objects: BTreeSet::new(),
109 frozen_objects: BTreeSet::new(),
110 shared_inputs: BTreeMap::new(),
111 events: None,
112 }
113 }
114}
115
116pub struct AdvanceEpochConfig {
117 pub safe_mode: bool,
118 pub protocol_version: ProtocolVersion,
119 pub output_objects: Vec<Object>,
120 pub epoch_commitments: Vec<CheckpointCommitment>,
121}
122
123impl Default for AdvanceEpochConfig {
124 fn default() -> Self {
125 Self {
126 safe_mode: false,
127 protocol_version: ProtocolVersion::MAX,
128 output_objects: vec![],
129 epoch_commitments: vec![],
130 }
131 }
132}
133
134impl TestCheckpointDataBuilder {
135 pub fn new(checkpoint: u64) -> Self {
136 Self {
137 live_objects: HashMap::new(),
138 wrapped_objects: HashMap::new(),
139 gas_map: HashMap::new(),
140 checkpoint_builder: CheckpointBuilder {
141 checkpoint,
142 epoch: 0,
143 network_total_transactions: 0,
144 timestamp_ms: 0,
145 transactions: vec![],
146 next_transaction: None,
147 },
148 }
149 }
150
151 pub fn with_epoch(mut self, epoch: u64) -> Self {
153 self.checkpoint_builder.epoch = epoch;
154 self
155 }
156
157 pub fn with_network_total_transactions(mut self, network_total_transactions: u64) -> Self {
159 self.checkpoint_builder.network_total_transactions = network_total_transactions;
160 self
161 }
162
163 pub fn with_timestamp_ms(mut self, timestamp_ms: CheckpointTimestamp) -> Self {
165 self.checkpoint_builder.timestamp_ms = timestamp_ms;
166 self
167 }
168
169 pub fn start_transaction(mut self, sender_idx: u8) -> Self {
175 assert!(self.checkpoint_builder.next_transaction.is_none());
176 let sender = Self::derive_address(sender_idx);
177 let gas_id = self.gas_map.entry(sender).or_insert_with(|| {
178 let gas = Object::with_owner_for_testing(sender);
179 let id = gas.id();
180 self.live_objects.insert(id, gas);
181 id
182 });
183 let gas_ref = self
184 .live_objects
185 .get(gas_id)
186 .cloned()
187 .unwrap()
188 .compute_object_reference();
189 self.checkpoint_builder.next_transaction =
190 Some(TransactionBuilder::new(sender_idx, gas_ref));
191 self
192 }
193
194 pub fn create_owned_object(self, object_idx: u64) -> Self {
199 self.create_sui_object(object_idx, GAS_VALUE_FOR_TESTING)
200 }
201
202 pub fn create_shared_object(self, object_idx: u64) -> Self {
207 self.create_coin_object_with_owner(
208 object_idx,
209 Owner::Shared {
210 initial_shared_version: SequenceNumber::MIN,
211 },
212 GAS_VALUE_FOR_TESTING,
213 GAS::type_tag(),
214 )
215 }
216
217 pub fn create_sui_object(self, object_idx: u64, balance: u64) -> Self {
221 let sender_idx = self
222 .checkpoint_builder
223 .next_transaction
224 .as_ref()
225 .unwrap()
226 .sender_idx;
227 self.create_coin_object(object_idx, sender_idx, balance, GAS::type_tag())
228 }
229
230 pub fn create_coin_object(
236 self,
237 object_idx: u64,
238 owner_idx: u8,
239 balance: u64,
240 coin_type: TypeTag,
241 ) -> Self {
242 self.create_coin_object_with_owner(
243 object_idx,
244 Owner::AddressOwner(Self::derive_address(owner_idx)),
245 balance,
246 coin_type,
247 )
248 }
249
250 fn create_coin_object_with_owner(
251 mut self,
252 object_idx: u64,
253 owner: Owner,
254 balance: u64,
255 coin_type: TypeTag,
256 ) -> Self {
257 let tx_builder = self.checkpoint_builder.next_transaction.as_mut().unwrap();
258 let object_id = Self::derive_object_id(object_idx);
259 assert!(
260 !self.live_objects.contains_key(&object_id),
261 "Object already exists: {}. Please use a different object index.",
262 object_id
263 );
264 let move_object = MoveObject::new_coin(
265 coin_type,
266 SequenceNumber::MIN,
268 object_id,
269 balance,
270 );
271 let object = Object::new_move(move_object, owner, TransactionDigest::ZERO);
272 tx_builder.created_objects.insert(object_id, object);
273 self
274 }
275
276 pub fn mutate_owned_object(mut self, object_idx: u64) -> Self {
279 let tx_builder = self.checkpoint_builder.next_transaction.as_mut().unwrap();
280 let object_id = Self::derive_object_id(object_idx);
281 let object = self
282 .live_objects
283 .get(&object_id)
284 .cloned()
285 .expect("Mutating an object that doesn't exist");
286 tx_builder.mutated_objects.insert(object_id, object);
287 self
288 }
289
290 pub fn mutate_shared_object(self, object_idx: u64) -> Self {
292 self.access_shared_object(object_idx, true)
293 }
294
295 pub fn transfer_object(self, object_idx: u64, recipient_idx: u8) -> Self {
299 self.change_object_owner(
300 object_idx,
301 Owner::AddressOwner(Self::derive_address(recipient_idx)),
302 )
303 }
304
305 pub fn change_object_owner(mut self, object_idx: u64, owner: Owner) -> Self {
309 let tx_builder = self.checkpoint_builder.next_transaction.as_mut().unwrap();
310 let object_id = Self::derive_object_id(object_idx);
311 let mut object = self.live_objects.get(&object_id).unwrap().clone();
312 object.owner = owner;
313 tx_builder.mutated_objects.insert(object_id, object);
314 self
315 }
316
317 pub fn transfer_coin_balance(
323 mut self,
324 object_idx: u64,
325 new_object_idx: u64,
326 recipient_idx: u8,
327 amount: u64,
328 ) -> Self {
329 let tx_builder = self.checkpoint_builder.next_transaction.as_mut().unwrap();
330 let object_id = Self::derive_object_id(object_idx);
331 let mut object = self
332 .live_objects
333 .get(&object_id)
334 .cloned()
335 .expect("Mutating an object that does not exist");
336 let coin_type = object.coin_type_maybe().unwrap();
337 let move_object = object.data.try_as_move_mut().unwrap();
339 let old_balance = move_object.get_coin_value_unsafe();
340 let new_balance = old_balance - amount;
341 move_object.set_coin_value_unsafe(new_balance);
342 tx_builder.mutated_objects.insert(object_id, object);
343
344 self.create_coin_object(new_object_idx, recipient_idx, amount, coin_type)
346 }
347
348 pub fn wrap_object(mut self, object_idx: u64) -> Self {
351 let tx_builder = self.checkpoint_builder.next_transaction.as_mut().unwrap();
352 let object_id = Self::derive_object_id(object_idx);
353 assert!(self.live_objects.contains_key(&object_id));
354 tx_builder.wrapped_objects.insert(object_id);
355 self
356 }
357
358 pub fn unwrap_object(mut self, object_idx: u64) -> Self {
361 let tx_builder = self.checkpoint_builder.next_transaction.as_mut().unwrap();
362 let object_id = Self::derive_object_id(object_idx);
363 assert!(self.wrapped_objects.contains_key(&object_id));
364 tx_builder.unwrapped_objects.insert(object_id);
365 self
366 }
367
368 pub fn delete_object(mut self, object_idx: u64) -> Self {
371 let tx_builder = self.checkpoint_builder.next_transaction.as_mut().unwrap();
372 let object_id = Self::derive_object_id(object_idx);
373 assert!(self.live_objects.contains_key(&object_id));
374 tx_builder.deleted_objects.insert(object_id);
375 self
376 }
377
378 pub fn read_frozen_object(mut self, object_id: u64) -> Self {
382 let tx_builder = self.checkpoint_builder.next_transaction.as_mut().unwrap();
383 let object_id = Self::derive_object_id(object_id);
384
385 let Some(obj) = self.live_objects.get(&object_id) else {
386 panic!("Frozen object not found");
387 };
388
389 assert!(obj.owner().is_immutable());
390 tx_builder
391 .frozen_objects
392 .insert(obj.compute_object_reference());
393 self
394 }
395
396 pub fn read_shared_object(self, object_idx: u64) -> Self {
398 self.access_shared_object(object_idx, false)
399 }
400
401 pub fn with_events(mut self, events: Vec<Event>) -> Self {
404 self.checkpoint_builder
405 .next_transaction
406 .as_mut()
407 .unwrap()
408 .events = Some(events);
409 self
410 }
411
412 pub fn add_move_call(
417 mut self,
418 package: ObjectID,
419 module: &'static str,
420 function: &'static str,
421 ) -> Self {
422 let tx_builder = self.checkpoint_builder.next_transaction.as_mut().unwrap();
423 tx_builder.move_calls.push((package, module, function));
424 self
425 }
426
427 pub fn finish_transaction(mut self) -> Self {
430 let TransactionBuilder {
431 sender_idx,
432 gas,
433 move_calls,
434 created_objects,
435 mutated_objects,
436 unwrapped_objects,
437 wrapped_objects,
438 deleted_objects,
439 frozen_objects,
440 shared_inputs,
441 events,
442 } = self.checkpoint_builder.next_transaction.take().unwrap();
443
444 let sender = Self::derive_address(sender_idx);
445 let events = events.map(|events| TransactionEvents { data: events });
446 let events_digest = events.as_ref().map(|events| events.digest());
447
448 let mut pt_builder = ProgrammableTransactionBuilder::new();
449 for (package, module, function) in move_calls {
450 pt_builder
451 .move_call(
452 package,
453 ident_str!(module).to_owned(),
454 ident_str!(function).to_owned(),
455 vec![],
456 vec![],
457 )
458 .unwrap();
459 }
460
461 for &object_ref in &frozen_objects {
462 pt_builder
463 .obj(ObjectArg::ImmOrOwnedObject(object_ref))
464 .expect("Failed to add frozen object input");
465 }
466
467 for (id, input) in &shared_inputs {
468 let &Owner::Shared {
469 initial_shared_version,
470 } = input.object.owner()
471 else {
472 panic!("Accessing a non-shared object as shared");
473 };
474
475 pt_builder
476 .obj(ObjectArg::SharedObject {
477 id: *id,
478 initial_shared_version,
479 mutability: if input.mutable {
480 SharedObjectMutability::Mutable
481 } else {
482 SharedObjectMutability::Immutable
483 },
484 })
485 .expect("Failed to add shared object input");
486 }
487
488 let pt = pt_builder.finish();
489 let tx_data = TransactionData::new(
490 TransactionKind::ProgrammableTransaction(pt),
491 sender,
492 gas,
493 1,
494 1,
495 );
496
497 let tx = Transaction::new(SenderSignedData::new(tx_data, vec![]));
498
499 let wrapped_objects: Vec<_> = wrapped_objects
500 .into_iter()
501 .map(|id| self.live_objects.remove(&id).unwrap())
502 .collect();
503 let deleted_objects: Vec<_> = deleted_objects
504 .into_iter()
505 .map(|id| self.live_objects.remove(&id).unwrap())
506 .collect();
507 let unwrapped_objects: Vec<_> = unwrapped_objects
508 .into_iter()
509 .map(|id| self.wrapped_objects.remove(&id).unwrap())
510 .collect();
511
512 let mut effects_builder = TestEffectsBuilder::new(tx.data())
513 .with_created_objects(
514 created_objects
515 .iter()
516 .map(|(id, o)| (*id, o.owner().clone())),
517 )
518 .with_mutated_objects(
519 mutated_objects
520 .iter()
521 .map(|(id, o)| (*id, o.version(), o.owner().clone())),
522 )
523 .with_wrapped_objects(wrapped_objects.iter().map(|o| (o.id(), o.version())))
524 .with_unwrapped_objects(
525 unwrapped_objects
526 .iter()
527 .map(|o| (o.id(), o.owner().clone())),
528 )
529 .with_deleted_objects(deleted_objects.iter().map(|o| (o.id(), o.version())))
530 .with_frozen_objects(frozen_objects.into_iter().map(|(id, _, _)| id))
531 .with_shared_input_versions(
532 shared_inputs
533 .iter()
534 .map(|(id, input)| (*id, input.object.version()))
535 .collect(),
536 );
537
538 if let Some(events_digest) = &events_digest {
539 effects_builder = effects_builder.with_events_digest(*events_digest);
540 }
541
542 let effects = effects_builder.build();
543 let lamport_version = effects.lamport_version();
544 let input_objects: Vec<_> = mutated_objects
545 .keys()
546 .chain(
547 shared_inputs
548 .iter()
549 .filter(|(_, i)| i.mutable)
550 .map(|(id, _)| id),
551 )
552 .map(|id| self.live_objects.get(id).unwrap().clone())
553 .chain(deleted_objects.clone())
554 .chain(wrapped_objects.clone())
555 .chain(std::iter::once(
556 self.live_objects.get(&gas.0).unwrap().clone(),
557 ))
558 .collect();
559 let output_objects: Vec<_> = created_objects
560 .values()
561 .cloned()
562 .chain(mutated_objects.values().cloned())
563 .chain(
564 shared_inputs
565 .values()
566 .filter(|i| i.mutable)
567 .map(|i| i.object.clone()),
568 )
569 .chain(unwrapped_objects.clone())
570 .chain(std::iter::once(
571 self.live_objects.get(&gas.0).cloned().unwrap(),
572 ))
573 .map(|mut o| {
574 o.data
575 .try_as_move_mut()
576 .unwrap()
577 .increment_version_to(lamport_version);
578 o
579 })
580 .collect();
581 self.live_objects
582 .extend(output_objects.iter().map(|o| (o.id(), o.clone())));
583 self.wrapped_objects
584 .extend(wrapped_objects.iter().map(|o| (o.id(), o.clone())));
585
586 self.checkpoint_builder
587 .transactions
588 .push(CheckpointTransaction {
589 transaction: tx,
590 effects,
591 events,
592 input_objects,
593 output_objects,
594 });
595 self
596 }
597
598 pub fn build_checkpoint(&mut self) -> CheckpointData {
601 assert!(self.checkpoint_builder.next_transaction.is_none());
602 let transactions = std::mem::take(&mut self.checkpoint_builder.transactions);
603 let contents = CheckpointContents::new_with_digests_only_for_tests(
604 transactions
605 .iter()
606 .map(|tx| ExecutionDigests::new(*tx.transaction.digest(), tx.effects.digest())),
607 );
608 self.checkpoint_builder.network_total_transactions += transactions.len() as u64;
609 let checkpoint_summary = CheckpointSummary::new(
610 &ProtocolConfig::get_for_max_version_UNSAFE(),
611 self.checkpoint_builder.epoch,
612 self.checkpoint_builder.checkpoint,
613 self.checkpoint_builder.network_total_transactions,
614 &contents,
615 None,
616 Default::default(),
617 None,
618 self.checkpoint_builder.timestamp_ms,
619 vec![],
620 Vec::new(),
621 );
622 let (committee, keys) = Committee::new_simple_test_committee();
623 let checkpoint_cert = CertifiedCheckpointSummary::new_from_keypairs_for_testing(
624 checkpoint_summary,
625 &keys,
626 &committee,
627 );
628 self.checkpoint_builder.checkpoint += 1;
629 CheckpointData {
630 checkpoint_summary: checkpoint_cert,
631 checkpoint_contents: contents,
632 transactions,
633 }
634 }
635
636 pub fn advance_epoch(
642 &mut self,
643 AdvanceEpochConfig {
644 safe_mode,
645 protocol_version,
646 output_objects,
647 epoch_commitments,
648 }: AdvanceEpochConfig,
649 ) -> CheckpointData {
650 let (committee, _) = Committee::new_simple_test_committee();
651 let tx_kind = EndOfEpochTransactionKind::new_change_epoch(
652 self.checkpoint_builder.epoch + 1,
653 protocol_version,
654 Default::default(),
655 Default::default(),
656 Default::default(),
657 Default::default(),
658 Default::default(),
659 Default::default(),
660 );
661
662 let end_of_epoch_tx = TransactionData::new(
665 TransactionKind::EndOfEpochTransaction(vec![tx_kind]),
666 SuiAddress::default(),
667 random_object_ref(),
668 1,
669 1,
670 )
671 .pipe(|data| SenderSignedData::new(data, vec![]))
672 .pipe(Transaction::new);
673
674 let events = if !safe_mode {
675 let system_epoch_info_event = SystemEpochInfoEvent {
676 epoch: self.checkpoint_builder.epoch,
677 protocol_version: protocol_version.as_u64(),
678 ..Default::default()
679 };
680 let struct_tag = StructTag {
681 address: SUI_SYSTEM_ADDRESS,
682 module: ident_str!("sui_system_state_inner").to_owned(),
683 name: ident_str!("SystemEpochInfoEvent").to_owned(),
684 type_params: vec![],
685 };
686 Some(vec![Event::new(
687 &SUI_SYSTEM_ADDRESS,
688 ident_str!("sui_system_state_inner"),
689 TestCheckpointDataBuilder::derive_address(0),
690 struct_tag,
691 bcs::to_bytes(&system_epoch_info_event).unwrap(),
692 )])
693 } else {
694 None
695 };
696
697 let transaction_events = events.map(|events| TransactionEvents { data: events });
698
699 self.checkpoint_builder
701 .transactions
702 .push(CheckpointTransaction {
703 transaction: end_of_epoch_tx,
704 effects: Default::default(),
705 events: transaction_events,
706 input_objects: vec![],
707 output_objects,
708 });
709
710 let mut checkpoint = self.build_checkpoint();
713 let end_of_epoch_data = EndOfEpochData {
714 next_epoch_committee: committee.voting_rights.clone(),
715 next_epoch_protocol_version: protocol_version,
716 epoch_commitments,
717 };
718 checkpoint.checkpoint_summary.end_of_epoch_data = Some(end_of_epoch_data);
719 self.checkpoint_builder.epoch += 1;
720 checkpoint
721 }
722
723 pub fn derive_object_id(object_idx: u64) -> ObjectID {
726 let mut bytes = [0; ObjectID::LENGTH];
728 bytes[0..8].copy_from_slice(&object_idx.to_le_bytes());
729 ObjectID::from_bytes(bytes).unwrap()
730 }
731
732 pub fn derive_address(address_idx: u8) -> SuiAddress {
734 dbg_addr(address_idx)
735 }
736
737 fn access_shared_object(mut self, object_idx: u64, mutability: bool) -> Self {
740 let tx_builder = self.checkpoint_builder.next_transaction.as_mut().unwrap();
741 let object_id = Self::derive_object_id(object_idx);
742 let object = self
743 .live_objects
744 .get(&object_id)
745 .cloned()
746 .expect("Accessing a shared object that doesn't exist");
747 tx_builder.shared_inputs.insert(
748 object_id,
749 Shared {
750 mutable: mutability,
751 object,
752 },
753 );
754 self
755 }
756}
757
758#[cfg(test)]
759mod tests {
760 use std::str::FromStr;
761
762 use move_core_types::ident_str;
763
764 use crate::transaction::{Command, ProgrammableMoveCall, TransactionDataAPI};
765
766 use super::*;
767 #[test]
768 fn test_basic_checkpoint_builder() {
769 let checkpoint = TestCheckpointDataBuilder::new(1)
771 .with_epoch(5)
772 .start_transaction(0)
773 .finish_transaction()
774 .build_checkpoint();
775
776 assert_eq!(*checkpoint.checkpoint_summary.sequence_number(), 1);
777 assert_eq!(checkpoint.checkpoint_summary.epoch, 5);
778 assert_eq!(checkpoint.transactions.len(), 1);
779 let tx = &checkpoint.transactions[0];
780 assert_eq!(
781 tx.transaction.sender_address(),
782 TestCheckpointDataBuilder::derive_address(0)
783 );
784 assert_eq!(tx.effects.mutated().len(), 1); assert_eq!(tx.effects.deleted().len(), 0);
786 assert_eq!(tx.effects.created().len(), 0);
787 assert_eq!(tx.input_objects.len(), 1);
788 assert_eq!(tx.output_objects.len(), 1);
789 }
790
791 #[test]
792 fn test_multiple_transactions() {
793 let checkpoint = TestCheckpointDataBuilder::new(1)
794 .start_transaction(0)
795 .finish_transaction()
796 .start_transaction(1)
797 .finish_transaction()
798 .start_transaction(2)
799 .finish_transaction()
800 .build_checkpoint();
801
802 assert_eq!(checkpoint.transactions.len(), 3);
803
804 let senders: Vec<_> = checkpoint
806 .transactions
807 .iter()
808 .map(|tx| tx.transaction.transaction_data().sender())
809 .collect();
810 assert_eq!(
811 senders,
812 vec![
813 TestCheckpointDataBuilder::derive_address(0),
814 TestCheckpointDataBuilder::derive_address(1),
815 TestCheckpointDataBuilder::derive_address(2)
816 ]
817 );
818 }
819
820 #[test]
821 fn test_object_creation() {
822 let checkpoint = TestCheckpointDataBuilder::new(1)
823 .start_transaction(0)
824 .create_owned_object(0)
825 .finish_transaction()
826 .build_checkpoint();
827
828 let tx = &checkpoint.transactions[0];
829 let created_obj_id = TestCheckpointDataBuilder::derive_object_id(0);
830
831 assert!(tx
833 .output_objects
834 .iter()
835 .any(|obj| obj.id() == created_obj_id));
836
837 assert!(tx
839 .effects
840 .created()
841 .iter()
842 .any(|((id, ..), owner)| *id == created_obj_id
843 && owner.get_owner_address().unwrap()
844 == TestCheckpointDataBuilder::derive_address(0)));
845 }
846
847 #[test]
848 fn test_object_mutation() {
849 let checkpoint = TestCheckpointDataBuilder::new(1)
850 .start_transaction(0)
851 .create_owned_object(0)
852 .finish_transaction()
853 .start_transaction(0)
854 .mutate_owned_object(0)
855 .finish_transaction()
856 .build_checkpoint();
857
858 let tx = &checkpoint.transactions[1];
859 let obj_id = TestCheckpointDataBuilder::derive_object_id(0);
860
861 assert!(tx.input_objects.iter().any(|obj| obj.id() == obj_id));
863 assert!(tx.output_objects.iter().any(|obj| obj.id() == obj_id));
864
865 assert!(tx
867 .effects
868 .mutated()
869 .iter()
870 .any(|((id, ..), _)| *id == obj_id));
871 }
872
873 #[test]
874 fn test_object_deletion() {
875 let checkpoint = TestCheckpointDataBuilder::new(1)
876 .start_transaction(0)
877 .create_owned_object(0)
878 .finish_transaction()
879 .start_transaction(0)
880 .delete_object(0)
881 .finish_transaction()
882 .build_checkpoint();
883
884 let tx = &checkpoint.transactions[1];
885 let obj_id = TestCheckpointDataBuilder::derive_object_id(0);
886
887 assert!(tx.input_objects.iter().any(|obj| obj.id() == obj_id));
889 assert!(!tx.output_objects.iter().any(|obj| obj.id() == obj_id));
890
891 assert!(tx.effects.deleted().iter().any(|(id, ..)| *id == obj_id));
893 }
894
895 #[test]
896 fn test_object_wrapping() {
897 let checkpoint = TestCheckpointDataBuilder::new(1)
898 .start_transaction(0)
899 .create_owned_object(0)
900 .finish_transaction()
901 .start_transaction(0)
902 .wrap_object(0)
903 .finish_transaction()
904 .start_transaction(0)
905 .unwrap_object(0)
906 .finish_transaction()
907 .build_checkpoint();
908
909 let tx = &checkpoint.transactions[1];
910 let obj_id = TestCheckpointDataBuilder::derive_object_id(0);
911
912 assert!(tx.input_objects.iter().any(|obj| obj.id() == obj_id));
914 assert!(!tx.output_objects.iter().any(|obj| obj.id() == obj_id));
915
916 assert!(tx.effects.wrapped().iter().any(|(id, ..)| *id == obj_id));
918
919 let tx = &checkpoint.transactions[2];
920
921 assert!(!tx.input_objects.iter().any(|obj| obj.id() == obj_id));
923 assert!(tx.output_objects.iter().any(|obj| obj.id() == obj_id));
924
925 assert!(tx
927 .effects
928 .unwrapped()
929 .iter()
930 .any(|((id, ..), _)| *id == obj_id));
931 }
932
933 #[test]
934 fn test_object_transfer() {
935 let checkpoint = TestCheckpointDataBuilder::new(1)
936 .start_transaction(0)
937 .create_owned_object(0)
938 .finish_transaction()
939 .start_transaction(1)
940 .transfer_object(0, 1)
941 .finish_transaction()
942 .build_checkpoint();
943
944 let tx = &checkpoint.transactions[1];
945 let obj_id = TestCheckpointDataBuilder::derive_object_id(0);
946
947 assert!(tx.input_objects.iter().any(|obj| obj.id() == obj_id));
949 assert!(tx.output_objects.iter().any(|obj| obj.id() == obj_id));
950
951 assert!(tx
953 .effects
954 .mutated()
955 .iter()
956 .any(|((id, ..), owner)| *id == obj_id
957 && owner.get_owner_address().unwrap()
958 == TestCheckpointDataBuilder::derive_address(1)));
959 }
960
961 #[test]
962 fn test_shared_object() {
963 let checkpoint = TestCheckpointDataBuilder::new(1)
964 .start_transaction(0)
965 .create_shared_object(0)
966 .finish_transaction()
967 .build_checkpoint();
968
969 let tx = &checkpoint.transactions[0];
970 let obj_id = TestCheckpointDataBuilder::derive_object_id(0);
971
972 assert!(tx
974 .output_objects
975 .iter()
976 .any(|obj| obj.id() == obj_id && obj.owner().is_shared()));
977 }
978
979 #[test]
980 fn test_freeze_object() {
981 let checkpoint = TestCheckpointDataBuilder::new(1)
982 .start_transaction(0)
983 .create_owned_object(0)
984 .finish_transaction()
985 .start_transaction(0)
986 .change_object_owner(0, Owner::Immutable)
987 .finish_transaction()
988 .build_checkpoint();
989
990 let tx = &checkpoint.transactions[1];
991 let obj_id = TestCheckpointDataBuilder::derive_object_id(0);
992
993 assert!(tx
995 .output_objects
996 .iter()
997 .any(|obj| obj.id() == obj_id && obj.owner().is_immutable()));
998 }
999
1000 #[test]
1001 fn test_sui_balance_transfer() {
1002 let checkpoint = TestCheckpointDataBuilder::new(1)
1003 .start_transaction(0)
1004 .create_sui_object(0, 100)
1005 .finish_transaction()
1006 .start_transaction(1)
1007 .transfer_coin_balance(0, 1, 1, 10)
1008 .finish_transaction()
1009 .build_checkpoint();
1010
1011 let tx = &checkpoint.transactions[0];
1012 let obj_id0 = TestCheckpointDataBuilder::derive_object_id(0);
1013
1014 assert!(tx.output_objects.iter().any(|obj| obj.id() == obj_id0
1016 && obj.is_gas_coin()
1017 && obj.data.try_as_move().unwrap().get_coin_value_unsafe() == 100));
1018
1019 let tx = &checkpoint.transactions[1];
1020 let obj_id1 = TestCheckpointDataBuilder::derive_object_id(1);
1021
1022 assert!(tx.output_objects.iter().any(|obj| obj.id() == obj_id0
1024 && obj.is_gas_coin()
1025 && obj.data.try_as_move().unwrap().get_coin_value_unsafe() == 90));
1026
1027 assert!(tx.output_objects.iter().any(|obj| obj.id() == obj_id1
1029 && obj.is_gas_coin()
1030 && obj.data.try_as_move().unwrap().get_coin_value_unsafe() == 10));
1031 }
1032
1033 #[test]
1034 fn test_coin_balance_transfer() {
1035 let type_tag = TypeTag::from_str("0x100::a::b").unwrap();
1036 let checkpoint = TestCheckpointDataBuilder::new(1)
1037 .start_transaction(0)
1038 .create_coin_object(0, 0, 100, type_tag.clone())
1039 .finish_transaction()
1040 .start_transaction(1)
1041 .transfer_coin_balance(0, 1, 1, 10)
1042 .finish_transaction()
1043 .build_checkpoint();
1044
1045 let tx = &checkpoint.transactions[1];
1046 let obj_id0 = TestCheckpointDataBuilder::derive_object_id(0);
1047 let obj_id1 = TestCheckpointDataBuilder::derive_object_id(1);
1048
1049 assert!(tx.output_objects.iter().any(|obj| obj.id() == obj_id0
1051 && obj.coin_type_maybe().unwrap() == type_tag
1052 && obj.data.try_as_move().unwrap().get_coin_value_unsafe() == 90));
1053
1054 assert!(tx.output_objects.iter().any(|obj| obj.id() == obj_id1
1056 && obj.coin_type_maybe().unwrap() == type_tag
1057 && obj.data.try_as_move().unwrap().get_coin_value_unsafe() == 10));
1058 }
1059
1060 #[test]
1061 fn test_events() {
1062 let checkpoint = TestCheckpointDataBuilder::new(1)
1063 .start_transaction(0)
1064 .with_events(vec![Event::new(
1065 &ObjectID::ZERO,
1066 ident_str!("test"),
1067 TestCheckpointDataBuilder::derive_address(0),
1068 GAS::type_(),
1069 vec![],
1070 )])
1071 .finish_transaction()
1072 .build_checkpoint();
1073 let tx = &checkpoint.transactions[0];
1074
1075 assert!(tx.effects.events_digest().is_some());
1077
1078 assert_eq!(tx.events.as_ref().unwrap().data.len(), 1);
1080 }
1081
1082 #[test]
1083 fn test_move_call() {
1084 let checkpoint = TestCheckpointDataBuilder::new(1)
1085 .start_transaction(0)
1086 .add_move_call(ObjectID::ZERO, "test", "test")
1087 .finish_transaction()
1088 .build_checkpoint();
1089 let tx = &checkpoint.transactions[0];
1090
1091 assert!(tx
1093 .transaction
1094 .transaction_data()
1095 .kind()
1096 .iter_commands()
1097 .any(|cmd| {
1098 cmd == &Command::MoveCall(Box::new(ProgrammableMoveCall {
1099 package: ObjectID::ZERO,
1100 module: "test".to_string(),
1101 function: "test".to_string(),
1102 type_arguments: vec![],
1103 arguments: vec![],
1104 }))
1105 }));
1106 }
1107
1108 #[test]
1109 fn test_multiple_checkpoints() {
1110 let mut builder = TestCheckpointDataBuilder::new(1)
1111 .start_transaction(0)
1112 .create_owned_object(0)
1113 .finish_transaction();
1114 let checkpoint1 = builder.build_checkpoint();
1115 builder = builder
1116 .start_transaction(0)
1117 .mutate_owned_object(0)
1118 .finish_transaction();
1119 let checkpoint2 = builder.build_checkpoint();
1120 builder = builder
1121 .start_transaction(0)
1122 .delete_object(0)
1123 .finish_transaction();
1124 let checkpoint3 = builder.build_checkpoint();
1125
1126 assert_eq!(checkpoint1.checkpoint_summary.sequence_number, 1);
1128 assert_eq!(checkpoint2.checkpoint_summary.sequence_number, 2);
1129 assert_eq!(checkpoint3.checkpoint_summary.sequence_number, 3);
1130 }
1131}