1use std::collections::{BTreeMap, BTreeSet, HashMap};
6
7use iota_protocol_config::ProtocolConfig;
8use iota_sdk_move_types::iota_system::iota_system_state_inner::SystemEpochInfoEventV2;
9use iota_sdk_types::{
10 Address, CheckpointContents, CheckpointSummary, EndOfEpochData, EndOfEpochTransactionKind,
11 Event, Identifier, MoveStruct, ObjectId, ObjectReference, Owner, SenderSignedTransaction,
12 SharedObjectReference, StructTag, Transaction, TransactionDigest, TransactionEffects,
13 TransactionEvents, TransactionKind, TypeTag, Version,
14};
15use tap::Pipe;
16
17use crate::{
18 base_types::{ExecutionDigests, dbg_addr, random_object_ref},
19 committee::Committee,
20 effects::{TestEffectsBuilder, TransactionEffectsAPI, TransactionEffectsExtForTesting},
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 total_stake: 0,
579 storage_charge: 0,
580 storage_rebate: 0,
581 storage_fund_balance: 0,
582 total_gas_fees: 0,
583 total_stake_rewards_distributed: 0,
584 burnt_tokens_amount: 0,
585 minted_tokens_amount: 0,
586 tips_amount: 0,
587 };
588 Some(vec![Event {
589 package_id: ObjectId::SYSTEM,
590 module: Identifier::from_static("iota_system_state_inner"),
591 sender: TestCheckpointDataBuilder::derive_address(0),
592 struct_tag: StructTag::new_system_epoch_info_event(),
593 contents: bcs::to_bytes(&system_epoch_info_event).unwrap(),
594 }])
595 } else {
596 None
597 };
598
599 let transaction_events = events.map(TransactionEvents);
600
601 let effects = TransactionEffects::new_empty_v1_for_testing(*end_of_epoch_tx.digest());
602
603 self.checkpoint_builder
605 .transactions
606 .push(CheckpointTransaction {
607 transaction: end_of_epoch_tx,
608 effects,
609 events: transaction_events,
610 input_objects: vec![],
611 output_objects: vec![],
612 });
613
614 let mut checkpoint = self.build_checkpoint();
617 let end_of_epoch_data = EndOfEpochData {
618 next_epoch_committee: committee.committee_members(),
619 next_epoch_protocol_version: protocol_config.version.as_u64(),
620 epoch_commitments: vec![],
621 epoch_supply_change: 0,
623 };
624 checkpoint.checkpoint_summary.end_of_epoch_data = Some(end_of_epoch_data);
625 self.checkpoint_builder.epoch += 1;
626 checkpoint
627 }
628
629 pub fn build_checkpoint(&mut self) -> CheckpointData {
633 assert!(self.checkpoint_builder.next_transaction.is_none());
634 let transactions = std::mem::take(&mut self.checkpoint_builder.transactions);
635 let contents = CheckpointContents::new_with_digests_only_for_tests(
636 transactions
637 .iter()
638 .map(|tx| ExecutionDigests::new(*tx.transaction.digest(), tx.effects.digest())),
639 );
640
641 self.checkpoint_builder.network_total_transactions += transactions.len() as u64;
642
643 let checkpoint_summary = CheckpointSummary::new_with_protocol_config(
644 &ProtocolConfig::get_for_max_version_UNSAFE(),
645 self.checkpoint_builder.epoch,
646 self.checkpoint_builder.checkpoint,
647 self.checkpoint_builder.network_total_transactions,
648 &contents,
649 None,
650 Default::default(),
651 None,
652 0,
653 vec![],
654 );
655
656 let (committee, keys) = Committee::new_simple_test_committee();
657
658 let checkpoint_cert = CertifiedCheckpointSummary::new_from_keypairs_for_testing(
659 checkpoint_summary,
660 &keys,
661 &committee,
662 );
663
664 self.checkpoint_builder.checkpoint += 1;
665 CheckpointData {
666 checkpoint_summary: checkpoint_cert,
667 checkpoint_contents: contents,
668 transactions,
669 }
670 }
671
672 pub fn derive_object_id(object_idx: u64) -> ObjectId {
676 let mut bytes = [0; ObjectId::LENGTH];
679 bytes[0..8].copy_from_slice(&object_idx.to_le_bytes());
680 ObjectId::from_bytes(bytes).unwrap()
681 }
682
683 pub fn derive_address(address_idx: u8) -> Address {
685 dbg_addr(address_idx)
686 }
687
688 fn access_shared_object(mut self, object_idx: u64, mutable: bool) -> Self {
691 let tx_builder = self.checkpoint_builder.next_transaction.as_mut().unwrap();
692 let object_id = Self::derive_object_id(object_idx);
693 let object = self
694 .live_objects
695 .get(&object_id)
696 .cloned()
697 .expect("Accessing a shared object that doesn't exist");
698 tx_builder
699 .shared_inputs
700 .insert(object_id, Shared { mutable, object });
701 self
702 }
703}
704
705#[cfg(test)]
706mod tests {
707 use std::str::FromStr;
708
709 use iota_sdk_types::Command;
710
711 use super::*;
712 use crate::{
713 ObjectId,
714 transaction::{TransactionAPI, TransactionKindExt},
715 };
716 #[test]
717 fn test_basic_checkpoint_builder() {
718 let checkpoint = TestCheckpointDataBuilder::new(1)
720 .with_epoch(5)
721 .start_transaction(0)
722 .finish_transaction()
723 .build_checkpoint();
724
725 assert_eq!(checkpoint.checkpoint_summary.sequence_number(), 1);
726 assert_eq!(checkpoint.checkpoint_summary.epoch, 5);
727 assert_eq!(checkpoint.transactions.len(), 1);
728 let tx = &checkpoint.transactions[0];
729 assert_eq!(
730 tx.transaction.sender_address(),
731 TestCheckpointDataBuilder::derive_address(0)
732 );
733 assert_eq!(tx.effects.mutated().len(), 1); assert_eq!(tx.effects.deleted().len(), 0);
735 assert_eq!(tx.effects.created().len(), 0);
736 assert_eq!(tx.input_objects.len(), 1);
737 assert_eq!(tx.output_objects.len(), 1);
738 }
739
740 #[test]
741 fn test_multiple_transactions() {
742 let checkpoint = TestCheckpointDataBuilder::new(1)
743 .start_transaction(0)
744 .finish_transaction()
745 .start_transaction(1)
746 .finish_transaction()
747 .start_transaction(2)
748 .finish_transaction()
749 .build_checkpoint();
750
751 assert_eq!(checkpoint.transactions.len(), 3);
752
753 let senders: Vec<_> = checkpoint
756 .transactions
757 .iter()
758 .map(|tx| tx.transaction.transaction().sender())
759 .collect();
760 assert_eq!(
761 senders,
762 vec![
763 TestCheckpointDataBuilder::derive_address(0),
764 TestCheckpointDataBuilder::derive_address(1),
765 TestCheckpointDataBuilder::derive_address(2)
766 ]
767 );
768 }
769
770 #[test]
771 fn test_object_creation() {
772 let checkpoint = TestCheckpointDataBuilder::new(1)
773 .start_transaction(0)
774 .create_owned_object(0)
775 .finish_transaction()
776 .build_checkpoint();
777
778 let tx = &checkpoint.transactions[0];
779 let created_obj_id = TestCheckpointDataBuilder::derive_object_id(0);
780
781 assert!(
783 tx.output_objects
784 .iter()
785 .any(|obj| obj.id() == created_obj_id)
786 );
787
788 assert!(
790 tx.effects
791 .created()
792 .iter()
793 .any(|created| created.reference().object_id == created_obj_id
794 && created.owner().address_or_object().unwrap()
795 == &TestCheckpointDataBuilder::derive_address(0))
796 );
797 }
798
799 #[test]
800 fn test_object_mutation() {
801 let checkpoint = TestCheckpointDataBuilder::new(1)
802 .start_transaction(0)
803 .create_owned_object(0)
804 .finish_transaction()
805 .start_transaction(0)
806 .mutate_owned_object(0)
807 .finish_transaction()
808 .build_checkpoint();
809
810 let tx = &checkpoint.transactions[1];
811 let obj_id = TestCheckpointDataBuilder::derive_object_id(0);
812
813 assert!(tx.input_objects.iter().any(|obj| obj.id() == obj_id));
815 assert!(tx.output_objects.iter().any(|obj| obj.id() == obj_id));
816
817 assert!(
819 tx.effects
820 .mutated()
821 .iter()
822 .any(|changed| changed.reference().object_id == obj_id)
823 );
824 }
825
826 #[test]
827 fn test_object_deletion() {
828 let checkpoint = TestCheckpointDataBuilder::new(1)
829 .start_transaction(0)
830 .create_owned_object(0)
831 .finish_transaction()
832 .start_transaction(0)
833 .delete_object(0)
834 .finish_transaction()
835 .build_checkpoint();
836
837 let tx = &checkpoint.transactions[1];
838 let obj_id = TestCheckpointDataBuilder::derive_object_id(0);
839
840 assert!(tx.input_objects.iter().any(|obj| obj.id() == obj_id));
842 assert!(!tx.output_objects.iter().any(|obj| obj.id() == obj_id));
843
844 assert!(
846 tx.effects
847 .deleted()
848 .iter()
849 .any(|object_ref| object_ref.object_id == obj_id)
850 );
851 }
852
853 #[test]
854 fn test_object_wrapping() {
855 let checkpoint = TestCheckpointDataBuilder::new(1)
856 .start_transaction(0)
857 .create_owned_object(0)
858 .finish_transaction()
859 .start_transaction(0)
860 .wrap_object(0)
861 .finish_transaction()
862 .start_transaction(0)
863 .unwrap_object(0)
864 .finish_transaction()
865 .build_checkpoint();
866
867 let tx = &checkpoint.transactions[1];
868 let obj_id = TestCheckpointDataBuilder::derive_object_id(0);
869
870 assert!(tx.input_objects.iter().any(|obj| obj.id() == obj_id));
872 assert!(!tx.output_objects.iter().any(|obj| obj.id() == obj_id));
873
874 assert!(
876 tx.effects
877 .wrapped()
878 .iter()
879 .any(|object_ref| object_ref.object_id == obj_id)
880 );
881
882 let tx = &checkpoint.transactions[2];
883
884 assert!(!tx.input_objects.iter().any(|obj| obj.id() == obj_id));
886 assert!(tx.output_objects.iter().any(|obj| obj.id() == obj_id));
887
888 assert!(
890 tx.effects
891 .unwrapped()
892 .iter()
893 .any(|changed| changed.reference().object_id == obj_id)
894 );
895 }
896
897 #[test]
898 fn test_object_transfer() {
899 let checkpoint = TestCheckpointDataBuilder::new(1)
900 .start_transaction(0)
901 .create_owned_object(0)
902 .finish_transaction()
903 .start_transaction(1)
904 .transfer_object(0, 1)
905 .finish_transaction()
906 .build_checkpoint();
907
908 let tx = &checkpoint.transactions[1];
909 let obj_id = TestCheckpointDataBuilder::derive_object_id(0);
910
911 assert!(tx.input_objects.iter().any(|obj| obj.id() == obj_id));
913 assert!(tx.output_objects.iter().any(|obj| obj.id() == obj_id));
914
915 assert!(
917 tx.effects
918 .mutated()
919 .iter()
920 .any(|changed| changed.reference().object_id == obj_id
921 && changed.owner().address_or_object().unwrap()
922 == &TestCheckpointDataBuilder::derive_address(1))
923 );
924 }
925
926 #[test]
927 fn test_shared_object() {
928 let checkpoint = TestCheckpointDataBuilder::new(1)
929 .start_transaction(0)
930 .create_shared_object(0)
931 .finish_transaction()
932 .build_checkpoint();
933
934 let tx = &checkpoint.transactions[0];
935 let obj_id = TestCheckpointDataBuilder::derive_object_id(0);
936
937 assert!(
939 tx.output_objects
940 .iter()
941 .any(|obj| obj.id() == obj_id && obj.owner().is_shared())
942 );
943 }
944
945 #[test]
946 fn test_freeze_object() {
947 let checkpoint = TestCheckpointDataBuilder::new(1)
948 .start_transaction(0)
949 .create_owned_object(0)
950 .finish_transaction()
951 .start_transaction(0)
952 .change_object_owner(0, Owner::Immutable)
953 .finish_transaction()
954 .build_checkpoint();
955
956 let tx = &checkpoint.transactions[1];
957 let obj_id = TestCheckpointDataBuilder::derive_object_id(0);
958
959 assert!(
961 tx.output_objects
962 .iter()
963 .any(|obj| obj.id() == obj_id && obj.owner().is_immutable())
964 );
965 }
966
967 #[test]
968 fn test_iota_balance_transfer() {
969 let checkpoint = TestCheckpointDataBuilder::new(1)
970 .start_transaction(0)
971 .create_iota_object(0, 100)
972 .finish_transaction()
973 .start_transaction(1)
974 .transfer_coin_balance(0, 1, 1, 10)
975 .finish_transaction()
976 .build_checkpoint();
977
978 let tx = &checkpoint.transactions[0];
979 let obj_id0 = TestCheckpointDataBuilder::derive_object_id(0);
980
981 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() == 100));
986
987 let tx = &checkpoint.transactions[1];
988 let obj_id1 = TestCheckpointDataBuilder::derive_object_id(1);
989
990 assert!(tx.output_objects.iter().any(|obj| obj.id() == obj_id0
992 && obj.is_gas_coin()
993 && obj.data.as_opt_struct().unwrap().get_coin_value_unchecked() == 90));
994
995 assert!(tx.output_objects.iter().any(|obj| obj.id() == obj_id1
997 && obj.is_gas_coin()
998 && obj.data.as_opt_struct().unwrap().get_coin_value_unchecked() == 10));
999 }
1000
1001 #[test]
1002 fn test_coin_balance_transfer() {
1003 let type_tag = TypeTag::from_str("0x100::a::b").unwrap();
1004 let checkpoint = TestCheckpointDataBuilder::new(1)
1005 .start_transaction(0)
1006 .create_coin_object(0, 0, 100, type_tag.clone())
1007 .finish_transaction()
1008 .start_transaction(1)
1009 .transfer_coin_balance(0, 1, 1, 10)
1010 .finish_transaction()
1011 .build_checkpoint();
1012
1013 let tx = &checkpoint.transactions[1];
1014 let obj_id0 = TestCheckpointDataBuilder::derive_object_id(0);
1015 let obj_id1 = TestCheckpointDataBuilder::derive_object_id(1);
1016
1017 assert!(tx.output_objects.iter().any(|obj| obj.id() == obj_id0
1019 && obj.opt_coin_type() == Some(&type_tag)
1020 && obj.data.as_opt_struct().unwrap().get_coin_value_unchecked() == 90));
1021
1022 assert!(tx.output_objects.iter().any(|obj| obj.id() == obj_id1
1024 && obj.opt_coin_type() == Some(&type_tag)
1025 && obj.data.as_opt_struct().unwrap().get_coin_value_unchecked() == 10));
1026 }
1027
1028 #[test]
1029 fn test_events() {
1030 let checkpoint = TestCheckpointDataBuilder::new(1)
1031 .start_transaction(0)
1032 .with_events(vec![Event {
1033 package_id: ObjectId::ZERO,
1034 module: Identifier::from_static("test"),
1035 sender: TestCheckpointDataBuilder::derive_address(0),
1036 struct_tag: StructTag::new_gas(),
1037 contents: vec![],
1038 }])
1039 .finish_transaction()
1040 .build_checkpoint();
1041 let tx = &checkpoint.transactions[0];
1042
1043 assert!(tx.effects.events_digest().is_some());
1045
1046 assert_eq!(tx.events.as_ref().unwrap().len(), 1);
1048 }
1049
1050 #[test]
1051 fn test_move_call() {
1052 let checkpoint = TestCheckpointDataBuilder::new(1)
1053 .start_transaction(0)
1054 .add_move_call(ObjectId::ZERO, "test", "test")
1055 .finish_transaction()
1056 .build_checkpoint();
1057 let tx = &checkpoint.transactions[0];
1058
1059 assert!(
1061 tx.transaction
1062 .transaction()
1063 .kind()
1064 .iter_commands()
1065 .any(|cmd| {
1066 cmd == &Command::new_move_call(
1067 ObjectId::ZERO,
1068 Identifier::new_unchecked("test"),
1069 Identifier::new_unchecked("test"),
1070 vec![],
1071 vec![],
1072 )
1073 })
1074 )
1075 }
1076
1077 #[test]
1078 fn test_multiple_checkpoints() {
1079 let mut builder = TestCheckpointDataBuilder::new(1)
1080 .start_transaction(0)
1081 .create_owned_object(0)
1082 .finish_transaction();
1083 let checkpoint1 = builder.build_checkpoint();
1084 builder = builder
1085 .start_transaction(0)
1086 .mutate_owned_object(0)
1087 .finish_transaction();
1088 let checkpoint2 = builder.build_checkpoint();
1089 builder = builder
1090 .start_transaction(0)
1091 .delete_object(0)
1092 .finish_transaction();
1093 let checkpoint3 = builder.build_checkpoint();
1094
1095 assert_eq!(checkpoint1.checkpoint_summary.sequence_number, 1);
1097 assert_eq!(checkpoint2.checkpoint_summary.sequence_number, 2);
1098 assert_eq!(checkpoint3.checkpoint_summary.sequence_number, 3);
1099 }
1100}