1use crate::{
8 cdk::types::Cycles,
9 config::schema::ComponentChildKind,
10 dto::{
11 fleet_registry::{FleetDirectorySnapshot, FleetRegistryVersion},
12 root_store::RootStoreBootstrapRequest,
13 },
14 ids::{
15 CanisterRole, ComponentBinding, ComponentChildBinding, ComponentInstanceId,
16 ComponentSpecId, ComponentTopologyDigest, FleetSubnetRootReleaseSet,
17 ManagedCanisterBinding,
18 },
19};
20use candid::{CandidType, Principal};
21use serde::{Deserialize, Serialize};
22
23#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
30pub struct RootComponentRegistryPreparationRequest {
31 pub store_bootstrap: RootStoreBootstrapRequest,
32 pub expected_fleet_registry: FleetRegistryVersion,
33}
34
35#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
42pub struct RootComponentInitialInventoryStatus {
43 pub fleet_activation_operation_id: [u8; 32],
44 pub component_count: u32,
45 pub inventory_hash: [u8; 32],
46 pub sealed_at_ns: u64,
47 pub directories_converged: bool,
48 pub root_runtime_activated: bool,
49}
50
51#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
58pub struct RootComponentRegistryStatusResponse {
59 pub fleet_subnet_root: Principal,
60 pub prepared_against_registry: FleetRegistryVersion,
61 pub release_set: FleetSubnetRootReleaseSet,
62 pub component_topology_digest: ComponentTopologyDigest,
63 pub next_allocation_sequence: u64,
64 pub reserved_component_instances: u32,
65 pub committed_component_instances: u32,
66 pub managed_descendants: u32,
67 pub known_created_component_canisters: u32,
68 pub encoded_bytes: u64,
69 pub initial_inventory: Option<RootComponentInitialInventoryStatus>,
70}
71
72#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
79pub struct RootComponentAllocationRequest {
80 pub operation_id: [u8; 32],
81 pub component_spec: ComponentSpecId,
82}
83
84#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
91pub struct RootComponentAllocationStatusRequest {
92 pub operation_id: [u8; 32],
93}
94
95#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
102pub struct RootComponentChildAllocationRequest {
103 pub operation_id: [u8; 32],
104 pub component: ComponentInstanceId,
105 pub expected_registry: ComponentRegistryHead,
106 pub child_role: CanisterRole,
107}
108
109#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
116pub struct RootComponentChildAllocationStatusRequest {
117 pub operation_id: [u8; 32],
118 pub component: ComponentInstanceId,
119}
120
121#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
128pub struct RootComponentSubtreeRemovalRequest {
129 pub operation_id: [u8; 32],
130 pub component: ComponentInstanceId,
131 pub target_canister_id: Principal,
132 pub expected_registry: ComponentRegistryHead,
133}
134
135#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
142pub struct RootComponentSubtreeRemovalAdvanceRequest {
143 pub operation_id: [u8; 32],
144 pub component: ComponentInstanceId,
145 pub expected_traversal_steps: u32,
146}
147
148#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
155pub struct RootComponentSubtreeRemovalStopPreparationRequest {
156 pub operation_id: [u8; 32],
157 pub component: ComponentInstanceId,
158 pub expected_traversal_steps: u32,
159 pub expected_leaf_canister_id: Principal,
160 pub expected_leaf_parent_canister_id: Principal,
161}
162
163#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
170pub struct RootComponentSubtreeRemovalStopRequest {
171 pub operation_id: [u8; 32],
172 pub component: ComponentInstanceId,
173 pub expected_traversal_steps: u32,
174 pub expected_leaf_canister_id: Principal,
175 pub expected_leaf_parent_canister_id: Principal,
176}
177
178#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
185pub struct RootComponentSubtreeRemovalDeletePreparationRequest {
186 pub operation_id: [u8; 32],
187 pub component: ComponentInstanceId,
188 pub expected_traversal_steps: u32,
189 pub expected_leaf_canister_id: Principal,
190 pub expected_leaf_parent_canister_id: Principal,
191}
192
193#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
200pub struct RootComponentSubtreeRemovalDeleteRequest {
201 pub operation_id: [u8; 32],
202 pub component: ComponentInstanceId,
203 pub expected_traversal_steps: u32,
204 pub expected_leaf_canister_id: Principal,
205 pub expected_leaf_parent_canister_id: Principal,
206}
207
208#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
215pub struct RootComponentSubtreeRemovalMembershipRemovalRequest {
216 pub operation_id: [u8; 32],
217 pub component: ComponentInstanceId,
218 pub expected_traversal_steps: u32,
219 pub expected_leaf_canister_id: Principal,
220 pub expected_leaf_parent_canister_id: Principal,
221}
222
223#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
230pub struct RootComponentSubtreeRemovalDirectorySynchronizationRequest {
231 pub operation_id: [u8; 32],
232 pub component: ComponentInstanceId,
233 pub expected_traversal_steps: u32,
234 pub expected_leaf_canister_id: Principal,
235 pub expected_leaf_parent_canister_id: Principal,
236}
237
238#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
245pub struct RootComponentSubtreeRemovalStatusRequest {
246 pub operation_id: [u8; 32],
247 pub component: ComponentInstanceId,
248}
249
250#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
257pub struct RootComponentChildCreationRequest {
258 pub operation_id: [u8; 32],
259 pub component: ComponentInstanceId,
260}
261
262#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
269pub struct RootComponentChildInstallRequest {
270 pub operation_id: [u8; 32],
271 pub component: ComponentInstanceId,
272}
273
274#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
281pub struct RootComponentChildCommitRequest {
282 pub operation_id: [u8; 32],
283 pub component: ComponentInstanceId,
284}
285
286#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
293pub struct RootComponentChildDirectoryPreparationRequest {
294 pub operation_id: [u8; 32],
295 pub component: ComponentInstanceId,
296}
297
298#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
305pub struct RootComponentChildRuntimeActivationRequest {
306 pub operation_id: [u8; 32],
307 pub component: ComponentInstanceId,
308}
309
310#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
317pub struct RootComponentChildMembershipActivationRequest {
318 pub operation_id: [u8; 32],
319 pub component: ComponentInstanceId,
320}
321
322#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
329pub struct RootComponentCreationRequest {
330 pub operation_id: [u8; 32],
331}
332
333#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
340pub struct RootComponentInstallRequest {
341 pub operation_id: [u8; 32],
342}
343
344#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
351pub struct RootComponentCommitRequest {
352 pub operation_id: [u8; 32],
353}
354
355#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
362pub struct RootComponentDirectoryPreparationRequest {
363 pub operation_id: [u8; 32],
364}
365
366#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
373pub struct RootComponentRuntimeActivationRequest {
374 pub operation_id: [u8; 32],
375}
376
377#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
384pub struct RootComponentMembershipActivationRequest {
385 pub operation_id: [u8; 32],
386}
387
388#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
395pub enum ComponentProvisioningOrigin {
396 FleetAdministrator { caller: Principal },
397}
398
399#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
406pub enum RootComponentAllocationPhase {
407 Reserved,
408 CreationIntent,
409 Created,
410 InstallIntent,
411 Installed,
412 Verified,
413 Committed,
414}
415
416#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
423#[expect(
424 clippy::large_enum_variant,
425 reason = "wire phases retain complete inline receipts for deterministic Candid responses"
426)]
427pub enum RootComponentSubtreeRemovalPhase {
428 Fenced,
429 Traversing(RootComponentSubtreeRemovalNode),
430 LeafSelected(RootComponentSubtreeRemovalNode),
431 StopIntent(RootComponentSubtreeRemovalStopIntent),
432 Stopped(RootComponentSubtreeRemovalStoppedReceipt),
433 DeleteIntent(RootComponentSubtreeRemovalDeleteIntent),
434 Deleted(RootComponentSubtreeRemovalDeletedReceipt),
435 MembershipRemoved(RootComponentSubtreeRemovalMembershipRemovedReceipt),
436 DirectorySynchronized(RootComponentSubtreeRemovalDirectorySynchronizedReceipt),
437}
438
439#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
446pub struct RootComponentSubtreeRemovalNode {
447 pub canister_id: Principal,
448 pub parent_canister_id: Principal,
449 pub role: CanisterRole,
450 pub kind: ComponentChildKind,
451 pub installed_artifact_hash: [u8; 32],
452 pub status: ComponentLifecycleStatus,
453}
454
455#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
462pub struct RootComponentSubtreeRemovalStopIntent {
463 pub leaf: RootComponentSubtreeRemovalNode,
464 pub controller: Principal,
465}
466
467#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
474pub struct RootComponentSubtreeRemovalStoppedReceipt {
475 pub stop: RootComponentSubtreeRemovalStopIntent,
476 pub observed_module_hash: [u8; 32],
477}
478
479#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
486pub struct RootComponentSubtreeRemovalDeleteIntent {
487 pub stopped: RootComponentSubtreeRemovalStoppedReceipt,
488}
489
490#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
497pub struct RootComponentSubtreeRemovalDeletedReceipt {
498 pub deletion: RootComponentSubtreeRemovalDeleteIntent,
499}
500
501#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
508pub struct RootComponentSubtreeRemovalMembershipRemovedReceipt {
509 pub deleted: RootComponentSubtreeRemovalDeletedReceipt,
510 pub removed_from_registry: ComponentRegistryHead,
511 pub previous_descendant_content_hash: [u8; 32],
512 pub previous_committed_descendants: u32,
513 pub registry: ComponentRegistryHead,
514 pub descendant_content_hash: [u8; 32],
515 pub registry_encoded_bytes: u64,
516 pub reserved_descendants: u32,
517 pub committed_descendants: u32,
518 pub directory_synchronized_at_ns: u64,
519 pub directory_authority_hash: [u8; 32],
520 pub parent_role_instances: u32,
521 pub root_managed_descendants: u32,
522 pub root_known_created_component_canisters: u32,
523}
524
525#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
532pub struct RootComponentSubtreeRemovalDirectoryConvergenceEvidence {
533 pub operation_id: [u8; 32],
534 pub canister_id: Principal,
535 pub activation: ComponentRuntimeActivationEvidence,
536}
537
538#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
545pub struct RootComponentSubtreeRemovalDirectorySynchronizedReceipt {
546 pub membership_removed: RootComponentSubtreeRemovalMembershipRemovedReceipt,
547 pub covered_fleet_registry_revision: u64,
548 pub covered_fleet_registry_content_hash: [u8; 32],
549 pub covered_component_registry: ComponentRegistryHead,
550 pub covered_authority_hash: [u8; 32],
551 pub owning_component: RootComponentSubtreeRemovalDirectoryConvergenceEvidence,
552 pub parent: Option<RootComponentSubtreeRemovalDirectoryConvergenceEvidence>,
553}
554
555#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
562pub enum ComponentLifecycleStatus {
563 Prepared,
564 Active,
565 Draining,
566 Removed,
567}
568
569#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
576pub struct ComponentRegistryHead {
577 pub component: ComponentInstanceId,
578 pub revision: u64,
579 pub content_hash: [u8; 32],
580}
581
582#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
589pub struct ComponentRegistryPartitionRequest {
590 pub component: ComponentInstanceId,
591}
592
593#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
600pub struct ComponentRegistryPartitionResponse {
601 pub head: ComponentRegistryHead,
602 pub binding: ComponentBinding,
603 pub provisioning_origin: ComponentProvisioningOrigin,
604 pub release_set: FleetSubnetRootReleaseSet,
605 pub status: ComponentLifecycleStatus,
606 pub reserved_descendants: u32,
607 pub committed_descendants: u32,
608 pub encoded_bytes: u64,
609}
610
611#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
618pub struct ComponentDirectoryProvenance {
619 pub component: ComponentBinding,
620 pub source_fleet_subnet_root: Principal,
621 pub component_registry_revision: u64,
622 pub component_registry_content_hash: [u8; 32],
623 pub synchronized_at_ns: u64,
624}
625
626#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
633pub struct ComponentDirectoryHead {
634 pub provenance: ComponentDirectoryProvenance,
635 pub descendant_count: u32,
636}
637
638#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
645pub struct ComponentDirectoryHeadRequest {
646 pub component: ComponentInstanceId,
647}
648
649#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
656pub struct ComponentDirectoryPageCursor(pub Vec<u8>);
657
658#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
665pub struct ComponentDirectoryPageRequest {
666 pub directory: ComponentDirectoryHead,
667 pub parent_canister_id: Option<Principal>,
668 pub role: Option<CanisterRole>,
669 pub status: Option<ComponentLifecycleStatus>,
670 pub cursor: Option<ComponentDirectoryPageCursor>,
671 pub limit: u16,
672}
673
674#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
681pub struct ComponentDirectoryChildEntry {
682 pub binding: ComponentChildBinding,
683 pub kind: ComponentChildKind,
684 pub installed_artifact_hash: [u8; 32],
685 pub status: ComponentLifecycleStatus,
686}
687
688#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
695pub struct ComponentDirectoryPageResponse {
696 pub directory: ComponentDirectoryHead,
697 pub entries: Vec<ComponentDirectoryChildEntry>,
698 pub next_cursor: Option<ComponentDirectoryPageCursor>,
699}
700
701#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
708pub struct ComponentRuntimeDirectoryAuthority {
709 pub fleet: FleetDirectorySnapshot,
710 pub component: ComponentDirectoryHead,
711}
712
713#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
720pub struct ComponentRuntimeDirectoryPreparationRequest {
721 pub operation_id: [u8; 32],
722 pub authority: ComponentRuntimeDirectoryAuthority,
723}
724
725#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
732pub struct ComponentRuntimeDirectorySynchronizationRequest {
733 pub operation_id: [u8; 32],
734 pub authority: ComponentRuntimeDirectoryAuthority,
735}
736
737#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
744pub enum ComponentRuntimePhase {
745 AwaitingDirectory,
746 DirectoryPrepared,
747 Active,
748}
749
750#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
757pub struct ComponentRuntimeActivationEvidence {
758 pub directory_authority_hash: [u8; 32],
759 pub activated_at_ns: u64,
760}
761
762#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
769pub struct ComponentRuntimeActivationRequest {
770 pub operation_id: [u8; 32],
771 pub directory_authority_hash: [u8; 32],
772}
773
774#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
781pub struct ComponentRuntimeStatusResponse {
782 pub operation_id: [u8; 32],
783 pub binding: ManagedCanisterBinding,
784 pub phase: ComponentRuntimePhase,
785 pub authority: Option<ComponentRuntimeDirectoryAuthority>,
786 pub authority_hash: Option<[u8; 32]>,
787 pub activation: Option<ComponentRuntimeActivationEvidence>,
788}
789
790#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
797pub struct ComponentRuntimeDirectoryConvergenceEvidence {
798 pub operation_id: [u8; 32],
799 pub binding: ManagedCanisterBinding,
800 pub covered_authority: ComponentRuntimeDirectoryAuthority,
801 pub covered_authority_hash: [u8; 32],
802 pub activation: ComponentRuntimeActivationEvidence,
803}
804
805#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
812pub struct RootComponentCreationEvidence {
813 pub wasm_store: Principal,
814 pub payload_hash: [u8; 32],
815 pub payload_size_bytes: u64,
816 pub initial_cycles: Cycles,
817 pub controller: Principal,
818 pub canister: Option<Principal>,
819}
820
821#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
828pub struct RootComponentInstallEvidence {
829 pub raw_module_hash: [u8; 32],
830 pub chunk_hashes: Vec<Vec<u8>>,
831 pub binding: ComponentBinding,
832}
833
834#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
841pub struct RootComponentChildInstallEvidence {
842 pub raw_module_hash: [u8; 32],
843 pub chunk_hashes: Vec<Vec<u8>>,
844 pub binding: ComponentChildBinding,
845}
846
847#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
854pub struct RootComponentAllocationResponse {
855 pub operation_id: [u8; 32],
856 pub allocation_sequence: u64,
857 pub component: ComponentInstanceId,
858 pub component_spec: ComponentSpecId,
859 pub spec_hash: [u8; 32],
860 pub role: CanisterRole,
861 pub provisioning_origin: ComponentProvisioningOrigin,
862 pub release_set: FleetSubnetRootReleaseSet,
863 pub phase: RootComponentAllocationPhase,
864 pub creation: Option<RootComponentCreationEvidence>,
865 pub installation: Option<RootComponentInstallEvidence>,
866}
867
868#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
875pub struct RootComponentChildAllocationResponse {
876 pub operation_id: [u8; 32],
877 pub component: ComponentInstanceId,
878 pub parent_canister_id: Principal,
879 pub parent_role: CanisterRole,
880 pub child_role: CanisterRole,
881 pub child_kind: ComponentChildKind,
882 pub maximum_instances_per_parent: u32,
883 pub maximum_descendants: u32,
884 pub maximum_registry_bytes: u64,
885 pub reserved_against_registry: ComponentRegistryHead,
886 pub release_set: FleetSubnetRootReleaseSet,
887 pub phase: RootComponentAllocationPhase,
888 pub creation: Option<RootComponentCreationEvidence>,
889 pub installation: Option<RootComponentChildInstallEvidence>,
890}
891
892#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
899pub struct RootComponentSubtreeRemovalResponse {
900 pub operation_id: [u8; 32],
901 pub component: ComponentInstanceId,
902 pub target_canister_id: Principal,
903 pub target_parent_canister_id: Principal,
904 pub target_role: CanisterRole,
905 pub target_status: ComponentLifecycleStatus,
906 pub reserved_against_registry: ComponentRegistryHead,
907 pub traversal_steps: u32,
908 pub phase: RootComponentSubtreeRemovalPhase,
909}
910
911#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
918pub struct RootComponentChildCommitResponse {
919 pub allocation: RootComponentChildAllocationResponse,
920 pub registry: ComponentRegistryPartitionResponse,
921 pub directory: ComponentDirectoryHead,
922}
923
924#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
931pub struct RootComponentChildDirectoryPreparationResponse {
932 pub committed: RootComponentChildCommitResponse,
933 pub child: ComponentRuntimeStatusResponse,
934 pub owning_component: ComponentRuntimeDirectoryConvergenceEvidence,
935 pub parent: Option<ComponentRuntimeDirectoryConvergenceEvidence>,
936}
937
938#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
945pub struct RootComponentChildRuntimeActivationResponse {
946 pub committed: RootComponentChildCommitResponse,
947 pub child: ComponentRuntimeStatusResponse,
948}
949
950#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
957pub struct RootComponentChildMembershipActivationResponse {
958 pub committed: RootComponentChildCommitResponse,
959 pub registry: ComponentRegistryPartitionResponse,
960 pub directory: ComponentDirectoryHead,
961 pub child: ComponentRuntimeStatusResponse,
962}
963
964#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
971pub struct RootComponentCommitResponse {
972 pub allocation: RootComponentAllocationResponse,
973 pub registry: ComponentRegistryPartitionResponse,
974 pub directory: ComponentDirectoryHead,
975}
976
977#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
984pub struct RootComponentDirectoryPreparationResponse {
985 pub committed: RootComponentCommitResponse,
986 pub target: ComponentRuntimeStatusResponse,
987}
988
989#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
996pub struct RootComponentRuntimeActivationResponse {
997 pub committed: RootComponentCommitResponse,
998 pub target: ComponentRuntimeStatusResponse,
999}
1000
1001#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1008pub struct RootComponentMembershipActivationResponse {
1009 pub allocation: RootComponentAllocationResponse,
1010 pub registry: ComponentRegistryPartitionResponse,
1011 pub directory: ComponentDirectoryHead,
1012 pub target: ComponentRuntimeStatusResponse,
1013}
1014
1015#[cfg(test)]
1016mod tests {
1017 use super::*;
1018 use crate::{
1019 dto::root_store::RootStoreBootstrapRequest,
1020 ids::{
1021 AppId, CanonicalNetworkId, FleetCoordinatorBinding, FleetId, FleetKey,
1022 FleetRegistryAuthority, ReleaseBuildId, ReleaseBuildNonce, ReleaseSetDigest, SubnetId,
1023 },
1024 };
1025
1026 #[test]
1027 fn component_registry_contracts_round_trip_through_candid() {
1028 let request = RootComponentRegistryPreparationRequest {
1029 store_bootstrap: RootStoreBootstrapRequest {
1030 manifest_payload_size_bytes: 128,
1031 },
1032 expected_fleet_registry: FleetRegistryVersion {
1033 authority: fleet_registry_authority(),
1034 revision: 4,
1035 content_hash: [5; 32],
1036 },
1037 };
1038 let response = RootComponentRegistryStatusResponse {
1039 fleet_subnet_root: Principal::from_slice(&[6; 29]),
1040 prepared_against_registry: request.expected_fleet_registry.clone(),
1041 release_set: FleetSubnetRootReleaseSet {
1042 release_build_id: ReleaseBuildId::from_nonce(ReleaseBuildNonce::from_random_bytes(
1043 [7; 32],
1044 )),
1045 manifest_digest: ReleaseSetDigest::from_bytes([8; 32]),
1046 },
1047 component_topology_digest: ComponentTopologyDigest::from_bytes([9; 32]),
1048 next_allocation_sequence: 1,
1049 reserved_component_instances: 0,
1050 committed_component_instances: 0,
1051 managed_descendants: 0,
1052 known_created_component_canisters: 0,
1053 encoded_bytes: 0,
1054 initial_inventory: Some(RootComponentInitialInventoryStatus {
1055 fleet_activation_operation_id: [10; 32],
1056 component_count: 0,
1057 inventory_hash: [11; 32],
1058 sealed_at_ns: 12,
1059 directories_converged: true,
1060 root_runtime_activated: true,
1061 }),
1062 };
1063 let allocation = RootComponentAllocationResponse {
1064 operation_id: [10; 32],
1065 allocation_sequence: 1,
1066 component: ComponentInstanceId::from_generated_bytes([11; 32]),
1067 component_spec: "projects".parse().expect("Component Spec ID"),
1068 spec_hash: [12; 32],
1069 role: CanisterRole::new("project_hub"),
1070 provisioning_origin: ComponentProvisioningOrigin::FleetAdministrator {
1071 caller: Principal::from_slice(&[13; 29]),
1072 },
1073 release_set: response.release_set,
1074 phase: RootComponentAllocationPhase::Reserved,
1075 creation: None,
1076 installation: None,
1077 };
1078 let created = RootComponentAllocationResponse {
1079 phase: RootComponentAllocationPhase::Created,
1080 creation: Some(RootComponentCreationEvidence {
1081 wasm_store: Principal::from_slice(&[14; 29]),
1082 payload_hash: [15; 32],
1083 payload_size_bytes: 4_096,
1084 initial_cycles: Cycles::new(5_000_000_000_000),
1085 controller: Principal::from_slice(&[6; 29]),
1086 canister: Some(Principal::from_slice(&[16; 29])),
1087 }),
1088 installation: None,
1089 ..allocation.clone()
1090 };
1091 let request_bytes = candid::encode_one(&request).expect("encode request");
1092 let response_bytes = candid::encode_one(&response).expect("encode response");
1093 let allocation_bytes = candid::encode_one(&allocation).expect("encode allocation");
1094 let created_bytes = candid::encode_one(&created).expect("encode created allocation");
1095
1096 assert_eq!(
1097 candid::decode_one::<RootComponentRegistryPreparationRequest>(&request_bytes)
1098 .expect("decode request"),
1099 request
1100 );
1101 assert_eq!(
1102 candid::decode_one::<RootComponentRegistryStatusResponse>(&response_bytes)
1103 .expect("decode response"),
1104 response
1105 );
1106 assert_eq!(
1107 candid::decode_one::<RootComponentAllocationResponse>(&allocation_bytes)
1108 .expect("decode allocation"),
1109 allocation
1110 );
1111 assert_eq!(
1112 candid::decode_one::<RootComponentAllocationResponse>(&created_bytes)
1113 .expect("decode created allocation"),
1114 created
1115 );
1116 }
1117
1118 #[test]
1119 fn component_commit_response_round_trips_through_candid() {
1120 let root = Principal::from_slice(&[6; 29]);
1121 let component = ComponentInstanceId::from_generated_bytes([11; 32]);
1122 let component_spec: ComponentSpecId = "projects".parse().expect("Component Spec ID");
1123 let release_set = FleetSubnetRootReleaseSet {
1124 release_build_id: ReleaseBuildId::from_nonce(ReleaseBuildNonce::from_random_bytes(
1125 [7; 32],
1126 )),
1127 manifest_digest: ReleaseSetDigest::from_bytes([8; 32]),
1128 };
1129 let provisioning_origin = ComponentProvisioningOrigin::FleetAdministrator {
1130 caller: Principal::from_slice(&[13; 29]),
1131 };
1132 let binding = ComponentBinding {
1133 authority: fleet_registry_authority(),
1134 component,
1135 component_spec: component_spec.clone(),
1136 spec_hash: [12; 32],
1137 role: CanisterRole::new("project_hub"),
1138 placement_subnet: SubnetId::from_principal(Principal::from_slice(&[17; 29])),
1139 fleet_subnet_root: root,
1140 canister_id: Principal::from_slice(&[16; 29]),
1141 };
1142 let head = ComponentRegistryHead {
1143 component,
1144 revision: 1,
1145 content_hash: [18; 32],
1146 };
1147 let committed = RootComponentCommitResponse {
1148 allocation: RootComponentAllocationResponse {
1149 operation_id: [10; 32],
1150 allocation_sequence: 1,
1151 component,
1152 component_spec,
1153 spec_hash: binding.spec_hash,
1154 role: binding.role.clone(),
1155 provisioning_origin: provisioning_origin.clone(),
1156 release_set,
1157 phase: RootComponentAllocationPhase::Committed,
1158 creation: Some(RootComponentCreationEvidence {
1159 wasm_store: Principal::from_slice(&[14; 29]),
1160 payload_hash: [15; 32],
1161 payload_size_bytes: 4_096,
1162 initial_cycles: Cycles::new(5_000_000_000_000),
1163 controller: root,
1164 canister: Some(binding.canister_id),
1165 }),
1166 installation: Some(RootComponentInstallEvidence {
1167 raw_module_hash: [20; 32],
1168 chunk_hashes: vec![vec![21; 32]],
1169 binding: binding.clone(),
1170 }),
1171 },
1172 registry: ComponentRegistryPartitionResponse {
1173 head: head.clone(),
1174 binding: binding.clone(),
1175 provisioning_origin,
1176 release_set,
1177 status: ComponentLifecycleStatus::Prepared,
1178 reserved_descendants: 0,
1179 committed_descendants: 0,
1180 encoded_bytes: 2_048,
1181 },
1182 directory: ComponentDirectoryHead {
1183 provenance: ComponentDirectoryProvenance {
1184 component: binding,
1185 source_fleet_subnet_root: root,
1186 component_registry_revision: head.revision,
1187 component_registry_content_hash: head.content_hash,
1188 synchronized_at_ns: 19,
1189 },
1190 descendant_count: 0,
1191 },
1192 };
1193 let committed_bytes = candid::encode_one(&committed).expect("encode committed allocation");
1194
1195 assert_eq!(
1196 candid::decode_one::<RootComponentCommitResponse>(&committed_bytes)
1197 .expect("decode committed allocation"),
1198 committed
1199 );
1200 }
1201
1202 #[test]
1203 fn component_directory_page_contracts_round_trip_through_candid() {
1204 let root = Principal::from_slice(&[6; 29]);
1205 let component = ComponentInstanceId::from_generated_bytes([11; 32]);
1206 let binding = ComponentBinding {
1207 authority: fleet_registry_authority(),
1208 component,
1209 component_spec: "projects".parse().expect("Component Spec ID"),
1210 spec_hash: [12; 32],
1211 role: CanisterRole::new("project_hub"),
1212 placement_subnet: SubnetId::from_principal(Principal::from_slice(&[17; 29])),
1213 fleet_subnet_root: root,
1214 canister_id: Principal::from_slice(&[16; 29]),
1215 };
1216 let directory = ComponentDirectoryHead {
1217 provenance: ComponentDirectoryProvenance {
1218 component: binding.clone(),
1219 source_fleet_subnet_root: root,
1220 component_registry_revision: 3,
1221 component_registry_content_hash: [18; 32],
1222 synchronized_at_ns: 19,
1223 },
1224 descendant_count: 1,
1225 };
1226 let request = ComponentDirectoryPageRequest {
1227 directory: directory.clone(),
1228 parent_canister_id: Some(binding.canister_id),
1229 role: Some(CanisterRole::new("project_instance")),
1230 status: Some(ComponentLifecycleStatus::Active),
1231 cursor: Some(ComponentDirectoryPageCursor(vec![20; 64])),
1232 limit: 50,
1233 };
1234 let response = ComponentDirectoryPageResponse {
1235 directory,
1236 entries: vec![ComponentDirectoryChildEntry {
1237 binding: ComponentChildBinding {
1238 component: binding.clone(),
1239 parent_canister_id: binding.canister_id,
1240 role: CanisterRole::new("project_instance"),
1241 canister_id: Principal::from_slice(&[21; 29]),
1242 },
1243 kind: ComponentChildKind::Instance,
1244 installed_artifact_hash: [22; 32],
1245 status: ComponentLifecycleStatus::Active,
1246 }],
1247 next_cursor: Some(ComponentDirectoryPageCursor(vec![23; 64])),
1248 };
1249 let request_bytes = candid::encode_one(&request).expect("encode Directory page request");
1250 let response_bytes = candid::encode_one(&response).expect("encode Directory page response");
1251
1252 assert_eq!(
1253 candid::decode_one::<ComponentDirectoryPageRequest>(&request_bytes)
1254 .expect("decode Directory page request"),
1255 request
1256 );
1257 assert_eq!(
1258 candid::decode_one::<ComponentDirectoryPageResponse>(&response_bytes)
1259 .expect("decode Directory page response"),
1260 response
1261 );
1262 }
1263
1264 fn fleet_registry_authority() -> FleetRegistryAuthority {
1265 FleetRegistryAuthority {
1266 binding: FleetCoordinatorBinding {
1267 fleet: crate::ids::FleetBinding {
1268 fleet: FleetKey {
1269 canonical_network_id: CanonicalNetworkId::ic_mainnet(),
1270 fleet_id: FleetId::from_generated_bytes([1; 32]),
1271 },
1272 app: AppId::from("toko"),
1273 },
1274 coordinator_subnet: SubnetId::from_principal(Principal::from_slice(&[2; 29])),
1275 coordinator: Principal::from_slice(&[3; 29]),
1276 },
1277 epoch: 1,
1278 }
1279 }
1280
1281 #[test]
1282 fn component_creation_request_round_trips_through_candid() {
1283 let request = RootComponentCreationRequest {
1284 operation_id: [10; 32],
1285 };
1286 let bytes = candid::encode_one(request).expect("encode creation request");
1287
1288 assert_eq!(
1289 candid::decode_one::<RootComponentCreationRequest>(&bytes)
1290 .expect("decode creation request"),
1291 request
1292 );
1293 }
1294
1295 #[test]
1296 #[expect(
1297 clippy::too_many_lines,
1298 reason = "one Candid contract test covers every subtree-removal phase receipt"
1299 )]
1300 fn component_subtree_removal_contracts_round_trip_through_candid() {
1301 let component = ComponentInstanceId::from_generated_bytes([41; 32]);
1302 let registry = ComponentRegistryHead {
1303 component,
1304 revision: 7,
1305 content_hash: [42; 32],
1306 };
1307 let request = RootComponentSubtreeRemovalRequest {
1308 operation_id: [43; 32],
1309 component,
1310 target_canister_id: Principal::from_slice(&[44; 29]),
1311 expected_registry: registry.clone(),
1312 };
1313 let status_request = RootComponentSubtreeRemovalStatusRequest {
1314 operation_id: request.operation_id,
1315 component,
1316 };
1317 let advance_request = RootComponentSubtreeRemovalAdvanceRequest {
1318 operation_id: request.operation_id,
1319 component,
1320 expected_traversal_steps: 1,
1321 };
1322 let stop_request = RootComponentSubtreeRemovalStopPreparationRequest {
1323 operation_id: request.operation_id,
1324 component,
1325 expected_traversal_steps: 2,
1326 expected_leaf_canister_id: Principal::from_slice(&[46; 29]),
1327 expected_leaf_parent_canister_id: request.target_canister_id,
1328 };
1329 let stopped = RootComponentSubtreeRemovalStoppedReceipt {
1330 observed_module_hash: [49; 32],
1331 stop: RootComponentSubtreeRemovalStopIntent {
1332 controller: Principal::from_slice(&[48; 29]),
1333 leaf: RootComponentSubtreeRemovalNode {
1334 canister_id: Principal::from_slice(&[46; 29]),
1335 parent_canister_id: request.target_canister_id,
1336 role: CanisterRole::new("project_ledger"),
1337 kind: ComponentChildKind::Singleton,
1338 installed_artifact_hash: [47; 32],
1339 status: ComponentLifecycleStatus::Active,
1340 },
1341 },
1342 };
1343 let response = RootComponentSubtreeRemovalResponse {
1344 operation_id: request.operation_id,
1345 component,
1346 target_canister_id: request.target_canister_id,
1347 target_parent_canister_id: Principal::from_slice(&[45; 29]),
1348 target_role: CanisterRole::new("project_instance"),
1349 target_status: ComponentLifecycleStatus::Active,
1350 reserved_against_registry: registry,
1351 traversal_steps: 2,
1352 phase: RootComponentSubtreeRemovalPhase::DirectorySynchronized(
1353 RootComponentSubtreeRemovalDirectorySynchronizedReceipt {
1354 membership_removed: RootComponentSubtreeRemovalMembershipRemovedReceipt {
1355 deleted: RootComponentSubtreeRemovalDeletedReceipt {
1356 deletion: RootComponentSubtreeRemovalDeleteIntent { stopped },
1357 },
1358 removed_from_registry: ComponentRegistryHead {
1359 component,
1360 revision: 8,
1361 content_hash: [50; 32],
1362 },
1363 previous_descendant_content_hash: [51; 32],
1364 previous_committed_descendants: 4,
1365 registry: ComponentRegistryHead {
1366 component,
1367 revision: 9,
1368 content_hash: [52; 32],
1369 },
1370 descendant_content_hash: [53; 32],
1371 registry_encoded_bytes: 4_096,
1372 reserved_descendants: 1,
1373 committed_descendants: 3,
1374 directory_synchronized_at_ns: 54,
1375 directory_authority_hash: [55; 32],
1376 parent_role_instances: 0,
1377 root_managed_descendants: 4,
1378 root_known_created_component_canisters: 4,
1379 },
1380 covered_fleet_registry_revision: 6,
1381 covered_fleet_registry_content_hash: [56; 32],
1382 covered_component_registry: ComponentRegistryHead {
1383 component,
1384 revision: 9,
1385 content_hash: [52; 32],
1386 },
1387 covered_authority_hash: [55; 32],
1388 owning_component: RootComponentSubtreeRemovalDirectoryConvergenceEvidence {
1389 operation_id: [57; 32],
1390 canister_id: Principal::from_slice(&[58; 29]),
1391 activation: ComponentRuntimeActivationEvidence {
1392 directory_authority_hash: [59; 32],
1393 activated_at_ns: 60,
1394 },
1395 },
1396 parent: Some(RootComponentSubtreeRemovalDirectoryConvergenceEvidence {
1397 operation_id: [61; 32],
1398 canister_id: request.target_canister_id,
1399 activation: ComponentRuntimeActivationEvidence {
1400 directory_authority_hash: [62; 32],
1401 activated_at_ns: 63,
1402 },
1403 }),
1404 },
1405 ),
1406 };
1407
1408 let request_bytes = candid::encode_one(&request).expect("encode subtree removal request");
1409 let advance_bytes =
1410 candid::encode_one(advance_request).expect("encode subtree removal advance request");
1411 let stop_bytes =
1412 candid::encode_one(stop_request).expect("encode subtree removal stop request");
1413 let status_bytes =
1414 candid::encode_one(status_request).expect("encode subtree removal status request");
1415 let response_bytes =
1416 candid::encode_one(&response).expect("encode subtree removal response");
1417
1418 assert_eq!(
1419 candid::decode_one::<RootComponentSubtreeRemovalRequest>(&request_bytes)
1420 .expect("decode subtree removal request"),
1421 request
1422 );
1423 assert_eq!(
1424 candid::decode_one::<RootComponentSubtreeRemovalAdvanceRequest>(&advance_bytes)
1425 .expect("decode subtree removal advance request"),
1426 advance_request
1427 );
1428 assert_eq!(
1429 candid::decode_one::<RootComponentSubtreeRemovalStopPreparationRequest>(&stop_bytes)
1430 .expect("decode subtree removal stop request"),
1431 stop_request
1432 );
1433 assert_eq!(
1434 candid::decode_one::<RootComponentSubtreeRemovalStatusRequest>(&status_bytes)
1435 .expect("decode subtree removal status request"),
1436 status_request
1437 );
1438 assert_eq!(
1439 candid::decode_one::<RootComponentSubtreeRemovalResponse>(&response_bytes)
1440 .expect("decode subtree removal response"),
1441 response
1442 );
1443 }
1444
1445 #[test]
1446 fn component_subtree_removal_deletion_requests_round_trip_through_candid() {
1447 let prepare = RootComponentSubtreeRemovalDeletePreparationRequest {
1448 operation_id: [50; 32],
1449 component: ComponentInstanceId::from_generated_bytes([51; 32]),
1450 expected_traversal_steps: 3,
1451 expected_leaf_canister_id: Principal::from_slice(&[52; 29]),
1452 expected_leaf_parent_canister_id: Principal::from_slice(&[53; 29]),
1453 };
1454 let request = RootComponentSubtreeRemovalDeleteRequest {
1455 operation_id: prepare.operation_id,
1456 component: prepare.component,
1457 expected_traversal_steps: prepare.expected_traversal_steps,
1458 expected_leaf_canister_id: prepare.expected_leaf_canister_id,
1459 expected_leaf_parent_canister_id: prepare.expected_leaf_parent_canister_id,
1460 };
1461 let membership_request = RootComponentSubtreeRemovalMembershipRemovalRequest {
1462 operation_id: prepare.operation_id,
1463 component: prepare.component,
1464 expected_traversal_steps: prepare.expected_traversal_steps,
1465 expected_leaf_canister_id: prepare.expected_leaf_canister_id,
1466 expected_leaf_parent_canister_id: prepare.expected_leaf_parent_canister_id,
1467 };
1468 let directory_request = RootComponentSubtreeRemovalDirectorySynchronizationRequest {
1469 operation_id: prepare.operation_id,
1470 component: prepare.component,
1471 expected_traversal_steps: prepare.expected_traversal_steps,
1472 expected_leaf_canister_id: prepare.expected_leaf_canister_id,
1473 expected_leaf_parent_canister_id: prepare.expected_leaf_parent_canister_id,
1474 };
1475 let prepare_bytes = candid::encode_one(prepare)
1476 .expect("encode subtree removal deletion preparation request");
1477 let request_bytes =
1478 candid::encode_one(request).expect("encode subtree removal deletion request");
1479 let membership_request_bytes = candid::encode_one(membership_request)
1480 .expect("encode subtree removal membership-removal request");
1481 let directory_request_bytes = candid::encode_one(directory_request)
1482 .expect("encode subtree removal Directory synchronization request");
1483
1484 assert_eq!(
1485 candid::decode_one::<RootComponentSubtreeRemovalDeletePreparationRequest>(
1486 &prepare_bytes
1487 )
1488 .expect("decode subtree removal deletion preparation request"),
1489 prepare
1490 );
1491 assert_eq!(
1492 candid::decode_one::<RootComponentSubtreeRemovalDeleteRequest>(&request_bytes)
1493 .expect("decode subtree removal deletion request"),
1494 request
1495 );
1496 assert_eq!(
1497 candid::decode_one::<RootComponentSubtreeRemovalMembershipRemovalRequest>(
1498 &membership_request_bytes
1499 )
1500 .expect("decode subtree removal membership-removal request"),
1501 membership_request
1502 );
1503 assert_eq!(
1504 candid::decode_one::<RootComponentSubtreeRemovalDirectorySynchronizationRequest>(
1505 &directory_request_bytes
1506 )
1507 .expect("decode subtree removal Directory synchronization request"),
1508 directory_request
1509 );
1510 }
1511
1512 #[test]
1513 fn component_subtree_removal_stop_request_round_trips_through_candid() {
1514 let request = RootComponentSubtreeRemovalStopRequest {
1515 operation_id: [50; 32],
1516 component: ComponentInstanceId::from_generated_bytes([51; 32]),
1517 expected_traversal_steps: 3,
1518 expected_leaf_canister_id: Principal::from_slice(&[52; 29]),
1519 expected_leaf_parent_canister_id: Principal::from_slice(&[53; 29]),
1520 };
1521 let bytes =
1522 candid::encode_one(request).expect("encode subtree removal stop execution request");
1523
1524 assert_eq!(
1525 candid::decode_one::<RootComponentSubtreeRemovalStopRequest>(&bytes)
1526 .expect("decode subtree removal stop execution request"),
1527 request
1528 );
1529 }
1530
1531 #[test]
1532 #[expect(
1533 clippy::too_many_lines,
1534 reason = "one round-trip test keeps the complete child lifecycle boundary coherent"
1535 )]
1536 fn component_child_lifecycle_contracts_round_trip_through_candid() {
1537 let component = ComponentInstanceId::from_generated_bytes([11; 32]);
1538 let registry = ComponentRegistryHead {
1539 component,
1540 revision: 2,
1541 content_hash: [12; 32],
1542 };
1543 let request = RootComponentChildAllocationRequest {
1544 operation_id: [13; 32],
1545 component,
1546 expected_registry: registry.clone(),
1547 child_role: CanisterRole::new("project_instance"),
1548 };
1549 let status_request = RootComponentChildAllocationStatusRequest {
1550 operation_id: request.operation_id,
1551 component,
1552 };
1553 let creation_request = RootComponentChildCreationRequest {
1554 operation_id: request.operation_id,
1555 component,
1556 };
1557 let install_request = RootComponentChildInstallRequest {
1558 operation_id: request.operation_id,
1559 component,
1560 };
1561 let commit_request = RootComponentChildCommitRequest {
1562 operation_id: request.operation_id,
1563 component,
1564 };
1565 let directory_request = RootComponentChildDirectoryPreparationRequest {
1566 operation_id: request.operation_id,
1567 component,
1568 };
1569 let activation_request = RootComponentChildRuntimeActivationRequest {
1570 operation_id: request.operation_id,
1571 component,
1572 };
1573 let membership_request = RootComponentChildMembershipActivationRequest {
1574 operation_id: request.operation_id,
1575 component,
1576 };
1577 let root = Principal::from_slice(&[17; 29]);
1578 let parent = Principal::from_slice(&[14; 29]);
1579 let child = Principal::from_slice(&[18; 29]);
1580 let child_binding = ComponentChildBinding {
1581 component: ComponentBinding {
1582 authority: fleet_registry_authority(),
1583 component,
1584 component_spec: "projects".parse().expect("Component Spec"),
1585 spec_hash: [19; 32],
1586 role: CanisterRole::new("project_hub"),
1587 placement_subnet: SubnetId::from_principal(Principal::from_slice(&[20; 29])),
1588 fleet_subnet_root: root,
1589 canister_id: parent,
1590 },
1591 parent_canister_id: parent,
1592 role: request.child_role.clone(),
1593 canister_id: child,
1594 };
1595 let response = RootComponentChildAllocationResponse {
1596 operation_id: request.operation_id,
1597 component,
1598 parent_canister_id: parent,
1599 parent_role: CanisterRole::new("project_hub"),
1600 child_role: request.child_role.clone(),
1601 child_kind: ComponentChildKind::Instance,
1602 maximum_instances_per_parent: 10_000,
1603 maximum_descendants: 20_000,
1604 maximum_registry_bytes: 16_777_216,
1605 reserved_against_registry: registry,
1606 release_set: FleetSubnetRootReleaseSet {
1607 release_build_id: ReleaseBuildId::from_nonce(ReleaseBuildNonce::from_random_bytes(
1608 [15; 32],
1609 )),
1610 manifest_digest: ReleaseSetDigest::from_bytes([16; 32]),
1611 },
1612 phase: RootComponentAllocationPhase::Verified,
1613 creation: Some(RootComponentCreationEvidence {
1614 wasm_store: Principal::from_slice(&[21; 29]),
1615 payload_hash: [22; 32],
1616 payload_size_bytes: 4_096,
1617 initial_cycles: Cycles::new(5_000_000_000_000),
1618 controller: root,
1619 canister: Some(child),
1620 }),
1621 installation: Some(RootComponentChildInstallEvidence {
1622 raw_module_hash: [23; 32],
1623 chunk_hashes: vec![vec![24; 32]],
1624 binding: child_binding.clone(),
1625 }),
1626 };
1627 let commit_response = RootComponentChildCommitResponse {
1628 allocation: response.clone(),
1629 registry: ComponentRegistryPartitionResponse {
1630 head: ComponentRegistryHead {
1631 component,
1632 revision: 3,
1633 content_hash: [25; 32],
1634 },
1635 binding: child_binding.component.clone(),
1636 provisioning_origin: ComponentProvisioningOrigin::FleetAdministrator {
1637 caller: Principal::from_slice(&[26; 29]),
1638 },
1639 release_set: response.release_set,
1640 status: ComponentLifecycleStatus::Active,
1641 reserved_descendants: 0,
1642 committed_descendants: 1,
1643 encoded_bytes: 8_192,
1644 },
1645 directory: ComponentDirectoryHead {
1646 provenance: ComponentDirectoryProvenance {
1647 component: child_binding.component.clone(),
1648 source_fleet_subnet_root: root,
1649 component_registry_revision: 3,
1650 component_registry_content_hash: [25; 32],
1651 synchronized_at_ns: 27,
1652 },
1653 descendant_count: 1,
1654 },
1655 };
1656 let runtime_authority = ComponentRuntimeDirectoryAuthority {
1657 fleet: FleetDirectorySnapshot {
1658 provenance: crate::dto::fleet_registry::FleetDirectoryProvenance {
1659 registry: FleetRegistryVersion {
1660 authority: fleet_registry_authority(),
1661 revision: 4,
1662 content_hash: [28; 32],
1663 },
1664 source_fleet_subnet_root: root,
1665 },
1666 fleet_subnet_roots: vec![
1667 crate::dto::fleet_registry::FleetSubnetRootDirectoryEntry {
1668 placement_subnet: commit_response.registry.binding.placement_subnet,
1669 fleet_subnet_root: root,
1670 status: crate::dto::fleet_registry::FleetSubnetRootStatus::Active,
1671 },
1672 ],
1673 },
1674 component: commit_response.directory.clone(),
1675 };
1676 let activation = ComponentRuntimeActivationEvidence {
1677 directory_authority_hash: [29; 32],
1678 activated_at_ns: 30,
1679 };
1680 let directory_response = RootComponentChildDirectoryPreparationResponse {
1681 committed: commit_response.clone(),
1682 child: ComponentRuntimeStatusResponse {
1683 operation_id: request.operation_id,
1684 binding: ManagedCanisterBinding::ComponentChild(child_binding.clone()),
1685 phase: ComponentRuntimePhase::DirectoryPrepared,
1686 authority: Some(runtime_authority.clone()),
1687 authority_hash: Some([31; 32]),
1688 activation: None,
1689 },
1690 owning_component: ComponentRuntimeDirectoryConvergenceEvidence {
1691 operation_id: [32; 32],
1692 binding: ManagedCanisterBinding::Component(child_binding.component.clone()),
1693 covered_authority: runtime_authority.clone(),
1694 covered_authority_hash: [31; 32],
1695 activation,
1696 },
1697 parent: None,
1698 };
1699 let activation_response = RootComponentChildRuntimeActivationResponse {
1700 committed: commit_response.clone(),
1701 child: ComponentRuntimeStatusResponse {
1702 operation_id: request.operation_id,
1703 binding: ManagedCanisterBinding::ComponentChild(child_binding.clone()),
1704 phase: ComponentRuntimePhase::Active,
1705 authority: Some(runtime_authority.clone()),
1706 authority_hash: Some([31; 32]),
1707 activation: Some(ComponentRuntimeActivationEvidence {
1708 directory_authority_hash: [31; 32],
1709 activated_at_ns: 33,
1710 }),
1711 },
1712 };
1713 let active_directory = ComponentDirectoryHead {
1714 provenance: ComponentDirectoryProvenance {
1715 component: child_binding.component.clone(),
1716 source_fleet_subnet_root: root,
1717 component_registry_revision: 4,
1718 component_registry_content_hash: [34; 32],
1719 synchronized_at_ns: 35,
1720 },
1721 descendant_count: 1,
1722 };
1723 let active_authority = ComponentRuntimeDirectoryAuthority {
1724 fleet: runtime_authority.fleet,
1725 component: active_directory.clone(),
1726 };
1727 let membership_response = RootComponentChildMembershipActivationResponse {
1728 committed: commit_response.clone(),
1729 registry: ComponentRegistryPartitionResponse {
1730 head: ComponentRegistryHead {
1731 component,
1732 revision: 4,
1733 content_hash: [34; 32],
1734 },
1735 binding: child_binding.component.clone(),
1736 provisioning_origin: commit_response.registry.provisioning_origin.clone(),
1737 release_set: commit_response.registry.release_set,
1738 status: ComponentLifecycleStatus::Active,
1739 reserved_descendants: 0,
1740 committed_descendants: 1,
1741 encoded_bytes: 8_256,
1742 },
1743 directory: active_directory,
1744 child: ComponentRuntimeStatusResponse {
1745 operation_id: request.operation_id,
1746 binding: ManagedCanisterBinding::ComponentChild(child_binding),
1747 phase: ComponentRuntimePhase::Active,
1748 authority: Some(active_authority),
1749 authority_hash: Some([36; 32]),
1750 activation: Some(ComponentRuntimeActivationEvidence {
1751 directory_authority_hash: [31; 32],
1752 activated_at_ns: 33,
1753 }),
1754 },
1755 };
1756
1757 let request_bytes = candid::encode_one(&request).expect("encode child reservation");
1758 let status_bytes =
1759 candid::encode_one(status_request).expect("encode child reservation status");
1760 let creation_bytes =
1761 candid::encode_one(creation_request).expect("encode child creation request");
1762 let install_bytes =
1763 candid::encode_one(install_request).expect("encode child install request");
1764 let response_bytes = candid::encode_one(&response).expect("encode child response");
1765 let commit_request_bytes =
1766 candid::encode_one(commit_request).expect("encode child commit request");
1767 let directory_request_bytes =
1768 candid::encode_one(directory_request).expect("encode child Directory request");
1769 let activation_request_bytes =
1770 candid::encode_one(activation_request).expect("encode child activation request");
1771 let membership_request_bytes =
1772 candid::encode_one(membership_request).expect("encode child membership request");
1773 let commit_response_bytes =
1774 candid::encode_one(&commit_response).expect("encode child commit response");
1775 let directory_response_bytes =
1776 candid::encode_one(&directory_response).expect("encode child Directory response");
1777 let activation_response_bytes =
1778 candid::encode_one(&activation_response).expect("encode child activation response");
1779 let membership_response_bytes =
1780 candid::encode_one(&membership_response).expect("encode child membership response");
1781
1782 assert_eq!(
1783 candid::decode_one::<RootComponentChildAllocationRequest>(&request_bytes)
1784 .expect("decode child reservation"),
1785 request
1786 );
1787 assert_eq!(
1788 candid::decode_one::<RootComponentChildAllocationStatusRequest>(&status_bytes)
1789 .expect("decode child reservation status"),
1790 status_request
1791 );
1792 assert_eq!(
1793 candid::decode_one::<RootComponentChildCreationRequest>(&creation_bytes)
1794 .expect("decode child creation request"),
1795 creation_request
1796 );
1797 assert_eq!(
1798 candid::decode_one::<RootComponentChildInstallRequest>(&install_bytes)
1799 .expect("decode child install request"),
1800 install_request
1801 );
1802 assert_eq!(
1803 candid::decode_one::<RootComponentChildAllocationResponse>(&response_bytes)
1804 .expect("decode child response"),
1805 response
1806 );
1807 assert_eq!(
1808 candid::decode_one::<RootComponentChildCommitRequest>(&commit_request_bytes)
1809 .expect("decode child commit request"),
1810 commit_request
1811 );
1812 assert_eq!(
1813 candid::decode_one::<RootComponentChildDirectoryPreparationRequest>(
1814 &directory_request_bytes
1815 )
1816 .expect("decode child Directory request"),
1817 directory_request
1818 );
1819 assert_eq!(
1820 candid::decode_one::<RootComponentChildCommitResponse>(&commit_response_bytes)
1821 .expect("decode child commit response"),
1822 commit_response
1823 );
1824 assert_eq!(
1825 candid::decode_one::<RootComponentChildDirectoryPreparationResponse>(
1826 &directory_response_bytes
1827 )
1828 .expect("decode child Directory response"),
1829 directory_response
1830 );
1831 assert_eq!(
1832 candid::decode_one::<RootComponentChildRuntimeActivationRequest>(
1833 &activation_request_bytes
1834 )
1835 .expect("decode child activation request"),
1836 activation_request
1837 );
1838 assert_eq!(
1839 candid::decode_one::<RootComponentChildRuntimeActivationResponse>(
1840 &activation_response_bytes
1841 )
1842 .expect("decode child activation response"),
1843 activation_response
1844 );
1845 assert_eq!(
1846 candid::decode_one::<RootComponentChildMembershipActivationRequest>(
1847 &membership_request_bytes
1848 )
1849 .expect("decode child membership request"),
1850 membership_request
1851 );
1852 assert_eq!(
1853 candid::decode_one::<RootComponentChildMembershipActivationResponse>(
1854 &membership_response_bytes
1855 )
1856 .expect("decode child membership response"),
1857 membership_response
1858 );
1859 }
1860
1861 #[test]
1862 fn component_install_request_round_trips_through_candid() {
1863 let request = RootComponentInstallRequest {
1864 operation_id: [10; 32],
1865 };
1866 let bytes = candid::encode_one(request).expect("encode install request");
1867
1868 assert_eq!(
1869 candid::decode_one::<RootComponentInstallRequest>(&bytes)
1870 .expect("decode install request"),
1871 request
1872 );
1873 }
1874
1875 #[test]
1876 fn component_commit_request_round_trips_through_candid() {
1877 let request = RootComponentCommitRequest {
1878 operation_id: [10; 32],
1879 };
1880 let bytes = candid::encode_one(request).expect("encode commit request");
1881
1882 assert_eq!(
1883 candid::decode_one::<RootComponentCommitRequest>(&bytes)
1884 .expect("decode commit request"),
1885 request
1886 );
1887 }
1888
1889 #[test]
1890 fn component_runtime_activation_requests_round_trip_through_candid() {
1891 let root_request = RootComponentRuntimeActivationRequest {
1892 operation_id: [22; 32],
1893 };
1894 let target_request = ComponentRuntimeActivationRequest {
1895 operation_id: root_request.operation_id,
1896 directory_authority_hash: [23; 32],
1897 };
1898 let membership_request = RootComponentMembershipActivationRequest {
1899 operation_id: root_request.operation_id,
1900 };
1901 let root_bytes = candid::encode_one(root_request).expect("encode root activation request");
1902 let target_bytes =
1903 candid::encode_one(target_request).expect("encode target activation request");
1904 let membership_bytes =
1905 candid::encode_one(membership_request).expect("encode membership activation request");
1906
1907 assert_eq!(
1908 candid::decode_one::<RootComponentRuntimeActivationRequest>(&root_bytes)
1909 .expect("decode root activation request"),
1910 root_request
1911 );
1912 assert_eq!(
1913 candid::decode_one::<ComponentRuntimeActivationRequest>(&target_bytes)
1914 .expect("decode target activation request"),
1915 target_request
1916 );
1917 assert_eq!(
1918 candid::decode_one::<RootComponentMembershipActivationRequest>(&membership_bytes)
1919 .expect("decode membership activation request"),
1920 membership_request
1921 );
1922 }
1923}