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