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 RootComponentSubtreeRemovalStatusRequest {
186 pub operation_id: [u8; 32],
187 pub component: ComponentInstanceId,
188}
189
190#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
197pub struct RootComponentChildCreationRequest {
198 pub operation_id: [u8; 32],
199 pub component: ComponentInstanceId,
200}
201
202#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
209pub struct RootComponentChildInstallRequest {
210 pub operation_id: [u8; 32],
211 pub component: ComponentInstanceId,
212}
213
214#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
221pub struct RootComponentChildCommitRequest {
222 pub operation_id: [u8; 32],
223 pub component: ComponentInstanceId,
224}
225
226#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
233pub struct RootComponentChildDirectoryPreparationRequest {
234 pub operation_id: [u8; 32],
235 pub component: ComponentInstanceId,
236}
237
238#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
245pub struct RootComponentChildRuntimeActivationRequest {
246 pub operation_id: [u8; 32],
247 pub component: ComponentInstanceId,
248}
249
250#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
257pub struct RootComponentChildMembershipActivationRequest {
258 pub operation_id: [u8; 32],
259 pub component: ComponentInstanceId,
260}
261
262#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
269pub struct RootComponentCreationRequest {
270 pub operation_id: [u8; 32],
271}
272
273#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
280pub struct RootComponentInstallRequest {
281 pub operation_id: [u8; 32],
282}
283
284#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
291pub struct RootComponentCommitRequest {
292 pub operation_id: [u8; 32],
293}
294
295#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
302pub struct RootComponentDirectoryPreparationRequest {
303 pub operation_id: [u8; 32],
304}
305
306#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
313pub struct RootComponentRuntimeActivationRequest {
314 pub operation_id: [u8; 32],
315}
316
317#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
324pub struct RootComponentMembershipActivationRequest {
325 pub operation_id: [u8; 32],
326}
327
328#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
335pub enum ComponentProvisioningOrigin {
336 FleetAdministrator { caller: Principal },
337}
338
339#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
346pub enum RootComponentAllocationPhase {
347 Reserved,
348 CreationIntent,
349 Created,
350 InstallIntent,
351 Installed,
352 Verified,
353 Committed,
354}
355
356#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
363pub enum RootComponentSubtreeRemovalPhase {
364 Fenced,
365 Traversing(RootComponentSubtreeRemovalNode),
366 LeafSelected(RootComponentSubtreeRemovalNode),
367 StopIntent(RootComponentSubtreeRemovalStopIntent),
368 Stopped(RootComponentSubtreeRemovalStoppedReceipt),
369}
370
371#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
378pub struct RootComponentSubtreeRemovalNode {
379 pub canister_id: Principal,
380 pub parent_canister_id: Principal,
381 pub role: CanisterRole,
382 pub kind: ComponentChildKind,
383 pub installed_artifact_hash: [u8; 32],
384 pub status: ComponentLifecycleStatus,
385}
386
387#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
394pub struct RootComponentSubtreeRemovalStopIntent {
395 pub leaf: RootComponentSubtreeRemovalNode,
396 pub controller: Principal,
397}
398
399#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
406pub struct RootComponentSubtreeRemovalStoppedReceipt {
407 pub stop: RootComponentSubtreeRemovalStopIntent,
408 pub observed_module_hash: [u8; 32],
409}
410
411#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
418pub enum ComponentLifecycleStatus {
419 Prepared,
420 Active,
421 Draining,
422 Removed,
423}
424
425#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
432pub struct ComponentRegistryHead {
433 pub component: ComponentInstanceId,
434 pub revision: u64,
435 pub content_hash: [u8; 32],
436}
437
438#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
445pub struct ComponentRegistryPartitionRequest {
446 pub component: ComponentInstanceId,
447}
448
449#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
456pub struct ComponentRegistryPartitionResponse {
457 pub head: ComponentRegistryHead,
458 pub binding: ComponentBinding,
459 pub provisioning_origin: ComponentProvisioningOrigin,
460 pub release_set: FleetSubnetRootReleaseSet,
461 pub status: ComponentLifecycleStatus,
462 pub reserved_descendants: u32,
463 pub committed_descendants: u32,
464 pub encoded_bytes: u64,
465}
466
467#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
474pub struct ComponentDirectoryProvenance {
475 pub component: ComponentBinding,
476 pub source_fleet_subnet_root: Principal,
477 pub component_registry_revision: u64,
478 pub component_registry_content_hash: [u8; 32],
479 pub synchronized_at_ns: u64,
480}
481
482#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
489pub struct ComponentDirectoryHead {
490 pub provenance: ComponentDirectoryProvenance,
491 pub descendant_count: u32,
492}
493
494#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
501pub struct ComponentDirectoryHeadRequest {
502 pub component: ComponentInstanceId,
503}
504
505#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
512pub struct ComponentDirectoryPageCursor(pub Vec<u8>);
513
514#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
521pub struct ComponentDirectoryPageRequest {
522 pub directory: ComponentDirectoryHead,
523 pub parent_canister_id: Option<Principal>,
524 pub role: Option<CanisterRole>,
525 pub status: Option<ComponentLifecycleStatus>,
526 pub cursor: Option<ComponentDirectoryPageCursor>,
527 pub limit: u16,
528}
529
530#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
537pub struct ComponentDirectoryChildEntry {
538 pub binding: ComponentChildBinding,
539 pub kind: ComponentChildKind,
540 pub installed_artifact_hash: [u8; 32],
541 pub status: ComponentLifecycleStatus,
542}
543
544#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
551pub struct ComponentDirectoryPageResponse {
552 pub directory: ComponentDirectoryHead,
553 pub entries: Vec<ComponentDirectoryChildEntry>,
554 pub next_cursor: Option<ComponentDirectoryPageCursor>,
555}
556
557#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
564pub struct ComponentRuntimeDirectoryAuthority {
565 pub fleet: FleetDirectorySnapshot,
566 pub component: ComponentDirectoryHead,
567}
568
569#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
576pub struct ComponentRuntimeDirectoryPreparationRequest {
577 pub operation_id: [u8; 32],
578 pub authority: ComponentRuntimeDirectoryAuthority,
579}
580
581#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
588pub struct ComponentRuntimeDirectorySynchronizationRequest {
589 pub operation_id: [u8; 32],
590 pub authority: ComponentRuntimeDirectoryAuthority,
591}
592
593#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
600pub enum ComponentRuntimePhase {
601 AwaitingDirectory,
602 DirectoryPrepared,
603 Active,
604}
605
606#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
613pub struct ComponentRuntimeActivationEvidence {
614 pub directory_authority_hash: [u8; 32],
615 pub activated_at_ns: u64,
616}
617
618#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
625pub struct ComponentRuntimeActivationRequest {
626 pub operation_id: [u8; 32],
627 pub directory_authority_hash: [u8; 32],
628}
629
630#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
637pub struct ComponentRuntimeStatusResponse {
638 pub operation_id: [u8; 32],
639 pub binding: ManagedCanisterBinding,
640 pub phase: ComponentRuntimePhase,
641 pub authority: Option<ComponentRuntimeDirectoryAuthority>,
642 pub authority_hash: Option<[u8; 32]>,
643 pub activation: Option<ComponentRuntimeActivationEvidence>,
644}
645
646#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
653pub struct ComponentRuntimeDirectoryConvergenceEvidence {
654 pub operation_id: [u8; 32],
655 pub binding: ManagedCanisterBinding,
656 pub covered_authority: ComponentRuntimeDirectoryAuthority,
657 pub covered_authority_hash: [u8; 32],
658 pub activation: ComponentRuntimeActivationEvidence,
659}
660
661#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
668pub struct RootComponentCreationEvidence {
669 pub wasm_store: Principal,
670 pub payload_hash: [u8; 32],
671 pub payload_size_bytes: u64,
672 pub initial_cycles: Cycles,
673 pub controller: Principal,
674 pub canister: Option<Principal>,
675}
676
677#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
684pub struct RootComponentInstallEvidence {
685 pub raw_module_hash: [u8; 32],
686 pub chunk_hashes: Vec<Vec<u8>>,
687 pub binding: ComponentBinding,
688}
689
690#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
697pub struct RootComponentChildInstallEvidence {
698 pub raw_module_hash: [u8; 32],
699 pub chunk_hashes: Vec<Vec<u8>>,
700 pub binding: ComponentChildBinding,
701}
702
703#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
710pub struct RootComponentAllocationResponse {
711 pub operation_id: [u8; 32],
712 pub allocation_sequence: u64,
713 pub component: ComponentInstanceId,
714 pub component_spec: ComponentSpecId,
715 pub spec_hash: [u8; 32],
716 pub role: CanisterRole,
717 pub provisioning_origin: ComponentProvisioningOrigin,
718 pub release_set: FleetSubnetRootReleaseSet,
719 pub phase: RootComponentAllocationPhase,
720 pub creation: Option<RootComponentCreationEvidence>,
721 pub installation: Option<RootComponentInstallEvidence>,
722}
723
724#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
731pub struct RootComponentChildAllocationResponse {
732 pub operation_id: [u8; 32],
733 pub component: ComponentInstanceId,
734 pub parent_canister_id: Principal,
735 pub parent_role: CanisterRole,
736 pub child_role: CanisterRole,
737 pub child_kind: ComponentChildKind,
738 pub maximum_instances_per_parent: u32,
739 pub maximum_descendants: u32,
740 pub maximum_registry_bytes: u64,
741 pub reserved_against_registry: ComponentRegistryHead,
742 pub release_set: FleetSubnetRootReleaseSet,
743 pub phase: RootComponentAllocationPhase,
744 pub creation: Option<RootComponentCreationEvidence>,
745 pub installation: Option<RootComponentChildInstallEvidence>,
746}
747
748#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
755pub struct RootComponentSubtreeRemovalResponse {
756 pub operation_id: [u8; 32],
757 pub component: ComponentInstanceId,
758 pub target_canister_id: Principal,
759 pub target_parent_canister_id: Principal,
760 pub target_role: CanisterRole,
761 pub target_status: ComponentLifecycleStatus,
762 pub reserved_against_registry: ComponentRegistryHead,
763 pub traversal_steps: u32,
764 pub phase: RootComponentSubtreeRemovalPhase,
765}
766
767#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
774pub struct RootComponentChildCommitResponse {
775 pub allocation: RootComponentChildAllocationResponse,
776 pub registry: ComponentRegistryPartitionResponse,
777 pub directory: ComponentDirectoryHead,
778}
779
780#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
787pub struct RootComponentChildDirectoryPreparationResponse {
788 pub committed: RootComponentChildCommitResponse,
789 pub child: ComponentRuntimeStatusResponse,
790 pub owning_component: ComponentRuntimeDirectoryConvergenceEvidence,
791 pub parent: Option<ComponentRuntimeDirectoryConvergenceEvidence>,
792}
793
794#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
801pub struct RootComponentChildRuntimeActivationResponse {
802 pub committed: RootComponentChildCommitResponse,
803 pub child: ComponentRuntimeStatusResponse,
804}
805
806#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
813pub struct RootComponentChildMembershipActivationResponse {
814 pub committed: RootComponentChildCommitResponse,
815 pub registry: ComponentRegistryPartitionResponse,
816 pub directory: ComponentDirectoryHead,
817 pub child: ComponentRuntimeStatusResponse,
818}
819
820#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
827pub struct RootComponentCommitResponse {
828 pub allocation: RootComponentAllocationResponse,
829 pub registry: ComponentRegistryPartitionResponse,
830 pub directory: ComponentDirectoryHead,
831}
832
833#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
840pub struct RootComponentDirectoryPreparationResponse {
841 pub committed: RootComponentCommitResponse,
842 pub target: ComponentRuntimeStatusResponse,
843}
844
845#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
852pub struct RootComponentRuntimeActivationResponse {
853 pub committed: RootComponentCommitResponse,
854 pub target: ComponentRuntimeStatusResponse,
855}
856
857#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
864pub struct RootComponentMembershipActivationResponse {
865 pub allocation: RootComponentAllocationResponse,
866 pub registry: ComponentRegistryPartitionResponse,
867 pub directory: ComponentDirectoryHead,
868 pub target: ComponentRuntimeStatusResponse,
869}
870
871#[cfg(test)]
872mod tests {
873 use super::*;
874 use crate::{
875 dto::root_store::RootStoreBootstrapRequest,
876 ids::{
877 AppId, CanonicalNetworkId, FleetCoordinatorBinding, FleetId, FleetKey,
878 FleetRegistryAuthority, ReleaseBuildId, ReleaseBuildNonce, ReleaseSetDigest, SubnetId,
879 },
880 };
881
882 #[test]
883 fn component_registry_contracts_round_trip_through_candid() {
884 let request = RootComponentRegistryPreparationRequest {
885 store_bootstrap: RootStoreBootstrapRequest {
886 manifest_payload_size_bytes: 128,
887 },
888 expected_fleet_registry: FleetRegistryVersion {
889 authority: fleet_registry_authority(),
890 revision: 4,
891 content_hash: [5; 32],
892 },
893 };
894 let response = RootComponentRegistryStatusResponse {
895 fleet_subnet_root: Principal::from_slice(&[6; 29]),
896 prepared_against_registry: request.expected_fleet_registry.clone(),
897 release_set: FleetSubnetRootReleaseSet {
898 release_build_id: ReleaseBuildId::from_nonce(ReleaseBuildNonce::from_random_bytes(
899 [7; 32],
900 )),
901 manifest_digest: ReleaseSetDigest::from_bytes([8; 32]),
902 },
903 component_topology_digest: ComponentTopologyDigest::from_bytes([9; 32]),
904 next_allocation_sequence: 1,
905 reserved_component_instances: 0,
906 committed_component_instances: 0,
907 managed_descendants: 0,
908 known_created_component_canisters: 0,
909 encoded_bytes: 0,
910 initial_inventory: Some(RootComponentInitialInventoryStatus {
911 fleet_activation_operation_id: [10; 32],
912 component_count: 0,
913 inventory_hash: [11; 32],
914 sealed_at_ns: 12,
915 directories_converged: true,
916 root_runtime_activated: true,
917 }),
918 };
919 let allocation = RootComponentAllocationResponse {
920 operation_id: [10; 32],
921 allocation_sequence: 1,
922 component: ComponentInstanceId::from_generated_bytes([11; 32]),
923 component_spec: "projects".parse().expect("Component Spec ID"),
924 spec_hash: [12; 32],
925 role: CanisterRole::new("project_hub"),
926 provisioning_origin: ComponentProvisioningOrigin::FleetAdministrator {
927 caller: Principal::from_slice(&[13; 29]),
928 },
929 release_set: response.release_set,
930 phase: RootComponentAllocationPhase::Reserved,
931 creation: None,
932 installation: None,
933 };
934 let created = RootComponentAllocationResponse {
935 phase: RootComponentAllocationPhase::Created,
936 creation: Some(RootComponentCreationEvidence {
937 wasm_store: Principal::from_slice(&[14; 29]),
938 payload_hash: [15; 32],
939 payload_size_bytes: 4_096,
940 initial_cycles: Cycles::new(5_000_000_000_000),
941 controller: Principal::from_slice(&[6; 29]),
942 canister: Some(Principal::from_slice(&[16; 29])),
943 }),
944 installation: None,
945 ..allocation.clone()
946 };
947 let request_bytes = candid::encode_one(&request).expect("encode request");
948 let response_bytes = candid::encode_one(&response).expect("encode response");
949 let allocation_bytes = candid::encode_one(&allocation).expect("encode allocation");
950 let created_bytes = candid::encode_one(&created).expect("encode created allocation");
951
952 assert_eq!(
953 candid::decode_one::<RootComponentRegistryPreparationRequest>(&request_bytes)
954 .expect("decode request"),
955 request
956 );
957 assert_eq!(
958 candid::decode_one::<RootComponentRegistryStatusResponse>(&response_bytes)
959 .expect("decode response"),
960 response
961 );
962 assert_eq!(
963 candid::decode_one::<RootComponentAllocationResponse>(&allocation_bytes)
964 .expect("decode allocation"),
965 allocation
966 );
967 assert_eq!(
968 candid::decode_one::<RootComponentAllocationResponse>(&created_bytes)
969 .expect("decode created allocation"),
970 created
971 );
972 }
973
974 #[test]
975 fn component_commit_response_round_trips_through_candid() {
976 let root = Principal::from_slice(&[6; 29]);
977 let component = ComponentInstanceId::from_generated_bytes([11; 32]);
978 let component_spec: ComponentSpecId = "projects".parse().expect("Component Spec ID");
979 let release_set = FleetSubnetRootReleaseSet {
980 release_build_id: ReleaseBuildId::from_nonce(ReleaseBuildNonce::from_random_bytes(
981 [7; 32],
982 )),
983 manifest_digest: ReleaseSetDigest::from_bytes([8; 32]),
984 };
985 let provisioning_origin = ComponentProvisioningOrigin::FleetAdministrator {
986 caller: Principal::from_slice(&[13; 29]),
987 };
988 let binding = ComponentBinding {
989 authority: fleet_registry_authority(),
990 component,
991 component_spec: component_spec.clone(),
992 spec_hash: [12; 32],
993 role: CanisterRole::new("project_hub"),
994 placement_subnet: SubnetId::from_principal(Principal::from_slice(&[17; 29])),
995 fleet_subnet_root: root,
996 canister_id: Principal::from_slice(&[16; 29]),
997 };
998 let head = ComponentRegistryHead {
999 component,
1000 revision: 1,
1001 content_hash: [18; 32],
1002 };
1003 let committed = RootComponentCommitResponse {
1004 allocation: RootComponentAllocationResponse {
1005 operation_id: [10; 32],
1006 allocation_sequence: 1,
1007 component,
1008 component_spec,
1009 spec_hash: binding.spec_hash,
1010 role: binding.role.clone(),
1011 provisioning_origin: provisioning_origin.clone(),
1012 release_set,
1013 phase: RootComponentAllocationPhase::Committed,
1014 creation: Some(RootComponentCreationEvidence {
1015 wasm_store: Principal::from_slice(&[14; 29]),
1016 payload_hash: [15; 32],
1017 payload_size_bytes: 4_096,
1018 initial_cycles: Cycles::new(5_000_000_000_000),
1019 controller: root,
1020 canister: Some(binding.canister_id),
1021 }),
1022 installation: Some(RootComponentInstallEvidence {
1023 raw_module_hash: [20; 32],
1024 chunk_hashes: vec![vec![21; 32]],
1025 binding: binding.clone(),
1026 }),
1027 },
1028 registry: ComponentRegistryPartitionResponse {
1029 head: head.clone(),
1030 binding: binding.clone(),
1031 provisioning_origin,
1032 release_set,
1033 status: ComponentLifecycleStatus::Prepared,
1034 reserved_descendants: 0,
1035 committed_descendants: 0,
1036 encoded_bytes: 2_048,
1037 },
1038 directory: ComponentDirectoryHead {
1039 provenance: ComponentDirectoryProvenance {
1040 component: binding,
1041 source_fleet_subnet_root: root,
1042 component_registry_revision: head.revision,
1043 component_registry_content_hash: head.content_hash,
1044 synchronized_at_ns: 19,
1045 },
1046 descendant_count: 0,
1047 },
1048 };
1049 let committed_bytes = candid::encode_one(&committed).expect("encode committed allocation");
1050
1051 assert_eq!(
1052 candid::decode_one::<RootComponentCommitResponse>(&committed_bytes)
1053 .expect("decode committed allocation"),
1054 committed
1055 );
1056 }
1057
1058 #[test]
1059 fn component_directory_page_contracts_round_trip_through_candid() {
1060 let root = Principal::from_slice(&[6; 29]);
1061 let component = ComponentInstanceId::from_generated_bytes([11; 32]);
1062 let binding = ComponentBinding {
1063 authority: fleet_registry_authority(),
1064 component,
1065 component_spec: "projects".parse().expect("Component Spec ID"),
1066 spec_hash: [12; 32],
1067 role: CanisterRole::new("project_hub"),
1068 placement_subnet: SubnetId::from_principal(Principal::from_slice(&[17; 29])),
1069 fleet_subnet_root: root,
1070 canister_id: Principal::from_slice(&[16; 29]),
1071 };
1072 let directory = ComponentDirectoryHead {
1073 provenance: ComponentDirectoryProvenance {
1074 component: binding.clone(),
1075 source_fleet_subnet_root: root,
1076 component_registry_revision: 3,
1077 component_registry_content_hash: [18; 32],
1078 synchronized_at_ns: 19,
1079 },
1080 descendant_count: 1,
1081 };
1082 let request = ComponentDirectoryPageRequest {
1083 directory: directory.clone(),
1084 parent_canister_id: Some(binding.canister_id),
1085 role: Some(CanisterRole::new("project_instance")),
1086 status: Some(ComponentLifecycleStatus::Active),
1087 cursor: Some(ComponentDirectoryPageCursor(vec![20; 64])),
1088 limit: 50,
1089 };
1090 let response = ComponentDirectoryPageResponse {
1091 directory,
1092 entries: vec![ComponentDirectoryChildEntry {
1093 binding: ComponentChildBinding {
1094 component: binding.clone(),
1095 parent_canister_id: binding.canister_id,
1096 role: CanisterRole::new("project_instance"),
1097 canister_id: Principal::from_slice(&[21; 29]),
1098 },
1099 kind: ComponentChildKind::Instance,
1100 installed_artifact_hash: [22; 32],
1101 status: ComponentLifecycleStatus::Active,
1102 }],
1103 next_cursor: Some(ComponentDirectoryPageCursor(vec![23; 64])),
1104 };
1105 let request_bytes = candid::encode_one(&request).expect("encode Directory page request");
1106 let response_bytes = candid::encode_one(&response).expect("encode Directory page response");
1107
1108 assert_eq!(
1109 candid::decode_one::<ComponentDirectoryPageRequest>(&request_bytes)
1110 .expect("decode Directory page request"),
1111 request
1112 );
1113 assert_eq!(
1114 candid::decode_one::<ComponentDirectoryPageResponse>(&response_bytes)
1115 .expect("decode Directory page response"),
1116 response
1117 );
1118 }
1119
1120 fn fleet_registry_authority() -> FleetRegistryAuthority {
1121 FleetRegistryAuthority {
1122 binding: FleetCoordinatorBinding {
1123 fleet: crate::ids::FleetBinding {
1124 fleet: FleetKey {
1125 canonical_network_id: CanonicalNetworkId::ic_mainnet(),
1126 fleet_id: FleetId::from_generated_bytes([1; 32]),
1127 },
1128 app: AppId::from("toko"),
1129 },
1130 coordinator_subnet: SubnetId::from_principal(Principal::from_slice(&[2; 29])),
1131 coordinator: Principal::from_slice(&[3; 29]),
1132 },
1133 epoch: 1,
1134 }
1135 }
1136
1137 #[test]
1138 fn component_creation_request_round_trips_through_candid() {
1139 let request = RootComponentCreationRequest {
1140 operation_id: [10; 32],
1141 };
1142 let bytes = candid::encode_one(request).expect("encode creation request");
1143
1144 assert_eq!(
1145 candid::decode_one::<RootComponentCreationRequest>(&bytes)
1146 .expect("decode creation request"),
1147 request
1148 );
1149 }
1150
1151 #[test]
1152 fn component_subtree_removal_contracts_round_trip_through_candid() {
1153 let component = ComponentInstanceId::from_generated_bytes([41; 32]);
1154 let registry = ComponentRegistryHead {
1155 component,
1156 revision: 7,
1157 content_hash: [42; 32],
1158 };
1159 let request = RootComponentSubtreeRemovalRequest {
1160 operation_id: [43; 32],
1161 component,
1162 target_canister_id: Principal::from_slice(&[44; 29]),
1163 expected_registry: registry.clone(),
1164 };
1165 let status_request = RootComponentSubtreeRemovalStatusRequest {
1166 operation_id: request.operation_id,
1167 component,
1168 };
1169 let advance_request = RootComponentSubtreeRemovalAdvanceRequest {
1170 operation_id: request.operation_id,
1171 component,
1172 expected_traversal_steps: 1,
1173 };
1174 let stop_request = RootComponentSubtreeRemovalStopPreparationRequest {
1175 operation_id: request.operation_id,
1176 component,
1177 expected_traversal_steps: 2,
1178 expected_leaf_canister_id: Principal::from_slice(&[46; 29]),
1179 expected_leaf_parent_canister_id: request.target_canister_id,
1180 };
1181 let response = RootComponentSubtreeRemovalResponse {
1182 operation_id: request.operation_id,
1183 component,
1184 target_canister_id: request.target_canister_id,
1185 target_parent_canister_id: Principal::from_slice(&[45; 29]),
1186 target_role: CanisterRole::new("project_instance"),
1187 target_status: ComponentLifecycleStatus::Active,
1188 reserved_against_registry: registry,
1189 traversal_steps: 2,
1190 phase: RootComponentSubtreeRemovalPhase::Stopped(
1191 RootComponentSubtreeRemovalStoppedReceipt {
1192 observed_module_hash: [49; 32],
1193 stop: RootComponentSubtreeRemovalStopIntent {
1194 controller: Principal::from_slice(&[48; 29]),
1195 leaf: RootComponentSubtreeRemovalNode {
1196 canister_id: Principal::from_slice(&[46; 29]),
1197 parent_canister_id: request.target_canister_id,
1198 role: CanisterRole::new("project_ledger"),
1199 kind: ComponentChildKind::Singleton,
1200 installed_artifact_hash: [47; 32],
1201 status: ComponentLifecycleStatus::Active,
1202 },
1203 },
1204 },
1205 ),
1206 };
1207
1208 let request_bytes = candid::encode_one(&request).expect("encode subtree removal request");
1209 let advance_bytes =
1210 candid::encode_one(advance_request).expect("encode subtree removal advance request");
1211 let stop_bytes =
1212 candid::encode_one(stop_request).expect("encode subtree removal stop request");
1213 let status_bytes =
1214 candid::encode_one(status_request).expect("encode subtree removal status request");
1215 let response_bytes =
1216 candid::encode_one(&response).expect("encode subtree removal response");
1217
1218 assert_eq!(
1219 candid::decode_one::<RootComponentSubtreeRemovalRequest>(&request_bytes)
1220 .expect("decode subtree removal request"),
1221 request
1222 );
1223 assert_eq!(
1224 candid::decode_one::<RootComponentSubtreeRemovalAdvanceRequest>(&advance_bytes)
1225 .expect("decode subtree removal advance request"),
1226 advance_request
1227 );
1228 assert_eq!(
1229 candid::decode_one::<RootComponentSubtreeRemovalStopPreparationRequest>(&stop_bytes)
1230 .expect("decode subtree removal stop request"),
1231 stop_request
1232 );
1233 assert_eq!(
1234 candid::decode_one::<RootComponentSubtreeRemovalStatusRequest>(&status_bytes)
1235 .expect("decode subtree removal status request"),
1236 status_request
1237 );
1238 assert_eq!(
1239 candid::decode_one::<RootComponentSubtreeRemovalResponse>(&response_bytes)
1240 .expect("decode subtree removal response"),
1241 response
1242 );
1243 }
1244
1245 #[test]
1246 fn component_subtree_removal_stop_request_round_trips_through_candid() {
1247 let request = RootComponentSubtreeRemovalStopRequest {
1248 operation_id: [50; 32],
1249 component: ComponentInstanceId::from_generated_bytes([51; 32]),
1250 expected_traversal_steps: 3,
1251 expected_leaf_canister_id: Principal::from_slice(&[52; 29]),
1252 expected_leaf_parent_canister_id: Principal::from_slice(&[53; 29]),
1253 };
1254 let bytes =
1255 candid::encode_one(request).expect("encode subtree removal stop execution request");
1256
1257 assert_eq!(
1258 candid::decode_one::<RootComponentSubtreeRemovalStopRequest>(&bytes)
1259 .expect("decode subtree removal stop execution request"),
1260 request
1261 );
1262 }
1263
1264 #[test]
1265 #[expect(
1266 clippy::too_many_lines,
1267 reason = "one round-trip test keeps the complete child lifecycle boundary coherent"
1268 )]
1269 fn component_child_lifecycle_contracts_round_trip_through_candid() {
1270 let component = ComponentInstanceId::from_generated_bytes([11; 32]);
1271 let registry = ComponentRegistryHead {
1272 component,
1273 revision: 2,
1274 content_hash: [12; 32],
1275 };
1276 let request = RootComponentChildAllocationRequest {
1277 operation_id: [13; 32],
1278 component,
1279 expected_registry: registry.clone(),
1280 child_role: CanisterRole::new("project_instance"),
1281 };
1282 let status_request = RootComponentChildAllocationStatusRequest {
1283 operation_id: request.operation_id,
1284 component,
1285 };
1286 let creation_request = RootComponentChildCreationRequest {
1287 operation_id: request.operation_id,
1288 component,
1289 };
1290 let install_request = RootComponentChildInstallRequest {
1291 operation_id: request.operation_id,
1292 component,
1293 };
1294 let commit_request = RootComponentChildCommitRequest {
1295 operation_id: request.operation_id,
1296 component,
1297 };
1298 let directory_request = RootComponentChildDirectoryPreparationRequest {
1299 operation_id: request.operation_id,
1300 component,
1301 };
1302 let activation_request = RootComponentChildRuntimeActivationRequest {
1303 operation_id: request.operation_id,
1304 component,
1305 };
1306 let membership_request = RootComponentChildMembershipActivationRequest {
1307 operation_id: request.operation_id,
1308 component,
1309 };
1310 let root = Principal::from_slice(&[17; 29]);
1311 let parent = Principal::from_slice(&[14; 29]);
1312 let child = Principal::from_slice(&[18; 29]);
1313 let child_binding = ComponentChildBinding {
1314 component: ComponentBinding {
1315 authority: fleet_registry_authority(),
1316 component,
1317 component_spec: "projects".parse().expect("Component Spec"),
1318 spec_hash: [19; 32],
1319 role: CanisterRole::new("project_hub"),
1320 placement_subnet: SubnetId::from_principal(Principal::from_slice(&[20; 29])),
1321 fleet_subnet_root: root,
1322 canister_id: parent,
1323 },
1324 parent_canister_id: parent,
1325 role: request.child_role.clone(),
1326 canister_id: child,
1327 };
1328 let response = RootComponentChildAllocationResponse {
1329 operation_id: request.operation_id,
1330 component,
1331 parent_canister_id: parent,
1332 parent_role: CanisterRole::new("project_hub"),
1333 child_role: request.child_role.clone(),
1334 child_kind: ComponentChildKind::Instance,
1335 maximum_instances_per_parent: 10_000,
1336 maximum_descendants: 20_000,
1337 maximum_registry_bytes: 16_777_216,
1338 reserved_against_registry: registry,
1339 release_set: FleetSubnetRootReleaseSet {
1340 release_build_id: ReleaseBuildId::from_nonce(ReleaseBuildNonce::from_random_bytes(
1341 [15; 32],
1342 )),
1343 manifest_digest: ReleaseSetDigest::from_bytes([16; 32]),
1344 },
1345 phase: RootComponentAllocationPhase::Verified,
1346 creation: Some(RootComponentCreationEvidence {
1347 wasm_store: Principal::from_slice(&[21; 29]),
1348 payload_hash: [22; 32],
1349 payload_size_bytes: 4_096,
1350 initial_cycles: Cycles::new(5_000_000_000_000),
1351 controller: root,
1352 canister: Some(child),
1353 }),
1354 installation: Some(RootComponentChildInstallEvidence {
1355 raw_module_hash: [23; 32],
1356 chunk_hashes: vec![vec![24; 32]],
1357 binding: child_binding.clone(),
1358 }),
1359 };
1360 let commit_response = RootComponentChildCommitResponse {
1361 allocation: response.clone(),
1362 registry: ComponentRegistryPartitionResponse {
1363 head: ComponentRegistryHead {
1364 component,
1365 revision: 3,
1366 content_hash: [25; 32],
1367 },
1368 binding: child_binding.component.clone(),
1369 provisioning_origin: ComponentProvisioningOrigin::FleetAdministrator {
1370 caller: Principal::from_slice(&[26; 29]),
1371 },
1372 release_set: response.release_set,
1373 status: ComponentLifecycleStatus::Active,
1374 reserved_descendants: 0,
1375 committed_descendants: 1,
1376 encoded_bytes: 8_192,
1377 },
1378 directory: ComponentDirectoryHead {
1379 provenance: ComponentDirectoryProvenance {
1380 component: child_binding.component.clone(),
1381 source_fleet_subnet_root: root,
1382 component_registry_revision: 3,
1383 component_registry_content_hash: [25; 32],
1384 synchronized_at_ns: 27,
1385 },
1386 descendant_count: 1,
1387 },
1388 };
1389 let runtime_authority = ComponentRuntimeDirectoryAuthority {
1390 fleet: FleetDirectorySnapshot {
1391 provenance: crate::dto::fleet_registry::FleetDirectoryProvenance {
1392 registry: FleetRegistryVersion {
1393 authority: fleet_registry_authority(),
1394 revision: 4,
1395 content_hash: [28; 32],
1396 },
1397 source_fleet_subnet_root: root,
1398 },
1399 fleet_subnet_roots: vec![
1400 crate::dto::fleet_registry::FleetSubnetRootDirectoryEntry {
1401 placement_subnet: commit_response.registry.binding.placement_subnet,
1402 fleet_subnet_root: root,
1403 status: crate::dto::fleet_registry::FleetSubnetRootStatus::Active,
1404 },
1405 ],
1406 },
1407 component: commit_response.directory.clone(),
1408 };
1409 let activation = ComponentRuntimeActivationEvidence {
1410 directory_authority_hash: [29; 32],
1411 activated_at_ns: 30,
1412 };
1413 let directory_response = RootComponentChildDirectoryPreparationResponse {
1414 committed: commit_response.clone(),
1415 child: ComponentRuntimeStatusResponse {
1416 operation_id: request.operation_id,
1417 binding: ManagedCanisterBinding::ComponentChild(child_binding.clone()),
1418 phase: ComponentRuntimePhase::DirectoryPrepared,
1419 authority: Some(runtime_authority.clone()),
1420 authority_hash: Some([31; 32]),
1421 activation: None,
1422 },
1423 owning_component: ComponentRuntimeDirectoryConvergenceEvidence {
1424 operation_id: [32; 32],
1425 binding: ManagedCanisterBinding::Component(child_binding.component.clone()),
1426 covered_authority: runtime_authority.clone(),
1427 covered_authority_hash: [31; 32],
1428 activation,
1429 },
1430 parent: None,
1431 };
1432 let activation_response = RootComponentChildRuntimeActivationResponse {
1433 committed: commit_response.clone(),
1434 child: ComponentRuntimeStatusResponse {
1435 operation_id: request.operation_id,
1436 binding: ManagedCanisterBinding::ComponentChild(child_binding.clone()),
1437 phase: ComponentRuntimePhase::Active,
1438 authority: Some(runtime_authority.clone()),
1439 authority_hash: Some([31; 32]),
1440 activation: Some(ComponentRuntimeActivationEvidence {
1441 directory_authority_hash: [31; 32],
1442 activated_at_ns: 33,
1443 }),
1444 },
1445 };
1446 let active_directory = ComponentDirectoryHead {
1447 provenance: ComponentDirectoryProvenance {
1448 component: child_binding.component.clone(),
1449 source_fleet_subnet_root: root,
1450 component_registry_revision: 4,
1451 component_registry_content_hash: [34; 32],
1452 synchronized_at_ns: 35,
1453 },
1454 descendant_count: 1,
1455 };
1456 let active_authority = ComponentRuntimeDirectoryAuthority {
1457 fleet: runtime_authority.fleet,
1458 component: active_directory.clone(),
1459 };
1460 let membership_response = RootComponentChildMembershipActivationResponse {
1461 committed: commit_response.clone(),
1462 registry: ComponentRegistryPartitionResponse {
1463 head: ComponentRegistryHead {
1464 component,
1465 revision: 4,
1466 content_hash: [34; 32],
1467 },
1468 binding: child_binding.component.clone(),
1469 provisioning_origin: commit_response.registry.provisioning_origin.clone(),
1470 release_set: commit_response.registry.release_set,
1471 status: ComponentLifecycleStatus::Active,
1472 reserved_descendants: 0,
1473 committed_descendants: 1,
1474 encoded_bytes: 8_256,
1475 },
1476 directory: active_directory,
1477 child: ComponentRuntimeStatusResponse {
1478 operation_id: request.operation_id,
1479 binding: ManagedCanisterBinding::ComponentChild(child_binding),
1480 phase: ComponentRuntimePhase::Active,
1481 authority: Some(active_authority),
1482 authority_hash: Some([36; 32]),
1483 activation: Some(ComponentRuntimeActivationEvidence {
1484 directory_authority_hash: [31; 32],
1485 activated_at_ns: 33,
1486 }),
1487 },
1488 };
1489
1490 let request_bytes = candid::encode_one(&request).expect("encode child reservation");
1491 let status_bytes =
1492 candid::encode_one(status_request).expect("encode child reservation status");
1493 let creation_bytes =
1494 candid::encode_one(creation_request).expect("encode child creation request");
1495 let install_bytes =
1496 candid::encode_one(install_request).expect("encode child install request");
1497 let response_bytes = candid::encode_one(&response).expect("encode child response");
1498 let commit_request_bytes =
1499 candid::encode_one(commit_request).expect("encode child commit request");
1500 let directory_request_bytes =
1501 candid::encode_one(directory_request).expect("encode child Directory request");
1502 let activation_request_bytes =
1503 candid::encode_one(activation_request).expect("encode child activation request");
1504 let membership_request_bytes =
1505 candid::encode_one(membership_request).expect("encode child membership request");
1506 let commit_response_bytes =
1507 candid::encode_one(&commit_response).expect("encode child commit response");
1508 let directory_response_bytes =
1509 candid::encode_one(&directory_response).expect("encode child Directory response");
1510 let activation_response_bytes =
1511 candid::encode_one(&activation_response).expect("encode child activation response");
1512 let membership_response_bytes =
1513 candid::encode_one(&membership_response).expect("encode child membership response");
1514
1515 assert_eq!(
1516 candid::decode_one::<RootComponentChildAllocationRequest>(&request_bytes)
1517 .expect("decode child reservation"),
1518 request
1519 );
1520 assert_eq!(
1521 candid::decode_one::<RootComponentChildAllocationStatusRequest>(&status_bytes)
1522 .expect("decode child reservation status"),
1523 status_request
1524 );
1525 assert_eq!(
1526 candid::decode_one::<RootComponentChildCreationRequest>(&creation_bytes)
1527 .expect("decode child creation request"),
1528 creation_request
1529 );
1530 assert_eq!(
1531 candid::decode_one::<RootComponentChildInstallRequest>(&install_bytes)
1532 .expect("decode child install request"),
1533 install_request
1534 );
1535 assert_eq!(
1536 candid::decode_one::<RootComponentChildAllocationResponse>(&response_bytes)
1537 .expect("decode child response"),
1538 response
1539 );
1540 assert_eq!(
1541 candid::decode_one::<RootComponentChildCommitRequest>(&commit_request_bytes)
1542 .expect("decode child commit request"),
1543 commit_request
1544 );
1545 assert_eq!(
1546 candid::decode_one::<RootComponentChildDirectoryPreparationRequest>(
1547 &directory_request_bytes
1548 )
1549 .expect("decode child Directory request"),
1550 directory_request
1551 );
1552 assert_eq!(
1553 candid::decode_one::<RootComponentChildCommitResponse>(&commit_response_bytes)
1554 .expect("decode child commit response"),
1555 commit_response
1556 );
1557 assert_eq!(
1558 candid::decode_one::<RootComponentChildDirectoryPreparationResponse>(
1559 &directory_response_bytes
1560 )
1561 .expect("decode child Directory response"),
1562 directory_response
1563 );
1564 assert_eq!(
1565 candid::decode_one::<RootComponentChildRuntimeActivationRequest>(
1566 &activation_request_bytes
1567 )
1568 .expect("decode child activation request"),
1569 activation_request
1570 );
1571 assert_eq!(
1572 candid::decode_one::<RootComponentChildRuntimeActivationResponse>(
1573 &activation_response_bytes
1574 )
1575 .expect("decode child activation response"),
1576 activation_response
1577 );
1578 assert_eq!(
1579 candid::decode_one::<RootComponentChildMembershipActivationRequest>(
1580 &membership_request_bytes
1581 )
1582 .expect("decode child membership request"),
1583 membership_request
1584 );
1585 assert_eq!(
1586 candid::decode_one::<RootComponentChildMembershipActivationResponse>(
1587 &membership_response_bytes
1588 )
1589 .expect("decode child membership response"),
1590 membership_response
1591 );
1592 }
1593
1594 #[test]
1595 fn component_install_request_round_trips_through_candid() {
1596 let request = RootComponentInstallRequest {
1597 operation_id: [10; 32],
1598 };
1599 let bytes = candid::encode_one(request).expect("encode install request");
1600
1601 assert_eq!(
1602 candid::decode_one::<RootComponentInstallRequest>(&bytes)
1603 .expect("decode install request"),
1604 request
1605 );
1606 }
1607
1608 #[test]
1609 fn component_commit_request_round_trips_through_candid() {
1610 let request = RootComponentCommitRequest {
1611 operation_id: [10; 32],
1612 };
1613 let bytes = candid::encode_one(request).expect("encode commit request");
1614
1615 assert_eq!(
1616 candid::decode_one::<RootComponentCommitRequest>(&bytes)
1617 .expect("decode commit request"),
1618 request
1619 );
1620 }
1621
1622 #[test]
1623 fn component_runtime_activation_requests_round_trip_through_candid() {
1624 let root_request = RootComponentRuntimeActivationRequest {
1625 operation_id: [22; 32],
1626 };
1627 let target_request = ComponentRuntimeActivationRequest {
1628 operation_id: root_request.operation_id,
1629 directory_authority_hash: [23; 32],
1630 };
1631 let membership_request = RootComponentMembershipActivationRequest {
1632 operation_id: root_request.operation_id,
1633 };
1634 let root_bytes = candid::encode_one(root_request).expect("encode root activation request");
1635 let target_bytes =
1636 candid::encode_one(target_request).expect("encode target activation request");
1637 let membership_bytes =
1638 candid::encode_one(membership_request).expect("encode membership activation request");
1639
1640 assert_eq!(
1641 candid::decode_one::<RootComponentRuntimeActivationRequest>(&root_bytes)
1642 .expect("decode root activation request"),
1643 root_request
1644 );
1645 assert_eq!(
1646 candid::decode_one::<ComponentRuntimeActivationRequest>(&target_bytes)
1647 .expect("decode target activation request"),
1648 target_request
1649 );
1650 assert_eq!(
1651 candid::decode_one::<RootComponentMembershipActivationRequest>(&membership_bytes)
1652 .expect("decode membership activation request"),
1653 membership_request
1654 );
1655 }
1656}