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, Debug, Deserialize, Eq, PartialEq, Serialize)]
334pub struct RootComponentFinalInventoryRequest {
335 pub operation_id: [u8; 32],
336 pub component: ComponentInstanceId,
337 pub expected_registry: ComponentRegistryHead,
338}
339
340#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
347pub struct RootComponentChildCreationRequest {
348 pub operation_id: [u8; 32],
349 pub component: ComponentInstanceId,
350}
351
352#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
359pub struct RootComponentChildInstallRequest {
360 pub operation_id: [u8; 32],
361 pub component: ComponentInstanceId,
362}
363
364#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
371pub struct RootComponentChildCommitRequest {
372 pub operation_id: [u8; 32],
373 pub component: ComponentInstanceId,
374}
375
376#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
383pub struct RootComponentChildDirectoryPreparationRequest {
384 pub operation_id: [u8; 32],
385 pub component: ComponentInstanceId,
386}
387
388#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
395pub struct RootComponentChildRuntimeActivationRequest {
396 pub operation_id: [u8; 32],
397 pub component: ComponentInstanceId,
398}
399
400#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
407pub struct RootComponentChildMembershipActivationRequest {
408 pub operation_id: [u8; 32],
409 pub component: ComponentInstanceId,
410}
411
412#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
419pub struct RootComponentCreationRequest {
420 pub operation_id: [u8; 32],
421}
422
423#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
430pub struct RootComponentInstallRequest {
431 pub operation_id: [u8; 32],
432}
433
434#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
441pub struct RootComponentCommitRequest {
442 pub operation_id: [u8; 32],
443}
444
445#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
452pub struct RootComponentDirectoryPreparationRequest {
453 pub operation_id: [u8; 32],
454}
455
456#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
463pub struct RootComponentRuntimeActivationRequest {
464 pub operation_id: [u8; 32],
465}
466
467#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
474pub struct RootComponentMembershipActivationRequest {
475 pub operation_id: [u8; 32],
476}
477
478#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
485pub enum ComponentProvisioningOrigin {
486 FleetAdministrator { caller: Principal },
487}
488
489#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
496pub enum RootComponentAllocationPhase {
497 Reserved,
498 CreationIntent,
499 Created,
500 InstallIntent,
501 Installed,
502 Verified,
503 Committed,
504}
505
506#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
513#[expect(
514 clippy::large_enum_variant,
515 reason = "wire phases retain complete inline receipts for deterministic Candid responses"
516)]
517pub enum RootComponentSubtreeRemovalPhase {
518 Fenced,
519 Traversing(RootComponentSubtreeRemovalNode),
520 LeafSelected(RootComponentSubtreeRemovalNode),
521 StopIntent(RootComponentSubtreeRemovalStopIntent),
522 Stopped(RootComponentSubtreeRemovalStoppedReceipt),
523 DeleteIntent(RootComponentSubtreeRemovalDeleteIntent),
524 Deleted(RootComponentSubtreeRemovalDeletedReceipt),
525 MembershipRemoved(RootComponentSubtreeRemovalMembershipRemovedReceipt),
526 DirectorySynchronized(RootComponentSubtreeRemovalDirectorySynchronizedReceipt),
527 Completed(RootComponentSubtreeRemovalCompletedReceipt),
528}
529
530#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
537pub struct RootComponentSubtreeRemovalNode {
538 pub canister_id: Principal,
539 pub parent_canister_id: Principal,
540 pub role: CanisterRole,
541 pub kind: ComponentChildKind,
542 pub installed_artifact_hash: [u8; 32],
543 pub status: ComponentLifecycleStatus,
544}
545
546#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
553pub struct RootComponentSubtreeRemovalStopIntent {
554 pub leaf: RootComponentSubtreeRemovalNode,
555 pub controller: Principal,
556}
557
558#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
565pub struct RootComponentSubtreeRemovalStoppedReceipt {
566 pub stop: RootComponentSubtreeRemovalStopIntent,
567 pub observed_module_hash: [u8; 32],
568}
569
570#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
577pub struct RootComponentSubtreeRemovalDeleteIntent {
578 pub stopped: RootComponentSubtreeRemovalStoppedReceipt,
579}
580
581#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
588pub struct RootComponentSubtreeRemovalDeletedReceipt {
589 pub deletion: RootComponentSubtreeRemovalDeleteIntent,
590}
591
592#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
599pub struct RootComponentSubtreeRemovalMembershipRemovedReceipt {
600 pub deleted: RootComponentSubtreeRemovalDeletedReceipt,
601 pub removed_from_registry: ComponentRegistryHead,
602 pub previous_descendant_content_hash: [u8; 32],
603 pub previous_committed_descendants: u32,
604 pub registry: ComponentRegistryHead,
605 pub descendant_content_hash: [u8; 32],
606 pub registry_encoded_bytes: u64,
607 pub reserved_descendants: u32,
608 pub committed_descendants: u32,
609 pub directory_synchronized_at_ns: u64,
610 pub directory_authority_hash: [u8; 32],
611 pub parent_role_instances: u32,
612 pub root_managed_descendants: u32,
613 pub root_known_created_component_canisters: u32,
614}
615
616#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
623pub struct RootComponentSubtreeRemovalDirectoryConvergenceEvidence {
624 pub operation_id: [u8; 32],
625 pub canister_id: Principal,
626 pub activation: ComponentRuntimeActivationEvidence,
627}
628
629#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
638pub struct RootComponentSubtreeRemovalDirectorySynchronizedReceipt {
639 pub membership_removed: RootComponentSubtreeRemovalMembershipRemovedReceipt,
640 pub covered_fleet_registry_revision: u64,
641 pub covered_fleet_registry_content_hash: [u8; 32],
642 pub covered_component_registry: ComponentRegistryHead,
643 pub covered_authority_hash: [u8; 32],
644 pub owning_component: Option<RootComponentSubtreeRemovalDirectoryConvergenceEvidence>,
645 pub parent: Option<RootComponentSubtreeRemovalDirectoryConvergenceEvidence>,
646}
647
648#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
655pub struct RootComponentSubtreeRemovalCompletedReceipt {
656 pub registry: ComponentRegistryHead,
657 pub directory_authority_hash: [u8; 32],
658}
659
660#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
667pub enum ComponentLifecycleStatus {
668 Prepared,
669 Active,
670 Draining,
671 Removed,
672}
673
674#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
681pub struct ComponentRegistryHead {
682 pub component: ComponentInstanceId,
683 pub revision: u64,
684 pub content_hash: [u8; 32],
685}
686
687#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
694pub struct ComponentRegistryPartitionRequest {
695 pub component: ComponentInstanceId,
696}
697
698#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
705pub struct ComponentRegistryPartitionResponse {
706 pub head: ComponentRegistryHead,
707 pub binding: ComponentBinding,
708 pub provisioning_origin: ComponentProvisioningOrigin,
709 pub release_set: FleetSubnetRootReleaseSet,
710 pub status: ComponentLifecycleStatus,
711 pub reserved_descendants: u32,
712 pub committed_descendants: u32,
713 pub encoded_bytes: u64,
714}
715
716#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
723pub struct ComponentDirectoryProvenance {
724 pub component: ComponentBinding,
725 pub source_fleet_subnet_root: Principal,
726 pub component_registry_revision: u64,
727 pub component_registry_content_hash: [u8; 32],
728 pub synchronized_at_ns: u64,
729}
730
731#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
738pub struct ComponentDirectoryHead {
739 pub provenance: ComponentDirectoryProvenance,
740 pub descendant_count: u32,
741}
742
743#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
750pub struct ComponentDirectoryHeadRequest {
751 pub component: ComponentInstanceId,
752}
753
754#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
761pub struct ComponentDirectoryPageCursor(pub Vec<u8>);
762
763#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
770pub struct ComponentDirectoryPageRequest {
771 pub directory: ComponentDirectoryHead,
772 pub parent_canister_id: Option<Principal>,
773 pub role: Option<CanisterRole>,
774 pub status: Option<ComponentLifecycleStatus>,
775 pub cursor: Option<ComponentDirectoryPageCursor>,
776 pub limit: u16,
777}
778
779#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
786pub struct ComponentDirectoryChildEntry {
787 pub binding: ComponentChildBinding,
788 pub kind: ComponentChildKind,
789 pub installed_artifact_hash: [u8; 32],
790 pub status: ComponentLifecycleStatus,
791}
792
793#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
800pub struct ComponentDirectoryPageResponse {
801 pub directory: ComponentDirectoryHead,
802 pub entries: Vec<ComponentDirectoryChildEntry>,
803 pub next_cursor: Option<ComponentDirectoryPageCursor>,
804}
805
806#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
813pub struct ComponentRuntimeDirectoryAuthority {
814 pub fleet: FleetDirectorySnapshot,
815 pub component: ComponentDirectoryHead,
816}
817
818#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
825pub struct ComponentRuntimeDirectoryPreparationRequest {
826 pub operation_id: [u8; 32],
827 pub authority: ComponentRuntimeDirectoryAuthority,
828}
829
830#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
837pub struct ComponentRuntimeDirectorySynchronizationRequest {
838 pub operation_id: [u8; 32],
839 pub authority: ComponentRuntimeDirectoryAuthority,
840}
841
842#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
849pub enum ComponentRuntimePhase {
850 AwaitingDirectory,
851 DirectoryPrepared,
852 Active,
853}
854
855#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
862pub struct ComponentRuntimeActivationEvidence {
863 pub directory_authority_hash: [u8; 32],
864 pub activated_at_ns: u64,
865}
866
867#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
874pub struct ComponentRuntimeActivationRequest {
875 pub operation_id: [u8; 32],
876 pub directory_authority_hash: [u8; 32],
877}
878
879#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
886pub struct ComponentRuntimeStatusResponse {
887 pub operation_id: [u8; 32],
888 pub binding: ManagedCanisterBinding,
889 pub phase: ComponentRuntimePhase,
890 pub authority: Option<ComponentRuntimeDirectoryAuthority>,
891 pub authority_hash: Option<[u8; 32]>,
892 pub activation: Option<ComponentRuntimeActivationEvidence>,
893}
894
895#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
902pub struct ComponentRuntimeDirectoryConvergenceEvidence {
903 pub operation_id: [u8; 32],
904 pub binding: ManagedCanisterBinding,
905 pub covered_authority: ComponentRuntimeDirectoryAuthority,
906 pub covered_authority_hash: [u8; 32],
907 pub activation: ComponentRuntimeActivationEvidence,
908}
909
910#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
917pub struct RootComponentCreationEvidence {
918 pub wasm_store: Principal,
919 pub payload_hash: [u8; 32],
920 pub payload_size_bytes: u64,
921 pub initial_cycles: Cycles,
922 pub controller: Principal,
923 pub canister: Option<Principal>,
924}
925
926#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
933pub struct RootComponentInstallEvidence {
934 pub raw_module_hash: [u8; 32],
935 pub chunk_hashes: Vec<Vec<u8>>,
936 pub binding: ComponentBinding,
937}
938
939#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
946pub struct RootComponentChildInstallEvidence {
947 pub raw_module_hash: [u8; 32],
948 pub chunk_hashes: Vec<Vec<u8>>,
949 pub binding: ComponentChildBinding,
950}
951
952#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
959pub struct RootComponentAllocationResponse {
960 pub operation_id: [u8; 32],
961 pub allocation_sequence: u64,
962 pub component: ComponentInstanceId,
963 pub component_spec: ComponentSpecId,
964 pub spec_hash: [u8; 32],
965 pub role: CanisterRole,
966 pub provisioning_origin: ComponentProvisioningOrigin,
967 pub release_set: FleetSubnetRootReleaseSet,
968 pub phase: RootComponentAllocationPhase,
969 pub creation: Option<RootComponentCreationEvidence>,
970 pub installation: Option<RootComponentInstallEvidence>,
971}
972
973#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
980pub struct RootComponentChildAllocationResponse {
981 pub operation_id: [u8; 32],
982 pub component: ComponentInstanceId,
983 pub parent_canister_id: Principal,
984 pub parent_role: CanisterRole,
985 pub child_role: CanisterRole,
986 pub child_kind: ComponentChildKind,
987 pub maximum_instances_per_parent: u32,
988 pub maximum_descendants: u32,
989 pub maximum_registry_bytes: u64,
990 pub reserved_against_registry: ComponentRegistryHead,
991 pub release_set: FleetSubnetRootReleaseSet,
992 pub phase: RootComponentAllocationPhase,
993 pub creation: Option<RootComponentCreationEvidence>,
994 pub installation: Option<RootComponentChildInstallEvidence>,
995}
996
997#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1004pub struct RootComponentSubtreeRemovalResponse {
1005 pub operation_id: [u8; 32],
1006 pub component: ComponentInstanceId,
1007 pub target_canister_id: Principal,
1008 pub target_parent_canister_id: Principal,
1009 pub target_role: CanisterRole,
1010 pub target_status: ComponentLifecycleStatus,
1011 pub reserved_against_registry: ComponentRegistryHead,
1012 pub maximum_completed_leaves: u32,
1013 pub completed_leaves: u32,
1014 pub traversal_steps: u32,
1015 pub phase: RootComponentSubtreeRemovalPhase,
1016}
1017
1018#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1025pub struct RootComponentDrainingResponse {
1026 pub operation_id: [u8; 32],
1027 pub component: ComponentInstanceId,
1028 pub previous_registry: ComponentRegistryHead,
1029 pub registry: ComponentRegistryHead,
1030 pub descendant_count: u32,
1031 pub descendant_content_hash: [u8; 32],
1032 pub directory_authority_hash: [u8; 32],
1033 pub started_at_ns: u64,
1034}
1035
1036#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1043pub struct RootComponentQuiescenceStopIntent {
1044 pub registry: ComponentRegistryHead,
1045 pub descendant_count: u32,
1046 pub descendant_content_hash: [u8; 32],
1047 pub canister_id: Principal,
1048 pub controller: Principal,
1049 pub expected_module_hash: [u8; 32],
1050 pub covered_fleet_registry_revision: u64,
1051 pub covered_fleet_registry_content_hash: [u8; 32],
1052 pub covered_authority_hash: [u8; 32],
1053 pub runtime_operation_id: [u8; 32],
1054 pub activation: ComponentRuntimeActivationEvidence,
1055 pub prepared_at_ns: u64,
1056}
1057
1058#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1065pub struct RootComponentQuiescentReceipt {
1066 pub stop: RootComponentQuiescenceStopIntent,
1067 pub observed_module_hash: [u8; 32],
1068 pub quiesced_at_ns: u64,
1069}
1070
1071#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1078pub enum RootComponentQuiescencePhase {
1079 StopIntent(RootComponentQuiescenceStopIntent),
1080 Quiescent(RootComponentQuiescentReceipt),
1081}
1082
1083#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1090pub struct RootComponentQuiescenceResponse {
1091 pub operation_id: [u8; 32],
1092 pub component: ComponentInstanceId,
1093 pub phase: RootComponentQuiescencePhase,
1094}
1095
1096#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1103pub struct RootComponentDrainingDescendantsEmpty {
1104 pub registry: ComponentRegistryHead,
1105 pub descendant_content_hash: [u8; 32],
1106}
1107
1108#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1115#[expect(
1116 clippy::large_enum_variant,
1117 reason = "wire result embeds the current durable subtree snapshot without a Rust-only indirection"
1118)]
1119pub enum RootComponentDrainingAdvancePhase {
1120 DescendantRemoval(RootComponentSubtreeRemovalResponse),
1121 DescendantsEmpty(RootComponentDrainingDescendantsEmpty),
1122}
1123
1124#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1131pub struct RootComponentDrainingAdvanceResponse {
1132 pub operation_id: [u8; 32],
1133 pub component: ComponentInstanceId,
1134 pub phase: RootComponentDrainingAdvancePhase,
1135}
1136
1137#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1144pub struct RootComponentFinalInventory {
1145 pub registry: ComponentRegistryHead,
1146 pub descendant_content_hash: [u8; 32],
1147 pub registry_encoded_bytes: u64,
1148 pub directory_synchronized_at_ns: u64,
1149 pub covered_fleet_registry_revision: u64,
1150 pub covered_fleet_registry_content_hash: [u8; 32],
1151 pub directory_authority_hash: [u8; 32],
1152 pub inventory_hash: [u8; 32],
1153 pub finalized_at_ns: u64,
1154}
1155
1156#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1163pub struct RootComponentFinalInventoryResponse {
1164 pub operation_id: [u8; 32],
1165 pub component: ComponentInstanceId,
1166 pub inventory: RootComponentFinalInventory,
1167}
1168
1169#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1176pub struct RootComponentChildCommitResponse {
1177 pub allocation: RootComponentChildAllocationResponse,
1178 pub registry: ComponentRegistryPartitionResponse,
1179 pub directory: ComponentDirectoryHead,
1180}
1181
1182#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1189pub struct RootComponentChildDirectoryPreparationResponse {
1190 pub committed: RootComponentChildCommitResponse,
1191 pub child: ComponentRuntimeStatusResponse,
1192 pub owning_component: ComponentRuntimeDirectoryConvergenceEvidence,
1193 pub parent: Option<ComponentRuntimeDirectoryConvergenceEvidence>,
1194}
1195
1196#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1203pub struct RootComponentChildRuntimeActivationResponse {
1204 pub committed: RootComponentChildCommitResponse,
1205 pub child: ComponentRuntimeStatusResponse,
1206}
1207
1208#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1215pub struct RootComponentChildMembershipActivationResponse {
1216 pub committed: RootComponentChildCommitResponse,
1217 pub registry: ComponentRegistryPartitionResponse,
1218 pub directory: ComponentDirectoryHead,
1219 pub child: ComponentRuntimeStatusResponse,
1220}
1221
1222#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1229pub struct RootComponentCommitResponse {
1230 pub allocation: RootComponentAllocationResponse,
1231 pub registry: ComponentRegistryPartitionResponse,
1232 pub directory: ComponentDirectoryHead,
1233}
1234
1235#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1242pub struct RootComponentDirectoryPreparationResponse {
1243 pub committed: RootComponentCommitResponse,
1244 pub target: ComponentRuntimeStatusResponse,
1245}
1246
1247#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1254pub struct RootComponentRuntimeActivationResponse {
1255 pub committed: RootComponentCommitResponse,
1256 pub target: ComponentRuntimeStatusResponse,
1257}
1258
1259#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1266pub struct RootComponentMembershipActivationResponse {
1267 pub allocation: RootComponentAllocationResponse,
1268 pub registry: ComponentRegistryPartitionResponse,
1269 pub directory: ComponentDirectoryHead,
1270 pub target: ComponentRuntimeStatusResponse,
1271}
1272
1273#[cfg(test)]
1274mod tests {
1275 use super::*;
1276 use crate::{
1277 dto::root_store::RootStoreBootstrapRequest,
1278 ids::{
1279 AppId, CanonicalNetworkId, FleetCoordinatorBinding, FleetId, FleetKey,
1280 FleetRegistryAuthority, ReleaseBuildId, ReleaseBuildNonce, ReleaseSetDigest, SubnetId,
1281 },
1282 };
1283
1284 #[test]
1285 fn component_registry_contracts_round_trip_through_candid() {
1286 let request = RootComponentRegistryPreparationRequest {
1287 store_bootstrap: RootStoreBootstrapRequest {
1288 manifest_payload_size_bytes: 128,
1289 },
1290 expected_fleet_registry: FleetRegistryVersion {
1291 authority: fleet_registry_authority(),
1292 revision: 4,
1293 content_hash: [5; 32],
1294 },
1295 };
1296 let response = RootComponentRegistryStatusResponse {
1297 fleet_subnet_root: Principal::from_slice(&[6; 29]),
1298 prepared_against_registry: request.expected_fleet_registry.clone(),
1299 release_set: FleetSubnetRootReleaseSet {
1300 release_build_id: ReleaseBuildId::from_nonce(ReleaseBuildNonce::from_random_bytes(
1301 [7; 32],
1302 )),
1303 manifest_digest: ReleaseSetDigest::from_bytes([8; 32]),
1304 },
1305 component_topology_digest: ComponentTopologyDigest::from_bytes([9; 32]),
1306 next_allocation_sequence: 1,
1307 reserved_component_instances: 0,
1308 committed_component_instances: 0,
1309 managed_descendants: 0,
1310 known_created_component_canisters: 0,
1311 encoded_bytes: 0,
1312 initial_inventory: Some(RootComponentInitialInventoryStatus {
1313 fleet_activation_operation_id: [10; 32],
1314 component_count: 0,
1315 inventory_hash: [11; 32],
1316 sealed_at_ns: 12,
1317 directories_converged: true,
1318 root_runtime_activated: true,
1319 }),
1320 };
1321 let allocation = RootComponentAllocationResponse {
1322 operation_id: [10; 32],
1323 allocation_sequence: 1,
1324 component: ComponentInstanceId::from_generated_bytes([11; 32]),
1325 component_spec: "projects".parse().expect("Component Spec ID"),
1326 spec_hash: [12; 32],
1327 role: CanisterRole::new("project_hub"),
1328 provisioning_origin: ComponentProvisioningOrigin::FleetAdministrator {
1329 caller: Principal::from_slice(&[13; 29]),
1330 },
1331 release_set: response.release_set,
1332 phase: RootComponentAllocationPhase::Reserved,
1333 creation: None,
1334 installation: None,
1335 };
1336 let created = RootComponentAllocationResponse {
1337 phase: RootComponentAllocationPhase::Created,
1338 creation: Some(RootComponentCreationEvidence {
1339 wasm_store: Principal::from_slice(&[14; 29]),
1340 payload_hash: [15; 32],
1341 payload_size_bytes: 4_096,
1342 initial_cycles: Cycles::new(5_000_000_000_000),
1343 controller: Principal::from_slice(&[6; 29]),
1344 canister: Some(Principal::from_slice(&[16; 29])),
1345 }),
1346 installation: None,
1347 ..allocation.clone()
1348 };
1349 let request_bytes = candid::encode_one(&request).expect("encode request");
1350 let response_bytes = candid::encode_one(&response).expect("encode response");
1351 let allocation_bytes = candid::encode_one(&allocation).expect("encode allocation");
1352 let created_bytes = candid::encode_one(&created).expect("encode created allocation");
1353
1354 assert_eq!(
1355 candid::decode_one::<RootComponentRegistryPreparationRequest>(&request_bytes)
1356 .expect("decode request"),
1357 request
1358 );
1359 assert_eq!(
1360 candid::decode_one::<RootComponentRegistryStatusResponse>(&response_bytes)
1361 .expect("decode response"),
1362 response
1363 );
1364 assert_eq!(
1365 candid::decode_one::<RootComponentAllocationResponse>(&allocation_bytes)
1366 .expect("decode allocation"),
1367 allocation
1368 );
1369 assert_eq!(
1370 candid::decode_one::<RootComponentAllocationResponse>(&created_bytes)
1371 .expect("decode created allocation"),
1372 created
1373 );
1374 }
1375
1376 #[test]
1377 fn component_commit_response_round_trips_through_candid() {
1378 let root = Principal::from_slice(&[6; 29]);
1379 let component = ComponentInstanceId::from_generated_bytes([11; 32]);
1380 let component_spec: ComponentSpecId = "projects".parse().expect("Component Spec ID");
1381 let release_set = FleetSubnetRootReleaseSet {
1382 release_build_id: ReleaseBuildId::from_nonce(ReleaseBuildNonce::from_random_bytes(
1383 [7; 32],
1384 )),
1385 manifest_digest: ReleaseSetDigest::from_bytes([8; 32]),
1386 };
1387 let provisioning_origin = ComponentProvisioningOrigin::FleetAdministrator {
1388 caller: Principal::from_slice(&[13; 29]),
1389 };
1390 let binding = ComponentBinding {
1391 authority: fleet_registry_authority(),
1392 component,
1393 component_spec: component_spec.clone(),
1394 spec_hash: [12; 32],
1395 role: CanisterRole::new("project_hub"),
1396 placement_subnet: SubnetId::from_principal(Principal::from_slice(&[17; 29])),
1397 fleet_subnet_root: root,
1398 canister_id: Principal::from_slice(&[16; 29]),
1399 };
1400 let head = ComponentRegistryHead {
1401 component,
1402 revision: 1,
1403 content_hash: [18; 32],
1404 };
1405 let committed = RootComponentCommitResponse {
1406 allocation: RootComponentAllocationResponse {
1407 operation_id: [10; 32],
1408 allocation_sequence: 1,
1409 component,
1410 component_spec,
1411 spec_hash: binding.spec_hash,
1412 role: binding.role.clone(),
1413 provisioning_origin: provisioning_origin.clone(),
1414 release_set,
1415 phase: RootComponentAllocationPhase::Committed,
1416 creation: Some(RootComponentCreationEvidence {
1417 wasm_store: Principal::from_slice(&[14; 29]),
1418 payload_hash: [15; 32],
1419 payload_size_bytes: 4_096,
1420 initial_cycles: Cycles::new(5_000_000_000_000),
1421 controller: root,
1422 canister: Some(binding.canister_id),
1423 }),
1424 installation: Some(RootComponentInstallEvidence {
1425 raw_module_hash: [20; 32],
1426 chunk_hashes: vec![vec![21; 32]],
1427 binding: binding.clone(),
1428 }),
1429 },
1430 registry: ComponentRegistryPartitionResponse {
1431 head: head.clone(),
1432 binding: binding.clone(),
1433 provisioning_origin,
1434 release_set,
1435 status: ComponentLifecycleStatus::Prepared,
1436 reserved_descendants: 0,
1437 committed_descendants: 0,
1438 encoded_bytes: 2_048,
1439 },
1440 directory: ComponentDirectoryHead {
1441 provenance: ComponentDirectoryProvenance {
1442 component: binding,
1443 source_fleet_subnet_root: root,
1444 component_registry_revision: head.revision,
1445 component_registry_content_hash: head.content_hash,
1446 synchronized_at_ns: 19,
1447 },
1448 descendant_count: 0,
1449 },
1450 };
1451 let committed_bytes = candid::encode_one(&committed).expect("encode committed allocation");
1452
1453 assert_eq!(
1454 candid::decode_one::<RootComponentCommitResponse>(&committed_bytes)
1455 .expect("decode committed allocation"),
1456 committed
1457 );
1458 }
1459
1460 #[test]
1461 fn component_directory_page_contracts_round_trip_through_candid() {
1462 let root = Principal::from_slice(&[6; 29]);
1463 let component = ComponentInstanceId::from_generated_bytes([11; 32]);
1464 let binding = ComponentBinding {
1465 authority: fleet_registry_authority(),
1466 component,
1467 component_spec: "projects".parse().expect("Component Spec ID"),
1468 spec_hash: [12; 32],
1469 role: CanisterRole::new("project_hub"),
1470 placement_subnet: SubnetId::from_principal(Principal::from_slice(&[17; 29])),
1471 fleet_subnet_root: root,
1472 canister_id: Principal::from_slice(&[16; 29]),
1473 };
1474 let directory = ComponentDirectoryHead {
1475 provenance: ComponentDirectoryProvenance {
1476 component: binding.clone(),
1477 source_fleet_subnet_root: root,
1478 component_registry_revision: 3,
1479 component_registry_content_hash: [18; 32],
1480 synchronized_at_ns: 19,
1481 },
1482 descendant_count: 1,
1483 };
1484 let request = ComponentDirectoryPageRequest {
1485 directory: directory.clone(),
1486 parent_canister_id: Some(binding.canister_id),
1487 role: Some(CanisterRole::new("project_instance")),
1488 status: Some(ComponentLifecycleStatus::Active),
1489 cursor: Some(ComponentDirectoryPageCursor(vec![20; 64])),
1490 limit: 50,
1491 };
1492 let response = ComponentDirectoryPageResponse {
1493 directory,
1494 entries: vec![ComponentDirectoryChildEntry {
1495 binding: ComponentChildBinding {
1496 component: binding.clone(),
1497 parent_canister_id: binding.canister_id,
1498 role: CanisterRole::new("project_instance"),
1499 canister_id: Principal::from_slice(&[21; 29]),
1500 },
1501 kind: ComponentChildKind::Instance,
1502 installed_artifact_hash: [22; 32],
1503 status: ComponentLifecycleStatus::Active,
1504 }],
1505 next_cursor: Some(ComponentDirectoryPageCursor(vec![23; 64])),
1506 };
1507 let request_bytes = candid::encode_one(&request).expect("encode Directory page request");
1508 let response_bytes = candid::encode_one(&response).expect("encode Directory page response");
1509
1510 assert_eq!(
1511 candid::decode_one::<ComponentDirectoryPageRequest>(&request_bytes)
1512 .expect("decode Directory page request"),
1513 request
1514 );
1515 assert_eq!(
1516 candid::decode_one::<ComponentDirectoryPageResponse>(&response_bytes)
1517 .expect("decode Directory page response"),
1518 response
1519 );
1520 }
1521
1522 fn fleet_registry_authority() -> FleetRegistryAuthority {
1523 FleetRegistryAuthority {
1524 binding: FleetCoordinatorBinding {
1525 fleet: crate::ids::FleetBinding {
1526 fleet: FleetKey {
1527 canonical_network_id: CanonicalNetworkId::ic_mainnet(),
1528 fleet_id: FleetId::from_generated_bytes([1; 32]),
1529 },
1530 app: AppId::from("toko"),
1531 },
1532 coordinator_subnet: SubnetId::from_principal(Principal::from_slice(&[2; 29])),
1533 coordinator: Principal::from_slice(&[3; 29]),
1534 },
1535 epoch: 1,
1536 }
1537 }
1538
1539 #[test]
1540 fn component_creation_request_round_trips_through_candid() {
1541 let request = RootComponentCreationRequest {
1542 operation_id: [10; 32],
1543 };
1544 let bytes = candid::encode_one(request).expect("encode creation request");
1545
1546 assert_eq!(
1547 candid::decode_one::<RootComponentCreationRequest>(&bytes)
1548 .expect("decode creation request"),
1549 request
1550 );
1551 }
1552
1553 #[test]
1554 #[expect(
1555 clippy::too_many_lines,
1556 reason = "one Candid contract test covers every subtree-removal phase receipt"
1557 )]
1558 fn component_subtree_removal_contracts_round_trip_through_candid() {
1559 let component = ComponentInstanceId::from_generated_bytes([41; 32]);
1560 let registry = ComponentRegistryHead {
1561 component,
1562 revision: 7,
1563 content_hash: [42; 32],
1564 };
1565 let request = RootComponentSubtreeRemovalRequest {
1566 operation_id: [43; 32],
1567 component,
1568 target_canister_id: Principal::from_slice(&[44; 29]),
1569 expected_registry: registry.clone(),
1570 };
1571 let status_request = RootComponentSubtreeRemovalStatusRequest {
1572 operation_id: request.operation_id,
1573 component,
1574 };
1575 let advance_request = RootComponentSubtreeRemovalAdvanceRequest {
1576 operation_id: request.operation_id,
1577 component,
1578 expected_traversal_steps: 1,
1579 };
1580 let stop_request = RootComponentSubtreeRemovalStopPreparationRequest {
1581 operation_id: request.operation_id,
1582 component,
1583 expected_traversal_steps: 2,
1584 expected_leaf_canister_id: Principal::from_slice(&[46; 29]),
1585 expected_leaf_parent_canister_id: request.target_canister_id,
1586 };
1587 let stopped = RootComponentSubtreeRemovalStoppedReceipt {
1588 observed_module_hash: [49; 32],
1589 stop: RootComponentSubtreeRemovalStopIntent {
1590 controller: Principal::from_slice(&[48; 29]),
1591 leaf: RootComponentSubtreeRemovalNode {
1592 canister_id: Principal::from_slice(&[46; 29]),
1593 parent_canister_id: request.target_canister_id,
1594 role: CanisterRole::new("project_ledger"),
1595 kind: ComponentChildKind::Singleton,
1596 installed_artifact_hash: [47; 32],
1597 status: ComponentLifecycleStatus::Active,
1598 },
1599 },
1600 };
1601 let response = RootComponentSubtreeRemovalResponse {
1602 operation_id: request.operation_id,
1603 component,
1604 target_canister_id: request.target_canister_id,
1605 target_parent_canister_id: Principal::from_slice(&[45; 29]),
1606 target_role: CanisterRole::new("project_instance"),
1607 target_status: ComponentLifecycleStatus::Active,
1608 reserved_against_registry: registry,
1609 maximum_completed_leaves: 4,
1610 completed_leaves: 1,
1611 traversal_steps: 2,
1612 phase: RootComponentSubtreeRemovalPhase::DirectorySynchronized(
1613 RootComponentSubtreeRemovalDirectorySynchronizedReceipt {
1614 membership_removed: RootComponentSubtreeRemovalMembershipRemovedReceipt {
1615 deleted: RootComponentSubtreeRemovalDeletedReceipt {
1616 deletion: RootComponentSubtreeRemovalDeleteIntent { stopped },
1617 },
1618 removed_from_registry: ComponentRegistryHead {
1619 component,
1620 revision: 8,
1621 content_hash: [50; 32],
1622 },
1623 previous_descendant_content_hash: [51; 32],
1624 previous_committed_descendants: 4,
1625 registry: ComponentRegistryHead {
1626 component,
1627 revision: 9,
1628 content_hash: [52; 32],
1629 },
1630 descendant_content_hash: [53; 32],
1631 registry_encoded_bytes: 4_096,
1632 reserved_descendants: 1,
1633 committed_descendants: 3,
1634 directory_synchronized_at_ns: 54,
1635 directory_authority_hash: [55; 32],
1636 parent_role_instances: 0,
1637 root_managed_descendants: 4,
1638 root_known_created_component_canisters: 4,
1639 },
1640 covered_fleet_registry_revision: 6,
1641 covered_fleet_registry_content_hash: [56; 32],
1642 covered_component_registry: ComponentRegistryHead {
1643 component,
1644 revision: 9,
1645 content_hash: [52; 32],
1646 },
1647 covered_authority_hash: [55; 32],
1648 owning_component: Some(
1649 RootComponentSubtreeRemovalDirectoryConvergenceEvidence {
1650 operation_id: [57; 32],
1651 canister_id: Principal::from_slice(&[58; 29]),
1652 activation: ComponentRuntimeActivationEvidence {
1653 directory_authority_hash: [59; 32],
1654 activated_at_ns: 60,
1655 },
1656 },
1657 ),
1658 parent: Some(RootComponentSubtreeRemovalDirectoryConvergenceEvidence {
1659 operation_id: [61; 32],
1660 canister_id: request.target_canister_id,
1661 activation: ComponentRuntimeActivationEvidence {
1662 directory_authority_hash: [62; 32],
1663 activated_at_ns: 63,
1664 },
1665 }),
1666 },
1667 ),
1668 };
1669
1670 let request_bytes = candid::encode_one(&request).expect("encode subtree removal request");
1671 let advance_bytes =
1672 candid::encode_one(advance_request).expect("encode subtree removal advance request");
1673 let stop_bytes =
1674 candid::encode_one(stop_request).expect("encode subtree removal stop request");
1675 let status_bytes =
1676 candid::encode_one(status_request).expect("encode subtree removal status request");
1677 let response_bytes =
1678 candid::encode_one(&response).expect("encode subtree removal response");
1679
1680 assert_eq!(
1681 candid::decode_one::<RootComponentSubtreeRemovalRequest>(&request_bytes)
1682 .expect("decode subtree removal request"),
1683 request
1684 );
1685 assert_eq!(
1686 candid::decode_one::<RootComponentSubtreeRemovalAdvanceRequest>(&advance_bytes)
1687 .expect("decode subtree removal advance request"),
1688 advance_request
1689 );
1690 assert_eq!(
1691 candid::decode_one::<RootComponentSubtreeRemovalStopPreparationRequest>(&stop_bytes)
1692 .expect("decode subtree removal stop request"),
1693 stop_request
1694 );
1695 assert_eq!(
1696 candid::decode_one::<RootComponentSubtreeRemovalStatusRequest>(&status_bytes)
1697 .expect("decode subtree removal status request"),
1698 status_request
1699 );
1700 assert_eq!(
1701 candid::decode_one::<RootComponentSubtreeRemovalResponse>(&response_bytes)
1702 .expect("decode subtree removal response"),
1703 response
1704 );
1705
1706 let mut quiescent_owner_response = response;
1707 let RootComponentSubtreeRemovalPhase::DirectorySynchronized(receipt) =
1708 &mut quiescent_owner_response.phase
1709 else {
1710 panic!("Directory-synchronized response");
1711 };
1712 receipt.owning_component = None;
1713 let quiescent_owner_bytes = candid::encode_one(&quiescent_owner_response)
1714 .expect("encode quiescent-owner subtree response");
1715 assert_eq!(
1716 candid::decode_one::<RootComponentSubtreeRemovalResponse>(&quiescent_owner_bytes)
1717 .expect("decode quiescent-owner subtree response"),
1718 quiescent_owner_response
1719 );
1720 }
1721
1722 #[test]
1723 fn component_draining_contracts_round_trip_through_candid() {
1724 let component = ComponentInstanceId::from_generated_bytes([60; 32]);
1725 let previous_registry = ComponentRegistryHead {
1726 component,
1727 revision: 7,
1728 content_hash: [61; 32],
1729 };
1730 let request = RootComponentDrainingRequest {
1731 operation_id: [62; 32],
1732 component,
1733 expected_registry: previous_registry.clone(),
1734 };
1735 let status_request = RootComponentDrainingStatusRequest {
1736 operation_id: request.operation_id,
1737 component,
1738 };
1739 let response = RootComponentDrainingResponse {
1740 operation_id: request.operation_id,
1741 component,
1742 previous_registry,
1743 registry: ComponentRegistryHead {
1744 component,
1745 revision: 8,
1746 content_hash: [63; 32],
1747 },
1748 descendant_count: 20_000,
1749 descendant_content_hash: [64; 32],
1750 directory_authority_hash: [65; 32],
1751 started_at_ns: 66,
1752 };
1753
1754 let request_bytes =
1755 candid::encode_one(&request).expect("encode Component draining request");
1756 let status_bytes =
1757 candid::encode_one(status_request).expect("encode Component draining status request");
1758 let response_bytes =
1759 candid::encode_one(&response).expect("encode Component draining response");
1760
1761 assert_eq!(
1762 candid::decode_one::<RootComponentDrainingRequest>(&request_bytes)
1763 .expect("decode Component draining request"),
1764 request
1765 );
1766 assert_eq!(
1767 candid::decode_one::<RootComponentDrainingStatusRequest>(&status_bytes)
1768 .expect("decode Component draining status request"),
1769 status_request
1770 );
1771 assert_eq!(
1772 candid::decode_one::<RootComponentDrainingResponse>(&response_bytes)
1773 .expect("decode Component draining response"),
1774 response
1775 );
1776 }
1777
1778 #[test]
1779 fn component_quiescence_contracts_round_trip_through_candid() {
1780 let component = ComponentInstanceId::from_generated_bytes([67; 32]);
1781 let registry = ComponentRegistryHead {
1782 component,
1783 revision: 9,
1784 content_hash: [68; 32],
1785 };
1786 let request = RootComponentQuiescenceRequest {
1787 operation_id: [69; 32],
1788 component,
1789 expected_registry: registry.clone(),
1790 };
1791 let status_request = RootComponentQuiescenceStatusRequest {
1792 operation_id: request.operation_id,
1793 component,
1794 };
1795 let stop = RootComponentQuiescenceStopIntent {
1796 registry,
1797 descendant_count: 20_000,
1798 descendant_content_hash: [70; 32],
1799 canister_id: Principal::from_slice(&[71; 29]),
1800 controller: Principal::from_slice(&[72; 29]),
1801 expected_module_hash: [73; 32],
1802 covered_fleet_registry_revision: 10,
1803 covered_fleet_registry_content_hash: [74; 32],
1804 covered_authority_hash: [75; 32],
1805 runtime_operation_id: [76; 32],
1806 activation: ComponentRuntimeActivationEvidence {
1807 directory_authority_hash: [77; 32],
1808 activated_at_ns: 78,
1809 },
1810 prepared_at_ns: 79,
1811 };
1812 let response = RootComponentQuiescenceResponse {
1813 operation_id: request.operation_id,
1814 component,
1815 phase: RootComponentQuiescencePhase::Quiescent(RootComponentQuiescentReceipt {
1816 stop,
1817 observed_module_hash: [73; 32],
1818 quiesced_at_ns: 80,
1819 }),
1820 };
1821
1822 let request_bytes = candid::encode_one(&request).expect("encode quiescence request");
1823 let status_bytes =
1824 candid::encode_one(status_request).expect("encode quiescence status request");
1825 let response_bytes = candid::encode_one(&response).expect("encode quiescence response");
1826 assert_eq!(
1827 candid::decode_one::<RootComponentQuiescenceRequest>(&request_bytes)
1828 .expect("decode quiescence request"),
1829 request
1830 );
1831 assert_eq!(
1832 candid::decode_one::<RootComponentQuiescenceStatusRequest>(&status_bytes)
1833 .expect("decode quiescence status request"),
1834 status_request
1835 );
1836 assert_eq!(
1837 candid::decode_one::<RootComponentQuiescenceResponse>(&response_bytes)
1838 .expect("decode quiescence response"),
1839 response
1840 );
1841 }
1842
1843 #[test]
1844 fn component_draining_advance_contracts_round_trip_through_candid() {
1845 let component = ComponentInstanceId::from_generated_bytes([81; 32]);
1846 let registry = ComponentRegistryHead {
1847 component,
1848 revision: 12,
1849 content_hash: [82; 32],
1850 };
1851 let request = RootComponentDrainingAdvanceRequest {
1852 operation_id: [83; 32],
1853 component,
1854 };
1855 let descendant_removal = RootComponentDrainingAdvanceResponse {
1856 operation_id: request.operation_id,
1857 component,
1858 phase: RootComponentDrainingAdvancePhase::DescendantRemoval(
1859 RootComponentSubtreeRemovalResponse {
1860 operation_id: [84; 32],
1861 component,
1862 target_canister_id: Principal::from_slice(&[85; 29]),
1863 target_parent_canister_id: Principal::from_slice(&[86; 29]),
1864 target_role: CanisterRole::new("project_instance"),
1865 target_status: ComponentLifecycleStatus::Active,
1866 reserved_against_registry: registry.clone(),
1867 maximum_completed_leaves: 20_000,
1868 completed_leaves: 0,
1869 traversal_steps: 0,
1870 phase: RootComponentSubtreeRemovalPhase::Fenced,
1871 },
1872 ),
1873 };
1874 let descendants_empty = RootComponentDrainingAdvanceResponse {
1875 operation_id: request.operation_id,
1876 component,
1877 phase: RootComponentDrainingAdvancePhase::DescendantsEmpty(
1878 RootComponentDrainingDescendantsEmpty {
1879 registry,
1880 descendant_content_hash: [87; 32],
1881 },
1882 ),
1883 };
1884
1885 let request_bytes =
1886 candid::encode_one(request).expect("encode Component draining advance request");
1887 let removal_bytes = candid::encode_one(&descendant_removal)
1888 .expect("encode Component draining removal response");
1889 let empty_bytes = candid::encode_one(&descendants_empty)
1890 .expect("encode Component draining empty response");
1891
1892 assert_eq!(
1893 candid::decode_one::<RootComponentDrainingAdvanceRequest>(&request_bytes)
1894 .expect("decode Component draining advance request"),
1895 request
1896 );
1897 assert_eq!(
1898 candid::decode_one::<RootComponentDrainingAdvanceResponse>(&removal_bytes)
1899 .expect("decode Component draining removal response"),
1900 descendant_removal
1901 );
1902 assert_eq!(
1903 candid::decode_one::<RootComponentDrainingAdvanceResponse>(&empty_bytes)
1904 .expect("decode Component draining empty response"),
1905 descendants_empty
1906 );
1907 }
1908
1909 #[test]
1910 fn component_final_inventory_contracts_round_trip_through_candid() {
1911 let component = ComponentInstanceId::from_generated_bytes([88; 32]);
1912 let registry = ComponentRegistryHead {
1913 component,
1914 revision: 21,
1915 content_hash: [89; 32],
1916 };
1917 let request = RootComponentFinalInventoryRequest {
1918 operation_id: [90; 32],
1919 component,
1920 expected_registry: registry.clone(),
1921 };
1922 let response = RootComponentFinalInventoryResponse {
1923 operation_id: request.operation_id,
1924 component,
1925 inventory: RootComponentFinalInventory {
1926 registry,
1927 descendant_content_hash: [91; 32],
1928 registry_encoded_bytes: 4_096,
1929 directory_synchronized_at_ns: 92,
1930 covered_fleet_registry_revision: 93,
1931 covered_fleet_registry_content_hash: [94; 32],
1932 directory_authority_hash: [95; 32],
1933 inventory_hash: [96; 32],
1934 finalized_at_ns: 97,
1935 },
1936 };
1937
1938 let request_bytes =
1939 candid::encode_one(&request).expect("encode Component final inventory request");
1940 let response_bytes =
1941 candid::encode_one(&response).expect("encode Component final inventory response");
1942 assert_eq!(
1943 candid::decode_one::<RootComponentFinalInventoryRequest>(&request_bytes)
1944 .expect("decode Component final inventory request"),
1945 request
1946 );
1947 assert_eq!(
1948 candid::decode_one::<RootComponentFinalInventoryResponse>(&response_bytes)
1949 .expect("decode Component final inventory response"),
1950 response
1951 );
1952 }
1953
1954 #[test]
1955 fn component_subtree_removal_deletion_requests_round_trip_through_candid() {
1956 let prepare = RootComponentSubtreeRemovalDeletePreparationRequest {
1957 operation_id: [50; 32],
1958 component: ComponentInstanceId::from_generated_bytes([51; 32]),
1959 expected_traversal_steps: 3,
1960 expected_leaf_canister_id: Principal::from_slice(&[52; 29]),
1961 expected_leaf_parent_canister_id: Principal::from_slice(&[53; 29]),
1962 };
1963 let request = RootComponentSubtreeRemovalDeleteRequest {
1964 operation_id: prepare.operation_id,
1965 component: prepare.component,
1966 expected_traversal_steps: prepare.expected_traversal_steps,
1967 expected_leaf_canister_id: prepare.expected_leaf_canister_id,
1968 expected_leaf_parent_canister_id: prepare.expected_leaf_parent_canister_id,
1969 };
1970 let membership_request = RootComponentSubtreeRemovalMembershipRemovalRequest {
1971 operation_id: prepare.operation_id,
1972 component: prepare.component,
1973 expected_traversal_steps: prepare.expected_traversal_steps,
1974 expected_leaf_canister_id: prepare.expected_leaf_canister_id,
1975 expected_leaf_parent_canister_id: prepare.expected_leaf_parent_canister_id,
1976 };
1977 let directory_request = RootComponentSubtreeRemovalDirectorySynchronizationRequest {
1978 operation_id: prepare.operation_id,
1979 component: prepare.component,
1980 expected_traversal_steps: prepare.expected_traversal_steps,
1981 expected_leaf_canister_id: prepare.expected_leaf_canister_id,
1982 expected_leaf_parent_canister_id: prepare.expected_leaf_parent_canister_id,
1983 };
1984 let finalization_request = RootComponentSubtreeRemovalLeafFinalizationRequest {
1985 operation_id: prepare.operation_id,
1986 component: prepare.component,
1987 expected_traversal_steps: prepare.expected_traversal_steps,
1988 expected_leaf_canister_id: prepare.expected_leaf_canister_id,
1989 expected_leaf_parent_canister_id: prepare.expected_leaf_parent_canister_id,
1990 };
1991 let prepare_bytes = candid::encode_one(prepare)
1992 .expect("encode subtree removal deletion preparation request");
1993 let request_bytes =
1994 candid::encode_one(request).expect("encode subtree removal deletion request");
1995 let membership_request_bytes = candid::encode_one(membership_request)
1996 .expect("encode subtree removal membership-removal request");
1997 let directory_request_bytes = candid::encode_one(directory_request)
1998 .expect("encode subtree removal Directory synchronization request");
1999 let finalization_request_bytes = candid::encode_one(finalization_request)
2000 .expect("encode subtree removal leaf-finalization request");
2001
2002 assert_eq!(
2003 candid::decode_one::<RootComponentSubtreeRemovalDeletePreparationRequest>(
2004 &prepare_bytes
2005 )
2006 .expect("decode subtree removal deletion preparation request"),
2007 prepare
2008 );
2009 assert_eq!(
2010 candid::decode_one::<RootComponentSubtreeRemovalDeleteRequest>(&request_bytes)
2011 .expect("decode subtree removal deletion request"),
2012 request
2013 );
2014 assert_eq!(
2015 candid::decode_one::<RootComponentSubtreeRemovalMembershipRemovalRequest>(
2016 &membership_request_bytes
2017 )
2018 .expect("decode subtree removal membership-removal request"),
2019 membership_request
2020 );
2021 assert_eq!(
2022 candid::decode_one::<RootComponentSubtreeRemovalDirectorySynchronizationRequest>(
2023 &directory_request_bytes
2024 )
2025 .expect("decode subtree removal Directory synchronization request"),
2026 directory_request
2027 );
2028 assert_eq!(
2029 candid::decode_one::<RootComponentSubtreeRemovalLeafFinalizationRequest>(
2030 &finalization_request_bytes
2031 )
2032 .expect("decode subtree removal leaf-finalization request"),
2033 finalization_request
2034 );
2035 }
2036
2037 #[test]
2038 fn component_subtree_removal_stop_request_round_trips_through_candid() {
2039 let request = RootComponentSubtreeRemovalStopRequest {
2040 operation_id: [50; 32],
2041 component: ComponentInstanceId::from_generated_bytes([51; 32]),
2042 expected_traversal_steps: 3,
2043 expected_leaf_canister_id: Principal::from_slice(&[52; 29]),
2044 expected_leaf_parent_canister_id: Principal::from_slice(&[53; 29]),
2045 };
2046 let bytes =
2047 candid::encode_one(request).expect("encode subtree removal stop execution request");
2048
2049 assert_eq!(
2050 candid::decode_one::<RootComponentSubtreeRemovalStopRequest>(&bytes)
2051 .expect("decode subtree removal stop execution request"),
2052 request
2053 );
2054 }
2055
2056 #[test]
2057 #[expect(
2058 clippy::too_many_lines,
2059 reason = "one round-trip test keeps the complete child lifecycle boundary coherent"
2060 )]
2061 fn component_child_lifecycle_contracts_round_trip_through_candid() {
2062 let component = ComponentInstanceId::from_generated_bytes([11; 32]);
2063 let registry = ComponentRegistryHead {
2064 component,
2065 revision: 2,
2066 content_hash: [12; 32],
2067 };
2068 let request = RootComponentChildAllocationRequest {
2069 operation_id: [13; 32],
2070 component,
2071 expected_registry: registry.clone(),
2072 child_role: CanisterRole::new("project_instance"),
2073 };
2074 let status_request = RootComponentChildAllocationStatusRequest {
2075 operation_id: request.operation_id,
2076 component,
2077 };
2078 let creation_request = RootComponentChildCreationRequest {
2079 operation_id: request.operation_id,
2080 component,
2081 };
2082 let install_request = RootComponentChildInstallRequest {
2083 operation_id: request.operation_id,
2084 component,
2085 };
2086 let commit_request = RootComponentChildCommitRequest {
2087 operation_id: request.operation_id,
2088 component,
2089 };
2090 let directory_request = RootComponentChildDirectoryPreparationRequest {
2091 operation_id: request.operation_id,
2092 component,
2093 };
2094 let activation_request = RootComponentChildRuntimeActivationRequest {
2095 operation_id: request.operation_id,
2096 component,
2097 };
2098 let membership_request = RootComponentChildMembershipActivationRequest {
2099 operation_id: request.operation_id,
2100 component,
2101 };
2102 let root = Principal::from_slice(&[17; 29]);
2103 let parent = Principal::from_slice(&[14; 29]);
2104 let child = Principal::from_slice(&[18; 29]);
2105 let child_binding = ComponentChildBinding {
2106 component: ComponentBinding {
2107 authority: fleet_registry_authority(),
2108 component,
2109 component_spec: "projects".parse().expect("Component Spec"),
2110 spec_hash: [19; 32],
2111 role: CanisterRole::new("project_hub"),
2112 placement_subnet: SubnetId::from_principal(Principal::from_slice(&[20; 29])),
2113 fleet_subnet_root: root,
2114 canister_id: parent,
2115 },
2116 parent_canister_id: parent,
2117 role: request.child_role.clone(),
2118 canister_id: child,
2119 };
2120 let response = RootComponentChildAllocationResponse {
2121 operation_id: request.operation_id,
2122 component,
2123 parent_canister_id: parent,
2124 parent_role: CanisterRole::new("project_hub"),
2125 child_role: request.child_role.clone(),
2126 child_kind: ComponentChildKind::Instance,
2127 maximum_instances_per_parent: 10_000,
2128 maximum_descendants: 20_000,
2129 maximum_registry_bytes: 16_777_216,
2130 reserved_against_registry: registry,
2131 release_set: FleetSubnetRootReleaseSet {
2132 release_build_id: ReleaseBuildId::from_nonce(ReleaseBuildNonce::from_random_bytes(
2133 [15; 32],
2134 )),
2135 manifest_digest: ReleaseSetDigest::from_bytes([16; 32]),
2136 },
2137 phase: RootComponentAllocationPhase::Verified,
2138 creation: Some(RootComponentCreationEvidence {
2139 wasm_store: Principal::from_slice(&[21; 29]),
2140 payload_hash: [22; 32],
2141 payload_size_bytes: 4_096,
2142 initial_cycles: Cycles::new(5_000_000_000_000),
2143 controller: root,
2144 canister: Some(child),
2145 }),
2146 installation: Some(RootComponentChildInstallEvidence {
2147 raw_module_hash: [23; 32],
2148 chunk_hashes: vec![vec![24; 32]],
2149 binding: child_binding.clone(),
2150 }),
2151 };
2152 let commit_response = RootComponentChildCommitResponse {
2153 allocation: response.clone(),
2154 registry: ComponentRegistryPartitionResponse {
2155 head: ComponentRegistryHead {
2156 component,
2157 revision: 3,
2158 content_hash: [25; 32],
2159 },
2160 binding: child_binding.component.clone(),
2161 provisioning_origin: ComponentProvisioningOrigin::FleetAdministrator {
2162 caller: Principal::from_slice(&[26; 29]),
2163 },
2164 release_set: response.release_set,
2165 status: ComponentLifecycleStatus::Active,
2166 reserved_descendants: 0,
2167 committed_descendants: 1,
2168 encoded_bytes: 8_192,
2169 },
2170 directory: ComponentDirectoryHead {
2171 provenance: ComponentDirectoryProvenance {
2172 component: child_binding.component.clone(),
2173 source_fleet_subnet_root: root,
2174 component_registry_revision: 3,
2175 component_registry_content_hash: [25; 32],
2176 synchronized_at_ns: 27,
2177 },
2178 descendant_count: 1,
2179 },
2180 };
2181 let runtime_authority = ComponentRuntimeDirectoryAuthority {
2182 fleet: FleetDirectorySnapshot {
2183 provenance: crate::dto::fleet_registry::FleetDirectoryProvenance {
2184 registry: FleetRegistryVersion {
2185 authority: fleet_registry_authority(),
2186 revision: 4,
2187 content_hash: [28; 32],
2188 },
2189 source_fleet_subnet_root: root,
2190 },
2191 fleet_subnet_roots: vec![
2192 crate::dto::fleet_registry::FleetSubnetRootDirectoryEntry {
2193 placement_subnet: commit_response.registry.binding.placement_subnet,
2194 fleet_subnet_root: root,
2195 status: crate::dto::fleet_registry::FleetSubnetRootStatus::Active,
2196 },
2197 ],
2198 },
2199 component: commit_response.directory.clone(),
2200 };
2201 let activation = ComponentRuntimeActivationEvidence {
2202 directory_authority_hash: [29; 32],
2203 activated_at_ns: 30,
2204 };
2205 let directory_response = RootComponentChildDirectoryPreparationResponse {
2206 committed: commit_response.clone(),
2207 child: ComponentRuntimeStatusResponse {
2208 operation_id: request.operation_id,
2209 binding: ManagedCanisterBinding::ComponentChild(child_binding.clone()),
2210 phase: ComponentRuntimePhase::DirectoryPrepared,
2211 authority: Some(runtime_authority.clone()),
2212 authority_hash: Some([31; 32]),
2213 activation: None,
2214 },
2215 owning_component: ComponentRuntimeDirectoryConvergenceEvidence {
2216 operation_id: [32; 32],
2217 binding: ManagedCanisterBinding::Component(child_binding.component.clone()),
2218 covered_authority: runtime_authority.clone(),
2219 covered_authority_hash: [31; 32],
2220 activation,
2221 },
2222 parent: None,
2223 };
2224 let activation_response = RootComponentChildRuntimeActivationResponse {
2225 committed: commit_response.clone(),
2226 child: ComponentRuntimeStatusResponse {
2227 operation_id: request.operation_id,
2228 binding: ManagedCanisterBinding::ComponentChild(child_binding.clone()),
2229 phase: ComponentRuntimePhase::Active,
2230 authority: Some(runtime_authority.clone()),
2231 authority_hash: Some([31; 32]),
2232 activation: Some(ComponentRuntimeActivationEvidence {
2233 directory_authority_hash: [31; 32],
2234 activated_at_ns: 33,
2235 }),
2236 },
2237 };
2238 let active_directory = ComponentDirectoryHead {
2239 provenance: ComponentDirectoryProvenance {
2240 component: child_binding.component.clone(),
2241 source_fleet_subnet_root: root,
2242 component_registry_revision: 4,
2243 component_registry_content_hash: [34; 32],
2244 synchronized_at_ns: 35,
2245 },
2246 descendant_count: 1,
2247 };
2248 let active_authority = ComponentRuntimeDirectoryAuthority {
2249 fleet: runtime_authority.fleet,
2250 component: active_directory.clone(),
2251 };
2252 let membership_response = RootComponentChildMembershipActivationResponse {
2253 committed: commit_response.clone(),
2254 registry: ComponentRegistryPartitionResponse {
2255 head: ComponentRegistryHead {
2256 component,
2257 revision: 4,
2258 content_hash: [34; 32],
2259 },
2260 binding: child_binding.component.clone(),
2261 provisioning_origin: commit_response.registry.provisioning_origin.clone(),
2262 release_set: commit_response.registry.release_set,
2263 status: ComponentLifecycleStatus::Active,
2264 reserved_descendants: 0,
2265 committed_descendants: 1,
2266 encoded_bytes: 8_256,
2267 },
2268 directory: active_directory,
2269 child: ComponentRuntimeStatusResponse {
2270 operation_id: request.operation_id,
2271 binding: ManagedCanisterBinding::ComponentChild(child_binding),
2272 phase: ComponentRuntimePhase::Active,
2273 authority: Some(active_authority),
2274 authority_hash: Some([36; 32]),
2275 activation: Some(ComponentRuntimeActivationEvidence {
2276 directory_authority_hash: [31; 32],
2277 activated_at_ns: 33,
2278 }),
2279 },
2280 };
2281
2282 let request_bytes = candid::encode_one(&request).expect("encode child reservation");
2283 let status_bytes =
2284 candid::encode_one(status_request).expect("encode child reservation status");
2285 let creation_bytes =
2286 candid::encode_one(creation_request).expect("encode child creation request");
2287 let install_bytes =
2288 candid::encode_one(install_request).expect("encode child install request");
2289 let response_bytes = candid::encode_one(&response).expect("encode child response");
2290 let commit_request_bytes =
2291 candid::encode_one(commit_request).expect("encode child commit request");
2292 let directory_request_bytes =
2293 candid::encode_one(directory_request).expect("encode child Directory request");
2294 let activation_request_bytes =
2295 candid::encode_one(activation_request).expect("encode child activation request");
2296 let membership_request_bytes =
2297 candid::encode_one(membership_request).expect("encode child membership request");
2298 let commit_response_bytes =
2299 candid::encode_one(&commit_response).expect("encode child commit response");
2300 let directory_response_bytes =
2301 candid::encode_one(&directory_response).expect("encode child Directory response");
2302 let activation_response_bytes =
2303 candid::encode_one(&activation_response).expect("encode child activation response");
2304 let membership_response_bytes =
2305 candid::encode_one(&membership_response).expect("encode child membership response");
2306
2307 assert_eq!(
2308 candid::decode_one::<RootComponentChildAllocationRequest>(&request_bytes)
2309 .expect("decode child reservation"),
2310 request
2311 );
2312 assert_eq!(
2313 candid::decode_one::<RootComponentChildAllocationStatusRequest>(&status_bytes)
2314 .expect("decode child reservation status"),
2315 status_request
2316 );
2317 assert_eq!(
2318 candid::decode_one::<RootComponentChildCreationRequest>(&creation_bytes)
2319 .expect("decode child creation request"),
2320 creation_request
2321 );
2322 assert_eq!(
2323 candid::decode_one::<RootComponentChildInstallRequest>(&install_bytes)
2324 .expect("decode child install request"),
2325 install_request
2326 );
2327 assert_eq!(
2328 candid::decode_one::<RootComponentChildAllocationResponse>(&response_bytes)
2329 .expect("decode child response"),
2330 response
2331 );
2332 assert_eq!(
2333 candid::decode_one::<RootComponentChildCommitRequest>(&commit_request_bytes)
2334 .expect("decode child commit request"),
2335 commit_request
2336 );
2337 assert_eq!(
2338 candid::decode_one::<RootComponentChildDirectoryPreparationRequest>(
2339 &directory_request_bytes
2340 )
2341 .expect("decode child Directory request"),
2342 directory_request
2343 );
2344 assert_eq!(
2345 candid::decode_one::<RootComponentChildCommitResponse>(&commit_response_bytes)
2346 .expect("decode child commit response"),
2347 commit_response
2348 );
2349 assert_eq!(
2350 candid::decode_one::<RootComponentChildDirectoryPreparationResponse>(
2351 &directory_response_bytes
2352 )
2353 .expect("decode child Directory response"),
2354 directory_response
2355 );
2356 assert_eq!(
2357 candid::decode_one::<RootComponentChildRuntimeActivationRequest>(
2358 &activation_request_bytes
2359 )
2360 .expect("decode child activation request"),
2361 activation_request
2362 );
2363 assert_eq!(
2364 candid::decode_one::<RootComponentChildRuntimeActivationResponse>(
2365 &activation_response_bytes
2366 )
2367 .expect("decode child activation response"),
2368 activation_response
2369 );
2370 assert_eq!(
2371 candid::decode_one::<RootComponentChildMembershipActivationRequest>(
2372 &membership_request_bytes
2373 )
2374 .expect("decode child membership request"),
2375 membership_request
2376 );
2377 assert_eq!(
2378 candid::decode_one::<RootComponentChildMembershipActivationResponse>(
2379 &membership_response_bytes
2380 )
2381 .expect("decode child membership response"),
2382 membership_response
2383 );
2384 }
2385
2386 #[test]
2387 fn component_install_request_round_trips_through_candid() {
2388 let request = RootComponentInstallRequest {
2389 operation_id: [10; 32],
2390 };
2391 let bytes = candid::encode_one(request).expect("encode install request");
2392
2393 assert_eq!(
2394 candid::decode_one::<RootComponentInstallRequest>(&bytes)
2395 .expect("decode install request"),
2396 request
2397 );
2398 }
2399
2400 #[test]
2401 fn component_commit_request_round_trips_through_candid() {
2402 let request = RootComponentCommitRequest {
2403 operation_id: [10; 32],
2404 };
2405 let bytes = candid::encode_one(request).expect("encode commit request");
2406
2407 assert_eq!(
2408 candid::decode_one::<RootComponentCommitRequest>(&bytes)
2409 .expect("decode commit request"),
2410 request
2411 );
2412 }
2413
2414 #[test]
2415 fn component_runtime_activation_requests_round_trip_through_candid() {
2416 let root_request = RootComponentRuntimeActivationRequest {
2417 operation_id: [22; 32],
2418 };
2419 let target_request = ComponentRuntimeActivationRequest {
2420 operation_id: root_request.operation_id,
2421 directory_authority_hash: [23; 32],
2422 };
2423 let membership_request = RootComponentMembershipActivationRequest {
2424 operation_id: root_request.operation_id,
2425 };
2426 let root_bytes = candid::encode_one(root_request).expect("encode root activation request");
2427 let target_bytes =
2428 candid::encode_one(target_request).expect("encode target activation request");
2429 let membership_bytes =
2430 candid::encode_one(membership_request).expect("encode membership activation request");
2431
2432 assert_eq!(
2433 candid::decode_one::<RootComponentRuntimeActivationRequest>(&root_bytes)
2434 .expect("decode root activation request"),
2435 root_request
2436 );
2437 assert_eq!(
2438 candid::decode_one::<ComponentRuntimeActivationRequest>(&target_bytes)
2439 .expect("decode target activation request"),
2440 target_request
2441 );
2442 assert_eq!(
2443 candid::decode_one::<RootComponentMembershipActivationRequest>(&membership_bytes)
2444 .expect("decode membership activation request"),
2445 membership_request
2446 );
2447 }
2448}