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, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
128pub struct RootComponentChildCreationRequest {
129 pub operation_id: [u8; 32],
130 pub component: ComponentInstanceId,
131}
132
133#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
140pub struct RootComponentChildInstallRequest {
141 pub operation_id: [u8; 32],
142 pub component: ComponentInstanceId,
143}
144
145#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
152pub struct RootComponentChildCommitRequest {
153 pub operation_id: [u8; 32],
154 pub component: ComponentInstanceId,
155}
156
157#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
164pub struct RootComponentChildDirectoryPreparationRequest {
165 pub operation_id: [u8; 32],
166 pub component: ComponentInstanceId,
167}
168
169#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
176pub struct RootComponentChildRuntimeActivationRequest {
177 pub operation_id: [u8; 32],
178 pub component: ComponentInstanceId,
179}
180
181#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
188pub struct RootComponentChildMembershipActivationRequest {
189 pub operation_id: [u8; 32],
190 pub component: ComponentInstanceId,
191}
192
193#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
200pub struct RootComponentCreationRequest {
201 pub operation_id: [u8; 32],
202}
203
204#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
211pub struct RootComponentInstallRequest {
212 pub operation_id: [u8; 32],
213}
214
215#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
222pub struct RootComponentCommitRequest {
223 pub operation_id: [u8; 32],
224}
225
226#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
233pub struct RootComponentDirectoryPreparationRequest {
234 pub operation_id: [u8; 32],
235}
236
237#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
244pub struct RootComponentRuntimeActivationRequest {
245 pub operation_id: [u8; 32],
246}
247
248#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
255pub struct RootComponentMembershipActivationRequest {
256 pub operation_id: [u8; 32],
257}
258
259#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
266pub enum ComponentProvisioningOrigin {
267 FleetAdministrator { caller: Principal },
268}
269
270#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
277pub enum RootComponentAllocationPhase {
278 Reserved,
279 CreationIntent,
280 Created,
281 InstallIntent,
282 Installed,
283 Verified,
284 Committed,
285}
286
287#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
294pub enum ComponentLifecycleStatus {
295 Prepared,
296 Active,
297 Draining,
298 Removed,
299}
300
301#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
308pub struct ComponentRegistryHead {
309 pub component: ComponentInstanceId,
310 pub revision: u64,
311 pub content_hash: [u8; 32],
312}
313
314#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
321pub struct ComponentRegistryPartitionRequest {
322 pub component: ComponentInstanceId,
323}
324
325#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
332pub struct ComponentRegistryPartitionResponse {
333 pub head: ComponentRegistryHead,
334 pub binding: ComponentBinding,
335 pub provisioning_origin: ComponentProvisioningOrigin,
336 pub release_set: FleetSubnetRootReleaseSet,
337 pub status: ComponentLifecycleStatus,
338 pub reserved_descendants: u32,
339 pub committed_descendants: u32,
340 pub encoded_bytes: u64,
341}
342
343#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
350pub struct ComponentDirectoryProvenance {
351 pub component: ComponentBinding,
352 pub source_fleet_subnet_root: Principal,
353 pub component_registry_revision: u64,
354 pub component_registry_content_hash: [u8; 32],
355 pub synchronized_at_ns: u64,
356}
357
358#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
365pub struct ComponentDirectoryHead {
366 pub provenance: ComponentDirectoryProvenance,
367 pub descendant_count: u32,
368}
369
370#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
377pub struct ComponentDirectoryHeadRequest {
378 pub component: ComponentInstanceId,
379}
380
381#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
388pub struct ComponentRuntimeDirectoryAuthority {
389 pub fleet: FleetDirectorySnapshot,
390 pub component: ComponentDirectoryHead,
391}
392
393#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
400pub struct ComponentRuntimeDirectoryPreparationRequest {
401 pub operation_id: [u8; 32],
402 pub authority: ComponentRuntimeDirectoryAuthority,
403}
404
405#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
412pub struct ComponentRuntimeDirectorySynchronizationRequest {
413 pub operation_id: [u8; 32],
414 pub authority: ComponentRuntimeDirectoryAuthority,
415}
416
417#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
424pub enum ComponentRuntimePhase {
425 AwaitingDirectory,
426 DirectoryPrepared,
427 Active,
428}
429
430#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
437pub struct ComponentRuntimeActivationEvidence {
438 pub directory_authority_hash: [u8; 32],
439 pub activated_at_ns: u64,
440}
441
442#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
449pub struct ComponentRuntimeActivationRequest {
450 pub operation_id: [u8; 32],
451 pub directory_authority_hash: [u8; 32],
452}
453
454#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
461pub struct ComponentRuntimeStatusResponse {
462 pub operation_id: [u8; 32],
463 pub binding: ManagedCanisterBinding,
464 pub phase: ComponentRuntimePhase,
465 pub authority: Option<ComponentRuntimeDirectoryAuthority>,
466 pub authority_hash: Option<[u8; 32]>,
467 pub activation: Option<ComponentRuntimeActivationEvidence>,
468}
469
470#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
477pub struct ComponentRuntimeDirectoryConvergenceEvidence {
478 pub operation_id: [u8; 32],
479 pub binding: ManagedCanisterBinding,
480 pub covered_authority: ComponentRuntimeDirectoryAuthority,
481 pub covered_authority_hash: [u8; 32],
482 pub activation: ComponentRuntimeActivationEvidence,
483}
484
485#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
492pub struct RootComponentCreationEvidence {
493 pub wasm_store: Principal,
494 pub payload_hash: [u8; 32],
495 pub payload_size_bytes: u64,
496 pub initial_cycles: Cycles,
497 pub controller: Principal,
498 pub canister: Option<Principal>,
499}
500
501#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
508pub struct RootComponentInstallEvidence {
509 pub raw_module_hash: [u8; 32],
510 pub chunk_hashes: Vec<Vec<u8>>,
511 pub binding: ComponentBinding,
512}
513
514#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
521pub struct RootComponentChildInstallEvidence {
522 pub raw_module_hash: [u8; 32],
523 pub chunk_hashes: Vec<Vec<u8>>,
524 pub binding: ComponentChildBinding,
525}
526
527#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
534pub struct RootComponentAllocationResponse {
535 pub operation_id: [u8; 32],
536 pub allocation_sequence: u64,
537 pub component: ComponentInstanceId,
538 pub component_spec: ComponentSpecId,
539 pub spec_hash: [u8; 32],
540 pub role: CanisterRole,
541 pub provisioning_origin: ComponentProvisioningOrigin,
542 pub release_set: FleetSubnetRootReleaseSet,
543 pub phase: RootComponentAllocationPhase,
544 pub creation: Option<RootComponentCreationEvidence>,
545 pub installation: Option<RootComponentInstallEvidence>,
546}
547
548#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
555pub struct RootComponentChildAllocationResponse {
556 pub operation_id: [u8; 32],
557 pub component: ComponentInstanceId,
558 pub parent_canister_id: Principal,
559 pub parent_role: CanisterRole,
560 pub child_role: CanisterRole,
561 pub child_kind: ComponentChildKind,
562 pub maximum_instances_per_parent: u32,
563 pub maximum_descendants: u32,
564 pub maximum_registry_bytes: u64,
565 pub reserved_against_registry: ComponentRegistryHead,
566 pub release_set: FleetSubnetRootReleaseSet,
567 pub phase: RootComponentAllocationPhase,
568 pub creation: Option<RootComponentCreationEvidence>,
569 pub installation: Option<RootComponentChildInstallEvidence>,
570}
571
572#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
579pub struct RootComponentChildCommitResponse {
580 pub allocation: RootComponentChildAllocationResponse,
581 pub registry: ComponentRegistryPartitionResponse,
582 pub directory: ComponentDirectoryHead,
583}
584
585#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
592pub struct RootComponentChildDirectoryPreparationResponse {
593 pub committed: RootComponentChildCommitResponse,
594 pub child: ComponentRuntimeStatusResponse,
595 pub owning_component: ComponentRuntimeDirectoryConvergenceEvidence,
596 pub parent: Option<ComponentRuntimeDirectoryConvergenceEvidence>,
597}
598
599#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
606pub struct RootComponentChildRuntimeActivationResponse {
607 pub committed: RootComponentChildCommitResponse,
608 pub child: ComponentRuntimeStatusResponse,
609}
610
611#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
618pub struct RootComponentChildMembershipActivationResponse {
619 pub committed: RootComponentChildCommitResponse,
620 pub registry: ComponentRegistryPartitionResponse,
621 pub directory: ComponentDirectoryHead,
622 pub child: ComponentRuntimeStatusResponse,
623}
624
625#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
632pub struct RootComponentCommitResponse {
633 pub allocation: RootComponentAllocationResponse,
634 pub registry: ComponentRegistryPartitionResponse,
635 pub directory: ComponentDirectoryHead,
636}
637
638#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
645pub struct RootComponentDirectoryPreparationResponse {
646 pub committed: RootComponentCommitResponse,
647 pub target: ComponentRuntimeStatusResponse,
648}
649
650#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
657pub struct RootComponentRuntimeActivationResponse {
658 pub committed: RootComponentCommitResponse,
659 pub target: ComponentRuntimeStatusResponse,
660}
661
662#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
669pub struct RootComponentMembershipActivationResponse {
670 pub allocation: RootComponentAllocationResponse,
671 pub registry: ComponentRegistryPartitionResponse,
672 pub directory: ComponentDirectoryHead,
673 pub target: ComponentRuntimeStatusResponse,
674}
675
676#[cfg(test)]
677mod tests {
678 use super::*;
679 use crate::{
680 dto::root_store::RootStoreBootstrapRequest,
681 ids::{
682 AppId, CanonicalNetworkId, FleetCoordinatorBinding, FleetId, FleetKey,
683 FleetRegistryAuthority, ReleaseBuildId, ReleaseBuildNonce, ReleaseSetDigest, SubnetId,
684 },
685 };
686
687 #[test]
688 fn component_registry_contracts_round_trip_through_candid() {
689 let request = RootComponentRegistryPreparationRequest {
690 store_bootstrap: RootStoreBootstrapRequest {
691 manifest_payload_size_bytes: 128,
692 },
693 expected_fleet_registry: FleetRegistryVersion {
694 authority: fleet_registry_authority(),
695 revision: 4,
696 content_hash: [5; 32],
697 },
698 };
699 let response = RootComponentRegistryStatusResponse {
700 fleet_subnet_root: Principal::from_slice(&[6; 29]),
701 prepared_against_registry: request.expected_fleet_registry.clone(),
702 release_set: FleetSubnetRootReleaseSet {
703 release_build_id: ReleaseBuildId::from_nonce(ReleaseBuildNonce::from_random_bytes(
704 [7; 32],
705 )),
706 manifest_digest: ReleaseSetDigest::from_bytes([8; 32]),
707 },
708 component_topology_digest: ComponentTopologyDigest::from_bytes([9; 32]),
709 next_allocation_sequence: 1,
710 reserved_component_instances: 0,
711 committed_component_instances: 0,
712 managed_descendants: 0,
713 known_created_component_canisters: 0,
714 encoded_bytes: 0,
715 initial_inventory: Some(RootComponentInitialInventoryStatus {
716 fleet_activation_operation_id: [10; 32],
717 component_count: 0,
718 inventory_hash: [11; 32],
719 sealed_at_ns: 12,
720 directories_converged: true,
721 root_runtime_activated: true,
722 }),
723 };
724 let allocation = RootComponentAllocationResponse {
725 operation_id: [10; 32],
726 allocation_sequence: 1,
727 component: ComponentInstanceId::from_generated_bytes([11; 32]),
728 component_spec: "projects".parse().expect("Component Spec ID"),
729 spec_hash: [12; 32],
730 role: CanisterRole::new("project_hub"),
731 provisioning_origin: ComponentProvisioningOrigin::FleetAdministrator {
732 caller: Principal::from_slice(&[13; 29]),
733 },
734 release_set: response.release_set,
735 phase: RootComponentAllocationPhase::Reserved,
736 creation: None,
737 installation: None,
738 };
739 let created = RootComponentAllocationResponse {
740 phase: RootComponentAllocationPhase::Created,
741 creation: Some(RootComponentCreationEvidence {
742 wasm_store: Principal::from_slice(&[14; 29]),
743 payload_hash: [15; 32],
744 payload_size_bytes: 4_096,
745 initial_cycles: Cycles::new(5_000_000_000_000),
746 controller: Principal::from_slice(&[6; 29]),
747 canister: Some(Principal::from_slice(&[16; 29])),
748 }),
749 installation: None,
750 ..allocation.clone()
751 };
752 let request_bytes = candid::encode_one(&request).expect("encode request");
753 let response_bytes = candid::encode_one(&response).expect("encode response");
754 let allocation_bytes = candid::encode_one(&allocation).expect("encode allocation");
755 let created_bytes = candid::encode_one(&created).expect("encode created allocation");
756
757 assert_eq!(
758 candid::decode_one::<RootComponentRegistryPreparationRequest>(&request_bytes)
759 .expect("decode request"),
760 request
761 );
762 assert_eq!(
763 candid::decode_one::<RootComponentRegistryStatusResponse>(&response_bytes)
764 .expect("decode response"),
765 response
766 );
767 assert_eq!(
768 candid::decode_one::<RootComponentAllocationResponse>(&allocation_bytes)
769 .expect("decode allocation"),
770 allocation
771 );
772 assert_eq!(
773 candid::decode_one::<RootComponentAllocationResponse>(&created_bytes)
774 .expect("decode created allocation"),
775 created
776 );
777 }
778
779 #[test]
780 fn component_commit_response_round_trips_through_candid() {
781 let root = Principal::from_slice(&[6; 29]);
782 let component = ComponentInstanceId::from_generated_bytes([11; 32]);
783 let component_spec: ComponentSpecId = "projects".parse().expect("Component Spec ID");
784 let release_set = FleetSubnetRootReleaseSet {
785 release_build_id: ReleaseBuildId::from_nonce(ReleaseBuildNonce::from_random_bytes(
786 [7; 32],
787 )),
788 manifest_digest: ReleaseSetDigest::from_bytes([8; 32]),
789 };
790 let provisioning_origin = ComponentProvisioningOrigin::FleetAdministrator {
791 caller: Principal::from_slice(&[13; 29]),
792 };
793 let binding = ComponentBinding {
794 authority: fleet_registry_authority(),
795 component,
796 component_spec: component_spec.clone(),
797 spec_hash: [12; 32],
798 role: CanisterRole::new("project_hub"),
799 placement_subnet: SubnetId::from_principal(Principal::from_slice(&[17; 29])),
800 fleet_subnet_root: root,
801 canister_id: Principal::from_slice(&[16; 29]),
802 };
803 let head = ComponentRegistryHead {
804 component,
805 revision: 1,
806 content_hash: [18; 32],
807 };
808 let committed = RootComponentCommitResponse {
809 allocation: RootComponentAllocationResponse {
810 operation_id: [10; 32],
811 allocation_sequence: 1,
812 component,
813 component_spec,
814 spec_hash: binding.spec_hash,
815 role: binding.role.clone(),
816 provisioning_origin: provisioning_origin.clone(),
817 release_set,
818 phase: RootComponentAllocationPhase::Committed,
819 creation: Some(RootComponentCreationEvidence {
820 wasm_store: Principal::from_slice(&[14; 29]),
821 payload_hash: [15; 32],
822 payload_size_bytes: 4_096,
823 initial_cycles: Cycles::new(5_000_000_000_000),
824 controller: root,
825 canister: Some(binding.canister_id),
826 }),
827 installation: Some(RootComponentInstallEvidence {
828 raw_module_hash: [20; 32],
829 chunk_hashes: vec![vec![21; 32]],
830 binding: binding.clone(),
831 }),
832 },
833 registry: ComponentRegistryPartitionResponse {
834 head: head.clone(),
835 binding: binding.clone(),
836 provisioning_origin,
837 release_set,
838 status: ComponentLifecycleStatus::Prepared,
839 reserved_descendants: 0,
840 committed_descendants: 0,
841 encoded_bytes: 2_048,
842 },
843 directory: ComponentDirectoryHead {
844 provenance: ComponentDirectoryProvenance {
845 component: binding,
846 source_fleet_subnet_root: root,
847 component_registry_revision: head.revision,
848 component_registry_content_hash: head.content_hash,
849 synchronized_at_ns: 19,
850 },
851 descendant_count: 0,
852 },
853 };
854 let committed_bytes = candid::encode_one(&committed).expect("encode committed allocation");
855
856 assert_eq!(
857 candid::decode_one::<RootComponentCommitResponse>(&committed_bytes)
858 .expect("decode committed allocation"),
859 committed
860 );
861 }
862
863 fn fleet_registry_authority() -> FleetRegistryAuthority {
864 FleetRegistryAuthority {
865 binding: FleetCoordinatorBinding {
866 fleet: crate::ids::FleetBinding {
867 fleet: FleetKey {
868 canonical_network_id: CanonicalNetworkId::public_ic(),
869 fleet_id: FleetId::from_generated_bytes([1; 32]),
870 },
871 app: AppId::from("toko"),
872 },
873 coordinator_subnet: SubnetId::from_principal(Principal::from_slice(&[2; 29])),
874 coordinator: Principal::from_slice(&[3; 29]),
875 },
876 epoch: 1,
877 }
878 }
879
880 #[test]
881 fn component_creation_request_round_trips_through_candid() {
882 let request = RootComponentCreationRequest {
883 operation_id: [10; 32],
884 };
885 let bytes = candid::encode_one(request).expect("encode creation request");
886
887 assert_eq!(
888 candid::decode_one::<RootComponentCreationRequest>(&bytes)
889 .expect("decode creation request"),
890 request
891 );
892 }
893
894 #[test]
895 #[expect(
896 clippy::too_many_lines,
897 reason = "one round-trip test keeps the complete child lifecycle boundary coherent"
898 )]
899 fn component_child_lifecycle_contracts_round_trip_through_candid() {
900 let component = ComponentInstanceId::from_generated_bytes([11; 32]);
901 let registry = ComponentRegistryHead {
902 component,
903 revision: 2,
904 content_hash: [12; 32],
905 };
906 let request = RootComponentChildAllocationRequest {
907 operation_id: [13; 32],
908 component,
909 expected_registry: registry.clone(),
910 child_role: CanisterRole::new("project_instance"),
911 };
912 let status_request = RootComponentChildAllocationStatusRequest {
913 operation_id: request.operation_id,
914 component,
915 };
916 let creation_request = RootComponentChildCreationRequest {
917 operation_id: request.operation_id,
918 component,
919 };
920 let install_request = RootComponentChildInstallRequest {
921 operation_id: request.operation_id,
922 component,
923 };
924 let commit_request = RootComponentChildCommitRequest {
925 operation_id: request.operation_id,
926 component,
927 };
928 let directory_request = RootComponentChildDirectoryPreparationRequest {
929 operation_id: request.operation_id,
930 component,
931 };
932 let activation_request = RootComponentChildRuntimeActivationRequest {
933 operation_id: request.operation_id,
934 component,
935 };
936 let membership_request = RootComponentChildMembershipActivationRequest {
937 operation_id: request.operation_id,
938 component,
939 };
940 let root = Principal::from_slice(&[17; 29]);
941 let parent = Principal::from_slice(&[14; 29]);
942 let child = Principal::from_slice(&[18; 29]);
943 let child_binding = ComponentChildBinding {
944 component: ComponentBinding {
945 authority: fleet_registry_authority(),
946 component,
947 component_spec: "projects".parse().expect("Component Spec"),
948 spec_hash: [19; 32],
949 role: CanisterRole::new("project_hub"),
950 placement_subnet: SubnetId::from_principal(Principal::from_slice(&[20; 29])),
951 fleet_subnet_root: root,
952 canister_id: parent,
953 },
954 parent_canister_id: parent,
955 role: request.child_role.clone(),
956 canister_id: child,
957 };
958 let response = RootComponentChildAllocationResponse {
959 operation_id: request.operation_id,
960 component,
961 parent_canister_id: parent,
962 parent_role: CanisterRole::new("project_hub"),
963 child_role: request.child_role.clone(),
964 child_kind: ComponentChildKind::Instance,
965 maximum_instances_per_parent: 10_000,
966 maximum_descendants: 20_000,
967 maximum_registry_bytes: 16_777_216,
968 reserved_against_registry: registry,
969 release_set: FleetSubnetRootReleaseSet {
970 release_build_id: ReleaseBuildId::from_nonce(ReleaseBuildNonce::from_random_bytes(
971 [15; 32],
972 )),
973 manifest_digest: ReleaseSetDigest::from_bytes([16; 32]),
974 },
975 phase: RootComponentAllocationPhase::Verified,
976 creation: Some(RootComponentCreationEvidence {
977 wasm_store: Principal::from_slice(&[21; 29]),
978 payload_hash: [22; 32],
979 payload_size_bytes: 4_096,
980 initial_cycles: Cycles::new(5_000_000_000_000),
981 controller: root,
982 canister: Some(child),
983 }),
984 installation: Some(RootComponentChildInstallEvidence {
985 raw_module_hash: [23; 32],
986 chunk_hashes: vec![vec![24; 32]],
987 binding: child_binding.clone(),
988 }),
989 };
990 let commit_response = RootComponentChildCommitResponse {
991 allocation: response.clone(),
992 registry: ComponentRegistryPartitionResponse {
993 head: ComponentRegistryHead {
994 component,
995 revision: 3,
996 content_hash: [25; 32],
997 },
998 binding: child_binding.component.clone(),
999 provisioning_origin: ComponentProvisioningOrigin::FleetAdministrator {
1000 caller: Principal::from_slice(&[26; 29]),
1001 },
1002 release_set: response.release_set,
1003 status: ComponentLifecycleStatus::Active,
1004 reserved_descendants: 0,
1005 committed_descendants: 1,
1006 encoded_bytes: 8_192,
1007 },
1008 directory: ComponentDirectoryHead {
1009 provenance: ComponentDirectoryProvenance {
1010 component: child_binding.component.clone(),
1011 source_fleet_subnet_root: root,
1012 component_registry_revision: 3,
1013 component_registry_content_hash: [25; 32],
1014 synchronized_at_ns: 27,
1015 },
1016 descendant_count: 1,
1017 },
1018 };
1019 let runtime_authority = ComponentRuntimeDirectoryAuthority {
1020 fleet: FleetDirectorySnapshot {
1021 provenance: crate::dto::fleet_registry::FleetDirectoryProvenance {
1022 registry: FleetRegistryVersion {
1023 authority: fleet_registry_authority(),
1024 revision: 4,
1025 content_hash: [28; 32],
1026 },
1027 source_fleet_subnet_root: root,
1028 },
1029 fleet_subnet_roots: vec![
1030 crate::dto::fleet_registry::FleetSubnetRootDirectoryEntry {
1031 placement_subnet: commit_response.registry.binding.placement_subnet,
1032 fleet_subnet_root: root,
1033 status: crate::dto::fleet_registry::FleetSubnetRootStatus::Active,
1034 },
1035 ],
1036 },
1037 component: commit_response.directory.clone(),
1038 };
1039 let activation = ComponentRuntimeActivationEvidence {
1040 directory_authority_hash: [29; 32],
1041 activated_at_ns: 30,
1042 };
1043 let directory_response = RootComponentChildDirectoryPreparationResponse {
1044 committed: commit_response.clone(),
1045 child: ComponentRuntimeStatusResponse {
1046 operation_id: request.operation_id,
1047 binding: ManagedCanisterBinding::ComponentChild(child_binding.clone()),
1048 phase: ComponentRuntimePhase::DirectoryPrepared,
1049 authority: Some(runtime_authority.clone()),
1050 authority_hash: Some([31; 32]),
1051 activation: None,
1052 },
1053 owning_component: ComponentRuntimeDirectoryConvergenceEvidence {
1054 operation_id: [32; 32],
1055 binding: ManagedCanisterBinding::Component(child_binding.component.clone()),
1056 covered_authority: runtime_authority.clone(),
1057 covered_authority_hash: [31; 32],
1058 activation,
1059 },
1060 parent: None,
1061 };
1062 let activation_response = RootComponentChildRuntimeActivationResponse {
1063 committed: commit_response.clone(),
1064 child: ComponentRuntimeStatusResponse {
1065 operation_id: request.operation_id,
1066 binding: ManagedCanisterBinding::ComponentChild(child_binding.clone()),
1067 phase: ComponentRuntimePhase::Active,
1068 authority: Some(runtime_authority.clone()),
1069 authority_hash: Some([31; 32]),
1070 activation: Some(ComponentRuntimeActivationEvidence {
1071 directory_authority_hash: [31; 32],
1072 activated_at_ns: 33,
1073 }),
1074 },
1075 };
1076 let active_directory = ComponentDirectoryHead {
1077 provenance: ComponentDirectoryProvenance {
1078 component: child_binding.component.clone(),
1079 source_fleet_subnet_root: root,
1080 component_registry_revision: 4,
1081 component_registry_content_hash: [34; 32],
1082 synchronized_at_ns: 35,
1083 },
1084 descendant_count: 1,
1085 };
1086 let active_authority = ComponentRuntimeDirectoryAuthority {
1087 fleet: runtime_authority.fleet,
1088 component: active_directory.clone(),
1089 };
1090 let membership_response = RootComponentChildMembershipActivationResponse {
1091 committed: commit_response.clone(),
1092 registry: ComponentRegistryPartitionResponse {
1093 head: ComponentRegistryHead {
1094 component,
1095 revision: 4,
1096 content_hash: [34; 32],
1097 },
1098 binding: child_binding.component.clone(),
1099 provisioning_origin: commit_response.registry.provisioning_origin.clone(),
1100 release_set: commit_response.registry.release_set,
1101 status: ComponentLifecycleStatus::Active,
1102 reserved_descendants: 0,
1103 committed_descendants: 1,
1104 encoded_bytes: 8_256,
1105 },
1106 directory: active_directory,
1107 child: ComponentRuntimeStatusResponse {
1108 operation_id: request.operation_id,
1109 binding: ManagedCanisterBinding::ComponentChild(child_binding),
1110 phase: ComponentRuntimePhase::Active,
1111 authority: Some(active_authority),
1112 authority_hash: Some([36; 32]),
1113 activation: Some(ComponentRuntimeActivationEvidence {
1114 directory_authority_hash: [31; 32],
1115 activated_at_ns: 33,
1116 }),
1117 },
1118 };
1119
1120 let request_bytes = candid::encode_one(&request).expect("encode child reservation");
1121 let status_bytes =
1122 candid::encode_one(status_request).expect("encode child reservation status");
1123 let creation_bytes =
1124 candid::encode_one(creation_request).expect("encode child creation request");
1125 let install_bytes =
1126 candid::encode_one(install_request).expect("encode child install request");
1127 let response_bytes = candid::encode_one(&response).expect("encode child response");
1128 let commit_request_bytes =
1129 candid::encode_one(commit_request).expect("encode child commit request");
1130 let directory_request_bytes =
1131 candid::encode_one(directory_request).expect("encode child Directory request");
1132 let activation_request_bytes =
1133 candid::encode_one(activation_request).expect("encode child activation request");
1134 let membership_request_bytes =
1135 candid::encode_one(membership_request).expect("encode child membership request");
1136 let commit_response_bytes =
1137 candid::encode_one(&commit_response).expect("encode child commit response");
1138 let directory_response_bytes =
1139 candid::encode_one(&directory_response).expect("encode child Directory response");
1140 let activation_response_bytes =
1141 candid::encode_one(&activation_response).expect("encode child activation response");
1142 let membership_response_bytes =
1143 candid::encode_one(&membership_response).expect("encode child membership response");
1144
1145 assert_eq!(
1146 candid::decode_one::<RootComponentChildAllocationRequest>(&request_bytes)
1147 .expect("decode child reservation"),
1148 request
1149 );
1150 assert_eq!(
1151 candid::decode_one::<RootComponentChildAllocationStatusRequest>(&status_bytes)
1152 .expect("decode child reservation status"),
1153 status_request
1154 );
1155 assert_eq!(
1156 candid::decode_one::<RootComponentChildCreationRequest>(&creation_bytes)
1157 .expect("decode child creation request"),
1158 creation_request
1159 );
1160 assert_eq!(
1161 candid::decode_one::<RootComponentChildInstallRequest>(&install_bytes)
1162 .expect("decode child install request"),
1163 install_request
1164 );
1165 assert_eq!(
1166 candid::decode_one::<RootComponentChildAllocationResponse>(&response_bytes)
1167 .expect("decode child response"),
1168 response
1169 );
1170 assert_eq!(
1171 candid::decode_one::<RootComponentChildCommitRequest>(&commit_request_bytes)
1172 .expect("decode child commit request"),
1173 commit_request
1174 );
1175 assert_eq!(
1176 candid::decode_one::<RootComponentChildDirectoryPreparationRequest>(
1177 &directory_request_bytes
1178 )
1179 .expect("decode child Directory request"),
1180 directory_request
1181 );
1182 assert_eq!(
1183 candid::decode_one::<RootComponentChildCommitResponse>(&commit_response_bytes)
1184 .expect("decode child commit response"),
1185 commit_response
1186 );
1187 assert_eq!(
1188 candid::decode_one::<RootComponentChildDirectoryPreparationResponse>(
1189 &directory_response_bytes
1190 )
1191 .expect("decode child Directory response"),
1192 directory_response
1193 );
1194 assert_eq!(
1195 candid::decode_one::<RootComponentChildRuntimeActivationRequest>(
1196 &activation_request_bytes
1197 )
1198 .expect("decode child activation request"),
1199 activation_request
1200 );
1201 assert_eq!(
1202 candid::decode_one::<RootComponentChildRuntimeActivationResponse>(
1203 &activation_response_bytes
1204 )
1205 .expect("decode child activation response"),
1206 activation_response
1207 );
1208 assert_eq!(
1209 candid::decode_one::<RootComponentChildMembershipActivationRequest>(
1210 &membership_request_bytes
1211 )
1212 .expect("decode child membership request"),
1213 membership_request
1214 );
1215 assert_eq!(
1216 candid::decode_one::<RootComponentChildMembershipActivationResponse>(
1217 &membership_response_bytes
1218 )
1219 .expect("decode child membership response"),
1220 membership_response
1221 );
1222 }
1223
1224 #[test]
1225 fn component_install_request_round_trips_through_candid() {
1226 let request = RootComponentInstallRequest {
1227 operation_id: [10; 32],
1228 };
1229 let bytes = candid::encode_one(request).expect("encode install request");
1230
1231 assert_eq!(
1232 candid::decode_one::<RootComponentInstallRequest>(&bytes)
1233 .expect("decode install request"),
1234 request
1235 );
1236 }
1237
1238 #[test]
1239 fn component_commit_request_round_trips_through_candid() {
1240 let request = RootComponentCommitRequest {
1241 operation_id: [10; 32],
1242 };
1243 let bytes = candid::encode_one(request).expect("encode commit request");
1244
1245 assert_eq!(
1246 candid::decode_one::<RootComponentCommitRequest>(&bytes)
1247 .expect("decode commit request"),
1248 request
1249 );
1250 }
1251
1252 #[test]
1253 fn component_runtime_activation_requests_round_trip_through_candid() {
1254 let root_request = RootComponentRuntimeActivationRequest {
1255 operation_id: [22; 32],
1256 };
1257 let target_request = ComponentRuntimeActivationRequest {
1258 operation_id: root_request.operation_id,
1259 directory_authority_hash: [23; 32],
1260 };
1261 let membership_request = RootComponentMembershipActivationRequest {
1262 operation_id: root_request.operation_id,
1263 };
1264 let root_bytes = candid::encode_one(root_request).expect("encode root activation request");
1265 let target_bytes =
1266 candid::encode_one(target_request).expect("encode target activation request");
1267 let membership_bytes =
1268 candid::encode_one(membership_request).expect("encode membership activation request");
1269
1270 assert_eq!(
1271 candid::decode_one::<RootComponentRuntimeActivationRequest>(&root_bytes)
1272 .expect("decode root activation request"),
1273 root_request
1274 );
1275 assert_eq!(
1276 candid::decode_one::<ComponentRuntimeActivationRequest>(&target_bytes)
1277 .expect("decode target activation request"),
1278 target_request
1279 );
1280 assert_eq!(
1281 candid::decode_one::<RootComponentMembershipActivationRequest>(&membership_bytes)
1282 .expect("decode membership activation request"),
1283 membership_request
1284 );
1285 }
1286}