1use crate::{
8 cdk::types::Cycles,
9 config::schema::ComponentChildKind,
10 dto::{
11 fleet_registry::{FleetDirectorySnapshot, FleetRegistryVersion},
12 root_store::RootStoreBootstrapRequest,
13 },
14 ids::{
15 CanisterRole, ComponentBinding, ComponentChildBinding, ComponentInstanceId,
16 ComponentSpecId, ComponentTopologyDigest, FleetSubnetRootReleaseSet,
17 ManagedCanisterBinding,
18 },
19};
20use candid::{CandidType, Principal};
21use serde::{Deserialize, Serialize};
22
23#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
30pub struct RootComponentRegistryPreparationRequest {
31 pub store_bootstrap: RootStoreBootstrapRequest,
32 pub expected_fleet_registry: FleetRegistryVersion,
33}
34
35#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
42pub struct RootComponentInitialInventoryStatus {
43 pub fleet_activation_operation_id: [u8; 32],
44 pub component_count: u32,
45 pub inventory_hash: [u8; 32],
46 pub sealed_at_ns: u64,
47 pub directories_converged: bool,
48 pub root_runtime_activated: bool,
49}
50
51#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
58pub struct RootComponentRegistryStatusResponse {
59 pub fleet_subnet_root: Principal,
60 pub prepared_against_registry: FleetRegistryVersion,
61 pub release_set: FleetSubnetRootReleaseSet,
62 pub component_topology_digest: ComponentTopologyDigest,
63 pub next_allocation_sequence: u64,
64 pub reserved_component_instances: u32,
65 pub committed_component_instances: u32,
66 pub managed_descendants: u32,
67 pub known_created_component_canisters: u32,
68 pub encoded_bytes: u64,
69 pub initial_inventory: Option<RootComponentInitialInventoryStatus>,
70}
71
72#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
79pub struct RootComponentAllocationRequest {
80 pub operation_id: [u8; 32],
81 pub component_spec: ComponentSpecId,
82}
83
84#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
91pub struct RootComponentAllocationStatusRequest {
92 pub operation_id: [u8; 32],
93}
94
95#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
102pub struct RootComponentChildAllocationRequest {
103 pub operation_id: [u8; 32],
104 pub component: ComponentInstanceId,
105 pub expected_registry: ComponentRegistryHead,
106 pub child_role: CanisterRole,
107}
108
109#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
116pub struct RootComponentChildAllocationStatusRequest {
117 pub operation_id: [u8; 32],
118 pub component: ComponentInstanceId,
119}
120
121#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
128pub struct RootComponentSubtreeRemovalRequest {
129 pub operation_id: [u8; 32],
130 pub component: ComponentInstanceId,
131 pub target_canister_id: Principal,
132 pub expected_registry: ComponentRegistryHead,
133}
134
135#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
142pub struct RootComponentSubtreeRemovalAdvanceRequest {
143 pub operation_id: [u8; 32],
144 pub component: ComponentInstanceId,
145 pub expected_traversal_steps: u32,
146}
147
148#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
155pub struct RootComponentSubtreeRemovalStopPreparationRequest {
156 pub operation_id: [u8; 32],
157 pub component: ComponentInstanceId,
158 pub expected_traversal_steps: u32,
159 pub expected_leaf_canister_id: Principal,
160 pub expected_leaf_parent_canister_id: Principal,
161}
162
163#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
170pub struct RootComponentSubtreeRemovalStopRequest {
171 pub operation_id: [u8; 32],
172 pub component: ComponentInstanceId,
173 pub expected_traversal_steps: u32,
174 pub expected_leaf_canister_id: Principal,
175 pub expected_leaf_parent_canister_id: Principal,
176}
177
178#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
185pub struct RootComponentSubtreeRemovalDeletePreparationRequest {
186 pub operation_id: [u8; 32],
187 pub component: ComponentInstanceId,
188 pub expected_traversal_steps: u32,
189 pub expected_leaf_canister_id: Principal,
190 pub expected_leaf_parent_canister_id: Principal,
191}
192
193#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
200pub struct RootComponentSubtreeRemovalDeleteRequest {
201 pub operation_id: [u8; 32],
202 pub component: ComponentInstanceId,
203 pub expected_traversal_steps: u32,
204 pub expected_leaf_canister_id: Principal,
205 pub expected_leaf_parent_canister_id: Principal,
206}
207
208#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
215pub struct RootComponentSubtreeRemovalMembershipRemovalRequest {
216 pub operation_id: [u8; 32],
217 pub component: ComponentInstanceId,
218 pub expected_traversal_steps: u32,
219 pub expected_leaf_canister_id: Principal,
220 pub expected_leaf_parent_canister_id: Principal,
221}
222
223#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
230pub struct RootComponentSubtreeRemovalStatusRequest {
231 pub operation_id: [u8; 32],
232 pub component: ComponentInstanceId,
233}
234
235#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
242pub struct RootComponentChildCreationRequest {
243 pub operation_id: [u8; 32],
244 pub component: ComponentInstanceId,
245}
246
247#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
254pub struct RootComponentChildInstallRequest {
255 pub operation_id: [u8; 32],
256 pub component: ComponentInstanceId,
257}
258
259#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
266pub struct RootComponentChildCommitRequest {
267 pub operation_id: [u8; 32],
268 pub component: ComponentInstanceId,
269}
270
271#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
278pub struct RootComponentChildDirectoryPreparationRequest {
279 pub operation_id: [u8; 32],
280 pub component: ComponentInstanceId,
281}
282
283#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
290pub struct RootComponentChildRuntimeActivationRequest {
291 pub operation_id: [u8; 32],
292 pub component: ComponentInstanceId,
293}
294
295#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
302pub struct RootComponentChildMembershipActivationRequest {
303 pub operation_id: [u8; 32],
304 pub component: ComponentInstanceId,
305}
306
307#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
314pub struct RootComponentCreationRequest {
315 pub operation_id: [u8; 32],
316}
317
318#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
325pub struct RootComponentInstallRequest {
326 pub operation_id: [u8; 32],
327}
328
329#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
336pub struct RootComponentCommitRequest {
337 pub operation_id: [u8; 32],
338}
339
340#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
347pub struct RootComponentDirectoryPreparationRequest {
348 pub operation_id: [u8; 32],
349}
350
351#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
358pub struct RootComponentRuntimeActivationRequest {
359 pub operation_id: [u8; 32],
360}
361
362#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
369pub struct RootComponentMembershipActivationRequest {
370 pub operation_id: [u8; 32],
371}
372
373#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
380pub enum ComponentProvisioningOrigin {
381 FleetAdministrator { caller: Principal },
382}
383
384#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
391pub enum RootComponentAllocationPhase {
392 Reserved,
393 CreationIntent,
394 Created,
395 InstallIntent,
396 Installed,
397 Verified,
398 Committed,
399}
400
401#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
408#[expect(
409 clippy::large_enum_variant,
410 reason = "wire phases retain complete inline receipts for deterministic Candid responses"
411)]
412pub enum RootComponentSubtreeRemovalPhase {
413 Fenced,
414 Traversing(RootComponentSubtreeRemovalNode),
415 LeafSelected(RootComponentSubtreeRemovalNode),
416 StopIntent(RootComponentSubtreeRemovalStopIntent),
417 Stopped(RootComponentSubtreeRemovalStoppedReceipt),
418 DeleteIntent(RootComponentSubtreeRemovalDeleteIntent),
419 Deleted(RootComponentSubtreeRemovalDeletedReceipt),
420 MembershipRemoved(RootComponentSubtreeRemovalMembershipRemovedReceipt),
421}
422
423#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
430pub struct RootComponentSubtreeRemovalNode {
431 pub canister_id: Principal,
432 pub parent_canister_id: Principal,
433 pub role: CanisterRole,
434 pub kind: ComponentChildKind,
435 pub installed_artifact_hash: [u8; 32],
436 pub status: ComponentLifecycleStatus,
437}
438
439#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
446pub struct RootComponentSubtreeRemovalStopIntent {
447 pub leaf: RootComponentSubtreeRemovalNode,
448 pub controller: Principal,
449}
450
451#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
458pub struct RootComponentSubtreeRemovalStoppedReceipt {
459 pub stop: RootComponentSubtreeRemovalStopIntent,
460 pub observed_module_hash: [u8; 32],
461}
462
463#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
470pub struct RootComponentSubtreeRemovalDeleteIntent {
471 pub stopped: RootComponentSubtreeRemovalStoppedReceipt,
472}
473
474#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
481pub struct RootComponentSubtreeRemovalDeletedReceipt {
482 pub deletion: RootComponentSubtreeRemovalDeleteIntent,
483}
484
485#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
492pub struct RootComponentSubtreeRemovalMembershipRemovedReceipt {
493 pub deleted: RootComponentSubtreeRemovalDeletedReceipt,
494 pub removed_from_registry: ComponentRegistryHead,
495 pub previous_descendant_content_hash: [u8; 32],
496 pub previous_committed_descendants: u32,
497 pub registry: ComponentRegistryHead,
498 pub descendant_content_hash: [u8; 32],
499 pub registry_encoded_bytes: u64,
500 pub reserved_descendants: u32,
501 pub committed_descendants: u32,
502 pub directory_synchronized_at_ns: u64,
503 pub directory_authority_hash: [u8; 32],
504 pub parent_role_instances: u32,
505 pub root_managed_descendants: u32,
506 pub root_known_created_component_canisters: u32,
507}
508
509#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
516pub enum ComponentLifecycleStatus {
517 Prepared,
518 Active,
519 Draining,
520 Removed,
521}
522
523#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
530pub struct ComponentRegistryHead {
531 pub component: ComponentInstanceId,
532 pub revision: u64,
533 pub content_hash: [u8; 32],
534}
535
536#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
543pub struct ComponentRegistryPartitionRequest {
544 pub component: ComponentInstanceId,
545}
546
547#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
554pub struct ComponentRegistryPartitionResponse {
555 pub head: ComponentRegistryHead,
556 pub binding: ComponentBinding,
557 pub provisioning_origin: ComponentProvisioningOrigin,
558 pub release_set: FleetSubnetRootReleaseSet,
559 pub status: ComponentLifecycleStatus,
560 pub reserved_descendants: u32,
561 pub committed_descendants: u32,
562 pub encoded_bytes: u64,
563}
564
565#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
572pub struct ComponentDirectoryProvenance {
573 pub component: ComponentBinding,
574 pub source_fleet_subnet_root: Principal,
575 pub component_registry_revision: u64,
576 pub component_registry_content_hash: [u8; 32],
577 pub synchronized_at_ns: u64,
578}
579
580#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
587pub struct ComponentDirectoryHead {
588 pub provenance: ComponentDirectoryProvenance,
589 pub descendant_count: u32,
590}
591
592#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
599pub struct ComponentDirectoryHeadRequest {
600 pub component: ComponentInstanceId,
601}
602
603#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
610pub struct ComponentDirectoryPageCursor(pub Vec<u8>);
611
612#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
619pub struct ComponentDirectoryPageRequest {
620 pub directory: ComponentDirectoryHead,
621 pub parent_canister_id: Option<Principal>,
622 pub role: Option<CanisterRole>,
623 pub status: Option<ComponentLifecycleStatus>,
624 pub cursor: Option<ComponentDirectoryPageCursor>,
625 pub limit: u16,
626}
627
628#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
635pub struct ComponentDirectoryChildEntry {
636 pub binding: ComponentChildBinding,
637 pub kind: ComponentChildKind,
638 pub installed_artifact_hash: [u8; 32],
639 pub status: ComponentLifecycleStatus,
640}
641
642#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
649pub struct ComponentDirectoryPageResponse {
650 pub directory: ComponentDirectoryHead,
651 pub entries: Vec<ComponentDirectoryChildEntry>,
652 pub next_cursor: Option<ComponentDirectoryPageCursor>,
653}
654
655#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
662pub struct ComponentRuntimeDirectoryAuthority {
663 pub fleet: FleetDirectorySnapshot,
664 pub component: ComponentDirectoryHead,
665}
666
667#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
674pub struct ComponentRuntimeDirectoryPreparationRequest {
675 pub operation_id: [u8; 32],
676 pub authority: ComponentRuntimeDirectoryAuthority,
677}
678
679#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
686pub struct ComponentRuntimeDirectorySynchronizationRequest {
687 pub operation_id: [u8; 32],
688 pub authority: ComponentRuntimeDirectoryAuthority,
689}
690
691#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
698pub enum ComponentRuntimePhase {
699 AwaitingDirectory,
700 DirectoryPrepared,
701 Active,
702}
703
704#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
711pub struct ComponentRuntimeActivationEvidence {
712 pub directory_authority_hash: [u8; 32],
713 pub activated_at_ns: u64,
714}
715
716#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
723pub struct ComponentRuntimeActivationRequest {
724 pub operation_id: [u8; 32],
725 pub directory_authority_hash: [u8; 32],
726}
727
728#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
735pub struct ComponentRuntimeStatusResponse {
736 pub operation_id: [u8; 32],
737 pub binding: ManagedCanisterBinding,
738 pub phase: ComponentRuntimePhase,
739 pub authority: Option<ComponentRuntimeDirectoryAuthority>,
740 pub authority_hash: Option<[u8; 32]>,
741 pub activation: Option<ComponentRuntimeActivationEvidence>,
742}
743
744#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
751pub struct ComponentRuntimeDirectoryConvergenceEvidence {
752 pub operation_id: [u8; 32],
753 pub binding: ManagedCanisterBinding,
754 pub covered_authority: ComponentRuntimeDirectoryAuthority,
755 pub covered_authority_hash: [u8; 32],
756 pub activation: ComponentRuntimeActivationEvidence,
757}
758
759#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
766pub struct RootComponentCreationEvidence {
767 pub wasm_store: Principal,
768 pub payload_hash: [u8; 32],
769 pub payload_size_bytes: u64,
770 pub initial_cycles: Cycles,
771 pub controller: Principal,
772 pub canister: Option<Principal>,
773}
774
775#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
782pub struct RootComponentInstallEvidence {
783 pub raw_module_hash: [u8; 32],
784 pub chunk_hashes: Vec<Vec<u8>>,
785 pub binding: ComponentBinding,
786}
787
788#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
795pub struct RootComponentChildInstallEvidence {
796 pub raw_module_hash: [u8; 32],
797 pub chunk_hashes: Vec<Vec<u8>>,
798 pub binding: ComponentChildBinding,
799}
800
801#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
808pub struct RootComponentAllocationResponse {
809 pub operation_id: [u8; 32],
810 pub allocation_sequence: u64,
811 pub component: ComponentInstanceId,
812 pub component_spec: ComponentSpecId,
813 pub spec_hash: [u8; 32],
814 pub role: CanisterRole,
815 pub provisioning_origin: ComponentProvisioningOrigin,
816 pub release_set: FleetSubnetRootReleaseSet,
817 pub phase: RootComponentAllocationPhase,
818 pub creation: Option<RootComponentCreationEvidence>,
819 pub installation: Option<RootComponentInstallEvidence>,
820}
821
822#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
829pub struct RootComponentChildAllocationResponse {
830 pub operation_id: [u8; 32],
831 pub component: ComponentInstanceId,
832 pub parent_canister_id: Principal,
833 pub parent_role: CanisterRole,
834 pub child_role: CanisterRole,
835 pub child_kind: ComponentChildKind,
836 pub maximum_instances_per_parent: u32,
837 pub maximum_descendants: u32,
838 pub maximum_registry_bytes: u64,
839 pub reserved_against_registry: ComponentRegistryHead,
840 pub release_set: FleetSubnetRootReleaseSet,
841 pub phase: RootComponentAllocationPhase,
842 pub creation: Option<RootComponentCreationEvidence>,
843 pub installation: Option<RootComponentChildInstallEvidence>,
844}
845
846#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
853pub struct RootComponentSubtreeRemovalResponse {
854 pub operation_id: [u8; 32],
855 pub component: ComponentInstanceId,
856 pub target_canister_id: Principal,
857 pub target_parent_canister_id: Principal,
858 pub target_role: CanisterRole,
859 pub target_status: ComponentLifecycleStatus,
860 pub reserved_against_registry: ComponentRegistryHead,
861 pub traversal_steps: u32,
862 pub phase: RootComponentSubtreeRemovalPhase,
863}
864
865#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
872pub struct RootComponentChildCommitResponse {
873 pub allocation: RootComponentChildAllocationResponse,
874 pub registry: ComponentRegistryPartitionResponse,
875 pub directory: ComponentDirectoryHead,
876}
877
878#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
885pub struct RootComponentChildDirectoryPreparationResponse {
886 pub committed: RootComponentChildCommitResponse,
887 pub child: ComponentRuntimeStatusResponse,
888 pub owning_component: ComponentRuntimeDirectoryConvergenceEvidence,
889 pub parent: Option<ComponentRuntimeDirectoryConvergenceEvidence>,
890}
891
892#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
899pub struct RootComponentChildRuntimeActivationResponse {
900 pub committed: RootComponentChildCommitResponse,
901 pub child: ComponentRuntimeStatusResponse,
902}
903
904#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
911pub struct RootComponentChildMembershipActivationResponse {
912 pub committed: RootComponentChildCommitResponse,
913 pub registry: ComponentRegistryPartitionResponse,
914 pub directory: ComponentDirectoryHead,
915 pub child: ComponentRuntimeStatusResponse,
916}
917
918#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
925pub struct RootComponentCommitResponse {
926 pub allocation: RootComponentAllocationResponse,
927 pub registry: ComponentRegistryPartitionResponse,
928 pub directory: ComponentDirectoryHead,
929}
930
931#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
938pub struct RootComponentDirectoryPreparationResponse {
939 pub committed: RootComponentCommitResponse,
940 pub target: ComponentRuntimeStatusResponse,
941}
942
943#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
950pub struct RootComponentRuntimeActivationResponse {
951 pub committed: RootComponentCommitResponse,
952 pub target: ComponentRuntimeStatusResponse,
953}
954
955#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
962pub struct RootComponentMembershipActivationResponse {
963 pub allocation: RootComponentAllocationResponse,
964 pub registry: ComponentRegistryPartitionResponse,
965 pub directory: ComponentDirectoryHead,
966 pub target: ComponentRuntimeStatusResponse,
967}
968
969#[cfg(test)]
970mod tests {
971 use super::*;
972 use crate::{
973 dto::root_store::RootStoreBootstrapRequest,
974 ids::{
975 AppId, CanonicalNetworkId, FleetCoordinatorBinding, FleetId, FleetKey,
976 FleetRegistryAuthority, ReleaseBuildId, ReleaseBuildNonce, ReleaseSetDigest, SubnetId,
977 },
978 };
979
980 #[test]
981 fn component_registry_contracts_round_trip_through_candid() {
982 let request = RootComponentRegistryPreparationRequest {
983 store_bootstrap: RootStoreBootstrapRequest {
984 manifest_payload_size_bytes: 128,
985 },
986 expected_fleet_registry: FleetRegistryVersion {
987 authority: fleet_registry_authority(),
988 revision: 4,
989 content_hash: [5; 32],
990 },
991 };
992 let response = RootComponentRegistryStatusResponse {
993 fleet_subnet_root: Principal::from_slice(&[6; 29]),
994 prepared_against_registry: request.expected_fleet_registry.clone(),
995 release_set: FleetSubnetRootReleaseSet {
996 release_build_id: ReleaseBuildId::from_nonce(ReleaseBuildNonce::from_random_bytes(
997 [7; 32],
998 )),
999 manifest_digest: ReleaseSetDigest::from_bytes([8; 32]),
1000 },
1001 component_topology_digest: ComponentTopologyDigest::from_bytes([9; 32]),
1002 next_allocation_sequence: 1,
1003 reserved_component_instances: 0,
1004 committed_component_instances: 0,
1005 managed_descendants: 0,
1006 known_created_component_canisters: 0,
1007 encoded_bytes: 0,
1008 initial_inventory: Some(RootComponentInitialInventoryStatus {
1009 fleet_activation_operation_id: [10; 32],
1010 component_count: 0,
1011 inventory_hash: [11; 32],
1012 sealed_at_ns: 12,
1013 directories_converged: true,
1014 root_runtime_activated: true,
1015 }),
1016 };
1017 let allocation = RootComponentAllocationResponse {
1018 operation_id: [10; 32],
1019 allocation_sequence: 1,
1020 component: ComponentInstanceId::from_generated_bytes([11; 32]),
1021 component_spec: "projects".parse().expect("Component Spec ID"),
1022 spec_hash: [12; 32],
1023 role: CanisterRole::new("project_hub"),
1024 provisioning_origin: ComponentProvisioningOrigin::FleetAdministrator {
1025 caller: Principal::from_slice(&[13; 29]),
1026 },
1027 release_set: response.release_set,
1028 phase: RootComponentAllocationPhase::Reserved,
1029 creation: None,
1030 installation: None,
1031 };
1032 let created = RootComponentAllocationResponse {
1033 phase: RootComponentAllocationPhase::Created,
1034 creation: Some(RootComponentCreationEvidence {
1035 wasm_store: Principal::from_slice(&[14; 29]),
1036 payload_hash: [15; 32],
1037 payload_size_bytes: 4_096,
1038 initial_cycles: Cycles::new(5_000_000_000_000),
1039 controller: Principal::from_slice(&[6; 29]),
1040 canister: Some(Principal::from_slice(&[16; 29])),
1041 }),
1042 installation: None,
1043 ..allocation.clone()
1044 };
1045 let request_bytes = candid::encode_one(&request).expect("encode request");
1046 let response_bytes = candid::encode_one(&response).expect("encode response");
1047 let allocation_bytes = candid::encode_one(&allocation).expect("encode allocation");
1048 let created_bytes = candid::encode_one(&created).expect("encode created allocation");
1049
1050 assert_eq!(
1051 candid::decode_one::<RootComponentRegistryPreparationRequest>(&request_bytes)
1052 .expect("decode request"),
1053 request
1054 );
1055 assert_eq!(
1056 candid::decode_one::<RootComponentRegistryStatusResponse>(&response_bytes)
1057 .expect("decode response"),
1058 response
1059 );
1060 assert_eq!(
1061 candid::decode_one::<RootComponentAllocationResponse>(&allocation_bytes)
1062 .expect("decode allocation"),
1063 allocation
1064 );
1065 assert_eq!(
1066 candid::decode_one::<RootComponentAllocationResponse>(&created_bytes)
1067 .expect("decode created allocation"),
1068 created
1069 );
1070 }
1071
1072 #[test]
1073 fn component_commit_response_round_trips_through_candid() {
1074 let root = Principal::from_slice(&[6; 29]);
1075 let component = ComponentInstanceId::from_generated_bytes([11; 32]);
1076 let component_spec: ComponentSpecId = "projects".parse().expect("Component Spec ID");
1077 let release_set = FleetSubnetRootReleaseSet {
1078 release_build_id: ReleaseBuildId::from_nonce(ReleaseBuildNonce::from_random_bytes(
1079 [7; 32],
1080 )),
1081 manifest_digest: ReleaseSetDigest::from_bytes([8; 32]),
1082 };
1083 let provisioning_origin = ComponentProvisioningOrigin::FleetAdministrator {
1084 caller: Principal::from_slice(&[13; 29]),
1085 };
1086 let binding = ComponentBinding {
1087 authority: fleet_registry_authority(),
1088 component,
1089 component_spec: component_spec.clone(),
1090 spec_hash: [12; 32],
1091 role: CanisterRole::new("project_hub"),
1092 placement_subnet: SubnetId::from_principal(Principal::from_slice(&[17; 29])),
1093 fleet_subnet_root: root,
1094 canister_id: Principal::from_slice(&[16; 29]),
1095 };
1096 let head = ComponentRegistryHead {
1097 component,
1098 revision: 1,
1099 content_hash: [18; 32],
1100 };
1101 let committed = RootComponentCommitResponse {
1102 allocation: RootComponentAllocationResponse {
1103 operation_id: [10; 32],
1104 allocation_sequence: 1,
1105 component,
1106 component_spec,
1107 spec_hash: binding.spec_hash,
1108 role: binding.role.clone(),
1109 provisioning_origin: provisioning_origin.clone(),
1110 release_set,
1111 phase: RootComponentAllocationPhase::Committed,
1112 creation: Some(RootComponentCreationEvidence {
1113 wasm_store: Principal::from_slice(&[14; 29]),
1114 payload_hash: [15; 32],
1115 payload_size_bytes: 4_096,
1116 initial_cycles: Cycles::new(5_000_000_000_000),
1117 controller: root,
1118 canister: Some(binding.canister_id),
1119 }),
1120 installation: Some(RootComponentInstallEvidence {
1121 raw_module_hash: [20; 32],
1122 chunk_hashes: vec![vec![21; 32]],
1123 binding: binding.clone(),
1124 }),
1125 },
1126 registry: ComponentRegistryPartitionResponse {
1127 head: head.clone(),
1128 binding: binding.clone(),
1129 provisioning_origin,
1130 release_set,
1131 status: ComponentLifecycleStatus::Prepared,
1132 reserved_descendants: 0,
1133 committed_descendants: 0,
1134 encoded_bytes: 2_048,
1135 },
1136 directory: ComponentDirectoryHead {
1137 provenance: ComponentDirectoryProvenance {
1138 component: binding,
1139 source_fleet_subnet_root: root,
1140 component_registry_revision: head.revision,
1141 component_registry_content_hash: head.content_hash,
1142 synchronized_at_ns: 19,
1143 },
1144 descendant_count: 0,
1145 },
1146 };
1147 let committed_bytes = candid::encode_one(&committed).expect("encode committed allocation");
1148
1149 assert_eq!(
1150 candid::decode_one::<RootComponentCommitResponse>(&committed_bytes)
1151 .expect("decode committed allocation"),
1152 committed
1153 );
1154 }
1155
1156 #[test]
1157 fn component_directory_page_contracts_round_trip_through_candid() {
1158 let root = Principal::from_slice(&[6; 29]);
1159 let component = ComponentInstanceId::from_generated_bytes([11; 32]);
1160 let binding = ComponentBinding {
1161 authority: fleet_registry_authority(),
1162 component,
1163 component_spec: "projects".parse().expect("Component Spec ID"),
1164 spec_hash: [12; 32],
1165 role: CanisterRole::new("project_hub"),
1166 placement_subnet: SubnetId::from_principal(Principal::from_slice(&[17; 29])),
1167 fleet_subnet_root: root,
1168 canister_id: Principal::from_slice(&[16; 29]),
1169 };
1170 let directory = ComponentDirectoryHead {
1171 provenance: ComponentDirectoryProvenance {
1172 component: binding.clone(),
1173 source_fleet_subnet_root: root,
1174 component_registry_revision: 3,
1175 component_registry_content_hash: [18; 32],
1176 synchronized_at_ns: 19,
1177 },
1178 descendant_count: 1,
1179 };
1180 let request = ComponentDirectoryPageRequest {
1181 directory: directory.clone(),
1182 parent_canister_id: Some(binding.canister_id),
1183 role: Some(CanisterRole::new("project_instance")),
1184 status: Some(ComponentLifecycleStatus::Active),
1185 cursor: Some(ComponentDirectoryPageCursor(vec![20; 64])),
1186 limit: 50,
1187 };
1188 let response = ComponentDirectoryPageResponse {
1189 directory,
1190 entries: vec![ComponentDirectoryChildEntry {
1191 binding: ComponentChildBinding {
1192 component: binding.clone(),
1193 parent_canister_id: binding.canister_id,
1194 role: CanisterRole::new("project_instance"),
1195 canister_id: Principal::from_slice(&[21; 29]),
1196 },
1197 kind: ComponentChildKind::Instance,
1198 installed_artifact_hash: [22; 32],
1199 status: ComponentLifecycleStatus::Active,
1200 }],
1201 next_cursor: Some(ComponentDirectoryPageCursor(vec![23; 64])),
1202 };
1203 let request_bytes = candid::encode_one(&request).expect("encode Directory page request");
1204 let response_bytes = candid::encode_one(&response).expect("encode Directory page response");
1205
1206 assert_eq!(
1207 candid::decode_one::<ComponentDirectoryPageRequest>(&request_bytes)
1208 .expect("decode Directory page request"),
1209 request
1210 );
1211 assert_eq!(
1212 candid::decode_one::<ComponentDirectoryPageResponse>(&response_bytes)
1213 .expect("decode Directory page response"),
1214 response
1215 );
1216 }
1217
1218 fn fleet_registry_authority() -> FleetRegistryAuthority {
1219 FleetRegistryAuthority {
1220 binding: FleetCoordinatorBinding {
1221 fleet: crate::ids::FleetBinding {
1222 fleet: FleetKey {
1223 canonical_network_id: CanonicalNetworkId::ic_mainnet(),
1224 fleet_id: FleetId::from_generated_bytes([1; 32]),
1225 },
1226 app: AppId::from("toko"),
1227 },
1228 coordinator_subnet: SubnetId::from_principal(Principal::from_slice(&[2; 29])),
1229 coordinator: Principal::from_slice(&[3; 29]),
1230 },
1231 epoch: 1,
1232 }
1233 }
1234
1235 #[test]
1236 fn component_creation_request_round_trips_through_candid() {
1237 let request = RootComponentCreationRequest {
1238 operation_id: [10; 32],
1239 };
1240 let bytes = candid::encode_one(request).expect("encode creation request");
1241
1242 assert_eq!(
1243 candid::decode_one::<RootComponentCreationRequest>(&bytes)
1244 .expect("decode creation request"),
1245 request
1246 );
1247 }
1248
1249 #[test]
1250 #[expect(
1251 clippy::too_many_lines,
1252 reason = "one Candid contract test covers every subtree-removal phase receipt"
1253 )]
1254 fn component_subtree_removal_contracts_round_trip_through_candid() {
1255 let component = ComponentInstanceId::from_generated_bytes([41; 32]);
1256 let registry = ComponentRegistryHead {
1257 component,
1258 revision: 7,
1259 content_hash: [42; 32],
1260 };
1261 let request = RootComponentSubtreeRemovalRequest {
1262 operation_id: [43; 32],
1263 component,
1264 target_canister_id: Principal::from_slice(&[44; 29]),
1265 expected_registry: registry.clone(),
1266 };
1267 let status_request = RootComponentSubtreeRemovalStatusRequest {
1268 operation_id: request.operation_id,
1269 component,
1270 };
1271 let advance_request = RootComponentSubtreeRemovalAdvanceRequest {
1272 operation_id: request.operation_id,
1273 component,
1274 expected_traversal_steps: 1,
1275 };
1276 let stop_request = RootComponentSubtreeRemovalStopPreparationRequest {
1277 operation_id: request.operation_id,
1278 component,
1279 expected_traversal_steps: 2,
1280 expected_leaf_canister_id: Principal::from_slice(&[46; 29]),
1281 expected_leaf_parent_canister_id: request.target_canister_id,
1282 };
1283 let stopped = RootComponentSubtreeRemovalStoppedReceipt {
1284 observed_module_hash: [49; 32],
1285 stop: RootComponentSubtreeRemovalStopIntent {
1286 controller: Principal::from_slice(&[48; 29]),
1287 leaf: RootComponentSubtreeRemovalNode {
1288 canister_id: Principal::from_slice(&[46; 29]),
1289 parent_canister_id: request.target_canister_id,
1290 role: CanisterRole::new("project_ledger"),
1291 kind: ComponentChildKind::Singleton,
1292 installed_artifact_hash: [47; 32],
1293 status: ComponentLifecycleStatus::Active,
1294 },
1295 },
1296 };
1297 let response = RootComponentSubtreeRemovalResponse {
1298 operation_id: request.operation_id,
1299 component,
1300 target_canister_id: request.target_canister_id,
1301 target_parent_canister_id: Principal::from_slice(&[45; 29]),
1302 target_role: CanisterRole::new("project_instance"),
1303 target_status: ComponentLifecycleStatus::Active,
1304 reserved_against_registry: registry,
1305 traversal_steps: 2,
1306 phase: RootComponentSubtreeRemovalPhase::MembershipRemoved(
1307 RootComponentSubtreeRemovalMembershipRemovedReceipt {
1308 deleted: RootComponentSubtreeRemovalDeletedReceipt {
1309 deletion: RootComponentSubtreeRemovalDeleteIntent { stopped },
1310 },
1311 removed_from_registry: ComponentRegistryHead {
1312 component,
1313 revision: 8,
1314 content_hash: [50; 32],
1315 },
1316 previous_descendant_content_hash: [51; 32],
1317 previous_committed_descendants: 4,
1318 registry: ComponentRegistryHead {
1319 component,
1320 revision: 9,
1321 content_hash: [52; 32],
1322 },
1323 descendant_content_hash: [53; 32],
1324 registry_encoded_bytes: 4_096,
1325 reserved_descendants: 1,
1326 committed_descendants: 3,
1327 directory_synchronized_at_ns: 54,
1328 directory_authority_hash: [55; 32],
1329 parent_role_instances: 0,
1330 root_managed_descendants: 4,
1331 root_known_created_component_canisters: 4,
1332 },
1333 ),
1334 };
1335
1336 let request_bytes = candid::encode_one(&request).expect("encode subtree removal request");
1337 let advance_bytes =
1338 candid::encode_one(advance_request).expect("encode subtree removal advance request");
1339 let stop_bytes =
1340 candid::encode_one(stop_request).expect("encode subtree removal stop request");
1341 let status_bytes =
1342 candid::encode_one(status_request).expect("encode subtree removal status request");
1343 let response_bytes =
1344 candid::encode_one(&response).expect("encode subtree removal response");
1345
1346 assert_eq!(
1347 candid::decode_one::<RootComponentSubtreeRemovalRequest>(&request_bytes)
1348 .expect("decode subtree removal request"),
1349 request
1350 );
1351 assert_eq!(
1352 candid::decode_one::<RootComponentSubtreeRemovalAdvanceRequest>(&advance_bytes)
1353 .expect("decode subtree removal advance request"),
1354 advance_request
1355 );
1356 assert_eq!(
1357 candid::decode_one::<RootComponentSubtreeRemovalStopPreparationRequest>(&stop_bytes)
1358 .expect("decode subtree removal stop request"),
1359 stop_request
1360 );
1361 assert_eq!(
1362 candid::decode_one::<RootComponentSubtreeRemovalStatusRequest>(&status_bytes)
1363 .expect("decode subtree removal status request"),
1364 status_request
1365 );
1366 assert_eq!(
1367 candid::decode_one::<RootComponentSubtreeRemovalResponse>(&response_bytes)
1368 .expect("decode subtree removal response"),
1369 response
1370 );
1371 }
1372
1373 #[test]
1374 fn component_subtree_removal_deletion_requests_round_trip_through_candid() {
1375 let prepare = RootComponentSubtreeRemovalDeletePreparationRequest {
1376 operation_id: [50; 32],
1377 component: ComponentInstanceId::from_generated_bytes([51; 32]),
1378 expected_traversal_steps: 3,
1379 expected_leaf_canister_id: Principal::from_slice(&[52; 29]),
1380 expected_leaf_parent_canister_id: Principal::from_slice(&[53; 29]),
1381 };
1382 let request = RootComponentSubtreeRemovalDeleteRequest {
1383 operation_id: prepare.operation_id,
1384 component: prepare.component,
1385 expected_traversal_steps: prepare.expected_traversal_steps,
1386 expected_leaf_canister_id: prepare.expected_leaf_canister_id,
1387 expected_leaf_parent_canister_id: prepare.expected_leaf_parent_canister_id,
1388 };
1389 let membership_request = RootComponentSubtreeRemovalMembershipRemovalRequest {
1390 operation_id: prepare.operation_id,
1391 component: prepare.component,
1392 expected_traversal_steps: prepare.expected_traversal_steps,
1393 expected_leaf_canister_id: prepare.expected_leaf_canister_id,
1394 expected_leaf_parent_canister_id: prepare.expected_leaf_parent_canister_id,
1395 };
1396 let prepare_bytes = candid::encode_one(prepare)
1397 .expect("encode subtree removal deletion preparation request");
1398 let request_bytes =
1399 candid::encode_one(request).expect("encode subtree removal deletion request");
1400 let membership_request_bytes = candid::encode_one(membership_request)
1401 .expect("encode subtree removal membership-removal request");
1402
1403 assert_eq!(
1404 candid::decode_one::<RootComponentSubtreeRemovalDeletePreparationRequest>(
1405 &prepare_bytes
1406 )
1407 .expect("decode subtree removal deletion preparation request"),
1408 prepare
1409 );
1410 assert_eq!(
1411 candid::decode_one::<RootComponentSubtreeRemovalDeleteRequest>(&request_bytes)
1412 .expect("decode subtree removal deletion request"),
1413 request
1414 );
1415 assert_eq!(
1416 candid::decode_one::<RootComponentSubtreeRemovalMembershipRemovalRequest>(
1417 &membership_request_bytes
1418 )
1419 .expect("decode subtree removal membership-removal request"),
1420 membership_request
1421 );
1422 }
1423
1424 #[test]
1425 fn component_subtree_removal_stop_request_round_trips_through_candid() {
1426 let request = RootComponentSubtreeRemovalStopRequest {
1427 operation_id: [50; 32],
1428 component: ComponentInstanceId::from_generated_bytes([51; 32]),
1429 expected_traversal_steps: 3,
1430 expected_leaf_canister_id: Principal::from_slice(&[52; 29]),
1431 expected_leaf_parent_canister_id: Principal::from_slice(&[53; 29]),
1432 };
1433 let bytes =
1434 candid::encode_one(request).expect("encode subtree removal stop execution request");
1435
1436 assert_eq!(
1437 candid::decode_one::<RootComponentSubtreeRemovalStopRequest>(&bytes)
1438 .expect("decode subtree removal stop execution request"),
1439 request
1440 );
1441 }
1442
1443 #[test]
1444 #[expect(
1445 clippy::too_many_lines,
1446 reason = "one round-trip test keeps the complete child lifecycle boundary coherent"
1447 )]
1448 fn component_child_lifecycle_contracts_round_trip_through_candid() {
1449 let component = ComponentInstanceId::from_generated_bytes([11; 32]);
1450 let registry = ComponentRegistryHead {
1451 component,
1452 revision: 2,
1453 content_hash: [12; 32],
1454 };
1455 let request = RootComponentChildAllocationRequest {
1456 operation_id: [13; 32],
1457 component,
1458 expected_registry: registry.clone(),
1459 child_role: CanisterRole::new("project_instance"),
1460 };
1461 let status_request = RootComponentChildAllocationStatusRequest {
1462 operation_id: request.operation_id,
1463 component,
1464 };
1465 let creation_request = RootComponentChildCreationRequest {
1466 operation_id: request.operation_id,
1467 component,
1468 };
1469 let install_request = RootComponentChildInstallRequest {
1470 operation_id: request.operation_id,
1471 component,
1472 };
1473 let commit_request = RootComponentChildCommitRequest {
1474 operation_id: request.operation_id,
1475 component,
1476 };
1477 let directory_request = RootComponentChildDirectoryPreparationRequest {
1478 operation_id: request.operation_id,
1479 component,
1480 };
1481 let activation_request = RootComponentChildRuntimeActivationRequest {
1482 operation_id: request.operation_id,
1483 component,
1484 };
1485 let membership_request = RootComponentChildMembershipActivationRequest {
1486 operation_id: request.operation_id,
1487 component,
1488 };
1489 let root = Principal::from_slice(&[17; 29]);
1490 let parent = Principal::from_slice(&[14; 29]);
1491 let child = Principal::from_slice(&[18; 29]);
1492 let child_binding = ComponentChildBinding {
1493 component: ComponentBinding {
1494 authority: fleet_registry_authority(),
1495 component,
1496 component_spec: "projects".parse().expect("Component Spec"),
1497 spec_hash: [19; 32],
1498 role: CanisterRole::new("project_hub"),
1499 placement_subnet: SubnetId::from_principal(Principal::from_slice(&[20; 29])),
1500 fleet_subnet_root: root,
1501 canister_id: parent,
1502 },
1503 parent_canister_id: parent,
1504 role: request.child_role.clone(),
1505 canister_id: child,
1506 };
1507 let response = RootComponentChildAllocationResponse {
1508 operation_id: request.operation_id,
1509 component,
1510 parent_canister_id: parent,
1511 parent_role: CanisterRole::new("project_hub"),
1512 child_role: request.child_role.clone(),
1513 child_kind: ComponentChildKind::Instance,
1514 maximum_instances_per_parent: 10_000,
1515 maximum_descendants: 20_000,
1516 maximum_registry_bytes: 16_777_216,
1517 reserved_against_registry: registry,
1518 release_set: FleetSubnetRootReleaseSet {
1519 release_build_id: ReleaseBuildId::from_nonce(ReleaseBuildNonce::from_random_bytes(
1520 [15; 32],
1521 )),
1522 manifest_digest: ReleaseSetDigest::from_bytes([16; 32]),
1523 },
1524 phase: RootComponentAllocationPhase::Verified,
1525 creation: Some(RootComponentCreationEvidence {
1526 wasm_store: Principal::from_slice(&[21; 29]),
1527 payload_hash: [22; 32],
1528 payload_size_bytes: 4_096,
1529 initial_cycles: Cycles::new(5_000_000_000_000),
1530 controller: root,
1531 canister: Some(child),
1532 }),
1533 installation: Some(RootComponentChildInstallEvidence {
1534 raw_module_hash: [23; 32],
1535 chunk_hashes: vec![vec![24; 32]],
1536 binding: child_binding.clone(),
1537 }),
1538 };
1539 let commit_response = RootComponentChildCommitResponse {
1540 allocation: response.clone(),
1541 registry: ComponentRegistryPartitionResponse {
1542 head: ComponentRegistryHead {
1543 component,
1544 revision: 3,
1545 content_hash: [25; 32],
1546 },
1547 binding: child_binding.component.clone(),
1548 provisioning_origin: ComponentProvisioningOrigin::FleetAdministrator {
1549 caller: Principal::from_slice(&[26; 29]),
1550 },
1551 release_set: response.release_set,
1552 status: ComponentLifecycleStatus::Active,
1553 reserved_descendants: 0,
1554 committed_descendants: 1,
1555 encoded_bytes: 8_192,
1556 },
1557 directory: ComponentDirectoryHead {
1558 provenance: ComponentDirectoryProvenance {
1559 component: child_binding.component.clone(),
1560 source_fleet_subnet_root: root,
1561 component_registry_revision: 3,
1562 component_registry_content_hash: [25; 32],
1563 synchronized_at_ns: 27,
1564 },
1565 descendant_count: 1,
1566 },
1567 };
1568 let runtime_authority = ComponentRuntimeDirectoryAuthority {
1569 fleet: FleetDirectorySnapshot {
1570 provenance: crate::dto::fleet_registry::FleetDirectoryProvenance {
1571 registry: FleetRegistryVersion {
1572 authority: fleet_registry_authority(),
1573 revision: 4,
1574 content_hash: [28; 32],
1575 },
1576 source_fleet_subnet_root: root,
1577 },
1578 fleet_subnet_roots: vec![
1579 crate::dto::fleet_registry::FleetSubnetRootDirectoryEntry {
1580 placement_subnet: commit_response.registry.binding.placement_subnet,
1581 fleet_subnet_root: root,
1582 status: crate::dto::fleet_registry::FleetSubnetRootStatus::Active,
1583 },
1584 ],
1585 },
1586 component: commit_response.directory.clone(),
1587 };
1588 let activation = ComponentRuntimeActivationEvidence {
1589 directory_authority_hash: [29; 32],
1590 activated_at_ns: 30,
1591 };
1592 let directory_response = RootComponentChildDirectoryPreparationResponse {
1593 committed: commit_response.clone(),
1594 child: ComponentRuntimeStatusResponse {
1595 operation_id: request.operation_id,
1596 binding: ManagedCanisterBinding::ComponentChild(child_binding.clone()),
1597 phase: ComponentRuntimePhase::DirectoryPrepared,
1598 authority: Some(runtime_authority.clone()),
1599 authority_hash: Some([31; 32]),
1600 activation: None,
1601 },
1602 owning_component: ComponentRuntimeDirectoryConvergenceEvidence {
1603 operation_id: [32; 32],
1604 binding: ManagedCanisterBinding::Component(child_binding.component.clone()),
1605 covered_authority: runtime_authority.clone(),
1606 covered_authority_hash: [31; 32],
1607 activation,
1608 },
1609 parent: None,
1610 };
1611 let activation_response = RootComponentChildRuntimeActivationResponse {
1612 committed: commit_response.clone(),
1613 child: ComponentRuntimeStatusResponse {
1614 operation_id: request.operation_id,
1615 binding: ManagedCanisterBinding::ComponentChild(child_binding.clone()),
1616 phase: ComponentRuntimePhase::Active,
1617 authority: Some(runtime_authority.clone()),
1618 authority_hash: Some([31; 32]),
1619 activation: Some(ComponentRuntimeActivationEvidence {
1620 directory_authority_hash: [31; 32],
1621 activated_at_ns: 33,
1622 }),
1623 },
1624 };
1625 let active_directory = ComponentDirectoryHead {
1626 provenance: ComponentDirectoryProvenance {
1627 component: child_binding.component.clone(),
1628 source_fleet_subnet_root: root,
1629 component_registry_revision: 4,
1630 component_registry_content_hash: [34; 32],
1631 synchronized_at_ns: 35,
1632 },
1633 descendant_count: 1,
1634 };
1635 let active_authority = ComponentRuntimeDirectoryAuthority {
1636 fleet: runtime_authority.fleet,
1637 component: active_directory.clone(),
1638 };
1639 let membership_response = RootComponentChildMembershipActivationResponse {
1640 committed: commit_response.clone(),
1641 registry: ComponentRegistryPartitionResponse {
1642 head: ComponentRegistryHead {
1643 component,
1644 revision: 4,
1645 content_hash: [34; 32],
1646 },
1647 binding: child_binding.component.clone(),
1648 provisioning_origin: commit_response.registry.provisioning_origin.clone(),
1649 release_set: commit_response.registry.release_set,
1650 status: ComponentLifecycleStatus::Active,
1651 reserved_descendants: 0,
1652 committed_descendants: 1,
1653 encoded_bytes: 8_256,
1654 },
1655 directory: active_directory,
1656 child: ComponentRuntimeStatusResponse {
1657 operation_id: request.operation_id,
1658 binding: ManagedCanisterBinding::ComponentChild(child_binding),
1659 phase: ComponentRuntimePhase::Active,
1660 authority: Some(active_authority),
1661 authority_hash: Some([36; 32]),
1662 activation: Some(ComponentRuntimeActivationEvidence {
1663 directory_authority_hash: [31; 32],
1664 activated_at_ns: 33,
1665 }),
1666 },
1667 };
1668
1669 let request_bytes = candid::encode_one(&request).expect("encode child reservation");
1670 let status_bytes =
1671 candid::encode_one(status_request).expect("encode child reservation status");
1672 let creation_bytes =
1673 candid::encode_one(creation_request).expect("encode child creation request");
1674 let install_bytes =
1675 candid::encode_one(install_request).expect("encode child install request");
1676 let response_bytes = candid::encode_one(&response).expect("encode child response");
1677 let commit_request_bytes =
1678 candid::encode_one(commit_request).expect("encode child commit request");
1679 let directory_request_bytes =
1680 candid::encode_one(directory_request).expect("encode child Directory request");
1681 let activation_request_bytes =
1682 candid::encode_one(activation_request).expect("encode child activation request");
1683 let membership_request_bytes =
1684 candid::encode_one(membership_request).expect("encode child membership request");
1685 let commit_response_bytes =
1686 candid::encode_one(&commit_response).expect("encode child commit response");
1687 let directory_response_bytes =
1688 candid::encode_one(&directory_response).expect("encode child Directory response");
1689 let activation_response_bytes =
1690 candid::encode_one(&activation_response).expect("encode child activation response");
1691 let membership_response_bytes =
1692 candid::encode_one(&membership_response).expect("encode child membership response");
1693
1694 assert_eq!(
1695 candid::decode_one::<RootComponentChildAllocationRequest>(&request_bytes)
1696 .expect("decode child reservation"),
1697 request
1698 );
1699 assert_eq!(
1700 candid::decode_one::<RootComponentChildAllocationStatusRequest>(&status_bytes)
1701 .expect("decode child reservation status"),
1702 status_request
1703 );
1704 assert_eq!(
1705 candid::decode_one::<RootComponentChildCreationRequest>(&creation_bytes)
1706 .expect("decode child creation request"),
1707 creation_request
1708 );
1709 assert_eq!(
1710 candid::decode_one::<RootComponentChildInstallRequest>(&install_bytes)
1711 .expect("decode child install request"),
1712 install_request
1713 );
1714 assert_eq!(
1715 candid::decode_one::<RootComponentChildAllocationResponse>(&response_bytes)
1716 .expect("decode child response"),
1717 response
1718 );
1719 assert_eq!(
1720 candid::decode_one::<RootComponentChildCommitRequest>(&commit_request_bytes)
1721 .expect("decode child commit request"),
1722 commit_request
1723 );
1724 assert_eq!(
1725 candid::decode_one::<RootComponentChildDirectoryPreparationRequest>(
1726 &directory_request_bytes
1727 )
1728 .expect("decode child Directory request"),
1729 directory_request
1730 );
1731 assert_eq!(
1732 candid::decode_one::<RootComponentChildCommitResponse>(&commit_response_bytes)
1733 .expect("decode child commit response"),
1734 commit_response
1735 );
1736 assert_eq!(
1737 candid::decode_one::<RootComponentChildDirectoryPreparationResponse>(
1738 &directory_response_bytes
1739 )
1740 .expect("decode child Directory response"),
1741 directory_response
1742 );
1743 assert_eq!(
1744 candid::decode_one::<RootComponentChildRuntimeActivationRequest>(
1745 &activation_request_bytes
1746 )
1747 .expect("decode child activation request"),
1748 activation_request
1749 );
1750 assert_eq!(
1751 candid::decode_one::<RootComponentChildRuntimeActivationResponse>(
1752 &activation_response_bytes
1753 )
1754 .expect("decode child activation response"),
1755 activation_response
1756 );
1757 assert_eq!(
1758 candid::decode_one::<RootComponentChildMembershipActivationRequest>(
1759 &membership_request_bytes
1760 )
1761 .expect("decode child membership request"),
1762 membership_request
1763 );
1764 assert_eq!(
1765 candid::decode_one::<RootComponentChildMembershipActivationResponse>(
1766 &membership_response_bytes
1767 )
1768 .expect("decode child membership response"),
1769 membership_response
1770 );
1771 }
1772
1773 #[test]
1774 fn component_install_request_round_trips_through_candid() {
1775 let request = RootComponentInstallRequest {
1776 operation_id: [10; 32],
1777 };
1778 let bytes = candid::encode_one(request).expect("encode install request");
1779
1780 assert_eq!(
1781 candid::decode_one::<RootComponentInstallRequest>(&bytes)
1782 .expect("decode install request"),
1783 request
1784 );
1785 }
1786
1787 #[test]
1788 fn component_commit_request_round_trips_through_candid() {
1789 let request = RootComponentCommitRequest {
1790 operation_id: [10; 32],
1791 };
1792 let bytes = candid::encode_one(request).expect("encode commit request");
1793
1794 assert_eq!(
1795 candid::decode_one::<RootComponentCommitRequest>(&bytes)
1796 .expect("decode commit request"),
1797 request
1798 );
1799 }
1800
1801 #[test]
1802 fn component_runtime_activation_requests_round_trip_through_candid() {
1803 let root_request = RootComponentRuntimeActivationRequest {
1804 operation_id: [22; 32],
1805 };
1806 let target_request = ComponentRuntimeActivationRequest {
1807 operation_id: root_request.operation_id,
1808 directory_authority_hash: [23; 32],
1809 };
1810 let membership_request = RootComponentMembershipActivationRequest {
1811 operation_id: root_request.operation_id,
1812 };
1813 let root_bytes = candid::encode_one(root_request).expect("encode root activation request");
1814 let target_bytes =
1815 candid::encode_one(target_request).expect("encode target activation request");
1816 let membership_bytes =
1817 candid::encode_one(membership_request).expect("encode membership activation request");
1818
1819 assert_eq!(
1820 candid::decode_one::<RootComponentRuntimeActivationRequest>(&root_bytes)
1821 .expect("decode root activation request"),
1822 root_request
1823 );
1824 assert_eq!(
1825 candid::decode_one::<ComponentRuntimeActivationRequest>(&target_bytes)
1826 .expect("decode target activation request"),
1827 target_request
1828 );
1829 assert_eq!(
1830 candid::decode_one::<RootComponentMembershipActivationRequest>(&membership_bytes)
1831 .expect("decode membership activation request"),
1832 membership_request
1833 );
1834 }
1835}