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 RootComponentCreationRequest {
165 pub operation_id: [u8; 32],
166}
167
168#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
175pub struct RootComponentInstallRequest {
176 pub operation_id: [u8; 32],
177}
178
179#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
186pub struct RootComponentCommitRequest {
187 pub operation_id: [u8; 32],
188}
189
190#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
197pub struct RootComponentDirectoryPreparationRequest {
198 pub operation_id: [u8; 32],
199}
200
201#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
208pub struct RootComponentRuntimeActivationRequest {
209 pub operation_id: [u8; 32],
210}
211
212#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
219pub struct RootComponentMembershipActivationRequest {
220 pub operation_id: [u8; 32],
221}
222
223#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
230pub enum ComponentProvisioningOrigin {
231 FleetAdministrator { caller: Principal },
232}
233
234#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
241pub enum RootComponentAllocationPhase {
242 Reserved,
243 CreationIntent,
244 Created,
245 InstallIntent,
246 Installed,
247 Verified,
248 Committed,
249}
250
251#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
258pub enum ComponentLifecycleStatus {
259 Prepared,
260 Active,
261 Draining,
262 Removed,
263}
264
265#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
272pub struct ComponentRegistryHead {
273 pub component: ComponentInstanceId,
274 pub revision: u64,
275 pub content_hash: [u8; 32],
276}
277
278#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
285pub struct ComponentRegistryPartitionRequest {
286 pub component: ComponentInstanceId,
287}
288
289#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
296pub struct ComponentRegistryPartitionResponse {
297 pub head: ComponentRegistryHead,
298 pub binding: ComponentBinding,
299 pub provisioning_origin: ComponentProvisioningOrigin,
300 pub release_set: FleetSubnetRootReleaseSet,
301 pub status: ComponentLifecycleStatus,
302 pub reserved_descendants: u32,
303 pub committed_descendants: u32,
304 pub encoded_bytes: u64,
305}
306
307#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
314pub struct ComponentDirectoryProvenance {
315 pub component: ComponentBinding,
316 pub source_fleet_subnet_root: Principal,
317 pub component_registry_revision: u64,
318 pub component_registry_content_hash: [u8; 32],
319 pub synchronized_at_ns: u64,
320}
321
322#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
329pub struct ComponentDirectoryHead {
330 pub provenance: ComponentDirectoryProvenance,
331 pub descendant_count: u32,
332}
333
334#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
341pub struct ComponentDirectoryHeadRequest {
342 pub component: ComponentInstanceId,
343}
344
345#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
352pub struct ComponentRuntimeDirectoryAuthority {
353 pub fleet: FleetDirectorySnapshot,
354 pub component: ComponentDirectoryHead,
355}
356
357#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
364pub struct ComponentRuntimeDirectoryPreparationRequest {
365 pub operation_id: [u8; 32],
366 pub authority: ComponentRuntimeDirectoryAuthority,
367}
368
369#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
376pub struct ComponentRuntimeDirectorySynchronizationRequest {
377 pub operation_id: [u8; 32],
378 pub authority: ComponentRuntimeDirectoryAuthority,
379}
380
381#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
388pub enum ComponentRuntimePhase {
389 AwaitingDirectory,
390 DirectoryPrepared,
391 Active,
392}
393
394#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
401pub struct ComponentRuntimeActivationEvidence {
402 pub directory_authority_hash: [u8; 32],
403 pub activated_at_ns: u64,
404}
405
406#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
413pub struct ComponentRuntimeActivationRequest {
414 pub operation_id: [u8; 32],
415 pub directory_authority_hash: [u8; 32],
416}
417
418#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
425pub struct ComponentRuntimeStatusResponse {
426 pub operation_id: [u8; 32],
427 pub binding: ManagedCanisterBinding,
428 pub phase: ComponentRuntimePhase,
429 pub authority: Option<ComponentRuntimeDirectoryAuthority>,
430 pub authority_hash: Option<[u8; 32]>,
431 pub activation: Option<ComponentRuntimeActivationEvidence>,
432}
433
434#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
441pub struct RootComponentCreationEvidence {
442 pub wasm_store: Principal,
443 pub payload_hash: [u8; 32],
444 pub payload_size_bytes: u64,
445 pub initial_cycles: Cycles,
446 pub controller: Principal,
447 pub canister: Option<Principal>,
448}
449
450#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
457pub struct RootComponentInstallEvidence {
458 pub raw_module_hash: [u8; 32],
459 pub chunk_hashes: Vec<Vec<u8>>,
460 pub binding: ComponentBinding,
461}
462
463#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
470pub struct RootComponentChildInstallEvidence {
471 pub raw_module_hash: [u8; 32],
472 pub chunk_hashes: Vec<Vec<u8>>,
473 pub binding: ComponentChildBinding,
474}
475
476#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
483pub struct RootComponentAllocationResponse {
484 pub operation_id: [u8; 32],
485 pub allocation_sequence: u64,
486 pub component: ComponentInstanceId,
487 pub component_spec: ComponentSpecId,
488 pub spec_hash: [u8; 32],
489 pub role: CanisterRole,
490 pub provisioning_origin: ComponentProvisioningOrigin,
491 pub release_set: FleetSubnetRootReleaseSet,
492 pub phase: RootComponentAllocationPhase,
493 pub creation: Option<RootComponentCreationEvidence>,
494 pub installation: Option<RootComponentInstallEvidence>,
495}
496
497#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
504pub struct RootComponentChildAllocationResponse {
505 pub operation_id: [u8; 32],
506 pub component: ComponentInstanceId,
507 pub parent_canister_id: Principal,
508 pub parent_role: CanisterRole,
509 pub child_role: CanisterRole,
510 pub child_kind: ComponentChildKind,
511 pub maximum_instances_per_parent: u32,
512 pub maximum_descendants: u32,
513 pub maximum_registry_bytes: u64,
514 pub reserved_against_registry: ComponentRegistryHead,
515 pub release_set: FleetSubnetRootReleaseSet,
516 pub phase: RootComponentAllocationPhase,
517 pub creation: Option<RootComponentCreationEvidence>,
518 pub installation: Option<RootComponentChildInstallEvidence>,
519}
520
521#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
528pub struct RootComponentChildCommitResponse {
529 pub allocation: RootComponentChildAllocationResponse,
530 pub registry: ComponentRegistryPartitionResponse,
531 pub directory: ComponentDirectoryHead,
532}
533
534#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
541pub struct RootComponentCommitResponse {
542 pub allocation: RootComponentAllocationResponse,
543 pub registry: ComponentRegistryPartitionResponse,
544 pub directory: ComponentDirectoryHead,
545}
546
547#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
554pub struct RootComponentDirectoryPreparationResponse {
555 pub committed: RootComponentCommitResponse,
556 pub target: ComponentRuntimeStatusResponse,
557}
558
559#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
566pub struct RootComponentRuntimeActivationResponse {
567 pub committed: RootComponentCommitResponse,
568 pub target: ComponentRuntimeStatusResponse,
569}
570
571#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
578pub struct RootComponentMembershipActivationResponse {
579 pub allocation: RootComponentAllocationResponse,
580 pub registry: ComponentRegistryPartitionResponse,
581 pub directory: ComponentDirectoryHead,
582 pub target: ComponentRuntimeStatusResponse,
583}
584
585#[cfg(test)]
586mod tests {
587 use super::*;
588 use crate::{
589 dto::root_store::RootStoreBootstrapRequest,
590 ids::{
591 AppId, CanonicalNetworkId, FleetCoordinatorBinding, FleetId, FleetKey,
592 FleetRegistryAuthority, ReleaseBuildId, ReleaseBuildNonce, ReleaseSetDigest, SubnetId,
593 },
594 };
595
596 #[test]
597 fn component_registry_contracts_round_trip_through_candid() {
598 let request = RootComponentRegistryPreparationRequest {
599 store_bootstrap: RootStoreBootstrapRequest {
600 manifest_payload_size_bytes: 128,
601 },
602 expected_fleet_registry: FleetRegistryVersion {
603 authority: fleet_registry_authority(),
604 revision: 4,
605 content_hash: [5; 32],
606 },
607 };
608 let response = RootComponentRegistryStatusResponse {
609 fleet_subnet_root: Principal::from_slice(&[6; 29]),
610 prepared_against_registry: request.expected_fleet_registry.clone(),
611 release_set: FleetSubnetRootReleaseSet {
612 release_build_id: ReleaseBuildId::from_nonce(ReleaseBuildNonce::from_random_bytes(
613 [7; 32],
614 )),
615 manifest_digest: ReleaseSetDigest::from_bytes([8; 32]),
616 },
617 component_topology_digest: ComponentTopologyDigest::from_bytes([9; 32]),
618 next_allocation_sequence: 1,
619 reserved_component_instances: 0,
620 committed_component_instances: 0,
621 managed_descendants: 0,
622 known_created_component_canisters: 0,
623 encoded_bytes: 0,
624 initial_inventory: Some(RootComponentInitialInventoryStatus {
625 fleet_activation_operation_id: [10; 32],
626 component_count: 0,
627 inventory_hash: [11; 32],
628 sealed_at_ns: 12,
629 directories_converged: true,
630 root_runtime_activated: true,
631 }),
632 };
633 let allocation = RootComponentAllocationResponse {
634 operation_id: [10; 32],
635 allocation_sequence: 1,
636 component: ComponentInstanceId::from_generated_bytes([11; 32]),
637 component_spec: "projects".parse().expect("Component Spec ID"),
638 spec_hash: [12; 32],
639 role: CanisterRole::new("project_hub"),
640 provisioning_origin: ComponentProvisioningOrigin::FleetAdministrator {
641 caller: Principal::from_slice(&[13; 29]),
642 },
643 release_set: response.release_set,
644 phase: RootComponentAllocationPhase::Reserved,
645 creation: None,
646 installation: None,
647 };
648 let created = RootComponentAllocationResponse {
649 phase: RootComponentAllocationPhase::Created,
650 creation: Some(RootComponentCreationEvidence {
651 wasm_store: Principal::from_slice(&[14; 29]),
652 payload_hash: [15; 32],
653 payload_size_bytes: 4_096,
654 initial_cycles: Cycles::new(5_000_000_000_000),
655 controller: Principal::from_slice(&[6; 29]),
656 canister: Some(Principal::from_slice(&[16; 29])),
657 }),
658 installation: None,
659 ..allocation.clone()
660 };
661 let request_bytes = candid::encode_one(&request).expect("encode request");
662 let response_bytes = candid::encode_one(&response).expect("encode response");
663 let allocation_bytes = candid::encode_one(&allocation).expect("encode allocation");
664 let created_bytes = candid::encode_one(&created).expect("encode created allocation");
665
666 assert_eq!(
667 candid::decode_one::<RootComponentRegistryPreparationRequest>(&request_bytes)
668 .expect("decode request"),
669 request
670 );
671 assert_eq!(
672 candid::decode_one::<RootComponentRegistryStatusResponse>(&response_bytes)
673 .expect("decode response"),
674 response
675 );
676 assert_eq!(
677 candid::decode_one::<RootComponentAllocationResponse>(&allocation_bytes)
678 .expect("decode allocation"),
679 allocation
680 );
681 assert_eq!(
682 candid::decode_one::<RootComponentAllocationResponse>(&created_bytes)
683 .expect("decode created allocation"),
684 created
685 );
686 }
687
688 #[test]
689 fn component_commit_response_round_trips_through_candid() {
690 let root = Principal::from_slice(&[6; 29]);
691 let component = ComponentInstanceId::from_generated_bytes([11; 32]);
692 let component_spec: ComponentSpecId = "projects".parse().expect("Component Spec ID");
693 let release_set = FleetSubnetRootReleaseSet {
694 release_build_id: ReleaseBuildId::from_nonce(ReleaseBuildNonce::from_random_bytes(
695 [7; 32],
696 )),
697 manifest_digest: ReleaseSetDigest::from_bytes([8; 32]),
698 };
699 let provisioning_origin = ComponentProvisioningOrigin::FleetAdministrator {
700 caller: Principal::from_slice(&[13; 29]),
701 };
702 let binding = ComponentBinding {
703 authority: fleet_registry_authority(),
704 component,
705 component_spec: component_spec.clone(),
706 spec_hash: [12; 32],
707 role: CanisterRole::new("project_hub"),
708 placement_subnet: SubnetId::from_principal(Principal::from_slice(&[17; 29])),
709 fleet_subnet_root: root,
710 canister_id: Principal::from_slice(&[16; 29]),
711 };
712 let head = ComponentRegistryHead {
713 component,
714 revision: 1,
715 content_hash: [18; 32],
716 };
717 let committed = RootComponentCommitResponse {
718 allocation: RootComponentAllocationResponse {
719 operation_id: [10; 32],
720 allocation_sequence: 1,
721 component,
722 component_spec,
723 spec_hash: binding.spec_hash,
724 role: binding.role.clone(),
725 provisioning_origin: provisioning_origin.clone(),
726 release_set,
727 phase: RootComponentAllocationPhase::Committed,
728 creation: Some(RootComponentCreationEvidence {
729 wasm_store: Principal::from_slice(&[14; 29]),
730 payload_hash: [15; 32],
731 payload_size_bytes: 4_096,
732 initial_cycles: Cycles::new(5_000_000_000_000),
733 controller: root,
734 canister: Some(binding.canister_id),
735 }),
736 installation: Some(RootComponentInstallEvidence {
737 raw_module_hash: [20; 32],
738 chunk_hashes: vec![vec![21; 32]],
739 binding: binding.clone(),
740 }),
741 },
742 registry: ComponentRegistryPartitionResponse {
743 head: head.clone(),
744 binding: binding.clone(),
745 provisioning_origin,
746 release_set,
747 status: ComponentLifecycleStatus::Prepared,
748 reserved_descendants: 0,
749 committed_descendants: 0,
750 encoded_bytes: 2_048,
751 },
752 directory: ComponentDirectoryHead {
753 provenance: ComponentDirectoryProvenance {
754 component: binding,
755 source_fleet_subnet_root: root,
756 component_registry_revision: head.revision,
757 component_registry_content_hash: head.content_hash,
758 synchronized_at_ns: 19,
759 },
760 descendant_count: 0,
761 },
762 };
763 let committed_bytes = candid::encode_one(&committed).expect("encode committed allocation");
764
765 assert_eq!(
766 candid::decode_one::<RootComponentCommitResponse>(&committed_bytes)
767 .expect("decode committed allocation"),
768 committed
769 );
770 }
771
772 fn fleet_registry_authority() -> FleetRegistryAuthority {
773 FleetRegistryAuthority {
774 binding: FleetCoordinatorBinding {
775 fleet: crate::ids::FleetBinding {
776 fleet: FleetKey {
777 canonical_network_id: CanonicalNetworkId::public_ic(),
778 fleet_id: FleetId::from_generated_bytes([1; 32]),
779 },
780 app: AppId::from("toko"),
781 },
782 coordinator_subnet: SubnetId::from_principal(Principal::from_slice(&[2; 29])),
783 coordinator: Principal::from_slice(&[3; 29]),
784 },
785 epoch: 1,
786 }
787 }
788
789 #[test]
790 fn component_creation_request_round_trips_through_candid() {
791 let request = RootComponentCreationRequest {
792 operation_id: [10; 32],
793 };
794 let bytes = candid::encode_one(request).expect("encode creation request");
795
796 assert_eq!(
797 candid::decode_one::<RootComponentCreationRequest>(&bytes)
798 .expect("decode creation request"),
799 request
800 );
801 }
802
803 #[test]
804 #[expect(
805 clippy::too_many_lines,
806 reason = "one round-trip test keeps the complete child lifecycle boundary coherent"
807 )]
808 fn component_child_lifecycle_contracts_round_trip_through_candid() {
809 let component = ComponentInstanceId::from_generated_bytes([11; 32]);
810 let registry = ComponentRegistryHead {
811 component,
812 revision: 2,
813 content_hash: [12; 32],
814 };
815 let request = RootComponentChildAllocationRequest {
816 operation_id: [13; 32],
817 component,
818 expected_registry: registry.clone(),
819 child_role: CanisterRole::new("project_instance"),
820 };
821 let status_request = RootComponentChildAllocationStatusRequest {
822 operation_id: request.operation_id,
823 component,
824 };
825 let creation_request = RootComponentChildCreationRequest {
826 operation_id: request.operation_id,
827 component,
828 };
829 let install_request = RootComponentChildInstallRequest {
830 operation_id: request.operation_id,
831 component,
832 };
833 let commit_request = RootComponentChildCommitRequest {
834 operation_id: request.operation_id,
835 component,
836 };
837 let root = Principal::from_slice(&[17; 29]);
838 let parent = Principal::from_slice(&[14; 29]);
839 let child = Principal::from_slice(&[18; 29]);
840 let child_binding = ComponentChildBinding {
841 component: ComponentBinding {
842 authority: fleet_registry_authority(),
843 component,
844 component_spec: "projects".parse().expect("Component Spec"),
845 spec_hash: [19; 32],
846 role: CanisterRole::new("project_hub"),
847 placement_subnet: SubnetId::from_principal(Principal::from_slice(&[20; 29])),
848 fleet_subnet_root: root,
849 canister_id: parent,
850 },
851 parent_canister_id: parent,
852 role: request.child_role.clone(),
853 canister_id: child,
854 };
855 let response = RootComponentChildAllocationResponse {
856 operation_id: request.operation_id,
857 component,
858 parent_canister_id: parent,
859 parent_role: CanisterRole::new("project_hub"),
860 child_role: request.child_role.clone(),
861 child_kind: ComponentChildKind::Instance,
862 maximum_instances_per_parent: 10_000,
863 maximum_descendants: 20_000,
864 maximum_registry_bytes: 16_777_216,
865 reserved_against_registry: registry,
866 release_set: FleetSubnetRootReleaseSet {
867 release_build_id: ReleaseBuildId::from_nonce(ReleaseBuildNonce::from_random_bytes(
868 [15; 32],
869 )),
870 manifest_digest: ReleaseSetDigest::from_bytes([16; 32]),
871 },
872 phase: RootComponentAllocationPhase::Verified,
873 creation: Some(RootComponentCreationEvidence {
874 wasm_store: Principal::from_slice(&[21; 29]),
875 payload_hash: [22; 32],
876 payload_size_bytes: 4_096,
877 initial_cycles: Cycles::new(5_000_000_000_000),
878 controller: root,
879 canister: Some(child),
880 }),
881 installation: Some(RootComponentChildInstallEvidence {
882 raw_module_hash: [23; 32],
883 chunk_hashes: vec![vec![24; 32]],
884 binding: child_binding.clone(),
885 }),
886 };
887 let commit_response = RootComponentChildCommitResponse {
888 allocation: response.clone(),
889 registry: ComponentRegistryPartitionResponse {
890 head: ComponentRegistryHead {
891 component,
892 revision: 3,
893 content_hash: [25; 32],
894 },
895 binding: child_binding.component.clone(),
896 provisioning_origin: ComponentProvisioningOrigin::FleetAdministrator {
897 caller: Principal::from_slice(&[26; 29]),
898 },
899 release_set: response.release_set,
900 status: ComponentLifecycleStatus::Active,
901 reserved_descendants: 0,
902 committed_descendants: 1,
903 encoded_bytes: 8_192,
904 },
905 directory: ComponentDirectoryHead {
906 provenance: ComponentDirectoryProvenance {
907 component: child_binding.component,
908 source_fleet_subnet_root: root,
909 component_registry_revision: 3,
910 component_registry_content_hash: [25; 32],
911 synchronized_at_ns: 27,
912 },
913 descendant_count: 1,
914 },
915 };
916
917 let request_bytes = candid::encode_one(&request).expect("encode child reservation");
918 let status_bytes =
919 candid::encode_one(status_request).expect("encode child reservation status");
920 let creation_bytes =
921 candid::encode_one(creation_request).expect("encode child creation request");
922 let install_bytes =
923 candid::encode_one(install_request).expect("encode child install request");
924 let response_bytes = candid::encode_one(&response).expect("encode child response");
925 let commit_request_bytes =
926 candid::encode_one(commit_request).expect("encode child commit request");
927 let commit_response_bytes =
928 candid::encode_one(&commit_response).expect("encode child commit response");
929
930 assert_eq!(
931 candid::decode_one::<RootComponentChildAllocationRequest>(&request_bytes)
932 .expect("decode child reservation"),
933 request
934 );
935 assert_eq!(
936 candid::decode_one::<RootComponentChildAllocationStatusRequest>(&status_bytes)
937 .expect("decode child reservation status"),
938 status_request
939 );
940 assert_eq!(
941 candid::decode_one::<RootComponentChildCreationRequest>(&creation_bytes)
942 .expect("decode child creation request"),
943 creation_request
944 );
945 assert_eq!(
946 candid::decode_one::<RootComponentChildInstallRequest>(&install_bytes)
947 .expect("decode child install request"),
948 install_request
949 );
950 assert_eq!(
951 candid::decode_one::<RootComponentChildAllocationResponse>(&response_bytes)
952 .expect("decode child response"),
953 response
954 );
955 assert_eq!(
956 candid::decode_one::<RootComponentChildCommitRequest>(&commit_request_bytes)
957 .expect("decode child commit request"),
958 commit_request
959 );
960 assert_eq!(
961 candid::decode_one::<RootComponentChildCommitResponse>(&commit_response_bytes)
962 .expect("decode child commit response"),
963 commit_response
964 );
965 }
966
967 #[test]
968 fn component_install_request_round_trips_through_candid() {
969 let request = RootComponentInstallRequest {
970 operation_id: [10; 32],
971 };
972 let bytes = candid::encode_one(request).expect("encode install request");
973
974 assert_eq!(
975 candid::decode_one::<RootComponentInstallRequest>(&bytes)
976 .expect("decode install request"),
977 request
978 );
979 }
980
981 #[test]
982 fn component_commit_request_round_trips_through_candid() {
983 let request = RootComponentCommitRequest {
984 operation_id: [10; 32],
985 };
986 let bytes = candid::encode_one(request).expect("encode commit request");
987
988 assert_eq!(
989 candid::decode_one::<RootComponentCommitRequest>(&bytes)
990 .expect("decode commit request"),
991 request
992 );
993 }
994
995 #[test]
996 fn component_runtime_activation_requests_round_trip_through_candid() {
997 let root_request = RootComponentRuntimeActivationRequest {
998 operation_id: [22; 32],
999 };
1000 let target_request = ComponentRuntimeActivationRequest {
1001 operation_id: root_request.operation_id,
1002 directory_authority_hash: [23; 32],
1003 };
1004 let membership_request = RootComponentMembershipActivationRequest {
1005 operation_id: root_request.operation_id,
1006 };
1007 let root_bytes = candid::encode_one(root_request).expect("encode root activation request");
1008 let target_bytes =
1009 candid::encode_one(target_request).expect("encode target activation request");
1010 let membership_bytes =
1011 candid::encode_one(membership_request).expect("encode membership activation request");
1012
1013 assert_eq!(
1014 candid::decode_one::<RootComponentRuntimeActivationRequest>(&root_bytes)
1015 .expect("decode root activation request"),
1016 root_request
1017 );
1018 assert_eq!(
1019 candid::decode_one::<ComponentRuntimeActivationRequest>(&target_bytes)
1020 .expect("decode target activation request"),
1021 target_request
1022 );
1023 assert_eq!(
1024 candid::decode_one::<RootComponentMembershipActivationRequest>(&membership_bytes)
1025 .expect("decode membership activation request"),
1026 membership_request
1027 );
1028 }
1029}