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 RootComponentSubtreeRemovalStatusRequest {
216 pub operation_id: [u8; 32],
217 pub component: ComponentInstanceId,
218}
219
220#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
227pub struct RootComponentChildCreationRequest {
228 pub operation_id: [u8; 32],
229 pub component: ComponentInstanceId,
230}
231
232#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
239pub struct RootComponentChildInstallRequest {
240 pub operation_id: [u8; 32],
241 pub component: ComponentInstanceId,
242}
243
244#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
251pub struct RootComponentChildCommitRequest {
252 pub operation_id: [u8; 32],
253 pub component: ComponentInstanceId,
254}
255
256#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
263pub struct RootComponentChildDirectoryPreparationRequest {
264 pub operation_id: [u8; 32],
265 pub component: ComponentInstanceId,
266}
267
268#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
275pub struct RootComponentChildRuntimeActivationRequest {
276 pub operation_id: [u8; 32],
277 pub component: ComponentInstanceId,
278}
279
280#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
287pub struct RootComponentChildMembershipActivationRequest {
288 pub operation_id: [u8; 32],
289 pub component: ComponentInstanceId,
290}
291
292#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
299pub struct RootComponentCreationRequest {
300 pub operation_id: [u8; 32],
301}
302
303#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
310pub struct RootComponentInstallRequest {
311 pub operation_id: [u8; 32],
312}
313
314#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
321pub struct RootComponentCommitRequest {
322 pub operation_id: [u8; 32],
323}
324
325#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
332pub struct RootComponentDirectoryPreparationRequest {
333 pub operation_id: [u8; 32],
334}
335
336#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
343pub struct RootComponentRuntimeActivationRequest {
344 pub operation_id: [u8; 32],
345}
346
347#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
354pub struct RootComponentMembershipActivationRequest {
355 pub operation_id: [u8; 32],
356}
357
358#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
365pub enum ComponentProvisioningOrigin {
366 FleetAdministrator { caller: Principal },
367}
368
369#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
376pub enum RootComponentAllocationPhase {
377 Reserved,
378 CreationIntent,
379 Created,
380 InstallIntent,
381 Installed,
382 Verified,
383 Committed,
384}
385
386#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
393pub enum RootComponentSubtreeRemovalPhase {
394 Fenced,
395 Traversing(RootComponentSubtreeRemovalNode),
396 LeafSelected(RootComponentSubtreeRemovalNode),
397 StopIntent(RootComponentSubtreeRemovalStopIntent),
398 Stopped(RootComponentSubtreeRemovalStoppedReceipt),
399 DeleteIntent(RootComponentSubtreeRemovalDeleteIntent),
400 Deleted(RootComponentSubtreeRemovalDeletedReceipt),
401}
402
403#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
410pub struct RootComponentSubtreeRemovalNode {
411 pub canister_id: Principal,
412 pub parent_canister_id: Principal,
413 pub role: CanisterRole,
414 pub kind: ComponentChildKind,
415 pub installed_artifact_hash: [u8; 32],
416 pub status: ComponentLifecycleStatus,
417}
418
419#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
426pub struct RootComponentSubtreeRemovalStopIntent {
427 pub leaf: RootComponentSubtreeRemovalNode,
428 pub controller: Principal,
429}
430
431#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
438pub struct RootComponentSubtreeRemovalStoppedReceipt {
439 pub stop: RootComponentSubtreeRemovalStopIntent,
440 pub observed_module_hash: [u8; 32],
441}
442
443#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
450pub struct RootComponentSubtreeRemovalDeleteIntent {
451 pub stopped: RootComponentSubtreeRemovalStoppedReceipt,
452}
453
454#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
461pub struct RootComponentSubtreeRemovalDeletedReceipt {
462 pub deletion: RootComponentSubtreeRemovalDeleteIntent,
463}
464
465#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
472pub enum ComponentLifecycleStatus {
473 Prepared,
474 Active,
475 Draining,
476 Removed,
477}
478
479#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
486pub struct ComponentRegistryHead {
487 pub component: ComponentInstanceId,
488 pub revision: u64,
489 pub content_hash: [u8; 32],
490}
491
492#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
499pub struct ComponentRegistryPartitionRequest {
500 pub component: ComponentInstanceId,
501}
502
503#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
510pub struct ComponentRegistryPartitionResponse {
511 pub head: ComponentRegistryHead,
512 pub binding: ComponentBinding,
513 pub provisioning_origin: ComponentProvisioningOrigin,
514 pub release_set: FleetSubnetRootReleaseSet,
515 pub status: ComponentLifecycleStatus,
516 pub reserved_descendants: u32,
517 pub committed_descendants: u32,
518 pub encoded_bytes: u64,
519}
520
521#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
528pub struct ComponentDirectoryProvenance {
529 pub component: ComponentBinding,
530 pub source_fleet_subnet_root: Principal,
531 pub component_registry_revision: u64,
532 pub component_registry_content_hash: [u8; 32],
533 pub synchronized_at_ns: u64,
534}
535
536#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
543pub struct ComponentDirectoryHead {
544 pub provenance: ComponentDirectoryProvenance,
545 pub descendant_count: u32,
546}
547
548#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
555pub struct ComponentDirectoryHeadRequest {
556 pub component: ComponentInstanceId,
557}
558
559#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
566pub struct ComponentDirectoryPageCursor(pub Vec<u8>);
567
568#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
575pub struct ComponentDirectoryPageRequest {
576 pub directory: ComponentDirectoryHead,
577 pub parent_canister_id: Option<Principal>,
578 pub role: Option<CanisterRole>,
579 pub status: Option<ComponentLifecycleStatus>,
580 pub cursor: Option<ComponentDirectoryPageCursor>,
581 pub limit: u16,
582}
583
584#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
591pub struct ComponentDirectoryChildEntry {
592 pub binding: ComponentChildBinding,
593 pub kind: ComponentChildKind,
594 pub installed_artifact_hash: [u8; 32],
595 pub status: ComponentLifecycleStatus,
596}
597
598#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
605pub struct ComponentDirectoryPageResponse {
606 pub directory: ComponentDirectoryHead,
607 pub entries: Vec<ComponentDirectoryChildEntry>,
608 pub next_cursor: Option<ComponentDirectoryPageCursor>,
609}
610
611#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
618pub struct ComponentRuntimeDirectoryAuthority {
619 pub fleet: FleetDirectorySnapshot,
620 pub component: ComponentDirectoryHead,
621}
622
623#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
630pub struct ComponentRuntimeDirectoryPreparationRequest {
631 pub operation_id: [u8; 32],
632 pub authority: ComponentRuntimeDirectoryAuthority,
633}
634
635#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
642pub struct ComponentRuntimeDirectorySynchronizationRequest {
643 pub operation_id: [u8; 32],
644 pub authority: ComponentRuntimeDirectoryAuthority,
645}
646
647#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
654pub enum ComponentRuntimePhase {
655 AwaitingDirectory,
656 DirectoryPrepared,
657 Active,
658}
659
660#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
667pub struct ComponentRuntimeActivationEvidence {
668 pub directory_authority_hash: [u8; 32],
669 pub activated_at_ns: u64,
670}
671
672#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
679pub struct ComponentRuntimeActivationRequest {
680 pub operation_id: [u8; 32],
681 pub directory_authority_hash: [u8; 32],
682}
683
684#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
691pub struct ComponentRuntimeStatusResponse {
692 pub operation_id: [u8; 32],
693 pub binding: ManagedCanisterBinding,
694 pub phase: ComponentRuntimePhase,
695 pub authority: Option<ComponentRuntimeDirectoryAuthority>,
696 pub authority_hash: Option<[u8; 32]>,
697 pub activation: Option<ComponentRuntimeActivationEvidence>,
698}
699
700#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
707pub struct ComponentRuntimeDirectoryConvergenceEvidence {
708 pub operation_id: [u8; 32],
709 pub binding: ManagedCanisterBinding,
710 pub covered_authority: ComponentRuntimeDirectoryAuthority,
711 pub covered_authority_hash: [u8; 32],
712 pub activation: ComponentRuntimeActivationEvidence,
713}
714
715#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
722pub struct RootComponentCreationEvidence {
723 pub wasm_store: Principal,
724 pub payload_hash: [u8; 32],
725 pub payload_size_bytes: u64,
726 pub initial_cycles: Cycles,
727 pub controller: Principal,
728 pub canister: Option<Principal>,
729}
730
731#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
738pub struct RootComponentInstallEvidence {
739 pub raw_module_hash: [u8; 32],
740 pub chunk_hashes: Vec<Vec<u8>>,
741 pub binding: ComponentBinding,
742}
743
744#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
751pub struct RootComponentChildInstallEvidence {
752 pub raw_module_hash: [u8; 32],
753 pub chunk_hashes: Vec<Vec<u8>>,
754 pub binding: ComponentChildBinding,
755}
756
757#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
764pub struct RootComponentAllocationResponse {
765 pub operation_id: [u8; 32],
766 pub allocation_sequence: u64,
767 pub component: ComponentInstanceId,
768 pub component_spec: ComponentSpecId,
769 pub spec_hash: [u8; 32],
770 pub role: CanisterRole,
771 pub provisioning_origin: ComponentProvisioningOrigin,
772 pub release_set: FleetSubnetRootReleaseSet,
773 pub phase: RootComponentAllocationPhase,
774 pub creation: Option<RootComponentCreationEvidence>,
775 pub installation: Option<RootComponentInstallEvidence>,
776}
777
778#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
785pub struct RootComponentChildAllocationResponse {
786 pub operation_id: [u8; 32],
787 pub component: ComponentInstanceId,
788 pub parent_canister_id: Principal,
789 pub parent_role: CanisterRole,
790 pub child_role: CanisterRole,
791 pub child_kind: ComponentChildKind,
792 pub maximum_instances_per_parent: u32,
793 pub maximum_descendants: u32,
794 pub maximum_registry_bytes: u64,
795 pub reserved_against_registry: ComponentRegistryHead,
796 pub release_set: FleetSubnetRootReleaseSet,
797 pub phase: RootComponentAllocationPhase,
798 pub creation: Option<RootComponentCreationEvidence>,
799 pub installation: Option<RootComponentChildInstallEvidence>,
800}
801
802#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
809pub struct RootComponentSubtreeRemovalResponse {
810 pub operation_id: [u8; 32],
811 pub component: ComponentInstanceId,
812 pub target_canister_id: Principal,
813 pub target_parent_canister_id: Principal,
814 pub target_role: CanisterRole,
815 pub target_status: ComponentLifecycleStatus,
816 pub reserved_against_registry: ComponentRegistryHead,
817 pub traversal_steps: u32,
818 pub phase: RootComponentSubtreeRemovalPhase,
819}
820
821#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
828pub struct RootComponentChildCommitResponse {
829 pub allocation: RootComponentChildAllocationResponse,
830 pub registry: ComponentRegistryPartitionResponse,
831 pub directory: ComponentDirectoryHead,
832}
833
834#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
841pub struct RootComponentChildDirectoryPreparationResponse {
842 pub committed: RootComponentChildCommitResponse,
843 pub child: ComponentRuntimeStatusResponse,
844 pub owning_component: ComponentRuntimeDirectoryConvergenceEvidence,
845 pub parent: Option<ComponentRuntimeDirectoryConvergenceEvidence>,
846}
847
848#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
855pub struct RootComponentChildRuntimeActivationResponse {
856 pub committed: RootComponentChildCommitResponse,
857 pub child: ComponentRuntimeStatusResponse,
858}
859
860#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
867pub struct RootComponentChildMembershipActivationResponse {
868 pub committed: RootComponentChildCommitResponse,
869 pub registry: ComponentRegistryPartitionResponse,
870 pub directory: ComponentDirectoryHead,
871 pub child: ComponentRuntimeStatusResponse,
872}
873
874#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
881pub struct RootComponentCommitResponse {
882 pub allocation: RootComponentAllocationResponse,
883 pub registry: ComponentRegistryPartitionResponse,
884 pub directory: ComponentDirectoryHead,
885}
886
887#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
894pub struct RootComponentDirectoryPreparationResponse {
895 pub committed: RootComponentCommitResponse,
896 pub target: ComponentRuntimeStatusResponse,
897}
898
899#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
906pub struct RootComponentRuntimeActivationResponse {
907 pub committed: RootComponentCommitResponse,
908 pub target: ComponentRuntimeStatusResponse,
909}
910
911#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
918pub struct RootComponentMembershipActivationResponse {
919 pub allocation: RootComponentAllocationResponse,
920 pub registry: ComponentRegistryPartitionResponse,
921 pub directory: ComponentDirectoryHead,
922 pub target: ComponentRuntimeStatusResponse,
923}
924
925#[cfg(test)]
926mod tests {
927 use super::*;
928 use crate::{
929 dto::root_store::RootStoreBootstrapRequest,
930 ids::{
931 AppId, CanonicalNetworkId, FleetCoordinatorBinding, FleetId, FleetKey,
932 FleetRegistryAuthority, ReleaseBuildId, ReleaseBuildNonce, ReleaseSetDigest, SubnetId,
933 },
934 };
935
936 #[test]
937 fn component_registry_contracts_round_trip_through_candid() {
938 let request = RootComponentRegistryPreparationRequest {
939 store_bootstrap: RootStoreBootstrapRequest {
940 manifest_payload_size_bytes: 128,
941 },
942 expected_fleet_registry: FleetRegistryVersion {
943 authority: fleet_registry_authority(),
944 revision: 4,
945 content_hash: [5; 32],
946 },
947 };
948 let response = RootComponentRegistryStatusResponse {
949 fleet_subnet_root: Principal::from_slice(&[6; 29]),
950 prepared_against_registry: request.expected_fleet_registry.clone(),
951 release_set: FleetSubnetRootReleaseSet {
952 release_build_id: ReleaseBuildId::from_nonce(ReleaseBuildNonce::from_random_bytes(
953 [7; 32],
954 )),
955 manifest_digest: ReleaseSetDigest::from_bytes([8; 32]),
956 },
957 component_topology_digest: ComponentTopologyDigest::from_bytes([9; 32]),
958 next_allocation_sequence: 1,
959 reserved_component_instances: 0,
960 committed_component_instances: 0,
961 managed_descendants: 0,
962 known_created_component_canisters: 0,
963 encoded_bytes: 0,
964 initial_inventory: Some(RootComponentInitialInventoryStatus {
965 fleet_activation_operation_id: [10; 32],
966 component_count: 0,
967 inventory_hash: [11; 32],
968 sealed_at_ns: 12,
969 directories_converged: true,
970 root_runtime_activated: true,
971 }),
972 };
973 let allocation = RootComponentAllocationResponse {
974 operation_id: [10; 32],
975 allocation_sequence: 1,
976 component: ComponentInstanceId::from_generated_bytes([11; 32]),
977 component_spec: "projects".parse().expect("Component Spec ID"),
978 spec_hash: [12; 32],
979 role: CanisterRole::new("project_hub"),
980 provisioning_origin: ComponentProvisioningOrigin::FleetAdministrator {
981 caller: Principal::from_slice(&[13; 29]),
982 },
983 release_set: response.release_set,
984 phase: RootComponentAllocationPhase::Reserved,
985 creation: None,
986 installation: None,
987 };
988 let created = RootComponentAllocationResponse {
989 phase: RootComponentAllocationPhase::Created,
990 creation: Some(RootComponentCreationEvidence {
991 wasm_store: Principal::from_slice(&[14; 29]),
992 payload_hash: [15; 32],
993 payload_size_bytes: 4_096,
994 initial_cycles: Cycles::new(5_000_000_000_000),
995 controller: Principal::from_slice(&[6; 29]),
996 canister: Some(Principal::from_slice(&[16; 29])),
997 }),
998 installation: None,
999 ..allocation.clone()
1000 };
1001 let request_bytes = candid::encode_one(&request).expect("encode request");
1002 let response_bytes = candid::encode_one(&response).expect("encode response");
1003 let allocation_bytes = candid::encode_one(&allocation).expect("encode allocation");
1004 let created_bytes = candid::encode_one(&created).expect("encode created allocation");
1005
1006 assert_eq!(
1007 candid::decode_one::<RootComponentRegistryPreparationRequest>(&request_bytes)
1008 .expect("decode request"),
1009 request
1010 );
1011 assert_eq!(
1012 candid::decode_one::<RootComponentRegistryStatusResponse>(&response_bytes)
1013 .expect("decode response"),
1014 response
1015 );
1016 assert_eq!(
1017 candid::decode_one::<RootComponentAllocationResponse>(&allocation_bytes)
1018 .expect("decode allocation"),
1019 allocation
1020 );
1021 assert_eq!(
1022 candid::decode_one::<RootComponentAllocationResponse>(&created_bytes)
1023 .expect("decode created allocation"),
1024 created
1025 );
1026 }
1027
1028 #[test]
1029 fn component_commit_response_round_trips_through_candid() {
1030 let root = Principal::from_slice(&[6; 29]);
1031 let component = ComponentInstanceId::from_generated_bytes([11; 32]);
1032 let component_spec: ComponentSpecId = "projects".parse().expect("Component Spec ID");
1033 let release_set = FleetSubnetRootReleaseSet {
1034 release_build_id: ReleaseBuildId::from_nonce(ReleaseBuildNonce::from_random_bytes(
1035 [7; 32],
1036 )),
1037 manifest_digest: ReleaseSetDigest::from_bytes([8; 32]),
1038 };
1039 let provisioning_origin = ComponentProvisioningOrigin::FleetAdministrator {
1040 caller: Principal::from_slice(&[13; 29]),
1041 };
1042 let binding = ComponentBinding {
1043 authority: fleet_registry_authority(),
1044 component,
1045 component_spec: component_spec.clone(),
1046 spec_hash: [12; 32],
1047 role: CanisterRole::new("project_hub"),
1048 placement_subnet: SubnetId::from_principal(Principal::from_slice(&[17; 29])),
1049 fleet_subnet_root: root,
1050 canister_id: Principal::from_slice(&[16; 29]),
1051 };
1052 let head = ComponentRegistryHead {
1053 component,
1054 revision: 1,
1055 content_hash: [18; 32],
1056 };
1057 let committed = RootComponentCommitResponse {
1058 allocation: RootComponentAllocationResponse {
1059 operation_id: [10; 32],
1060 allocation_sequence: 1,
1061 component,
1062 component_spec,
1063 spec_hash: binding.spec_hash,
1064 role: binding.role.clone(),
1065 provisioning_origin: provisioning_origin.clone(),
1066 release_set,
1067 phase: RootComponentAllocationPhase::Committed,
1068 creation: Some(RootComponentCreationEvidence {
1069 wasm_store: Principal::from_slice(&[14; 29]),
1070 payload_hash: [15; 32],
1071 payload_size_bytes: 4_096,
1072 initial_cycles: Cycles::new(5_000_000_000_000),
1073 controller: root,
1074 canister: Some(binding.canister_id),
1075 }),
1076 installation: Some(RootComponentInstallEvidence {
1077 raw_module_hash: [20; 32],
1078 chunk_hashes: vec![vec![21; 32]],
1079 binding: binding.clone(),
1080 }),
1081 },
1082 registry: ComponentRegistryPartitionResponse {
1083 head: head.clone(),
1084 binding: binding.clone(),
1085 provisioning_origin,
1086 release_set,
1087 status: ComponentLifecycleStatus::Prepared,
1088 reserved_descendants: 0,
1089 committed_descendants: 0,
1090 encoded_bytes: 2_048,
1091 },
1092 directory: ComponentDirectoryHead {
1093 provenance: ComponentDirectoryProvenance {
1094 component: binding,
1095 source_fleet_subnet_root: root,
1096 component_registry_revision: head.revision,
1097 component_registry_content_hash: head.content_hash,
1098 synchronized_at_ns: 19,
1099 },
1100 descendant_count: 0,
1101 },
1102 };
1103 let committed_bytes = candid::encode_one(&committed).expect("encode committed allocation");
1104
1105 assert_eq!(
1106 candid::decode_one::<RootComponentCommitResponse>(&committed_bytes)
1107 .expect("decode committed allocation"),
1108 committed
1109 );
1110 }
1111
1112 #[test]
1113 fn component_directory_page_contracts_round_trip_through_candid() {
1114 let root = Principal::from_slice(&[6; 29]);
1115 let component = ComponentInstanceId::from_generated_bytes([11; 32]);
1116 let binding = ComponentBinding {
1117 authority: fleet_registry_authority(),
1118 component,
1119 component_spec: "projects".parse().expect("Component Spec ID"),
1120 spec_hash: [12; 32],
1121 role: CanisterRole::new("project_hub"),
1122 placement_subnet: SubnetId::from_principal(Principal::from_slice(&[17; 29])),
1123 fleet_subnet_root: root,
1124 canister_id: Principal::from_slice(&[16; 29]),
1125 };
1126 let directory = ComponentDirectoryHead {
1127 provenance: ComponentDirectoryProvenance {
1128 component: binding.clone(),
1129 source_fleet_subnet_root: root,
1130 component_registry_revision: 3,
1131 component_registry_content_hash: [18; 32],
1132 synchronized_at_ns: 19,
1133 },
1134 descendant_count: 1,
1135 };
1136 let request = ComponentDirectoryPageRequest {
1137 directory: directory.clone(),
1138 parent_canister_id: Some(binding.canister_id),
1139 role: Some(CanisterRole::new("project_instance")),
1140 status: Some(ComponentLifecycleStatus::Active),
1141 cursor: Some(ComponentDirectoryPageCursor(vec![20; 64])),
1142 limit: 50,
1143 };
1144 let response = ComponentDirectoryPageResponse {
1145 directory,
1146 entries: vec![ComponentDirectoryChildEntry {
1147 binding: ComponentChildBinding {
1148 component: binding.clone(),
1149 parent_canister_id: binding.canister_id,
1150 role: CanisterRole::new("project_instance"),
1151 canister_id: Principal::from_slice(&[21; 29]),
1152 },
1153 kind: ComponentChildKind::Instance,
1154 installed_artifact_hash: [22; 32],
1155 status: ComponentLifecycleStatus::Active,
1156 }],
1157 next_cursor: Some(ComponentDirectoryPageCursor(vec![23; 64])),
1158 };
1159 let request_bytes = candid::encode_one(&request).expect("encode Directory page request");
1160 let response_bytes = candid::encode_one(&response).expect("encode Directory page response");
1161
1162 assert_eq!(
1163 candid::decode_one::<ComponentDirectoryPageRequest>(&request_bytes)
1164 .expect("decode Directory page request"),
1165 request
1166 );
1167 assert_eq!(
1168 candid::decode_one::<ComponentDirectoryPageResponse>(&response_bytes)
1169 .expect("decode Directory page response"),
1170 response
1171 );
1172 }
1173
1174 fn fleet_registry_authority() -> FleetRegistryAuthority {
1175 FleetRegistryAuthority {
1176 binding: FleetCoordinatorBinding {
1177 fleet: crate::ids::FleetBinding {
1178 fleet: FleetKey {
1179 canonical_network_id: CanonicalNetworkId::ic_mainnet(),
1180 fleet_id: FleetId::from_generated_bytes([1; 32]),
1181 },
1182 app: AppId::from("toko"),
1183 },
1184 coordinator_subnet: SubnetId::from_principal(Principal::from_slice(&[2; 29])),
1185 coordinator: Principal::from_slice(&[3; 29]),
1186 },
1187 epoch: 1,
1188 }
1189 }
1190
1191 #[test]
1192 fn component_creation_request_round_trips_through_candid() {
1193 let request = RootComponentCreationRequest {
1194 operation_id: [10; 32],
1195 };
1196 let bytes = candid::encode_one(request).expect("encode creation request");
1197
1198 assert_eq!(
1199 candid::decode_one::<RootComponentCreationRequest>(&bytes)
1200 .expect("decode creation request"),
1201 request
1202 );
1203 }
1204
1205 #[test]
1206 fn component_subtree_removal_contracts_round_trip_through_candid() {
1207 let component = ComponentInstanceId::from_generated_bytes([41; 32]);
1208 let registry = ComponentRegistryHead {
1209 component,
1210 revision: 7,
1211 content_hash: [42; 32],
1212 };
1213 let request = RootComponentSubtreeRemovalRequest {
1214 operation_id: [43; 32],
1215 component,
1216 target_canister_id: Principal::from_slice(&[44; 29]),
1217 expected_registry: registry.clone(),
1218 };
1219 let status_request = RootComponentSubtreeRemovalStatusRequest {
1220 operation_id: request.operation_id,
1221 component,
1222 };
1223 let advance_request = RootComponentSubtreeRemovalAdvanceRequest {
1224 operation_id: request.operation_id,
1225 component,
1226 expected_traversal_steps: 1,
1227 };
1228 let stop_request = RootComponentSubtreeRemovalStopPreparationRequest {
1229 operation_id: request.operation_id,
1230 component,
1231 expected_traversal_steps: 2,
1232 expected_leaf_canister_id: Principal::from_slice(&[46; 29]),
1233 expected_leaf_parent_canister_id: request.target_canister_id,
1234 };
1235 let stopped = RootComponentSubtreeRemovalStoppedReceipt {
1236 observed_module_hash: [49; 32],
1237 stop: RootComponentSubtreeRemovalStopIntent {
1238 controller: Principal::from_slice(&[48; 29]),
1239 leaf: RootComponentSubtreeRemovalNode {
1240 canister_id: Principal::from_slice(&[46; 29]),
1241 parent_canister_id: request.target_canister_id,
1242 role: CanisterRole::new("project_ledger"),
1243 kind: ComponentChildKind::Singleton,
1244 installed_artifact_hash: [47; 32],
1245 status: ComponentLifecycleStatus::Active,
1246 },
1247 },
1248 };
1249 let response = RootComponentSubtreeRemovalResponse {
1250 operation_id: request.operation_id,
1251 component,
1252 target_canister_id: request.target_canister_id,
1253 target_parent_canister_id: Principal::from_slice(&[45; 29]),
1254 target_role: CanisterRole::new("project_instance"),
1255 target_status: ComponentLifecycleStatus::Active,
1256 reserved_against_registry: registry,
1257 traversal_steps: 2,
1258 phase: RootComponentSubtreeRemovalPhase::Deleted(
1259 RootComponentSubtreeRemovalDeletedReceipt {
1260 deletion: RootComponentSubtreeRemovalDeleteIntent { stopped },
1261 },
1262 ),
1263 };
1264
1265 let request_bytes = candid::encode_one(&request).expect("encode subtree removal request");
1266 let advance_bytes =
1267 candid::encode_one(advance_request).expect("encode subtree removal advance request");
1268 let stop_bytes =
1269 candid::encode_one(stop_request).expect("encode subtree removal stop request");
1270 let status_bytes =
1271 candid::encode_one(status_request).expect("encode subtree removal status request");
1272 let response_bytes =
1273 candid::encode_one(&response).expect("encode subtree removal response");
1274
1275 assert_eq!(
1276 candid::decode_one::<RootComponentSubtreeRemovalRequest>(&request_bytes)
1277 .expect("decode subtree removal request"),
1278 request
1279 );
1280 assert_eq!(
1281 candid::decode_one::<RootComponentSubtreeRemovalAdvanceRequest>(&advance_bytes)
1282 .expect("decode subtree removal advance request"),
1283 advance_request
1284 );
1285 assert_eq!(
1286 candid::decode_one::<RootComponentSubtreeRemovalStopPreparationRequest>(&stop_bytes)
1287 .expect("decode subtree removal stop request"),
1288 stop_request
1289 );
1290 assert_eq!(
1291 candid::decode_one::<RootComponentSubtreeRemovalStatusRequest>(&status_bytes)
1292 .expect("decode subtree removal status request"),
1293 status_request
1294 );
1295 assert_eq!(
1296 candid::decode_one::<RootComponentSubtreeRemovalResponse>(&response_bytes)
1297 .expect("decode subtree removal response"),
1298 response
1299 );
1300 }
1301
1302 #[test]
1303 fn component_subtree_removal_deletion_requests_round_trip_through_candid() {
1304 let prepare = RootComponentSubtreeRemovalDeletePreparationRequest {
1305 operation_id: [50; 32],
1306 component: ComponentInstanceId::from_generated_bytes([51; 32]),
1307 expected_traversal_steps: 3,
1308 expected_leaf_canister_id: Principal::from_slice(&[52; 29]),
1309 expected_leaf_parent_canister_id: Principal::from_slice(&[53; 29]),
1310 };
1311 let request = RootComponentSubtreeRemovalDeleteRequest {
1312 operation_id: prepare.operation_id,
1313 component: prepare.component,
1314 expected_traversal_steps: prepare.expected_traversal_steps,
1315 expected_leaf_canister_id: prepare.expected_leaf_canister_id,
1316 expected_leaf_parent_canister_id: prepare.expected_leaf_parent_canister_id,
1317 };
1318 let prepare_bytes = candid::encode_one(prepare)
1319 .expect("encode subtree removal deletion preparation request");
1320 let request_bytes =
1321 candid::encode_one(request).expect("encode subtree removal deletion request");
1322
1323 assert_eq!(
1324 candid::decode_one::<RootComponentSubtreeRemovalDeletePreparationRequest>(
1325 &prepare_bytes
1326 )
1327 .expect("decode subtree removal deletion preparation request"),
1328 prepare
1329 );
1330 assert_eq!(
1331 candid::decode_one::<RootComponentSubtreeRemovalDeleteRequest>(&request_bytes)
1332 .expect("decode subtree removal deletion request"),
1333 request
1334 );
1335 }
1336
1337 #[test]
1338 fn component_subtree_removal_stop_request_round_trips_through_candid() {
1339 let request = RootComponentSubtreeRemovalStopRequest {
1340 operation_id: [50; 32],
1341 component: ComponentInstanceId::from_generated_bytes([51; 32]),
1342 expected_traversal_steps: 3,
1343 expected_leaf_canister_id: Principal::from_slice(&[52; 29]),
1344 expected_leaf_parent_canister_id: Principal::from_slice(&[53; 29]),
1345 };
1346 let bytes =
1347 candid::encode_one(request).expect("encode subtree removal stop execution request");
1348
1349 assert_eq!(
1350 candid::decode_one::<RootComponentSubtreeRemovalStopRequest>(&bytes)
1351 .expect("decode subtree removal stop execution request"),
1352 request
1353 );
1354 }
1355
1356 #[test]
1357 #[expect(
1358 clippy::too_many_lines,
1359 reason = "one round-trip test keeps the complete child lifecycle boundary coherent"
1360 )]
1361 fn component_child_lifecycle_contracts_round_trip_through_candid() {
1362 let component = ComponentInstanceId::from_generated_bytes([11; 32]);
1363 let registry = ComponentRegistryHead {
1364 component,
1365 revision: 2,
1366 content_hash: [12; 32],
1367 };
1368 let request = RootComponentChildAllocationRequest {
1369 operation_id: [13; 32],
1370 component,
1371 expected_registry: registry.clone(),
1372 child_role: CanisterRole::new("project_instance"),
1373 };
1374 let status_request = RootComponentChildAllocationStatusRequest {
1375 operation_id: request.operation_id,
1376 component,
1377 };
1378 let creation_request = RootComponentChildCreationRequest {
1379 operation_id: request.operation_id,
1380 component,
1381 };
1382 let install_request = RootComponentChildInstallRequest {
1383 operation_id: request.operation_id,
1384 component,
1385 };
1386 let commit_request = RootComponentChildCommitRequest {
1387 operation_id: request.operation_id,
1388 component,
1389 };
1390 let directory_request = RootComponentChildDirectoryPreparationRequest {
1391 operation_id: request.operation_id,
1392 component,
1393 };
1394 let activation_request = RootComponentChildRuntimeActivationRequest {
1395 operation_id: request.operation_id,
1396 component,
1397 };
1398 let membership_request = RootComponentChildMembershipActivationRequest {
1399 operation_id: request.operation_id,
1400 component,
1401 };
1402 let root = Principal::from_slice(&[17; 29]);
1403 let parent = Principal::from_slice(&[14; 29]);
1404 let child = Principal::from_slice(&[18; 29]);
1405 let child_binding = ComponentChildBinding {
1406 component: ComponentBinding {
1407 authority: fleet_registry_authority(),
1408 component,
1409 component_spec: "projects".parse().expect("Component Spec"),
1410 spec_hash: [19; 32],
1411 role: CanisterRole::new("project_hub"),
1412 placement_subnet: SubnetId::from_principal(Principal::from_slice(&[20; 29])),
1413 fleet_subnet_root: root,
1414 canister_id: parent,
1415 },
1416 parent_canister_id: parent,
1417 role: request.child_role.clone(),
1418 canister_id: child,
1419 };
1420 let response = RootComponentChildAllocationResponse {
1421 operation_id: request.operation_id,
1422 component,
1423 parent_canister_id: parent,
1424 parent_role: CanisterRole::new("project_hub"),
1425 child_role: request.child_role.clone(),
1426 child_kind: ComponentChildKind::Instance,
1427 maximum_instances_per_parent: 10_000,
1428 maximum_descendants: 20_000,
1429 maximum_registry_bytes: 16_777_216,
1430 reserved_against_registry: registry,
1431 release_set: FleetSubnetRootReleaseSet {
1432 release_build_id: ReleaseBuildId::from_nonce(ReleaseBuildNonce::from_random_bytes(
1433 [15; 32],
1434 )),
1435 manifest_digest: ReleaseSetDigest::from_bytes([16; 32]),
1436 },
1437 phase: RootComponentAllocationPhase::Verified,
1438 creation: Some(RootComponentCreationEvidence {
1439 wasm_store: Principal::from_slice(&[21; 29]),
1440 payload_hash: [22; 32],
1441 payload_size_bytes: 4_096,
1442 initial_cycles: Cycles::new(5_000_000_000_000),
1443 controller: root,
1444 canister: Some(child),
1445 }),
1446 installation: Some(RootComponentChildInstallEvidence {
1447 raw_module_hash: [23; 32],
1448 chunk_hashes: vec![vec![24; 32]],
1449 binding: child_binding.clone(),
1450 }),
1451 };
1452 let commit_response = RootComponentChildCommitResponse {
1453 allocation: response.clone(),
1454 registry: ComponentRegistryPartitionResponse {
1455 head: ComponentRegistryHead {
1456 component,
1457 revision: 3,
1458 content_hash: [25; 32],
1459 },
1460 binding: child_binding.component.clone(),
1461 provisioning_origin: ComponentProvisioningOrigin::FleetAdministrator {
1462 caller: Principal::from_slice(&[26; 29]),
1463 },
1464 release_set: response.release_set,
1465 status: ComponentLifecycleStatus::Active,
1466 reserved_descendants: 0,
1467 committed_descendants: 1,
1468 encoded_bytes: 8_192,
1469 },
1470 directory: ComponentDirectoryHead {
1471 provenance: ComponentDirectoryProvenance {
1472 component: child_binding.component.clone(),
1473 source_fleet_subnet_root: root,
1474 component_registry_revision: 3,
1475 component_registry_content_hash: [25; 32],
1476 synchronized_at_ns: 27,
1477 },
1478 descendant_count: 1,
1479 },
1480 };
1481 let runtime_authority = ComponentRuntimeDirectoryAuthority {
1482 fleet: FleetDirectorySnapshot {
1483 provenance: crate::dto::fleet_registry::FleetDirectoryProvenance {
1484 registry: FleetRegistryVersion {
1485 authority: fleet_registry_authority(),
1486 revision: 4,
1487 content_hash: [28; 32],
1488 },
1489 source_fleet_subnet_root: root,
1490 },
1491 fleet_subnet_roots: vec![
1492 crate::dto::fleet_registry::FleetSubnetRootDirectoryEntry {
1493 placement_subnet: commit_response.registry.binding.placement_subnet,
1494 fleet_subnet_root: root,
1495 status: crate::dto::fleet_registry::FleetSubnetRootStatus::Active,
1496 },
1497 ],
1498 },
1499 component: commit_response.directory.clone(),
1500 };
1501 let activation = ComponentRuntimeActivationEvidence {
1502 directory_authority_hash: [29; 32],
1503 activated_at_ns: 30,
1504 };
1505 let directory_response = RootComponentChildDirectoryPreparationResponse {
1506 committed: commit_response.clone(),
1507 child: ComponentRuntimeStatusResponse {
1508 operation_id: request.operation_id,
1509 binding: ManagedCanisterBinding::ComponentChild(child_binding.clone()),
1510 phase: ComponentRuntimePhase::DirectoryPrepared,
1511 authority: Some(runtime_authority.clone()),
1512 authority_hash: Some([31; 32]),
1513 activation: None,
1514 },
1515 owning_component: ComponentRuntimeDirectoryConvergenceEvidence {
1516 operation_id: [32; 32],
1517 binding: ManagedCanisterBinding::Component(child_binding.component.clone()),
1518 covered_authority: runtime_authority.clone(),
1519 covered_authority_hash: [31; 32],
1520 activation,
1521 },
1522 parent: None,
1523 };
1524 let activation_response = RootComponentChildRuntimeActivationResponse {
1525 committed: commit_response.clone(),
1526 child: ComponentRuntimeStatusResponse {
1527 operation_id: request.operation_id,
1528 binding: ManagedCanisterBinding::ComponentChild(child_binding.clone()),
1529 phase: ComponentRuntimePhase::Active,
1530 authority: Some(runtime_authority.clone()),
1531 authority_hash: Some([31; 32]),
1532 activation: Some(ComponentRuntimeActivationEvidence {
1533 directory_authority_hash: [31; 32],
1534 activated_at_ns: 33,
1535 }),
1536 },
1537 };
1538 let active_directory = ComponentDirectoryHead {
1539 provenance: ComponentDirectoryProvenance {
1540 component: child_binding.component.clone(),
1541 source_fleet_subnet_root: root,
1542 component_registry_revision: 4,
1543 component_registry_content_hash: [34; 32],
1544 synchronized_at_ns: 35,
1545 },
1546 descendant_count: 1,
1547 };
1548 let active_authority = ComponentRuntimeDirectoryAuthority {
1549 fleet: runtime_authority.fleet,
1550 component: active_directory.clone(),
1551 };
1552 let membership_response = RootComponentChildMembershipActivationResponse {
1553 committed: commit_response.clone(),
1554 registry: ComponentRegistryPartitionResponse {
1555 head: ComponentRegistryHead {
1556 component,
1557 revision: 4,
1558 content_hash: [34; 32],
1559 },
1560 binding: child_binding.component.clone(),
1561 provisioning_origin: commit_response.registry.provisioning_origin.clone(),
1562 release_set: commit_response.registry.release_set,
1563 status: ComponentLifecycleStatus::Active,
1564 reserved_descendants: 0,
1565 committed_descendants: 1,
1566 encoded_bytes: 8_256,
1567 },
1568 directory: active_directory,
1569 child: ComponentRuntimeStatusResponse {
1570 operation_id: request.operation_id,
1571 binding: ManagedCanisterBinding::ComponentChild(child_binding),
1572 phase: ComponentRuntimePhase::Active,
1573 authority: Some(active_authority),
1574 authority_hash: Some([36; 32]),
1575 activation: Some(ComponentRuntimeActivationEvidence {
1576 directory_authority_hash: [31; 32],
1577 activated_at_ns: 33,
1578 }),
1579 },
1580 };
1581
1582 let request_bytes = candid::encode_one(&request).expect("encode child reservation");
1583 let status_bytes =
1584 candid::encode_one(status_request).expect("encode child reservation status");
1585 let creation_bytes =
1586 candid::encode_one(creation_request).expect("encode child creation request");
1587 let install_bytes =
1588 candid::encode_one(install_request).expect("encode child install request");
1589 let response_bytes = candid::encode_one(&response).expect("encode child response");
1590 let commit_request_bytes =
1591 candid::encode_one(commit_request).expect("encode child commit request");
1592 let directory_request_bytes =
1593 candid::encode_one(directory_request).expect("encode child Directory request");
1594 let activation_request_bytes =
1595 candid::encode_one(activation_request).expect("encode child activation request");
1596 let membership_request_bytes =
1597 candid::encode_one(membership_request).expect("encode child membership request");
1598 let commit_response_bytes =
1599 candid::encode_one(&commit_response).expect("encode child commit response");
1600 let directory_response_bytes =
1601 candid::encode_one(&directory_response).expect("encode child Directory response");
1602 let activation_response_bytes =
1603 candid::encode_one(&activation_response).expect("encode child activation response");
1604 let membership_response_bytes =
1605 candid::encode_one(&membership_response).expect("encode child membership response");
1606
1607 assert_eq!(
1608 candid::decode_one::<RootComponentChildAllocationRequest>(&request_bytes)
1609 .expect("decode child reservation"),
1610 request
1611 );
1612 assert_eq!(
1613 candid::decode_one::<RootComponentChildAllocationStatusRequest>(&status_bytes)
1614 .expect("decode child reservation status"),
1615 status_request
1616 );
1617 assert_eq!(
1618 candid::decode_one::<RootComponentChildCreationRequest>(&creation_bytes)
1619 .expect("decode child creation request"),
1620 creation_request
1621 );
1622 assert_eq!(
1623 candid::decode_one::<RootComponentChildInstallRequest>(&install_bytes)
1624 .expect("decode child install request"),
1625 install_request
1626 );
1627 assert_eq!(
1628 candid::decode_one::<RootComponentChildAllocationResponse>(&response_bytes)
1629 .expect("decode child response"),
1630 response
1631 );
1632 assert_eq!(
1633 candid::decode_one::<RootComponentChildCommitRequest>(&commit_request_bytes)
1634 .expect("decode child commit request"),
1635 commit_request
1636 );
1637 assert_eq!(
1638 candid::decode_one::<RootComponentChildDirectoryPreparationRequest>(
1639 &directory_request_bytes
1640 )
1641 .expect("decode child Directory request"),
1642 directory_request
1643 );
1644 assert_eq!(
1645 candid::decode_one::<RootComponentChildCommitResponse>(&commit_response_bytes)
1646 .expect("decode child commit response"),
1647 commit_response
1648 );
1649 assert_eq!(
1650 candid::decode_one::<RootComponentChildDirectoryPreparationResponse>(
1651 &directory_response_bytes
1652 )
1653 .expect("decode child Directory response"),
1654 directory_response
1655 );
1656 assert_eq!(
1657 candid::decode_one::<RootComponentChildRuntimeActivationRequest>(
1658 &activation_request_bytes
1659 )
1660 .expect("decode child activation request"),
1661 activation_request
1662 );
1663 assert_eq!(
1664 candid::decode_one::<RootComponentChildRuntimeActivationResponse>(
1665 &activation_response_bytes
1666 )
1667 .expect("decode child activation response"),
1668 activation_response
1669 );
1670 assert_eq!(
1671 candid::decode_one::<RootComponentChildMembershipActivationRequest>(
1672 &membership_request_bytes
1673 )
1674 .expect("decode child membership request"),
1675 membership_request
1676 );
1677 assert_eq!(
1678 candid::decode_one::<RootComponentChildMembershipActivationResponse>(
1679 &membership_response_bytes
1680 )
1681 .expect("decode child membership response"),
1682 membership_response
1683 );
1684 }
1685
1686 #[test]
1687 fn component_install_request_round_trips_through_candid() {
1688 let request = RootComponentInstallRequest {
1689 operation_id: [10; 32],
1690 };
1691 let bytes = candid::encode_one(request).expect("encode install request");
1692
1693 assert_eq!(
1694 candid::decode_one::<RootComponentInstallRequest>(&bytes)
1695 .expect("decode install request"),
1696 request
1697 );
1698 }
1699
1700 #[test]
1701 fn component_commit_request_round_trips_through_candid() {
1702 let request = RootComponentCommitRequest {
1703 operation_id: [10; 32],
1704 };
1705 let bytes = candid::encode_one(request).expect("encode commit request");
1706
1707 assert_eq!(
1708 candid::decode_one::<RootComponentCommitRequest>(&bytes)
1709 .expect("decode commit request"),
1710 request
1711 );
1712 }
1713
1714 #[test]
1715 fn component_runtime_activation_requests_round_trip_through_candid() {
1716 let root_request = RootComponentRuntimeActivationRequest {
1717 operation_id: [22; 32],
1718 };
1719 let target_request = ComponentRuntimeActivationRequest {
1720 operation_id: root_request.operation_id,
1721 directory_authority_hash: [23; 32],
1722 };
1723 let membership_request = RootComponentMembershipActivationRequest {
1724 operation_id: root_request.operation_id,
1725 };
1726 let root_bytes = candid::encode_one(root_request).expect("encode root activation request");
1727 let target_bytes =
1728 candid::encode_one(target_request).expect("encode target activation request");
1729 let membership_bytes =
1730 candid::encode_one(membership_request).expect("encode membership activation request");
1731
1732 assert_eq!(
1733 candid::decode_one::<RootComponentRuntimeActivationRequest>(&root_bytes)
1734 .expect("decode root activation request"),
1735 root_request
1736 );
1737 assert_eq!(
1738 candid::decode_one::<ComponentRuntimeActivationRequest>(&target_bytes)
1739 .expect("decode target activation request"),
1740 target_request
1741 );
1742 assert_eq!(
1743 candid::decode_one::<RootComponentMembershipActivationRequest>(&membership_bytes)
1744 .expect("decode membership activation request"),
1745 membership_request
1746 );
1747 }
1748}