1use std::collections::{BTreeMap, BTreeSet, HashMap};
6
7use iota_protocol_config::ProtocolConfig;
8use iota_sdk_types::{
9 Address, EndOfEpochTransactionKind, Event, Identifier, MoveStruct, ObjectId, ObjectReference,
10 Owner, SenderSignedTransaction, SharedObjectReference, StructTag, Transaction,
11 TransactionDigest, TransactionEffects, TransactionEvents, TransactionKind, TypeTag, Version,
12 checkpoint::{CheckpointContents, CheckpointSummary, EndOfEpochData},
13};
14use tap::Pipe;
15
16use crate::{
17 base_types::{ExecutionDigests, dbg_addr, random_object_ref},
18 committee::Committee,
19 effects::{TestEffectsBuilder, TransactionEffectsAPI, TransactionEffectsExtForTesting},
20 event::SystemEpochInfoEventV2,
21 full_checkpoint_content::{CheckpointData, CheckpointTransaction},
22 gas_coin::GAS,
23 messages_checkpoint::{
24 CertifiedCheckpointSummary, CheckpointContentsExt, CheckpointSummaryExt,
25 },
26 object::{GAS_VALUE_FOR_TESTING, MoveStructExt, Object},
27 programmable_transaction_builder::ProgrammableTransactionBuilder,
28 transaction::{CallArg, TransactionAPI, TransactionEnvelope},
29};
30
31pub struct TestCheckpointDataBuilder {
45 live_objects: HashMap<ObjectId, Object>,
47 wrapped_objects: HashMap<ObjectId, Object>,
49 gas_map: HashMap<Address, ObjectId>,
53
54 checkpoint_builder: CheckpointBuilder,
58}
59
60struct CheckpointBuilder {
61 checkpoint: u64,
63 epoch: u64,
65 network_total_transactions: u64,
67 transactions: Vec<CheckpointTransaction>,
69 next_transaction: Option<TransactionBuilder>,
71}
72
73struct TransactionBuilder {
74 sender_idx: u8,
75 gas: ObjectReference,
76 move_calls: Vec<(ObjectId, &'static str, &'static str)>,
77 created_objects: BTreeMap<ObjectId, Object>,
78 mutated_objects: BTreeMap<ObjectId, Object>,
79 unwrapped_objects: BTreeSet<ObjectId>,
80 wrapped_objects: BTreeSet<ObjectId>,
81 deleted_objects: BTreeSet<ObjectId>,
82 frozen_objects: BTreeSet<ObjectReference>,
83 shared_inputs: BTreeMap<ObjectId, Shared>,
84 events: Option<Vec<Event>>,
85}
86
87struct Shared {
88 mutable: bool,
89 object: Object,
90}
91
92impl TransactionBuilder {
93 pub fn new(sender_idx: u8, gas: ObjectReference) -> Self {
94 Self {
95 sender_idx,
96 gas,
97 move_calls: vec![],
98 created_objects: BTreeMap::new(),
99 mutated_objects: BTreeMap::new(),
100 unwrapped_objects: BTreeSet::new(),
101 wrapped_objects: BTreeSet::new(),
102 deleted_objects: BTreeSet::new(),
103 frozen_objects: BTreeSet::new(),
104 shared_inputs: BTreeMap::new(),
105 events: None,
106 }
107 }
108}
109
110impl TestCheckpointDataBuilder {
111 pub fn new(checkpoint: u64) -> Self {
112 Self {
113 live_objects: HashMap::new(),
114 wrapped_objects: HashMap::new(),
115 gas_map: HashMap::new(),
116 checkpoint_builder: CheckpointBuilder {
117 checkpoint,
118 epoch: 0,
119 network_total_transactions: 0,
120 transactions: vec![],
121 next_transaction: None,
122 },
123 }
124 }
125
126 pub fn with_epoch(mut self, epoch: u64) -> Self {
128 self.checkpoint_builder.epoch = epoch;
129 self
130 }
131
132 pub fn start_transaction(mut self, sender_idx: u8) -> Self {
139 assert!(self.checkpoint_builder.next_transaction.is_none());
140 let sender = Self::derive_address(sender_idx);
141 let gas_id = self.gas_map.entry(sender).or_insert_with(|| {
142 let gas = Object::with_owner_for_testing(sender);
143 let id = gas.id();
144 self.live_objects.insert(id, gas);
145 id
146 });
147 let gas_ref = self.live_objects.get(gas_id).cloned().unwrap().object_ref();
148 self.checkpoint_builder.next_transaction =
149 Some(TransactionBuilder::new(sender_idx, gas_ref));
150 self
151 }
152
153 pub fn create_owned_object(self, object_idx: u64) -> Self {
158 self.create_iota_object(object_idx, GAS_VALUE_FOR_TESTING)
159 }
160
161 pub fn create_shared_object(self, object_idx: u64) -> Self {
166 self.create_coin_object_with_owner(
167 object_idx,
168 Owner::Shared(Version::MIN_VALID_INCL),
169 GAS_VALUE_FOR_TESTING,
170 GAS::type_tag(),
171 )
172 }
173
174 pub fn create_iota_object(self, object_idx: u64, balance: u64) -> Self {
178 let sender_idx = self
179 .checkpoint_builder
180 .next_transaction
181 .as_ref()
182 .unwrap()
183 .sender_idx;
184 self.create_coin_object(object_idx, sender_idx, balance, GAS::type_tag())
185 }
186
187 pub fn create_coin_object(
193 self,
194 object_idx: u64,
195 owner_idx: u8,
196 balance: u64,
197 coin_type: TypeTag,
198 ) -> Self {
199 self.create_coin_object_with_owner(
200 object_idx,
201 Owner::Address(Self::derive_address(owner_idx)),
202 balance,
203 coin_type,
204 )
205 }
206
207 fn create_coin_object_with_owner(
208 mut self,
209 object_idx: u64,
210 owner: Owner,
211 balance: u64,
212 coin_type: TypeTag,
213 ) -> Self {
214 let tx_builder = self.checkpoint_builder.next_transaction.as_mut().unwrap();
215 let object_id = Self::derive_object_id(object_idx);
216 assert!(
217 !self.live_objects.contains_key(&object_id),
218 "Object already exists: {object_id}. Please use a different object index.",
219 );
220 let move_struct = MoveStruct::new_coin(
221 coin_type,
222 Version::MIN_VALID_INCL,
225 object_id,
226 balance,
227 );
228 let object = Object::new_move(move_struct, owner, TransactionDigest::ZERO);
229 tx_builder.created_objects.insert(object_id, object);
230 self
231 }
232
233 pub fn mutate_owned_object(mut self, object_idx: u64) -> Self {
236 let tx_builder = self.checkpoint_builder.next_transaction.as_mut().unwrap();
237 let object_id = Self::derive_object_id(object_idx);
238 let object = self
239 .live_objects
240 .get(&object_id)
241 .cloned()
242 .expect("Mutating an object that doesn't exist");
243 tx_builder.mutated_objects.insert(object_id, object);
244 self
245 }
246
247 pub fn mutate_shared_object(self, object_idx: u64) -> Self {
249 self.access_shared_object(object_idx, true)
250 }
251
252 pub fn transfer_object(self, object_idx: u64, recipient_idx: u8) -> Self {
257 self.change_object_owner(
258 object_idx,
259 Owner::Address(Self::derive_address(recipient_idx)),
260 )
261 }
262
263 pub fn change_object_owner(mut self, object_idx: u64, owner: Owner) -> Self {
267 let tx_builder = self.checkpoint_builder.next_transaction.as_mut().unwrap();
268 let object_id = Self::derive_object_id(object_idx);
269 let mut object = self.live_objects.get(&object_id).unwrap().clone();
270 object.owner = owner;
271 tx_builder.mutated_objects.insert(object_id, object);
272 self
273 }
274
275 pub fn transfer_coin_balance(
281 mut self,
282 object_idx: u64,
283 new_object_idx: u64,
284 recipient_idx: u8,
285 amount: u64,
286 ) -> Self {
287 let tx_builder = self.checkpoint_builder.next_transaction.as_mut().unwrap();
288 let object_id = Self::derive_object_id(object_idx);
289 let mut object = self
290 .live_objects
291 .get(&object_id)
292 .cloned()
293 .expect("Mutating an object that does not exist");
294 let coin_type = object.coin_type_opt().cloned().unwrap();
295 let move_object = object.data.as_opt_mut_struct().unwrap();
297 let old_balance = move_object.get_coin_value_unchecked();
298 let new_balance = old_balance - amount;
299 move_object.set_coin_value_unchecked(new_balance);
300 tx_builder.mutated_objects.insert(object_id, object);
301
302 self.create_coin_object(new_object_idx, recipient_idx, amount, coin_type)
304 }
305
306 pub fn wrap_object(mut self, object_idx: u64) -> Self {
309 let tx_builder = self.checkpoint_builder.next_transaction.as_mut().unwrap();
310 let object_id = Self::derive_object_id(object_idx);
311 assert!(self.live_objects.contains_key(&object_id));
312 tx_builder.wrapped_objects.insert(object_id);
313 self
314 }
315
316 pub fn unwrap_object(mut self, object_idx: u64) -> Self {
319 let tx_builder = self.checkpoint_builder.next_transaction.as_mut().unwrap();
320 let object_id = Self::derive_object_id(object_idx);
321 assert!(self.wrapped_objects.contains_key(&object_id));
322 tx_builder.unwrapped_objects.insert(object_id);
323 self
324 }
325
326 pub fn delete_object(mut self, object_idx: u64) -> Self {
329 let tx_builder = self.checkpoint_builder.next_transaction.as_mut().unwrap();
330 let object_id = Self::derive_object_id(object_idx);
331 assert!(self.live_objects.contains_key(&object_id));
332 tx_builder.deleted_objects.insert(object_id);
333 self
334 }
335
336 pub fn read_frozen_object(mut self, object_id: u64) -> Self {
341 let tx_builder = self.checkpoint_builder.next_transaction.as_mut().unwrap();
342 let object_id = Self::derive_object_id(object_id);
343
344 let obj = self
345 .live_objects
346 .get(&object_id)
347 .expect("Frozen object not found");
348
349 assert!(obj.owner().is_immutable());
350 tx_builder.frozen_objects.insert(obj.object_ref());
351 self
352 }
353
354 pub fn read_shared_object(self, object_idx: u64) -> Self {
356 self.access_shared_object(object_idx, false)
357 }
358
359 pub fn with_events(mut self, events: Vec<Event>) -> Self {
362 self.checkpoint_builder
363 .next_transaction
364 .as_mut()
365 .unwrap()
366 .events = Some(events);
367 self
368 }
369
370 pub fn add_move_call(
375 mut self,
376 package: ObjectId,
377 module: &'static str,
378 function: &'static str,
379 ) -> Self {
380 let tx_builder = self.checkpoint_builder.next_transaction.as_mut().unwrap();
381 tx_builder.move_calls.push((package, module, function));
382 self
383 }
384
385 pub fn finish_transaction(mut self) -> Self {
389 let TransactionBuilder {
390 sender_idx,
391 gas,
392 move_calls,
393 created_objects,
394 mutated_objects,
395 unwrapped_objects,
396 wrapped_objects,
397 deleted_objects,
398 frozen_objects,
399 shared_inputs,
400 events,
401 } = self.checkpoint_builder.next_transaction.take().unwrap();
402
403 let sender = Self::derive_address(sender_idx);
404 let events = events.map(TransactionEvents);
405 let events_digest = events.as_ref().map(|events| events.digest());
406
407 let mut pt_builder = ProgrammableTransactionBuilder::new();
408 for (package, module, function) in move_calls {
409 pt_builder
410 .move_call(
411 package,
412 Identifier::from_static(module),
413 Identifier::from_static(function),
414 vec![],
415 vec![],
416 )
417 .unwrap();
418 }
419
420 for &object_ref in &frozen_objects {
421 pt_builder
422 .obj(CallArg::ImmutableOrOwned(object_ref))
423 .expect("Failed to add frozen object input");
424 }
425
426 for (id, input) in &shared_inputs {
427 let &Owner::Shared(initial_shared_version) = input.object.owner() else {
428 panic!("Accessing a non-shared object as shared");
429 };
430
431 pt_builder
432 .obj(CallArg::Shared(SharedObjectReference::new(
433 *id,
434 initial_shared_version,
435 input.mutable,
436 )))
437 .expect("Failed to add shared object input");
438 }
439
440 let pt = pt_builder.finish();
441 let tx = Transaction::new(TransactionKind::Programmable(pt), sender, gas, 1, 1);
442 let tx = TransactionEnvelope::new(SenderSignedTransaction::new(tx, vec![]));
443
444 let wrapped_objects: Vec<_> = wrapped_objects
445 .into_iter()
446 .map(|id| self.live_objects.remove(&id).unwrap())
447 .collect();
448 let deleted_objects: Vec<_> = deleted_objects
449 .into_iter()
450 .map(|id| self.live_objects.remove(&id).unwrap())
451 .collect();
452 let unwrapped_objects: Vec<_> = unwrapped_objects
453 .into_iter()
454 .map(|id| self.wrapped_objects.remove(&id).unwrap())
455 .collect();
456
457 let mut effects_builder = TestEffectsBuilder::new(tx.data())
458 .with_created_objects(created_objects.iter().map(|(id, o)| (*id, *o.owner())))
459 .with_mutated_objects(
460 mutated_objects
461 .iter()
462 .map(|(id, o)| (*id, o.version(), *o.owner())),
463 )
464 .with_wrapped_objects(wrapped_objects.iter().map(|o| (o.id(), o.version())))
465 .with_unwrapped_objects(unwrapped_objects.iter().map(|o| (o.id(), *o.owner())))
466 .with_deleted_objects(deleted_objects.iter().map(|o| (o.id(), o.version())))
467 .with_frozen_objects(
468 frozen_objects
469 .into_iter()
470 .map(|object_ref| object_ref.object_id),
471 )
472 .with_shared_input_versions(
473 shared_inputs
474 .iter()
475 .map(|(id, input)| (*id, input.object.version()))
476 .collect(),
477 );
478
479 if let Some(events_digest) = &events_digest {
480 effects_builder = effects_builder.with_events_digest(*events_digest);
481 }
482
483 let effects = effects_builder.build();
484 let lamport_version = effects.lamport_version();
485 let input_objects: Vec<_> = mutated_objects
486 .keys()
487 .chain(
488 shared_inputs
489 .iter()
490 .filter(|(_, i)| i.mutable)
491 .map(|(id, _)| id),
492 )
493 .map(|id| self.live_objects.get(id).unwrap().clone())
494 .chain(deleted_objects)
495 .chain(wrapped_objects.clone())
496 .chain(std::iter::once(
497 self.live_objects.get(&gas.object_id).unwrap().clone(),
498 ))
499 .collect();
500 let output_objects: Vec<_> = created_objects
501 .values()
502 .cloned()
503 .chain(mutated_objects.values().cloned())
504 .chain(
505 shared_inputs
506 .values()
507 .filter(|i| i.mutable)
508 .map(|i| i.object.clone()),
509 )
510 .chain(unwrapped_objects)
511 .chain(std::iter::once(
512 self.live_objects.get(&gas.object_id).cloned().unwrap(),
513 ))
514 .map(|mut o| {
515 o.data
516 .as_opt_mut_struct()
517 .unwrap()
518 .increment_version_to(lamport_version);
519 o
520 })
521 .collect();
522 self.live_objects
523 .extend(output_objects.iter().map(|o| (o.id(), o.clone())));
524 self.wrapped_objects
525 .extend(wrapped_objects.iter().map(|o| (o.id(), o.clone())));
526
527 self.checkpoint_builder
528 .transactions
529 .push(CheckpointTransaction {
530 transaction: tx,
531 effects,
532 events,
533 input_objects,
534 output_objects,
535 });
536 self
537 }
538
539 pub fn advance_epoch(&mut self, safe_mode: bool) -> CheckpointData {
544 let (committee, _) = Committee::new_simple_test_committee();
545 let protocol_config = ProtocolConfig::get_for_max_version_UNSAFE();
546 let tx_kind = EndOfEpochTransactionKind::new_change_epoch(
547 self.checkpoint_builder.epoch + 1,
548 protocol_config.version.as_u64(),
549 Default::default(),
550 Default::default(),
551 Default::default(),
552 Default::default(),
553 Default::default(),
554 Default::default(),
555 );
556
557 let end_of_epoch_tx = Transaction::new(
561 TransactionKind::EndOfEpoch(vec![tx_kind]),
562 Address::ZERO,
563 random_object_ref(),
564 1,
565 1,
566 )
567 .pipe(|tx| SenderSignedTransaction::new(tx, vec![]))
568 .pipe(TransactionEnvelope::new);
569
570 let events = if !safe_mode {
571 let system_epoch_info_event = SystemEpochInfoEventV2 {
572 epoch: self.checkpoint_builder.epoch,
573 protocol_version: protocol_config.version.as_u64(),
574 ..Default::default()
575 };
576 Some(vec![Event {
577 package_id: ObjectId::SYSTEM,
578 module: Identifier::from_static("iota_system_state_inner"),
579 sender: TestCheckpointDataBuilder::derive_address(0),
580 type_: StructTag::new_system_epoch_info_event(),
581 contents: bcs::to_bytes(&system_epoch_info_event).unwrap(),
582 }])
583 } else {
584 None
585 };
586
587 let transaction_events = events.map(TransactionEvents);
588
589 let effects = TransactionEffects::new_empty_v1_for_testing(*end_of_epoch_tx.digest());
590
591 self.checkpoint_builder
593 .transactions
594 .push(CheckpointTransaction {
595 transaction: end_of_epoch_tx,
596 effects,
597 events: transaction_events,
598 input_objects: vec![],
599 output_objects: vec![],
600 });
601
602 let mut checkpoint = self.build_checkpoint();
605 let end_of_epoch_data = EndOfEpochData {
606 next_epoch_committee: committee.committee_members(),
607 next_epoch_protocol_version: protocol_config.version.as_u64(),
608 epoch_commitments: vec![],
609 epoch_supply_change: 0,
611 };
612 checkpoint.checkpoint_summary.end_of_epoch_data = Some(end_of_epoch_data);
613 self.checkpoint_builder.epoch += 1;
614 checkpoint
615 }
616
617 pub fn build_checkpoint(&mut self) -> CheckpointData {
621 assert!(self.checkpoint_builder.next_transaction.is_none());
622 let transactions = std::mem::take(&mut self.checkpoint_builder.transactions);
623 let contents = CheckpointContents::new_with_digests_only_for_tests(
624 transactions
625 .iter()
626 .map(|tx| ExecutionDigests::new(*tx.transaction.digest(), tx.effects.digest())),
627 );
628
629 self.checkpoint_builder.network_total_transactions += transactions.len() as u64;
630
631 let checkpoint_summary = CheckpointSummary::new_with_protocol_config(
632 &ProtocolConfig::get_for_max_version_UNSAFE(),
633 self.checkpoint_builder.epoch,
634 self.checkpoint_builder.checkpoint,
635 self.checkpoint_builder.network_total_transactions,
636 &contents,
637 None,
638 Default::default(),
639 None,
640 0,
641 vec![],
642 );
643
644 let (committee, keys) = Committee::new_simple_test_committee();
645
646 let checkpoint_cert = CertifiedCheckpointSummary::new_from_keypairs_for_testing(
647 checkpoint_summary,
648 &keys,
649 &committee,
650 );
651
652 self.checkpoint_builder.checkpoint += 1;
653 CheckpointData {
654 checkpoint_summary: checkpoint_cert,
655 checkpoint_contents: contents,
656 transactions,
657 }
658 }
659
660 pub fn derive_object_id(object_idx: u64) -> ObjectId {
664 let mut bytes = [0; ObjectId::LENGTH];
667 bytes[0..8].copy_from_slice(&object_idx.to_le_bytes());
668 ObjectId::from_bytes(bytes).unwrap()
669 }
670
671 pub fn derive_address(address_idx: u8) -> Address {
673 dbg_addr(address_idx)
674 }
675
676 fn access_shared_object(mut self, object_idx: u64, mutable: bool) -> Self {
679 let tx_builder = self.checkpoint_builder.next_transaction.as_mut().unwrap();
680 let object_id = Self::derive_object_id(object_idx);
681 let object = self
682 .live_objects
683 .get(&object_id)
684 .cloned()
685 .expect("Accessing a shared object that doesn't exist");
686 tx_builder
687 .shared_inputs
688 .insert(object_id, Shared { mutable, object });
689 self
690 }
691}
692
693#[cfg(test)]
694mod tests {
695 use std::str::FromStr;
696
697 use iota_sdk_types::Command;
698
699 use super::*;
700 use crate::{
701 ObjectId,
702 transaction::{TransactionAPI, TransactionKindExt},
703 };
704 #[test]
705 fn test_basic_checkpoint_builder() {
706 let checkpoint = TestCheckpointDataBuilder::new(1)
708 .with_epoch(5)
709 .start_transaction(0)
710 .finish_transaction()
711 .build_checkpoint();
712
713 assert_eq!(checkpoint.checkpoint_summary.sequence_number(), 1);
714 assert_eq!(checkpoint.checkpoint_summary.epoch, 5);
715 assert_eq!(checkpoint.transactions.len(), 1);
716 let tx = &checkpoint.transactions[0];
717 assert_eq!(
718 tx.transaction.sender_address(),
719 TestCheckpointDataBuilder::derive_address(0)
720 );
721 assert_eq!(tx.effects.mutated().len(), 1); assert_eq!(tx.effects.deleted().len(), 0);
723 assert_eq!(tx.effects.created().len(), 0);
724 assert_eq!(tx.input_objects.len(), 1);
725 assert_eq!(tx.output_objects.len(), 1);
726 }
727
728 #[test]
729 fn test_multiple_transactions() {
730 let checkpoint = TestCheckpointDataBuilder::new(1)
731 .start_transaction(0)
732 .finish_transaction()
733 .start_transaction(1)
734 .finish_transaction()
735 .start_transaction(2)
736 .finish_transaction()
737 .build_checkpoint();
738
739 assert_eq!(checkpoint.transactions.len(), 3);
740
741 let senders: Vec<_> = checkpoint
744 .transactions
745 .iter()
746 .map(|tx| tx.transaction.transaction().sender())
747 .collect();
748 assert_eq!(
749 senders,
750 vec![
751 TestCheckpointDataBuilder::derive_address(0),
752 TestCheckpointDataBuilder::derive_address(1),
753 TestCheckpointDataBuilder::derive_address(2)
754 ]
755 );
756 }
757
758 #[test]
759 fn test_object_creation() {
760 let checkpoint = TestCheckpointDataBuilder::new(1)
761 .start_transaction(0)
762 .create_owned_object(0)
763 .finish_transaction()
764 .build_checkpoint();
765
766 let tx = &checkpoint.transactions[0];
767 let created_obj_id = TestCheckpointDataBuilder::derive_object_id(0);
768
769 assert!(
771 tx.output_objects
772 .iter()
773 .any(|obj| obj.id() == created_obj_id)
774 );
775
776 assert!(
778 tx.effects
779 .created()
780 .iter()
781 .any(|(object_ref, owner)| object_ref.object_id == created_obj_id
782 && owner.address_or_object().unwrap()
783 == &TestCheckpointDataBuilder::derive_address(0))
784 );
785 }
786
787 #[test]
788 fn test_object_mutation() {
789 let checkpoint = TestCheckpointDataBuilder::new(1)
790 .start_transaction(0)
791 .create_owned_object(0)
792 .finish_transaction()
793 .start_transaction(0)
794 .mutate_owned_object(0)
795 .finish_transaction()
796 .build_checkpoint();
797
798 let tx = &checkpoint.transactions[1];
799 let obj_id = TestCheckpointDataBuilder::derive_object_id(0);
800
801 assert!(tx.input_objects.iter().any(|obj| obj.id() == obj_id));
803 assert!(tx.output_objects.iter().any(|obj| obj.id() == obj_id));
804
805 assert!(
807 tx.effects
808 .mutated()
809 .iter()
810 .any(|(object_ref, _)| object_ref.object_id == obj_id)
811 );
812 }
813
814 #[test]
815 fn test_object_deletion() {
816 let checkpoint = TestCheckpointDataBuilder::new(1)
817 .start_transaction(0)
818 .create_owned_object(0)
819 .finish_transaction()
820 .start_transaction(0)
821 .delete_object(0)
822 .finish_transaction()
823 .build_checkpoint();
824
825 let tx = &checkpoint.transactions[1];
826 let obj_id = TestCheckpointDataBuilder::derive_object_id(0);
827
828 assert!(tx.input_objects.iter().any(|obj| obj.id() == obj_id));
830 assert!(!tx.output_objects.iter().any(|obj| obj.id() == obj_id));
831
832 assert!(
834 tx.effects
835 .deleted()
836 .iter()
837 .any(|object_ref| object_ref.object_id == obj_id)
838 );
839 }
840
841 #[test]
842 fn test_object_wrapping() {
843 let checkpoint = TestCheckpointDataBuilder::new(1)
844 .start_transaction(0)
845 .create_owned_object(0)
846 .finish_transaction()
847 .start_transaction(0)
848 .wrap_object(0)
849 .finish_transaction()
850 .start_transaction(0)
851 .unwrap_object(0)
852 .finish_transaction()
853 .build_checkpoint();
854
855 let tx = &checkpoint.transactions[1];
856 let obj_id = TestCheckpointDataBuilder::derive_object_id(0);
857
858 assert!(tx.input_objects.iter().any(|obj| obj.id() == obj_id));
860 assert!(!tx.output_objects.iter().any(|obj| obj.id() == obj_id));
861
862 assert!(
864 tx.effects
865 .wrapped()
866 .iter()
867 .any(|object_ref| object_ref.object_id == obj_id)
868 );
869
870 let tx = &checkpoint.transactions[2];
871
872 assert!(!tx.input_objects.iter().any(|obj| obj.id() == obj_id));
874 assert!(tx.output_objects.iter().any(|obj| obj.id() == obj_id));
875
876 assert!(
878 tx.effects
879 .unwrapped()
880 .iter()
881 .any(|(object_ref, _owner)| object_ref.object_id == obj_id)
882 );
883 }
884
885 #[test]
886 fn test_object_transfer() {
887 let checkpoint = TestCheckpointDataBuilder::new(1)
888 .start_transaction(0)
889 .create_owned_object(0)
890 .finish_transaction()
891 .start_transaction(1)
892 .transfer_object(0, 1)
893 .finish_transaction()
894 .build_checkpoint();
895
896 let tx = &checkpoint.transactions[1];
897 let obj_id = TestCheckpointDataBuilder::derive_object_id(0);
898
899 assert!(tx.input_objects.iter().any(|obj| obj.id() == obj_id));
901 assert!(tx.output_objects.iter().any(|obj| obj.id() == obj_id));
902
903 assert!(
905 tx.effects
906 .mutated()
907 .iter()
908 .any(|(object_ref, owner)| object_ref.object_id == obj_id
909 && owner.address_or_object().unwrap()
910 == &TestCheckpointDataBuilder::derive_address(1))
911 );
912 }
913
914 #[test]
915 fn test_shared_object() {
916 let checkpoint = TestCheckpointDataBuilder::new(1)
917 .start_transaction(0)
918 .create_shared_object(0)
919 .finish_transaction()
920 .build_checkpoint();
921
922 let tx = &checkpoint.transactions[0];
923 let obj_id = TestCheckpointDataBuilder::derive_object_id(0);
924
925 assert!(
927 tx.output_objects
928 .iter()
929 .any(|obj| obj.id() == obj_id && obj.owner().is_shared())
930 );
931 }
932
933 #[test]
934 fn test_freeze_object() {
935 let checkpoint = TestCheckpointDataBuilder::new(1)
936 .start_transaction(0)
937 .create_owned_object(0)
938 .finish_transaction()
939 .start_transaction(0)
940 .change_object_owner(0, Owner::Immutable)
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!(
949 tx.output_objects
950 .iter()
951 .any(|obj| obj.id() == obj_id && obj.owner().is_immutable())
952 );
953 }
954
955 #[test]
956 fn test_iota_balance_transfer() {
957 let checkpoint = TestCheckpointDataBuilder::new(1)
958 .start_transaction(0)
959 .create_iota_object(0, 100)
960 .finish_transaction()
961 .start_transaction(1)
962 .transfer_coin_balance(0, 1, 1, 10)
963 .finish_transaction()
964 .build_checkpoint();
965
966 let tx = &checkpoint.transactions[0];
967 let obj_id0 = TestCheckpointDataBuilder::derive_object_id(0);
968
969 assert!(tx.output_objects.iter().any(|obj| obj.id() == obj_id0
972 && obj.is_gas_coin()
973 && obj.data.as_opt_struct().unwrap().get_coin_value_unchecked() == 100));
974
975 let tx = &checkpoint.transactions[1];
976 let obj_id1 = TestCheckpointDataBuilder::derive_object_id(1);
977
978 assert!(tx.output_objects.iter().any(|obj| obj.id() == obj_id0
980 && obj.is_gas_coin()
981 && obj.data.as_opt_struct().unwrap().get_coin_value_unchecked() == 90));
982
983 assert!(tx.output_objects.iter().any(|obj| obj.id() == obj_id1
985 && obj.is_gas_coin()
986 && obj.data.as_opt_struct().unwrap().get_coin_value_unchecked() == 10));
987 }
988
989 #[test]
990 fn test_coin_balance_transfer() {
991 let type_tag = TypeTag::from_str("0x100::a::b").unwrap();
992 let checkpoint = TestCheckpointDataBuilder::new(1)
993 .start_transaction(0)
994 .create_coin_object(0, 0, 100, type_tag.clone())
995 .finish_transaction()
996 .start_transaction(1)
997 .transfer_coin_balance(0, 1, 1, 10)
998 .finish_transaction()
999 .build_checkpoint();
1000
1001 let tx = &checkpoint.transactions[1];
1002 let obj_id0 = TestCheckpointDataBuilder::derive_object_id(0);
1003 let obj_id1 = TestCheckpointDataBuilder::derive_object_id(1);
1004
1005 assert!(tx.output_objects.iter().any(|obj| obj.id() == obj_id0
1007 && obj.coin_type_opt() == Some(&type_tag)
1008 && obj.data.as_opt_struct().unwrap().get_coin_value_unchecked() == 90));
1009
1010 assert!(tx.output_objects.iter().any(|obj| obj.id() == obj_id1
1012 && obj.coin_type_opt() == Some(&type_tag)
1013 && obj.data.as_opt_struct().unwrap().get_coin_value_unchecked() == 10));
1014 }
1015
1016 #[test]
1017 fn test_events() {
1018 let checkpoint = TestCheckpointDataBuilder::new(1)
1019 .start_transaction(0)
1020 .with_events(vec![Event {
1021 package_id: ObjectId::ZERO,
1022 module: Identifier::from_static("test"),
1023 sender: TestCheckpointDataBuilder::derive_address(0),
1024 type_: StructTag::new_gas(),
1025 contents: vec![],
1026 }])
1027 .finish_transaction()
1028 .build_checkpoint();
1029 let tx = &checkpoint.transactions[0];
1030
1031 assert!(tx.effects.events_digest().is_some());
1033
1034 assert_eq!(tx.events.as_ref().unwrap().len(), 1);
1036 }
1037
1038 #[test]
1039 fn test_move_call() {
1040 let checkpoint = TestCheckpointDataBuilder::new(1)
1041 .start_transaction(0)
1042 .add_move_call(ObjectId::ZERO, "test", "test")
1043 .finish_transaction()
1044 .build_checkpoint();
1045 let tx = &checkpoint.transactions[0];
1046
1047 assert!(
1049 tx.transaction
1050 .transaction()
1051 .kind()
1052 .iter_commands()
1053 .any(|cmd| {
1054 cmd == &Command::new_move_call(
1055 ObjectId::ZERO,
1056 Identifier::new_unchecked("test"),
1057 Identifier::new_unchecked("test"),
1058 vec![],
1059 vec![],
1060 )
1061 })
1062 )
1063 }
1064
1065 #[test]
1066 fn test_multiple_checkpoints() {
1067 let mut builder = TestCheckpointDataBuilder::new(1)
1068 .start_transaction(0)
1069 .create_owned_object(0)
1070 .finish_transaction();
1071 let checkpoint1 = builder.build_checkpoint();
1072 builder = builder
1073 .start_transaction(0)
1074 .mutate_owned_object(0)
1075 .finish_transaction();
1076 let checkpoint2 = builder.build_checkpoint();
1077 builder = builder
1078 .start_transaction(0)
1079 .delete_object(0)
1080 .finish_transaction();
1081 let checkpoint3 = builder.build_checkpoint();
1082
1083 assert_eq!(checkpoint1.checkpoint_summary.sequence_number, 1);
1085 assert_eq!(checkpoint2.checkpoint_summary.sequence_number, 2);
1086 assert_eq!(checkpoint3.checkpoint_summary.sequence_number, 3);
1087 }
1088}