1use crate::{
8 cdk::types::Cycles,
9 config::schema::ComponentChildKind,
10 dto::{
11 fleet_registry::{FleetDirectorySnapshot, FleetRegistryVersion},
12 root_store::RootStoreBootstrapRequest,
13 },
14 ids::{
15 CanisterRole, ComponentBinding, ComponentChildBinding, ComponentInstanceId,
16 ComponentSpecId, ComponentTopologyDigest, FleetSubnetRootReleaseSet,
17 ManagedCanisterBinding,
18 },
19};
20use candid::{CandidType, Principal};
21use serde::{Deserialize, Serialize};
22
23#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
30pub struct RootComponentRegistryPreparationRequest {
31 pub store_bootstrap: RootStoreBootstrapRequest,
32 pub expected_fleet_registry: FleetRegistryVersion,
33}
34
35#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
42pub struct RootComponentInitialInventoryStatus {
43 pub fleet_activation_operation_id: [u8; 32],
44 pub component_count: u32,
45 pub inventory_hash: [u8; 32],
46 pub sealed_at_ns: u64,
47 pub directories_converged: bool,
48 pub root_runtime_activated: bool,
49}
50
51#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
58pub struct RootComponentRegistryStatusResponse {
59 pub fleet_subnet_root: Principal,
60 pub prepared_against_registry: FleetRegistryVersion,
61 pub release_set: FleetSubnetRootReleaseSet,
62 pub component_topology_digest: ComponentTopologyDigest,
63 pub next_allocation_sequence: u64,
64 pub reserved_component_instances: u32,
65 pub committed_component_instances: u32,
66 pub managed_descendants: u32,
67 pub known_created_component_canisters: u32,
68 pub encoded_bytes: u64,
69 pub initial_inventory: Option<RootComponentInitialInventoryStatus>,
70}
71
72#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
79pub struct RootComponentAllocationRequest {
80 pub operation_id: [u8; 32],
81 pub component_spec: ComponentSpecId,
82}
83
84#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
91pub struct RootComponentAllocationStatusRequest {
92 pub operation_id: [u8; 32],
93}
94
95#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
102pub struct RootComponentChildAllocationRequest {
103 pub operation_id: [u8; 32],
104 pub component: ComponentInstanceId,
105 pub expected_registry: ComponentRegistryHead,
106 pub child_role: CanisterRole,
107}
108
109#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
116pub struct RootComponentChildAllocationStatusRequest {
117 pub operation_id: [u8; 32],
118 pub component: ComponentInstanceId,
119}
120
121#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
128pub struct RootComponentSubtreeRemovalRequest {
129 pub operation_id: [u8; 32],
130 pub component: ComponentInstanceId,
131 pub target_canister_id: Principal,
132 pub expected_registry: ComponentRegistryHead,
133}
134
135#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
142pub struct RootComponentSubtreeRemovalAdvanceRequest {
143 pub operation_id: [u8; 32],
144 pub component: ComponentInstanceId,
145 pub expected_traversal_steps: u32,
146}
147
148#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
155pub struct RootComponentSubtreeRemovalStopPreparationRequest {
156 pub operation_id: [u8; 32],
157 pub component: ComponentInstanceId,
158 pub expected_traversal_steps: u32,
159 pub expected_leaf_canister_id: Principal,
160 pub expected_leaf_parent_canister_id: Principal,
161}
162
163#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
170pub struct RootComponentSubtreeRemovalStopRequest {
171 pub operation_id: [u8; 32],
172 pub component: ComponentInstanceId,
173 pub expected_traversal_steps: u32,
174 pub expected_leaf_canister_id: Principal,
175 pub expected_leaf_parent_canister_id: Principal,
176}
177
178#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
185pub struct RootComponentSubtreeRemovalDeletePreparationRequest {
186 pub operation_id: [u8; 32],
187 pub component: ComponentInstanceId,
188 pub expected_traversal_steps: u32,
189 pub expected_leaf_canister_id: Principal,
190 pub expected_leaf_parent_canister_id: Principal,
191}
192
193#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
200pub struct RootComponentSubtreeRemovalDeleteRequest {
201 pub operation_id: [u8; 32],
202 pub component: ComponentInstanceId,
203 pub expected_traversal_steps: u32,
204 pub expected_leaf_canister_id: Principal,
205 pub expected_leaf_parent_canister_id: Principal,
206}
207
208#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
215pub struct RootComponentSubtreeRemovalMembershipRemovalRequest {
216 pub operation_id: [u8; 32],
217 pub component: ComponentInstanceId,
218 pub expected_traversal_steps: u32,
219 pub expected_leaf_canister_id: Principal,
220 pub expected_leaf_parent_canister_id: Principal,
221}
222
223#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
230pub struct RootComponentSubtreeRemovalDirectorySynchronizationRequest {
231 pub operation_id: [u8; 32],
232 pub component: ComponentInstanceId,
233 pub expected_traversal_steps: u32,
234 pub expected_leaf_canister_id: Principal,
235 pub expected_leaf_parent_canister_id: Principal,
236}
237
238#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
245pub struct RootComponentSubtreeRemovalLeafFinalizationRequest {
246 pub operation_id: [u8; 32],
247 pub component: ComponentInstanceId,
248 pub expected_traversal_steps: u32,
249 pub expected_leaf_canister_id: Principal,
250 pub expected_leaf_parent_canister_id: Principal,
251}
252
253#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
260pub struct RootComponentSubtreeRemovalStatusRequest {
261 pub operation_id: [u8; 32],
262 pub component: ComponentInstanceId,
263}
264
265#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
272pub struct RootComponentDrainingRequest {
273 pub operation_id: [u8; 32],
274 pub component: ComponentInstanceId,
275 pub expected_registry: ComponentRegistryHead,
276}
277
278#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
285pub struct RootComponentDrainingStatusRequest {
286 pub operation_id: [u8; 32],
287 pub component: ComponentInstanceId,
288}
289
290#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
297pub struct RootComponentQuiescenceRequest {
298 pub operation_id: [u8; 32],
299 pub component: ComponentInstanceId,
300 pub expected_registry: ComponentRegistryHead,
301}
302
303#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
310pub struct RootComponentQuiescenceStatusRequest {
311 pub operation_id: [u8; 32],
312 pub component: ComponentInstanceId,
313}
314
315#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
322pub struct RootComponentDrainingAdvanceRequest {
323 pub operation_id: [u8; 32],
324 pub component: ComponentInstanceId,
325}
326
327#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
334pub struct RootComponentChildCreationRequest {
335 pub operation_id: [u8; 32],
336 pub component: ComponentInstanceId,
337}
338
339#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
346pub struct RootComponentChildInstallRequest {
347 pub operation_id: [u8; 32],
348 pub component: ComponentInstanceId,
349}
350
351#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
358pub struct RootComponentChildCommitRequest {
359 pub operation_id: [u8; 32],
360 pub component: ComponentInstanceId,
361}
362
363#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
370pub struct RootComponentChildDirectoryPreparationRequest {
371 pub operation_id: [u8; 32],
372 pub component: ComponentInstanceId,
373}
374
375#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
382pub struct RootComponentChildRuntimeActivationRequest {
383 pub operation_id: [u8; 32],
384 pub component: ComponentInstanceId,
385}
386
387#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
394pub struct RootComponentChildMembershipActivationRequest {
395 pub operation_id: [u8; 32],
396 pub component: ComponentInstanceId,
397}
398
399#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
406pub struct RootComponentCreationRequest {
407 pub operation_id: [u8; 32],
408}
409
410#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
417pub struct RootComponentInstallRequest {
418 pub operation_id: [u8; 32],
419}
420
421#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
428pub struct RootComponentCommitRequest {
429 pub operation_id: [u8; 32],
430}
431
432#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
439pub struct RootComponentDirectoryPreparationRequest {
440 pub operation_id: [u8; 32],
441}
442
443#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
450pub struct RootComponentRuntimeActivationRequest {
451 pub operation_id: [u8; 32],
452}
453
454#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
461pub struct RootComponentMembershipActivationRequest {
462 pub operation_id: [u8; 32],
463}
464
465#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
472pub enum ComponentProvisioningOrigin {
473 FleetAdministrator { caller: Principal },
474}
475
476#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
483pub enum RootComponentAllocationPhase {
484 Reserved,
485 CreationIntent,
486 Created,
487 InstallIntent,
488 Installed,
489 Verified,
490 Committed,
491}
492
493#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
500#[expect(
501 clippy::large_enum_variant,
502 reason = "wire phases retain complete inline receipts for deterministic Candid responses"
503)]
504pub enum RootComponentSubtreeRemovalPhase {
505 Fenced,
506 Traversing(RootComponentSubtreeRemovalNode),
507 LeafSelected(RootComponentSubtreeRemovalNode),
508 StopIntent(RootComponentSubtreeRemovalStopIntent),
509 Stopped(RootComponentSubtreeRemovalStoppedReceipt),
510 DeleteIntent(RootComponentSubtreeRemovalDeleteIntent),
511 Deleted(RootComponentSubtreeRemovalDeletedReceipt),
512 MembershipRemoved(RootComponentSubtreeRemovalMembershipRemovedReceipt),
513 DirectorySynchronized(RootComponentSubtreeRemovalDirectorySynchronizedReceipt),
514 Completed(RootComponentSubtreeRemovalCompletedReceipt),
515}
516
517#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
524pub struct RootComponentSubtreeRemovalNode {
525 pub canister_id: Principal,
526 pub parent_canister_id: Principal,
527 pub role: CanisterRole,
528 pub kind: ComponentChildKind,
529 pub installed_artifact_hash: [u8; 32],
530 pub status: ComponentLifecycleStatus,
531}
532
533#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
540pub struct RootComponentSubtreeRemovalStopIntent {
541 pub leaf: RootComponentSubtreeRemovalNode,
542 pub controller: Principal,
543}
544
545#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
552pub struct RootComponentSubtreeRemovalStoppedReceipt {
553 pub stop: RootComponentSubtreeRemovalStopIntent,
554 pub observed_module_hash: [u8; 32],
555}
556
557#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
564pub struct RootComponentSubtreeRemovalDeleteIntent {
565 pub stopped: RootComponentSubtreeRemovalStoppedReceipt,
566}
567
568#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
575pub struct RootComponentSubtreeRemovalDeletedReceipt {
576 pub deletion: RootComponentSubtreeRemovalDeleteIntent,
577}
578
579#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
586pub struct RootComponentSubtreeRemovalMembershipRemovedReceipt {
587 pub deleted: RootComponentSubtreeRemovalDeletedReceipt,
588 pub removed_from_registry: ComponentRegistryHead,
589 pub previous_descendant_content_hash: [u8; 32],
590 pub previous_committed_descendants: u32,
591 pub registry: ComponentRegistryHead,
592 pub descendant_content_hash: [u8; 32],
593 pub registry_encoded_bytes: u64,
594 pub reserved_descendants: u32,
595 pub committed_descendants: u32,
596 pub directory_synchronized_at_ns: u64,
597 pub directory_authority_hash: [u8; 32],
598 pub parent_role_instances: u32,
599 pub root_managed_descendants: u32,
600 pub root_known_created_component_canisters: u32,
601}
602
603#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
610pub struct RootComponentSubtreeRemovalDirectoryConvergenceEvidence {
611 pub operation_id: [u8; 32],
612 pub canister_id: Principal,
613 pub activation: ComponentRuntimeActivationEvidence,
614}
615
616#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
625pub struct RootComponentSubtreeRemovalDirectorySynchronizedReceipt {
626 pub membership_removed: RootComponentSubtreeRemovalMembershipRemovedReceipt,
627 pub covered_fleet_registry_revision: u64,
628 pub covered_fleet_registry_content_hash: [u8; 32],
629 pub covered_component_registry: ComponentRegistryHead,
630 pub covered_authority_hash: [u8; 32],
631 pub owning_component: Option<RootComponentSubtreeRemovalDirectoryConvergenceEvidence>,
632 pub parent: Option<RootComponentSubtreeRemovalDirectoryConvergenceEvidence>,
633}
634
635#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
642pub struct RootComponentSubtreeRemovalCompletedReceipt {
643 pub registry: ComponentRegistryHead,
644 pub directory_authority_hash: [u8; 32],
645}
646
647#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
654pub enum ComponentLifecycleStatus {
655 Prepared,
656 Active,
657 Draining,
658 Removed,
659}
660
661#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
668pub struct ComponentRegistryHead {
669 pub component: ComponentInstanceId,
670 pub revision: u64,
671 pub content_hash: [u8; 32],
672}
673
674#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
681pub struct ComponentRegistryPartitionRequest {
682 pub component: ComponentInstanceId,
683}
684
685#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
692pub struct ComponentRegistryPartitionResponse {
693 pub head: ComponentRegistryHead,
694 pub binding: ComponentBinding,
695 pub provisioning_origin: ComponentProvisioningOrigin,
696 pub release_set: FleetSubnetRootReleaseSet,
697 pub status: ComponentLifecycleStatus,
698 pub reserved_descendants: u32,
699 pub committed_descendants: u32,
700 pub encoded_bytes: u64,
701}
702
703#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
710pub struct ComponentDirectoryProvenance {
711 pub component: ComponentBinding,
712 pub source_fleet_subnet_root: Principal,
713 pub component_registry_revision: u64,
714 pub component_registry_content_hash: [u8; 32],
715 pub synchronized_at_ns: u64,
716}
717
718#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
725pub struct ComponentDirectoryHead {
726 pub provenance: ComponentDirectoryProvenance,
727 pub descendant_count: u32,
728}
729
730#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
737pub struct ComponentDirectoryHeadRequest {
738 pub component: ComponentInstanceId,
739}
740
741#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
748pub struct ComponentDirectoryPageCursor(pub Vec<u8>);
749
750#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
757pub struct ComponentDirectoryPageRequest {
758 pub directory: ComponentDirectoryHead,
759 pub parent_canister_id: Option<Principal>,
760 pub role: Option<CanisterRole>,
761 pub status: Option<ComponentLifecycleStatus>,
762 pub cursor: Option<ComponentDirectoryPageCursor>,
763 pub limit: u16,
764}
765
766#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
773pub struct ComponentDirectoryChildEntry {
774 pub binding: ComponentChildBinding,
775 pub kind: ComponentChildKind,
776 pub installed_artifact_hash: [u8; 32],
777 pub status: ComponentLifecycleStatus,
778}
779
780#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
787pub struct ComponentDirectoryPageResponse {
788 pub directory: ComponentDirectoryHead,
789 pub entries: Vec<ComponentDirectoryChildEntry>,
790 pub next_cursor: Option<ComponentDirectoryPageCursor>,
791}
792
793#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
800pub struct ComponentRuntimeDirectoryAuthority {
801 pub fleet: FleetDirectorySnapshot,
802 pub component: ComponentDirectoryHead,
803}
804
805#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
812pub struct ComponentRuntimeDirectoryPreparationRequest {
813 pub operation_id: [u8; 32],
814 pub authority: ComponentRuntimeDirectoryAuthority,
815}
816
817#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
824pub struct ComponentRuntimeDirectorySynchronizationRequest {
825 pub operation_id: [u8; 32],
826 pub authority: ComponentRuntimeDirectoryAuthority,
827}
828
829#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
836pub enum ComponentRuntimePhase {
837 AwaitingDirectory,
838 DirectoryPrepared,
839 Active,
840}
841
842#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
849pub struct ComponentRuntimeActivationEvidence {
850 pub directory_authority_hash: [u8; 32],
851 pub activated_at_ns: u64,
852}
853
854#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
861pub struct ComponentRuntimeActivationRequest {
862 pub operation_id: [u8; 32],
863 pub directory_authority_hash: [u8; 32],
864}
865
866#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
873pub struct ComponentRuntimeStatusResponse {
874 pub operation_id: [u8; 32],
875 pub binding: ManagedCanisterBinding,
876 pub phase: ComponentRuntimePhase,
877 pub authority: Option<ComponentRuntimeDirectoryAuthority>,
878 pub authority_hash: Option<[u8; 32]>,
879 pub activation: Option<ComponentRuntimeActivationEvidence>,
880}
881
882#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
889pub struct ComponentRuntimeDirectoryConvergenceEvidence {
890 pub operation_id: [u8; 32],
891 pub binding: ManagedCanisterBinding,
892 pub covered_authority: ComponentRuntimeDirectoryAuthority,
893 pub covered_authority_hash: [u8; 32],
894 pub activation: ComponentRuntimeActivationEvidence,
895}
896
897#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
904pub struct RootComponentCreationEvidence {
905 pub wasm_store: Principal,
906 pub payload_hash: [u8; 32],
907 pub payload_size_bytes: u64,
908 pub initial_cycles: Cycles,
909 pub controller: Principal,
910 pub canister: Option<Principal>,
911}
912
913#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
920pub struct RootComponentInstallEvidence {
921 pub raw_module_hash: [u8; 32],
922 pub chunk_hashes: Vec<Vec<u8>>,
923 pub binding: ComponentBinding,
924}
925
926#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
933pub struct RootComponentChildInstallEvidence {
934 pub raw_module_hash: [u8; 32],
935 pub chunk_hashes: Vec<Vec<u8>>,
936 pub binding: ComponentChildBinding,
937}
938
939#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
946pub struct RootComponentAllocationResponse {
947 pub operation_id: [u8; 32],
948 pub allocation_sequence: u64,
949 pub component: ComponentInstanceId,
950 pub component_spec: ComponentSpecId,
951 pub spec_hash: [u8; 32],
952 pub role: CanisterRole,
953 pub provisioning_origin: ComponentProvisioningOrigin,
954 pub release_set: FleetSubnetRootReleaseSet,
955 pub phase: RootComponentAllocationPhase,
956 pub creation: Option<RootComponentCreationEvidence>,
957 pub installation: Option<RootComponentInstallEvidence>,
958}
959
960#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
967pub struct RootComponentChildAllocationResponse {
968 pub operation_id: [u8; 32],
969 pub component: ComponentInstanceId,
970 pub parent_canister_id: Principal,
971 pub parent_role: CanisterRole,
972 pub child_role: CanisterRole,
973 pub child_kind: ComponentChildKind,
974 pub maximum_instances_per_parent: u32,
975 pub maximum_descendants: u32,
976 pub maximum_registry_bytes: u64,
977 pub reserved_against_registry: ComponentRegistryHead,
978 pub release_set: FleetSubnetRootReleaseSet,
979 pub phase: RootComponentAllocationPhase,
980 pub creation: Option<RootComponentCreationEvidence>,
981 pub installation: Option<RootComponentChildInstallEvidence>,
982}
983
984#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
991pub struct RootComponentSubtreeRemovalResponse {
992 pub operation_id: [u8; 32],
993 pub component: ComponentInstanceId,
994 pub target_canister_id: Principal,
995 pub target_parent_canister_id: Principal,
996 pub target_role: CanisterRole,
997 pub target_status: ComponentLifecycleStatus,
998 pub reserved_against_registry: ComponentRegistryHead,
999 pub maximum_completed_leaves: u32,
1000 pub completed_leaves: u32,
1001 pub traversal_steps: u32,
1002 pub phase: RootComponentSubtreeRemovalPhase,
1003}
1004
1005#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1012pub struct RootComponentDrainingResponse {
1013 pub operation_id: [u8; 32],
1014 pub component: ComponentInstanceId,
1015 pub previous_registry: ComponentRegistryHead,
1016 pub registry: ComponentRegistryHead,
1017 pub descendant_count: u32,
1018 pub descendant_content_hash: [u8; 32],
1019 pub directory_authority_hash: [u8; 32],
1020 pub started_at_ns: u64,
1021}
1022
1023#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1030pub struct RootComponentQuiescenceStopIntent {
1031 pub registry: ComponentRegistryHead,
1032 pub descendant_count: u32,
1033 pub descendant_content_hash: [u8; 32],
1034 pub canister_id: Principal,
1035 pub controller: Principal,
1036 pub expected_module_hash: [u8; 32],
1037 pub covered_fleet_registry_revision: u64,
1038 pub covered_fleet_registry_content_hash: [u8; 32],
1039 pub covered_authority_hash: [u8; 32],
1040 pub runtime_operation_id: [u8; 32],
1041 pub activation: ComponentRuntimeActivationEvidence,
1042 pub prepared_at_ns: u64,
1043}
1044
1045#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1052pub struct RootComponentQuiescentReceipt {
1053 pub stop: RootComponentQuiescenceStopIntent,
1054 pub observed_module_hash: [u8; 32],
1055 pub quiesced_at_ns: u64,
1056}
1057
1058#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1065pub enum RootComponentQuiescencePhase {
1066 StopIntent(RootComponentQuiescenceStopIntent),
1067 Quiescent(RootComponentQuiescentReceipt),
1068}
1069
1070#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1077pub struct RootComponentQuiescenceResponse {
1078 pub operation_id: [u8; 32],
1079 pub component: ComponentInstanceId,
1080 pub phase: RootComponentQuiescencePhase,
1081}
1082
1083#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1090pub struct RootComponentDrainingDescendantsEmpty {
1091 pub registry: ComponentRegistryHead,
1092 pub descendant_content_hash: [u8; 32],
1093}
1094
1095#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1102#[expect(
1103 clippy::large_enum_variant,
1104 reason = "wire result embeds the current durable subtree snapshot without a Rust-only indirection"
1105)]
1106pub enum RootComponentDrainingAdvancePhase {
1107 DescendantRemoval(RootComponentSubtreeRemovalResponse),
1108 DescendantsEmpty(RootComponentDrainingDescendantsEmpty),
1109}
1110
1111#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1118pub struct RootComponentDrainingAdvanceResponse {
1119 pub operation_id: [u8; 32],
1120 pub component: ComponentInstanceId,
1121 pub phase: RootComponentDrainingAdvancePhase,
1122}
1123
1124#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1131pub struct RootComponentChildCommitResponse {
1132 pub allocation: RootComponentChildAllocationResponse,
1133 pub registry: ComponentRegistryPartitionResponse,
1134 pub directory: ComponentDirectoryHead,
1135}
1136
1137#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1144pub struct RootComponentChildDirectoryPreparationResponse {
1145 pub committed: RootComponentChildCommitResponse,
1146 pub child: ComponentRuntimeStatusResponse,
1147 pub owning_component: ComponentRuntimeDirectoryConvergenceEvidence,
1148 pub parent: Option<ComponentRuntimeDirectoryConvergenceEvidence>,
1149}
1150
1151#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1158pub struct RootComponentChildRuntimeActivationResponse {
1159 pub committed: RootComponentChildCommitResponse,
1160 pub child: ComponentRuntimeStatusResponse,
1161}
1162
1163#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1170pub struct RootComponentChildMembershipActivationResponse {
1171 pub committed: RootComponentChildCommitResponse,
1172 pub registry: ComponentRegistryPartitionResponse,
1173 pub directory: ComponentDirectoryHead,
1174 pub child: ComponentRuntimeStatusResponse,
1175}
1176
1177#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1184pub struct RootComponentCommitResponse {
1185 pub allocation: RootComponentAllocationResponse,
1186 pub registry: ComponentRegistryPartitionResponse,
1187 pub directory: ComponentDirectoryHead,
1188}
1189
1190#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1197pub struct RootComponentDirectoryPreparationResponse {
1198 pub committed: RootComponentCommitResponse,
1199 pub target: ComponentRuntimeStatusResponse,
1200}
1201
1202#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1209pub struct RootComponentRuntimeActivationResponse {
1210 pub committed: RootComponentCommitResponse,
1211 pub target: ComponentRuntimeStatusResponse,
1212}
1213
1214#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1221pub struct RootComponentMembershipActivationResponse {
1222 pub allocation: RootComponentAllocationResponse,
1223 pub registry: ComponentRegistryPartitionResponse,
1224 pub directory: ComponentDirectoryHead,
1225 pub target: ComponentRuntimeStatusResponse,
1226}
1227
1228#[cfg(test)]
1229mod tests {
1230 use super::*;
1231 use crate::{
1232 dto::root_store::RootStoreBootstrapRequest,
1233 ids::{
1234 AppId, CanonicalNetworkId, FleetCoordinatorBinding, FleetId, FleetKey,
1235 FleetRegistryAuthority, ReleaseBuildId, ReleaseBuildNonce, ReleaseSetDigest, SubnetId,
1236 },
1237 };
1238
1239 #[test]
1240 fn component_registry_contracts_round_trip_through_candid() {
1241 let request = RootComponentRegistryPreparationRequest {
1242 store_bootstrap: RootStoreBootstrapRequest {
1243 manifest_payload_size_bytes: 128,
1244 },
1245 expected_fleet_registry: FleetRegistryVersion {
1246 authority: fleet_registry_authority(),
1247 revision: 4,
1248 content_hash: [5; 32],
1249 },
1250 };
1251 let response = RootComponentRegistryStatusResponse {
1252 fleet_subnet_root: Principal::from_slice(&[6; 29]),
1253 prepared_against_registry: request.expected_fleet_registry.clone(),
1254 release_set: FleetSubnetRootReleaseSet {
1255 release_build_id: ReleaseBuildId::from_nonce(ReleaseBuildNonce::from_random_bytes(
1256 [7; 32],
1257 )),
1258 manifest_digest: ReleaseSetDigest::from_bytes([8; 32]),
1259 },
1260 component_topology_digest: ComponentTopologyDigest::from_bytes([9; 32]),
1261 next_allocation_sequence: 1,
1262 reserved_component_instances: 0,
1263 committed_component_instances: 0,
1264 managed_descendants: 0,
1265 known_created_component_canisters: 0,
1266 encoded_bytes: 0,
1267 initial_inventory: Some(RootComponentInitialInventoryStatus {
1268 fleet_activation_operation_id: [10; 32],
1269 component_count: 0,
1270 inventory_hash: [11; 32],
1271 sealed_at_ns: 12,
1272 directories_converged: true,
1273 root_runtime_activated: true,
1274 }),
1275 };
1276 let allocation = RootComponentAllocationResponse {
1277 operation_id: [10; 32],
1278 allocation_sequence: 1,
1279 component: ComponentInstanceId::from_generated_bytes([11; 32]),
1280 component_spec: "projects".parse().expect("Component Spec ID"),
1281 spec_hash: [12; 32],
1282 role: CanisterRole::new("project_hub"),
1283 provisioning_origin: ComponentProvisioningOrigin::FleetAdministrator {
1284 caller: Principal::from_slice(&[13; 29]),
1285 },
1286 release_set: response.release_set,
1287 phase: RootComponentAllocationPhase::Reserved,
1288 creation: None,
1289 installation: None,
1290 };
1291 let created = RootComponentAllocationResponse {
1292 phase: RootComponentAllocationPhase::Created,
1293 creation: Some(RootComponentCreationEvidence {
1294 wasm_store: Principal::from_slice(&[14; 29]),
1295 payload_hash: [15; 32],
1296 payload_size_bytes: 4_096,
1297 initial_cycles: Cycles::new(5_000_000_000_000),
1298 controller: Principal::from_slice(&[6; 29]),
1299 canister: Some(Principal::from_slice(&[16; 29])),
1300 }),
1301 installation: None,
1302 ..allocation.clone()
1303 };
1304 let request_bytes = candid::encode_one(&request).expect("encode request");
1305 let response_bytes = candid::encode_one(&response).expect("encode response");
1306 let allocation_bytes = candid::encode_one(&allocation).expect("encode allocation");
1307 let created_bytes = candid::encode_one(&created).expect("encode created allocation");
1308
1309 assert_eq!(
1310 candid::decode_one::<RootComponentRegistryPreparationRequest>(&request_bytes)
1311 .expect("decode request"),
1312 request
1313 );
1314 assert_eq!(
1315 candid::decode_one::<RootComponentRegistryStatusResponse>(&response_bytes)
1316 .expect("decode response"),
1317 response
1318 );
1319 assert_eq!(
1320 candid::decode_one::<RootComponentAllocationResponse>(&allocation_bytes)
1321 .expect("decode allocation"),
1322 allocation
1323 );
1324 assert_eq!(
1325 candid::decode_one::<RootComponentAllocationResponse>(&created_bytes)
1326 .expect("decode created allocation"),
1327 created
1328 );
1329 }
1330
1331 #[test]
1332 fn component_commit_response_round_trips_through_candid() {
1333 let root = Principal::from_slice(&[6; 29]);
1334 let component = ComponentInstanceId::from_generated_bytes([11; 32]);
1335 let component_spec: ComponentSpecId = "projects".parse().expect("Component Spec ID");
1336 let release_set = FleetSubnetRootReleaseSet {
1337 release_build_id: ReleaseBuildId::from_nonce(ReleaseBuildNonce::from_random_bytes(
1338 [7; 32],
1339 )),
1340 manifest_digest: ReleaseSetDigest::from_bytes([8; 32]),
1341 };
1342 let provisioning_origin = ComponentProvisioningOrigin::FleetAdministrator {
1343 caller: Principal::from_slice(&[13; 29]),
1344 };
1345 let binding = ComponentBinding {
1346 authority: fleet_registry_authority(),
1347 component,
1348 component_spec: component_spec.clone(),
1349 spec_hash: [12; 32],
1350 role: CanisterRole::new("project_hub"),
1351 placement_subnet: SubnetId::from_principal(Principal::from_slice(&[17; 29])),
1352 fleet_subnet_root: root,
1353 canister_id: Principal::from_slice(&[16; 29]),
1354 };
1355 let head = ComponentRegistryHead {
1356 component,
1357 revision: 1,
1358 content_hash: [18; 32],
1359 };
1360 let committed = RootComponentCommitResponse {
1361 allocation: RootComponentAllocationResponse {
1362 operation_id: [10; 32],
1363 allocation_sequence: 1,
1364 component,
1365 component_spec,
1366 spec_hash: binding.spec_hash,
1367 role: binding.role.clone(),
1368 provisioning_origin: provisioning_origin.clone(),
1369 release_set,
1370 phase: RootComponentAllocationPhase::Committed,
1371 creation: Some(RootComponentCreationEvidence {
1372 wasm_store: Principal::from_slice(&[14; 29]),
1373 payload_hash: [15; 32],
1374 payload_size_bytes: 4_096,
1375 initial_cycles: Cycles::new(5_000_000_000_000),
1376 controller: root,
1377 canister: Some(binding.canister_id),
1378 }),
1379 installation: Some(RootComponentInstallEvidence {
1380 raw_module_hash: [20; 32],
1381 chunk_hashes: vec![vec![21; 32]],
1382 binding: binding.clone(),
1383 }),
1384 },
1385 registry: ComponentRegistryPartitionResponse {
1386 head: head.clone(),
1387 binding: binding.clone(),
1388 provisioning_origin,
1389 release_set,
1390 status: ComponentLifecycleStatus::Prepared,
1391 reserved_descendants: 0,
1392 committed_descendants: 0,
1393 encoded_bytes: 2_048,
1394 },
1395 directory: ComponentDirectoryHead {
1396 provenance: ComponentDirectoryProvenance {
1397 component: binding,
1398 source_fleet_subnet_root: root,
1399 component_registry_revision: head.revision,
1400 component_registry_content_hash: head.content_hash,
1401 synchronized_at_ns: 19,
1402 },
1403 descendant_count: 0,
1404 },
1405 };
1406 let committed_bytes = candid::encode_one(&committed).expect("encode committed allocation");
1407
1408 assert_eq!(
1409 candid::decode_one::<RootComponentCommitResponse>(&committed_bytes)
1410 .expect("decode committed allocation"),
1411 committed
1412 );
1413 }
1414
1415 #[test]
1416 fn component_directory_page_contracts_round_trip_through_candid() {
1417 let root = Principal::from_slice(&[6; 29]);
1418 let component = ComponentInstanceId::from_generated_bytes([11; 32]);
1419 let binding = ComponentBinding {
1420 authority: fleet_registry_authority(),
1421 component,
1422 component_spec: "projects".parse().expect("Component Spec ID"),
1423 spec_hash: [12; 32],
1424 role: CanisterRole::new("project_hub"),
1425 placement_subnet: SubnetId::from_principal(Principal::from_slice(&[17; 29])),
1426 fleet_subnet_root: root,
1427 canister_id: Principal::from_slice(&[16; 29]),
1428 };
1429 let directory = ComponentDirectoryHead {
1430 provenance: ComponentDirectoryProvenance {
1431 component: binding.clone(),
1432 source_fleet_subnet_root: root,
1433 component_registry_revision: 3,
1434 component_registry_content_hash: [18; 32],
1435 synchronized_at_ns: 19,
1436 },
1437 descendant_count: 1,
1438 };
1439 let request = ComponentDirectoryPageRequest {
1440 directory: directory.clone(),
1441 parent_canister_id: Some(binding.canister_id),
1442 role: Some(CanisterRole::new("project_instance")),
1443 status: Some(ComponentLifecycleStatus::Active),
1444 cursor: Some(ComponentDirectoryPageCursor(vec![20; 64])),
1445 limit: 50,
1446 };
1447 let response = ComponentDirectoryPageResponse {
1448 directory,
1449 entries: vec![ComponentDirectoryChildEntry {
1450 binding: ComponentChildBinding {
1451 component: binding.clone(),
1452 parent_canister_id: binding.canister_id,
1453 role: CanisterRole::new("project_instance"),
1454 canister_id: Principal::from_slice(&[21; 29]),
1455 },
1456 kind: ComponentChildKind::Instance,
1457 installed_artifact_hash: [22; 32],
1458 status: ComponentLifecycleStatus::Active,
1459 }],
1460 next_cursor: Some(ComponentDirectoryPageCursor(vec![23; 64])),
1461 };
1462 let request_bytes = candid::encode_one(&request).expect("encode Directory page request");
1463 let response_bytes = candid::encode_one(&response).expect("encode Directory page response");
1464
1465 assert_eq!(
1466 candid::decode_one::<ComponentDirectoryPageRequest>(&request_bytes)
1467 .expect("decode Directory page request"),
1468 request
1469 );
1470 assert_eq!(
1471 candid::decode_one::<ComponentDirectoryPageResponse>(&response_bytes)
1472 .expect("decode Directory page response"),
1473 response
1474 );
1475 }
1476
1477 fn fleet_registry_authority() -> FleetRegistryAuthority {
1478 FleetRegistryAuthority {
1479 binding: FleetCoordinatorBinding {
1480 fleet: crate::ids::FleetBinding {
1481 fleet: FleetKey {
1482 canonical_network_id: CanonicalNetworkId::ic_mainnet(),
1483 fleet_id: FleetId::from_generated_bytes([1; 32]),
1484 },
1485 app: AppId::from("toko"),
1486 },
1487 coordinator_subnet: SubnetId::from_principal(Principal::from_slice(&[2; 29])),
1488 coordinator: Principal::from_slice(&[3; 29]),
1489 },
1490 epoch: 1,
1491 }
1492 }
1493
1494 #[test]
1495 fn component_creation_request_round_trips_through_candid() {
1496 let request = RootComponentCreationRequest {
1497 operation_id: [10; 32],
1498 };
1499 let bytes = candid::encode_one(request).expect("encode creation request");
1500
1501 assert_eq!(
1502 candid::decode_one::<RootComponentCreationRequest>(&bytes)
1503 .expect("decode creation request"),
1504 request
1505 );
1506 }
1507
1508 #[test]
1509 #[expect(
1510 clippy::too_many_lines,
1511 reason = "one Candid contract test covers every subtree-removal phase receipt"
1512 )]
1513 fn component_subtree_removal_contracts_round_trip_through_candid() {
1514 let component = ComponentInstanceId::from_generated_bytes([41; 32]);
1515 let registry = ComponentRegistryHead {
1516 component,
1517 revision: 7,
1518 content_hash: [42; 32],
1519 };
1520 let request = RootComponentSubtreeRemovalRequest {
1521 operation_id: [43; 32],
1522 component,
1523 target_canister_id: Principal::from_slice(&[44; 29]),
1524 expected_registry: registry.clone(),
1525 };
1526 let status_request = RootComponentSubtreeRemovalStatusRequest {
1527 operation_id: request.operation_id,
1528 component,
1529 };
1530 let advance_request = RootComponentSubtreeRemovalAdvanceRequest {
1531 operation_id: request.operation_id,
1532 component,
1533 expected_traversal_steps: 1,
1534 };
1535 let stop_request = RootComponentSubtreeRemovalStopPreparationRequest {
1536 operation_id: request.operation_id,
1537 component,
1538 expected_traversal_steps: 2,
1539 expected_leaf_canister_id: Principal::from_slice(&[46; 29]),
1540 expected_leaf_parent_canister_id: request.target_canister_id,
1541 };
1542 let stopped = RootComponentSubtreeRemovalStoppedReceipt {
1543 observed_module_hash: [49; 32],
1544 stop: RootComponentSubtreeRemovalStopIntent {
1545 controller: Principal::from_slice(&[48; 29]),
1546 leaf: RootComponentSubtreeRemovalNode {
1547 canister_id: Principal::from_slice(&[46; 29]),
1548 parent_canister_id: request.target_canister_id,
1549 role: CanisterRole::new("project_ledger"),
1550 kind: ComponentChildKind::Singleton,
1551 installed_artifact_hash: [47; 32],
1552 status: ComponentLifecycleStatus::Active,
1553 },
1554 },
1555 };
1556 let response = RootComponentSubtreeRemovalResponse {
1557 operation_id: request.operation_id,
1558 component,
1559 target_canister_id: request.target_canister_id,
1560 target_parent_canister_id: Principal::from_slice(&[45; 29]),
1561 target_role: CanisterRole::new("project_instance"),
1562 target_status: ComponentLifecycleStatus::Active,
1563 reserved_against_registry: registry,
1564 maximum_completed_leaves: 4,
1565 completed_leaves: 1,
1566 traversal_steps: 2,
1567 phase: RootComponentSubtreeRemovalPhase::DirectorySynchronized(
1568 RootComponentSubtreeRemovalDirectorySynchronizedReceipt {
1569 membership_removed: RootComponentSubtreeRemovalMembershipRemovedReceipt {
1570 deleted: RootComponentSubtreeRemovalDeletedReceipt {
1571 deletion: RootComponentSubtreeRemovalDeleteIntent { stopped },
1572 },
1573 removed_from_registry: ComponentRegistryHead {
1574 component,
1575 revision: 8,
1576 content_hash: [50; 32],
1577 },
1578 previous_descendant_content_hash: [51; 32],
1579 previous_committed_descendants: 4,
1580 registry: ComponentRegistryHead {
1581 component,
1582 revision: 9,
1583 content_hash: [52; 32],
1584 },
1585 descendant_content_hash: [53; 32],
1586 registry_encoded_bytes: 4_096,
1587 reserved_descendants: 1,
1588 committed_descendants: 3,
1589 directory_synchronized_at_ns: 54,
1590 directory_authority_hash: [55; 32],
1591 parent_role_instances: 0,
1592 root_managed_descendants: 4,
1593 root_known_created_component_canisters: 4,
1594 },
1595 covered_fleet_registry_revision: 6,
1596 covered_fleet_registry_content_hash: [56; 32],
1597 covered_component_registry: ComponentRegistryHead {
1598 component,
1599 revision: 9,
1600 content_hash: [52; 32],
1601 },
1602 covered_authority_hash: [55; 32],
1603 owning_component: Some(
1604 RootComponentSubtreeRemovalDirectoryConvergenceEvidence {
1605 operation_id: [57; 32],
1606 canister_id: Principal::from_slice(&[58; 29]),
1607 activation: ComponentRuntimeActivationEvidence {
1608 directory_authority_hash: [59; 32],
1609 activated_at_ns: 60,
1610 },
1611 },
1612 ),
1613 parent: Some(RootComponentSubtreeRemovalDirectoryConvergenceEvidence {
1614 operation_id: [61; 32],
1615 canister_id: request.target_canister_id,
1616 activation: ComponentRuntimeActivationEvidence {
1617 directory_authority_hash: [62; 32],
1618 activated_at_ns: 63,
1619 },
1620 }),
1621 },
1622 ),
1623 };
1624
1625 let request_bytes = candid::encode_one(&request).expect("encode subtree removal request");
1626 let advance_bytes =
1627 candid::encode_one(advance_request).expect("encode subtree removal advance request");
1628 let stop_bytes =
1629 candid::encode_one(stop_request).expect("encode subtree removal stop request");
1630 let status_bytes =
1631 candid::encode_one(status_request).expect("encode subtree removal status request");
1632 let response_bytes =
1633 candid::encode_one(&response).expect("encode subtree removal response");
1634
1635 assert_eq!(
1636 candid::decode_one::<RootComponentSubtreeRemovalRequest>(&request_bytes)
1637 .expect("decode subtree removal request"),
1638 request
1639 );
1640 assert_eq!(
1641 candid::decode_one::<RootComponentSubtreeRemovalAdvanceRequest>(&advance_bytes)
1642 .expect("decode subtree removal advance request"),
1643 advance_request
1644 );
1645 assert_eq!(
1646 candid::decode_one::<RootComponentSubtreeRemovalStopPreparationRequest>(&stop_bytes)
1647 .expect("decode subtree removal stop request"),
1648 stop_request
1649 );
1650 assert_eq!(
1651 candid::decode_one::<RootComponentSubtreeRemovalStatusRequest>(&status_bytes)
1652 .expect("decode subtree removal status request"),
1653 status_request
1654 );
1655 assert_eq!(
1656 candid::decode_one::<RootComponentSubtreeRemovalResponse>(&response_bytes)
1657 .expect("decode subtree removal response"),
1658 response
1659 );
1660
1661 let mut quiescent_owner_response = response;
1662 let RootComponentSubtreeRemovalPhase::DirectorySynchronized(receipt) =
1663 &mut quiescent_owner_response.phase
1664 else {
1665 panic!("Directory-synchronized response");
1666 };
1667 receipt.owning_component = None;
1668 let quiescent_owner_bytes = candid::encode_one(&quiescent_owner_response)
1669 .expect("encode quiescent-owner subtree response");
1670 assert_eq!(
1671 candid::decode_one::<RootComponentSubtreeRemovalResponse>(&quiescent_owner_bytes)
1672 .expect("decode quiescent-owner subtree response"),
1673 quiescent_owner_response
1674 );
1675 }
1676
1677 #[test]
1678 fn component_draining_contracts_round_trip_through_candid() {
1679 let component = ComponentInstanceId::from_generated_bytes([60; 32]);
1680 let previous_registry = ComponentRegistryHead {
1681 component,
1682 revision: 7,
1683 content_hash: [61; 32],
1684 };
1685 let request = RootComponentDrainingRequest {
1686 operation_id: [62; 32],
1687 component,
1688 expected_registry: previous_registry.clone(),
1689 };
1690 let status_request = RootComponentDrainingStatusRequest {
1691 operation_id: request.operation_id,
1692 component,
1693 };
1694 let response = RootComponentDrainingResponse {
1695 operation_id: request.operation_id,
1696 component,
1697 previous_registry,
1698 registry: ComponentRegistryHead {
1699 component,
1700 revision: 8,
1701 content_hash: [63; 32],
1702 },
1703 descendant_count: 20_000,
1704 descendant_content_hash: [64; 32],
1705 directory_authority_hash: [65; 32],
1706 started_at_ns: 66,
1707 };
1708
1709 let request_bytes =
1710 candid::encode_one(&request).expect("encode Component draining request");
1711 let status_bytes =
1712 candid::encode_one(status_request).expect("encode Component draining status request");
1713 let response_bytes =
1714 candid::encode_one(&response).expect("encode Component draining response");
1715
1716 assert_eq!(
1717 candid::decode_one::<RootComponentDrainingRequest>(&request_bytes)
1718 .expect("decode Component draining request"),
1719 request
1720 );
1721 assert_eq!(
1722 candid::decode_one::<RootComponentDrainingStatusRequest>(&status_bytes)
1723 .expect("decode Component draining status request"),
1724 status_request
1725 );
1726 assert_eq!(
1727 candid::decode_one::<RootComponentDrainingResponse>(&response_bytes)
1728 .expect("decode Component draining response"),
1729 response
1730 );
1731 }
1732
1733 #[test]
1734 fn component_quiescence_contracts_round_trip_through_candid() {
1735 let component = ComponentInstanceId::from_generated_bytes([67; 32]);
1736 let registry = ComponentRegistryHead {
1737 component,
1738 revision: 9,
1739 content_hash: [68; 32],
1740 };
1741 let request = RootComponentQuiescenceRequest {
1742 operation_id: [69; 32],
1743 component,
1744 expected_registry: registry.clone(),
1745 };
1746 let status_request = RootComponentQuiescenceStatusRequest {
1747 operation_id: request.operation_id,
1748 component,
1749 };
1750 let stop = RootComponentQuiescenceStopIntent {
1751 registry,
1752 descendant_count: 20_000,
1753 descendant_content_hash: [70; 32],
1754 canister_id: Principal::from_slice(&[71; 29]),
1755 controller: Principal::from_slice(&[72; 29]),
1756 expected_module_hash: [73; 32],
1757 covered_fleet_registry_revision: 10,
1758 covered_fleet_registry_content_hash: [74; 32],
1759 covered_authority_hash: [75; 32],
1760 runtime_operation_id: [76; 32],
1761 activation: ComponentRuntimeActivationEvidence {
1762 directory_authority_hash: [77; 32],
1763 activated_at_ns: 78,
1764 },
1765 prepared_at_ns: 79,
1766 };
1767 let response = RootComponentQuiescenceResponse {
1768 operation_id: request.operation_id,
1769 component,
1770 phase: RootComponentQuiescencePhase::Quiescent(RootComponentQuiescentReceipt {
1771 stop,
1772 observed_module_hash: [73; 32],
1773 quiesced_at_ns: 80,
1774 }),
1775 };
1776
1777 let request_bytes = candid::encode_one(&request).expect("encode quiescence request");
1778 let status_bytes =
1779 candid::encode_one(status_request).expect("encode quiescence status request");
1780 let response_bytes = candid::encode_one(&response).expect("encode quiescence response");
1781 assert_eq!(
1782 candid::decode_one::<RootComponentQuiescenceRequest>(&request_bytes)
1783 .expect("decode quiescence request"),
1784 request
1785 );
1786 assert_eq!(
1787 candid::decode_one::<RootComponentQuiescenceStatusRequest>(&status_bytes)
1788 .expect("decode quiescence status request"),
1789 status_request
1790 );
1791 assert_eq!(
1792 candid::decode_one::<RootComponentQuiescenceResponse>(&response_bytes)
1793 .expect("decode quiescence response"),
1794 response
1795 );
1796 }
1797
1798 #[test]
1799 fn component_draining_advance_contracts_round_trip_through_candid() {
1800 let component = ComponentInstanceId::from_generated_bytes([81; 32]);
1801 let registry = ComponentRegistryHead {
1802 component,
1803 revision: 12,
1804 content_hash: [82; 32],
1805 };
1806 let request = RootComponentDrainingAdvanceRequest {
1807 operation_id: [83; 32],
1808 component,
1809 };
1810 let descendant_removal = RootComponentDrainingAdvanceResponse {
1811 operation_id: request.operation_id,
1812 component,
1813 phase: RootComponentDrainingAdvancePhase::DescendantRemoval(
1814 RootComponentSubtreeRemovalResponse {
1815 operation_id: [84; 32],
1816 component,
1817 target_canister_id: Principal::from_slice(&[85; 29]),
1818 target_parent_canister_id: Principal::from_slice(&[86; 29]),
1819 target_role: CanisterRole::new("project_instance"),
1820 target_status: ComponentLifecycleStatus::Active,
1821 reserved_against_registry: registry.clone(),
1822 maximum_completed_leaves: 20_000,
1823 completed_leaves: 0,
1824 traversal_steps: 0,
1825 phase: RootComponentSubtreeRemovalPhase::Fenced,
1826 },
1827 ),
1828 };
1829 let descendants_empty = RootComponentDrainingAdvanceResponse {
1830 operation_id: request.operation_id,
1831 component,
1832 phase: RootComponentDrainingAdvancePhase::DescendantsEmpty(
1833 RootComponentDrainingDescendantsEmpty {
1834 registry,
1835 descendant_content_hash: [87; 32],
1836 },
1837 ),
1838 };
1839
1840 let request_bytes =
1841 candid::encode_one(request).expect("encode Component draining advance request");
1842 let removal_bytes = candid::encode_one(&descendant_removal)
1843 .expect("encode Component draining removal response");
1844 let empty_bytes = candid::encode_one(&descendants_empty)
1845 .expect("encode Component draining empty response");
1846
1847 assert_eq!(
1848 candid::decode_one::<RootComponentDrainingAdvanceRequest>(&request_bytes)
1849 .expect("decode Component draining advance request"),
1850 request
1851 );
1852 assert_eq!(
1853 candid::decode_one::<RootComponentDrainingAdvanceResponse>(&removal_bytes)
1854 .expect("decode Component draining removal response"),
1855 descendant_removal
1856 );
1857 assert_eq!(
1858 candid::decode_one::<RootComponentDrainingAdvanceResponse>(&empty_bytes)
1859 .expect("decode Component draining empty response"),
1860 descendants_empty
1861 );
1862 }
1863
1864 #[test]
1865 fn component_subtree_removal_deletion_requests_round_trip_through_candid() {
1866 let prepare = RootComponentSubtreeRemovalDeletePreparationRequest {
1867 operation_id: [50; 32],
1868 component: ComponentInstanceId::from_generated_bytes([51; 32]),
1869 expected_traversal_steps: 3,
1870 expected_leaf_canister_id: Principal::from_slice(&[52; 29]),
1871 expected_leaf_parent_canister_id: Principal::from_slice(&[53; 29]),
1872 };
1873 let request = RootComponentSubtreeRemovalDeleteRequest {
1874 operation_id: prepare.operation_id,
1875 component: prepare.component,
1876 expected_traversal_steps: prepare.expected_traversal_steps,
1877 expected_leaf_canister_id: prepare.expected_leaf_canister_id,
1878 expected_leaf_parent_canister_id: prepare.expected_leaf_parent_canister_id,
1879 };
1880 let membership_request = RootComponentSubtreeRemovalMembershipRemovalRequest {
1881 operation_id: prepare.operation_id,
1882 component: prepare.component,
1883 expected_traversal_steps: prepare.expected_traversal_steps,
1884 expected_leaf_canister_id: prepare.expected_leaf_canister_id,
1885 expected_leaf_parent_canister_id: prepare.expected_leaf_parent_canister_id,
1886 };
1887 let directory_request = RootComponentSubtreeRemovalDirectorySynchronizationRequest {
1888 operation_id: prepare.operation_id,
1889 component: prepare.component,
1890 expected_traversal_steps: prepare.expected_traversal_steps,
1891 expected_leaf_canister_id: prepare.expected_leaf_canister_id,
1892 expected_leaf_parent_canister_id: prepare.expected_leaf_parent_canister_id,
1893 };
1894 let finalization_request = RootComponentSubtreeRemovalLeafFinalizationRequest {
1895 operation_id: prepare.operation_id,
1896 component: prepare.component,
1897 expected_traversal_steps: prepare.expected_traversal_steps,
1898 expected_leaf_canister_id: prepare.expected_leaf_canister_id,
1899 expected_leaf_parent_canister_id: prepare.expected_leaf_parent_canister_id,
1900 };
1901 let prepare_bytes = candid::encode_one(prepare)
1902 .expect("encode subtree removal deletion preparation request");
1903 let request_bytes =
1904 candid::encode_one(request).expect("encode subtree removal deletion request");
1905 let membership_request_bytes = candid::encode_one(membership_request)
1906 .expect("encode subtree removal membership-removal request");
1907 let directory_request_bytes = candid::encode_one(directory_request)
1908 .expect("encode subtree removal Directory synchronization request");
1909 let finalization_request_bytes = candid::encode_one(finalization_request)
1910 .expect("encode subtree removal leaf-finalization request");
1911
1912 assert_eq!(
1913 candid::decode_one::<RootComponentSubtreeRemovalDeletePreparationRequest>(
1914 &prepare_bytes
1915 )
1916 .expect("decode subtree removal deletion preparation request"),
1917 prepare
1918 );
1919 assert_eq!(
1920 candid::decode_one::<RootComponentSubtreeRemovalDeleteRequest>(&request_bytes)
1921 .expect("decode subtree removal deletion request"),
1922 request
1923 );
1924 assert_eq!(
1925 candid::decode_one::<RootComponentSubtreeRemovalMembershipRemovalRequest>(
1926 &membership_request_bytes
1927 )
1928 .expect("decode subtree removal membership-removal request"),
1929 membership_request
1930 );
1931 assert_eq!(
1932 candid::decode_one::<RootComponentSubtreeRemovalDirectorySynchronizationRequest>(
1933 &directory_request_bytes
1934 )
1935 .expect("decode subtree removal Directory synchronization request"),
1936 directory_request
1937 );
1938 assert_eq!(
1939 candid::decode_one::<RootComponentSubtreeRemovalLeafFinalizationRequest>(
1940 &finalization_request_bytes
1941 )
1942 .expect("decode subtree removal leaf-finalization request"),
1943 finalization_request
1944 );
1945 }
1946
1947 #[test]
1948 fn component_subtree_removal_stop_request_round_trips_through_candid() {
1949 let request = RootComponentSubtreeRemovalStopRequest {
1950 operation_id: [50; 32],
1951 component: ComponentInstanceId::from_generated_bytes([51; 32]),
1952 expected_traversal_steps: 3,
1953 expected_leaf_canister_id: Principal::from_slice(&[52; 29]),
1954 expected_leaf_parent_canister_id: Principal::from_slice(&[53; 29]),
1955 };
1956 let bytes =
1957 candid::encode_one(request).expect("encode subtree removal stop execution request");
1958
1959 assert_eq!(
1960 candid::decode_one::<RootComponentSubtreeRemovalStopRequest>(&bytes)
1961 .expect("decode subtree removal stop execution request"),
1962 request
1963 );
1964 }
1965
1966 #[test]
1967 #[expect(
1968 clippy::too_many_lines,
1969 reason = "one round-trip test keeps the complete child lifecycle boundary coherent"
1970 )]
1971 fn component_child_lifecycle_contracts_round_trip_through_candid() {
1972 let component = ComponentInstanceId::from_generated_bytes([11; 32]);
1973 let registry = ComponentRegistryHead {
1974 component,
1975 revision: 2,
1976 content_hash: [12; 32],
1977 };
1978 let request = RootComponentChildAllocationRequest {
1979 operation_id: [13; 32],
1980 component,
1981 expected_registry: registry.clone(),
1982 child_role: CanisterRole::new("project_instance"),
1983 };
1984 let status_request = RootComponentChildAllocationStatusRequest {
1985 operation_id: request.operation_id,
1986 component,
1987 };
1988 let creation_request = RootComponentChildCreationRequest {
1989 operation_id: request.operation_id,
1990 component,
1991 };
1992 let install_request = RootComponentChildInstallRequest {
1993 operation_id: request.operation_id,
1994 component,
1995 };
1996 let commit_request = RootComponentChildCommitRequest {
1997 operation_id: request.operation_id,
1998 component,
1999 };
2000 let directory_request = RootComponentChildDirectoryPreparationRequest {
2001 operation_id: request.operation_id,
2002 component,
2003 };
2004 let activation_request = RootComponentChildRuntimeActivationRequest {
2005 operation_id: request.operation_id,
2006 component,
2007 };
2008 let membership_request = RootComponentChildMembershipActivationRequest {
2009 operation_id: request.operation_id,
2010 component,
2011 };
2012 let root = Principal::from_slice(&[17; 29]);
2013 let parent = Principal::from_slice(&[14; 29]);
2014 let child = Principal::from_slice(&[18; 29]);
2015 let child_binding = ComponentChildBinding {
2016 component: ComponentBinding {
2017 authority: fleet_registry_authority(),
2018 component,
2019 component_spec: "projects".parse().expect("Component Spec"),
2020 spec_hash: [19; 32],
2021 role: CanisterRole::new("project_hub"),
2022 placement_subnet: SubnetId::from_principal(Principal::from_slice(&[20; 29])),
2023 fleet_subnet_root: root,
2024 canister_id: parent,
2025 },
2026 parent_canister_id: parent,
2027 role: request.child_role.clone(),
2028 canister_id: child,
2029 };
2030 let response = RootComponentChildAllocationResponse {
2031 operation_id: request.operation_id,
2032 component,
2033 parent_canister_id: parent,
2034 parent_role: CanisterRole::new("project_hub"),
2035 child_role: request.child_role.clone(),
2036 child_kind: ComponentChildKind::Instance,
2037 maximum_instances_per_parent: 10_000,
2038 maximum_descendants: 20_000,
2039 maximum_registry_bytes: 16_777_216,
2040 reserved_against_registry: registry,
2041 release_set: FleetSubnetRootReleaseSet {
2042 release_build_id: ReleaseBuildId::from_nonce(ReleaseBuildNonce::from_random_bytes(
2043 [15; 32],
2044 )),
2045 manifest_digest: ReleaseSetDigest::from_bytes([16; 32]),
2046 },
2047 phase: RootComponentAllocationPhase::Verified,
2048 creation: Some(RootComponentCreationEvidence {
2049 wasm_store: Principal::from_slice(&[21; 29]),
2050 payload_hash: [22; 32],
2051 payload_size_bytes: 4_096,
2052 initial_cycles: Cycles::new(5_000_000_000_000),
2053 controller: root,
2054 canister: Some(child),
2055 }),
2056 installation: Some(RootComponentChildInstallEvidence {
2057 raw_module_hash: [23; 32],
2058 chunk_hashes: vec![vec![24; 32]],
2059 binding: child_binding.clone(),
2060 }),
2061 };
2062 let commit_response = RootComponentChildCommitResponse {
2063 allocation: response.clone(),
2064 registry: ComponentRegistryPartitionResponse {
2065 head: ComponentRegistryHead {
2066 component,
2067 revision: 3,
2068 content_hash: [25; 32],
2069 },
2070 binding: child_binding.component.clone(),
2071 provisioning_origin: ComponentProvisioningOrigin::FleetAdministrator {
2072 caller: Principal::from_slice(&[26; 29]),
2073 },
2074 release_set: response.release_set,
2075 status: ComponentLifecycleStatus::Active,
2076 reserved_descendants: 0,
2077 committed_descendants: 1,
2078 encoded_bytes: 8_192,
2079 },
2080 directory: ComponentDirectoryHead {
2081 provenance: ComponentDirectoryProvenance {
2082 component: child_binding.component.clone(),
2083 source_fleet_subnet_root: root,
2084 component_registry_revision: 3,
2085 component_registry_content_hash: [25; 32],
2086 synchronized_at_ns: 27,
2087 },
2088 descendant_count: 1,
2089 },
2090 };
2091 let runtime_authority = ComponentRuntimeDirectoryAuthority {
2092 fleet: FleetDirectorySnapshot {
2093 provenance: crate::dto::fleet_registry::FleetDirectoryProvenance {
2094 registry: FleetRegistryVersion {
2095 authority: fleet_registry_authority(),
2096 revision: 4,
2097 content_hash: [28; 32],
2098 },
2099 source_fleet_subnet_root: root,
2100 },
2101 fleet_subnet_roots: vec![
2102 crate::dto::fleet_registry::FleetSubnetRootDirectoryEntry {
2103 placement_subnet: commit_response.registry.binding.placement_subnet,
2104 fleet_subnet_root: root,
2105 status: crate::dto::fleet_registry::FleetSubnetRootStatus::Active,
2106 },
2107 ],
2108 },
2109 component: commit_response.directory.clone(),
2110 };
2111 let activation = ComponentRuntimeActivationEvidence {
2112 directory_authority_hash: [29; 32],
2113 activated_at_ns: 30,
2114 };
2115 let directory_response = RootComponentChildDirectoryPreparationResponse {
2116 committed: commit_response.clone(),
2117 child: ComponentRuntimeStatusResponse {
2118 operation_id: request.operation_id,
2119 binding: ManagedCanisterBinding::ComponentChild(child_binding.clone()),
2120 phase: ComponentRuntimePhase::DirectoryPrepared,
2121 authority: Some(runtime_authority.clone()),
2122 authority_hash: Some([31; 32]),
2123 activation: None,
2124 },
2125 owning_component: ComponentRuntimeDirectoryConvergenceEvidence {
2126 operation_id: [32; 32],
2127 binding: ManagedCanisterBinding::Component(child_binding.component.clone()),
2128 covered_authority: runtime_authority.clone(),
2129 covered_authority_hash: [31; 32],
2130 activation,
2131 },
2132 parent: None,
2133 };
2134 let activation_response = RootComponentChildRuntimeActivationResponse {
2135 committed: commit_response.clone(),
2136 child: ComponentRuntimeStatusResponse {
2137 operation_id: request.operation_id,
2138 binding: ManagedCanisterBinding::ComponentChild(child_binding.clone()),
2139 phase: ComponentRuntimePhase::Active,
2140 authority: Some(runtime_authority.clone()),
2141 authority_hash: Some([31; 32]),
2142 activation: Some(ComponentRuntimeActivationEvidence {
2143 directory_authority_hash: [31; 32],
2144 activated_at_ns: 33,
2145 }),
2146 },
2147 };
2148 let active_directory = ComponentDirectoryHead {
2149 provenance: ComponentDirectoryProvenance {
2150 component: child_binding.component.clone(),
2151 source_fleet_subnet_root: root,
2152 component_registry_revision: 4,
2153 component_registry_content_hash: [34; 32],
2154 synchronized_at_ns: 35,
2155 },
2156 descendant_count: 1,
2157 };
2158 let active_authority = ComponentRuntimeDirectoryAuthority {
2159 fleet: runtime_authority.fleet,
2160 component: active_directory.clone(),
2161 };
2162 let membership_response = RootComponentChildMembershipActivationResponse {
2163 committed: commit_response.clone(),
2164 registry: ComponentRegistryPartitionResponse {
2165 head: ComponentRegistryHead {
2166 component,
2167 revision: 4,
2168 content_hash: [34; 32],
2169 },
2170 binding: child_binding.component.clone(),
2171 provisioning_origin: commit_response.registry.provisioning_origin.clone(),
2172 release_set: commit_response.registry.release_set,
2173 status: ComponentLifecycleStatus::Active,
2174 reserved_descendants: 0,
2175 committed_descendants: 1,
2176 encoded_bytes: 8_256,
2177 },
2178 directory: active_directory,
2179 child: ComponentRuntimeStatusResponse {
2180 operation_id: request.operation_id,
2181 binding: ManagedCanisterBinding::ComponentChild(child_binding),
2182 phase: ComponentRuntimePhase::Active,
2183 authority: Some(active_authority),
2184 authority_hash: Some([36; 32]),
2185 activation: Some(ComponentRuntimeActivationEvidence {
2186 directory_authority_hash: [31; 32],
2187 activated_at_ns: 33,
2188 }),
2189 },
2190 };
2191
2192 let request_bytes = candid::encode_one(&request).expect("encode child reservation");
2193 let status_bytes =
2194 candid::encode_one(status_request).expect("encode child reservation status");
2195 let creation_bytes =
2196 candid::encode_one(creation_request).expect("encode child creation request");
2197 let install_bytes =
2198 candid::encode_one(install_request).expect("encode child install request");
2199 let response_bytes = candid::encode_one(&response).expect("encode child response");
2200 let commit_request_bytes =
2201 candid::encode_one(commit_request).expect("encode child commit request");
2202 let directory_request_bytes =
2203 candid::encode_one(directory_request).expect("encode child Directory request");
2204 let activation_request_bytes =
2205 candid::encode_one(activation_request).expect("encode child activation request");
2206 let membership_request_bytes =
2207 candid::encode_one(membership_request).expect("encode child membership request");
2208 let commit_response_bytes =
2209 candid::encode_one(&commit_response).expect("encode child commit response");
2210 let directory_response_bytes =
2211 candid::encode_one(&directory_response).expect("encode child Directory response");
2212 let activation_response_bytes =
2213 candid::encode_one(&activation_response).expect("encode child activation response");
2214 let membership_response_bytes =
2215 candid::encode_one(&membership_response).expect("encode child membership response");
2216
2217 assert_eq!(
2218 candid::decode_one::<RootComponentChildAllocationRequest>(&request_bytes)
2219 .expect("decode child reservation"),
2220 request
2221 );
2222 assert_eq!(
2223 candid::decode_one::<RootComponentChildAllocationStatusRequest>(&status_bytes)
2224 .expect("decode child reservation status"),
2225 status_request
2226 );
2227 assert_eq!(
2228 candid::decode_one::<RootComponentChildCreationRequest>(&creation_bytes)
2229 .expect("decode child creation request"),
2230 creation_request
2231 );
2232 assert_eq!(
2233 candid::decode_one::<RootComponentChildInstallRequest>(&install_bytes)
2234 .expect("decode child install request"),
2235 install_request
2236 );
2237 assert_eq!(
2238 candid::decode_one::<RootComponentChildAllocationResponse>(&response_bytes)
2239 .expect("decode child response"),
2240 response
2241 );
2242 assert_eq!(
2243 candid::decode_one::<RootComponentChildCommitRequest>(&commit_request_bytes)
2244 .expect("decode child commit request"),
2245 commit_request
2246 );
2247 assert_eq!(
2248 candid::decode_one::<RootComponentChildDirectoryPreparationRequest>(
2249 &directory_request_bytes
2250 )
2251 .expect("decode child Directory request"),
2252 directory_request
2253 );
2254 assert_eq!(
2255 candid::decode_one::<RootComponentChildCommitResponse>(&commit_response_bytes)
2256 .expect("decode child commit response"),
2257 commit_response
2258 );
2259 assert_eq!(
2260 candid::decode_one::<RootComponentChildDirectoryPreparationResponse>(
2261 &directory_response_bytes
2262 )
2263 .expect("decode child Directory response"),
2264 directory_response
2265 );
2266 assert_eq!(
2267 candid::decode_one::<RootComponentChildRuntimeActivationRequest>(
2268 &activation_request_bytes
2269 )
2270 .expect("decode child activation request"),
2271 activation_request
2272 );
2273 assert_eq!(
2274 candid::decode_one::<RootComponentChildRuntimeActivationResponse>(
2275 &activation_response_bytes
2276 )
2277 .expect("decode child activation response"),
2278 activation_response
2279 );
2280 assert_eq!(
2281 candid::decode_one::<RootComponentChildMembershipActivationRequest>(
2282 &membership_request_bytes
2283 )
2284 .expect("decode child membership request"),
2285 membership_request
2286 );
2287 assert_eq!(
2288 candid::decode_one::<RootComponentChildMembershipActivationResponse>(
2289 &membership_response_bytes
2290 )
2291 .expect("decode child membership response"),
2292 membership_response
2293 );
2294 }
2295
2296 #[test]
2297 fn component_install_request_round_trips_through_candid() {
2298 let request = RootComponentInstallRequest {
2299 operation_id: [10; 32],
2300 };
2301 let bytes = candid::encode_one(request).expect("encode install request");
2302
2303 assert_eq!(
2304 candid::decode_one::<RootComponentInstallRequest>(&bytes)
2305 .expect("decode install request"),
2306 request
2307 );
2308 }
2309
2310 #[test]
2311 fn component_commit_request_round_trips_through_candid() {
2312 let request = RootComponentCommitRequest {
2313 operation_id: [10; 32],
2314 };
2315 let bytes = candid::encode_one(request).expect("encode commit request");
2316
2317 assert_eq!(
2318 candid::decode_one::<RootComponentCommitRequest>(&bytes)
2319 .expect("decode commit request"),
2320 request
2321 );
2322 }
2323
2324 #[test]
2325 fn component_runtime_activation_requests_round_trip_through_candid() {
2326 let root_request = RootComponentRuntimeActivationRequest {
2327 operation_id: [22; 32],
2328 };
2329 let target_request = ComponentRuntimeActivationRequest {
2330 operation_id: root_request.operation_id,
2331 directory_authority_hash: [23; 32],
2332 };
2333 let membership_request = RootComponentMembershipActivationRequest {
2334 operation_id: root_request.operation_id,
2335 };
2336 let root_bytes = candid::encode_one(root_request).expect("encode root activation request");
2337 let target_bytes =
2338 candid::encode_one(target_request).expect("encode target activation request");
2339 let membership_bytes =
2340 candid::encode_one(membership_request).expect("encode membership activation request");
2341
2342 assert_eq!(
2343 candid::decode_one::<RootComponentRuntimeActivationRequest>(&root_bytes)
2344 .expect("decode root activation request"),
2345 root_request
2346 );
2347 assert_eq!(
2348 candid::decode_one::<ComponentRuntimeActivationRequest>(&target_bytes)
2349 .expect("decode target activation request"),
2350 target_request
2351 );
2352 assert_eq!(
2353 candid::decode_one::<RootComponentMembershipActivationRequest>(&membership_bytes)
2354 .expect("decode membership activation request"),
2355 membership_request
2356 );
2357 }
2358}