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