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, ComponentInstanceId, ComponentSpecId,
16 ComponentTopologyDigest, FleetSubnetRootReleaseSet, ManagedCanisterBinding,
17 },
18};
19use candid::{CandidType, Principal};
20use serde::{Deserialize, Serialize};
21
22#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
29pub struct RootComponentRegistryPreparationRequest {
30 pub store_bootstrap: RootStoreBootstrapRequest,
31 pub expected_fleet_registry: FleetRegistryVersion,
32}
33
34#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
41pub struct RootComponentInitialInventoryStatus {
42 pub fleet_activation_operation_id: [u8; 32],
43 pub component_count: u32,
44 pub inventory_hash: [u8; 32],
45 pub sealed_at_ns: u64,
46 pub directories_converged: bool,
47 pub root_runtime_activated: bool,
48}
49
50#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
57pub struct RootComponentRegistryStatusResponse {
58 pub fleet_subnet_root: Principal,
59 pub prepared_against_registry: FleetRegistryVersion,
60 pub release_set: FleetSubnetRootReleaseSet,
61 pub component_topology_digest: ComponentTopologyDigest,
62 pub next_allocation_sequence: u64,
63 pub reserved_component_instances: u32,
64 pub committed_component_instances: u32,
65 pub managed_descendants: u32,
66 pub known_created_component_canisters: u32,
67 pub encoded_bytes: u64,
68 pub initial_inventory: Option<RootComponentInitialInventoryStatus>,
69}
70
71#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
78pub struct RootComponentAllocationRequest {
79 pub operation_id: [u8; 32],
80 pub component_spec: ComponentSpecId,
81}
82
83#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
90pub struct RootComponentAllocationStatusRequest {
91 pub operation_id: [u8; 32],
92}
93
94#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
101pub struct RootComponentChildAllocationRequest {
102 pub operation_id: [u8; 32],
103 pub component: ComponentInstanceId,
104 pub expected_registry: ComponentRegistryHead,
105 pub child_role: CanisterRole,
106}
107
108#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
115pub struct RootComponentChildAllocationStatusRequest {
116 pub operation_id: [u8; 32],
117 pub component: ComponentInstanceId,
118}
119
120#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
127pub struct RootComponentChildCreationRequest {
128 pub operation_id: [u8; 32],
129 pub component: ComponentInstanceId,
130}
131
132#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
139pub struct RootComponentCreationRequest {
140 pub operation_id: [u8; 32],
141}
142
143#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
150pub struct RootComponentInstallRequest {
151 pub operation_id: [u8; 32],
152}
153
154#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
161pub struct RootComponentCommitRequest {
162 pub operation_id: [u8; 32],
163}
164
165#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
172pub struct RootComponentDirectoryPreparationRequest {
173 pub operation_id: [u8; 32],
174}
175
176#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
183pub struct RootComponentRuntimeActivationRequest {
184 pub operation_id: [u8; 32],
185}
186
187#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
194pub struct RootComponentMembershipActivationRequest {
195 pub operation_id: [u8; 32],
196}
197
198#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
205pub enum ComponentProvisioningOrigin {
206 FleetAdministrator { caller: Principal },
207}
208
209#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
216pub enum RootComponentAllocationPhase {
217 Reserved,
218 CreationIntent,
219 Created,
220 InstallIntent,
221 Installed,
222 Verified,
223 Committed,
224}
225
226#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
233pub enum ComponentLifecycleStatus {
234 Prepared,
235 Active,
236 Draining,
237 Removed,
238}
239
240#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
247pub struct ComponentRegistryHead {
248 pub component: ComponentInstanceId,
249 pub revision: u64,
250 pub content_hash: [u8; 32],
251}
252
253#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
260pub struct ComponentRegistryPartitionRequest {
261 pub component: ComponentInstanceId,
262}
263
264#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
271pub struct ComponentRegistryPartitionResponse {
272 pub head: ComponentRegistryHead,
273 pub binding: ComponentBinding,
274 pub provisioning_origin: ComponentProvisioningOrigin,
275 pub release_set: FleetSubnetRootReleaseSet,
276 pub status: ComponentLifecycleStatus,
277 pub reserved_descendants: u32,
278 pub committed_descendants: u32,
279 pub encoded_bytes: u64,
280}
281
282#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
289pub struct ComponentDirectoryProvenance {
290 pub component: ComponentBinding,
291 pub source_fleet_subnet_root: Principal,
292 pub component_registry_revision: u64,
293 pub component_registry_content_hash: [u8; 32],
294 pub synchronized_at_ns: u64,
295}
296
297#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
304pub struct ComponentDirectoryHead {
305 pub provenance: ComponentDirectoryProvenance,
306 pub descendant_count: u32,
307}
308
309#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
316pub struct ComponentDirectoryHeadRequest {
317 pub component: ComponentInstanceId,
318}
319
320#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
327pub struct ComponentRuntimeDirectoryAuthority {
328 pub fleet: FleetDirectorySnapshot,
329 pub component: ComponentDirectoryHead,
330}
331
332#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
339pub struct ComponentRuntimeDirectoryPreparationRequest {
340 pub operation_id: [u8; 32],
341 pub authority: ComponentRuntimeDirectoryAuthority,
342}
343
344#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
351pub struct ComponentRuntimeDirectorySynchronizationRequest {
352 pub operation_id: [u8; 32],
353 pub authority: ComponentRuntimeDirectoryAuthority,
354}
355
356#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
363pub enum ComponentRuntimePhase {
364 AwaitingDirectory,
365 DirectoryPrepared,
366 Active,
367}
368
369#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
376pub struct ComponentRuntimeActivationEvidence {
377 pub directory_authority_hash: [u8; 32],
378 pub activated_at_ns: u64,
379}
380
381#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
388pub struct ComponentRuntimeActivationRequest {
389 pub operation_id: [u8; 32],
390 pub directory_authority_hash: [u8; 32],
391}
392
393#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
400pub struct ComponentRuntimeStatusResponse {
401 pub operation_id: [u8; 32],
402 pub binding: ManagedCanisterBinding,
403 pub phase: ComponentRuntimePhase,
404 pub authority: Option<ComponentRuntimeDirectoryAuthority>,
405 pub authority_hash: Option<[u8; 32]>,
406 pub activation: Option<ComponentRuntimeActivationEvidence>,
407}
408
409#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
416pub struct RootComponentCreationEvidence {
417 pub wasm_store: Principal,
418 pub payload_hash: [u8; 32],
419 pub payload_size_bytes: u64,
420 pub initial_cycles: Cycles,
421 pub controller: Principal,
422 pub canister: Option<Principal>,
423}
424
425#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
432pub struct RootComponentInstallEvidence {
433 pub raw_module_hash: [u8; 32],
434 pub chunk_hashes: Vec<Vec<u8>>,
435 pub binding: ComponentBinding,
436}
437
438#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
445pub struct RootComponentAllocationResponse {
446 pub operation_id: [u8; 32],
447 pub allocation_sequence: u64,
448 pub component: ComponentInstanceId,
449 pub component_spec: ComponentSpecId,
450 pub spec_hash: [u8; 32],
451 pub role: CanisterRole,
452 pub provisioning_origin: ComponentProvisioningOrigin,
453 pub release_set: FleetSubnetRootReleaseSet,
454 pub phase: RootComponentAllocationPhase,
455 pub creation: Option<RootComponentCreationEvidence>,
456 pub installation: Option<RootComponentInstallEvidence>,
457}
458
459#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
466pub struct RootComponentChildAllocationResponse {
467 pub operation_id: [u8; 32],
468 pub component: ComponentInstanceId,
469 pub parent_canister_id: Principal,
470 pub parent_role: CanisterRole,
471 pub child_role: CanisterRole,
472 pub child_kind: ComponentChildKind,
473 pub maximum_instances_per_parent: u32,
474 pub maximum_descendants: u32,
475 pub maximum_registry_bytes: u64,
476 pub reserved_against_registry: ComponentRegistryHead,
477 pub release_set: FleetSubnetRootReleaseSet,
478 pub phase: RootComponentAllocationPhase,
479 pub creation: Option<RootComponentCreationEvidence>,
480}
481
482#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
489pub struct RootComponentCommitResponse {
490 pub allocation: RootComponentAllocationResponse,
491 pub registry: ComponentRegistryPartitionResponse,
492 pub directory: ComponentDirectoryHead,
493}
494
495#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
502pub struct RootComponentDirectoryPreparationResponse {
503 pub committed: RootComponentCommitResponse,
504 pub target: ComponentRuntimeStatusResponse,
505}
506
507#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
514pub struct RootComponentRuntimeActivationResponse {
515 pub committed: RootComponentCommitResponse,
516 pub target: ComponentRuntimeStatusResponse,
517}
518
519#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
526pub struct RootComponentMembershipActivationResponse {
527 pub allocation: RootComponentAllocationResponse,
528 pub registry: ComponentRegistryPartitionResponse,
529 pub directory: ComponentDirectoryHead,
530 pub target: ComponentRuntimeStatusResponse,
531}
532
533#[cfg(test)]
534mod tests {
535 use super::*;
536 use crate::{
537 dto::root_store::RootStoreBootstrapRequest,
538 ids::{
539 AppId, CanonicalNetworkId, FleetCoordinatorBinding, FleetId, FleetKey,
540 FleetRegistryAuthority, ReleaseBuildId, ReleaseBuildNonce, ReleaseSetDigest, SubnetId,
541 },
542 };
543
544 #[test]
545 fn component_registry_contracts_round_trip_through_candid() {
546 let request = RootComponentRegistryPreparationRequest {
547 store_bootstrap: RootStoreBootstrapRequest {
548 manifest_payload_size_bytes: 128,
549 },
550 expected_fleet_registry: FleetRegistryVersion {
551 authority: fleet_registry_authority(),
552 revision: 4,
553 content_hash: [5; 32],
554 },
555 };
556 let response = RootComponentRegistryStatusResponse {
557 fleet_subnet_root: Principal::from_slice(&[6; 29]),
558 prepared_against_registry: request.expected_fleet_registry.clone(),
559 release_set: FleetSubnetRootReleaseSet {
560 release_build_id: ReleaseBuildId::from_nonce(ReleaseBuildNonce::from_random_bytes(
561 [7; 32],
562 )),
563 manifest_digest: ReleaseSetDigest::from_bytes([8; 32]),
564 },
565 component_topology_digest: ComponentTopologyDigest::from_bytes([9; 32]),
566 next_allocation_sequence: 1,
567 reserved_component_instances: 0,
568 committed_component_instances: 0,
569 managed_descendants: 0,
570 known_created_component_canisters: 0,
571 encoded_bytes: 0,
572 initial_inventory: Some(RootComponentInitialInventoryStatus {
573 fleet_activation_operation_id: [10; 32],
574 component_count: 0,
575 inventory_hash: [11; 32],
576 sealed_at_ns: 12,
577 directories_converged: true,
578 root_runtime_activated: true,
579 }),
580 };
581 let allocation = RootComponentAllocationResponse {
582 operation_id: [10; 32],
583 allocation_sequence: 1,
584 component: ComponentInstanceId::from_generated_bytes([11; 32]),
585 component_spec: "projects".parse().expect("Component Spec ID"),
586 spec_hash: [12; 32],
587 role: CanisterRole::new("project_hub"),
588 provisioning_origin: ComponentProvisioningOrigin::FleetAdministrator {
589 caller: Principal::from_slice(&[13; 29]),
590 },
591 release_set: response.release_set,
592 phase: RootComponentAllocationPhase::Reserved,
593 creation: None,
594 installation: None,
595 };
596 let created = RootComponentAllocationResponse {
597 phase: RootComponentAllocationPhase::Created,
598 creation: Some(RootComponentCreationEvidence {
599 wasm_store: Principal::from_slice(&[14; 29]),
600 payload_hash: [15; 32],
601 payload_size_bytes: 4_096,
602 initial_cycles: Cycles::new(5_000_000_000_000),
603 controller: Principal::from_slice(&[6; 29]),
604 canister: Some(Principal::from_slice(&[16; 29])),
605 }),
606 installation: None,
607 ..allocation.clone()
608 };
609 let request_bytes = candid::encode_one(&request).expect("encode request");
610 let response_bytes = candid::encode_one(&response).expect("encode response");
611 let allocation_bytes = candid::encode_one(&allocation).expect("encode allocation");
612 let created_bytes = candid::encode_one(&created).expect("encode created allocation");
613
614 assert_eq!(
615 candid::decode_one::<RootComponentRegistryPreparationRequest>(&request_bytes)
616 .expect("decode request"),
617 request
618 );
619 assert_eq!(
620 candid::decode_one::<RootComponentRegistryStatusResponse>(&response_bytes)
621 .expect("decode response"),
622 response
623 );
624 assert_eq!(
625 candid::decode_one::<RootComponentAllocationResponse>(&allocation_bytes)
626 .expect("decode allocation"),
627 allocation
628 );
629 assert_eq!(
630 candid::decode_one::<RootComponentAllocationResponse>(&created_bytes)
631 .expect("decode created allocation"),
632 created
633 );
634 }
635
636 #[test]
637 fn component_commit_response_round_trips_through_candid() {
638 let root = Principal::from_slice(&[6; 29]);
639 let component = ComponentInstanceId::from_generated_bytes([11; 32]);
640 let component_spec: ComponentSpecId = "projects".parse().expect("Component Spec ID");
641 let release_set = FleetSubnetRootReleaseSet {
642 release_build_id: ReleaseBuildId::from_nonce(ReleaseBuildNonce::from_random_bytes(
643 [7; 32],
644 )),
645 manifest_digest: ReleaseSetDigest::from_bytes([8; 32]),
646 };
647 let provisioning_origin = ComponentProvisioningOrigin::FleetAdministrator {
648 caller: Principal::from_slice(&[13; 29]),
649 };
650 let binding = ComponentBinding {
651 authority: fleet_registry_authority(),
652 component,
653 component_spec: component_spec.clone(),
654 spec_hash: [12; 32],
655 role: CanisterRole::new("project_hub"),
656 placement_subnet: SubnetId::from_principal(Principal::from_slice(&[17; 29])),
657 fleet_subnet_root: root,
658 canister_id: Principal::from_slice(&[16; 29]),
659 };
660 let head = ComponentRegistryHead {
661 component,
662 revision: 1,
663 content_hash: [18; 32],
664 };
665 let committed = RootComponentCommitResponse {
666 allocation: RootComponentAllocationResponse {
667 operation_id: [10; 32],
668 allocation_sequence: 1,
669 component,
670 component_spec,
671 spec_hash: binding.spec_hash,
672 role: binding.role.clone(),
673 provisioning_origin: provisioning_origin.clone(),
674 release_set,
675 phase: RootComponentAllocationPhase::Committed,
676 creation: Some(RootComponentCreationEvidence {
677 wasm_store: Principal::from_slice(&[14; 29]),
678 payload_hash: [15; 32],
679 payload_size_bytes: 4_096,
680 initial_cycles: Cycles::new(5_000_000_000_000),
681 controller: root,
682 canister: Some(binding.canister_id),
683 }),
684 installation: Some(RootComponentInstallEvidence {
685 raw_module_hash: [20; 32],
686 chunk_hashes: vec![vec![21; 32]],
687 binding: binding.clone(),
688 }),
689 },
690 registry: ComponentRegistryPartitionResponse {
691 head: head.clone(),
692 binding: binding.clone(),
693 provisioning_origin,
694 release_set,
695 status: ComponentLifecycleStatus::Prepared,
696 reserved_descendants: 0,
697 committed_descendants: 0,
698 encoded_bytes: 2_048,
699 },
700 directory: ComponentDirectoryHead {
701 provenance: ComponentDirectoryProvenance {
702 component: binding,
703 source_fleet_subnet_root: root,
704 component_registry_revision: head.revision,
705 component_registry_content_hash: head.content_hash,
706 synchronized_at_ns: 19,
707 },
708 descendant_count: 0,
709 },
710 };
711 let committed_bytes = candid::encode_one(&committed).expect("encode committed allocation");
712
713 assert_eq!(
714 candid::decode_one::<RootComponentCommitResponse>(&committed_bytes)
715 .expect("decode committed allocation"),
716 committed
717 );
718 }
719
720 fn fleet_registry_authority() -> FleetRegistryAuthority {
721 FleetRegistryAuthority {
722 binding: FleetCoordinatorBinding {
723 fleet: crate::ids::FleetBinding {
724 fleet: FleetKey {
725 canonical_network_id: CanonicalNetworkId::public_ic(),
726 fleet_id: FleetId::from_generated_bytes([1; 32]),
727 },
728 app: AppId::from("toko"),
729 },
730 coordinator_subnet: SubnetId::from_principal(Principal::from_slice(&[2; 29])),
731 coordinator: Principal::from_slice(&[3; 29]),
732 },
733 epoch: 1,
734 }
735 }
736
737 #[test]
738 fn component_creation_request_round_trips_through_candid() {
739 let request = RootComponentCreationRequest {
740 operation_id: [10; 32],
741 };
742 let bytes = candid::encode_one(request).expect("encode creation request");
743
744 assert_eq!(
745 candid::decode_one::<RootComponentCreationRequest>(&bytes)
746 .expect("decode creation request"),
747 request
748 );
749 }
750
751 #[test]
752 fn component_child_reservation_contracts_round_trip_through_candid() {
753 let component = ComponentInstanceId::from_generated_bytes([11; 32]);
754 let registry = ComponentRegistryHead {
755 component,
756 revision: 2,
757 content_hash: [12; 32],
758 };
759 let request = RootComponentChildAllocationRequest {
760 operation_id: [13; 32],
761 component,
762 expected_registry: registry.clone(),
763 child_role: CanisterRole::new("project_instance"),
764 };
765 let status_request = RootComponentChildAllocationStatusRequest {
766 operation_id: request.operation_id,
767 component,
768 };
769 let creation_request = RootComponentChildCreationRequest {
770 operation_id: request.operation_id,
771 component,
772 };
773 let response = RootComponentChildAllocationResponse {
774 operation_id: request.operation_id,
775 component,
776 parent_canister_id: Principal::from_slice(&[14; 29]),
777 parent_role: CanisterRole::new("project_hub"),
778 child_role: request.child_role.clone(),
779 child_kind: ComponentChildKind::Instance,
780 maximum_instances_per_parent: 10_000,
781 maximum_descendants: 20_000,
782 maximum_registry_bytes: 16_777_216,
783 reserved_against_registry: registry,
784 release_set: FleetSubnetRootReleaseSet {
785 release_build_id: ReleaseBuildId::from_nonce(ReleaseBuildNonce::from_random_bytes(
786 [15; 32],
787 )),
788 manifest_digest: ReleaseSetDigest::from_bytes([16; 32]),
789 },
790 phase: RootComponentAllocationPhase::Reserved,
791 creation: None,
792 };
793
794 let request_bytes = candid::encode_one(&request).expect("encode child reservation");
795 let status_bytes =
796 candid::encode_one(status_request).expect("encode child reservation status");
797 let creation_bytes =
798 candid::encode_one(creation_request).expect("encode child creation request");
799 let response_bytes = candid::encode_one(&response).expect("encode child response");
800
801 assert_eq!(
802 candid::decode_one::<RootComponentChildAllocationRequest>(&request_bytes)
803 .expect("decode child reservation"),
804 request
805 );
806 assert_eq!(
807 candid::decode_one::<RootComponentChildAllocationStatusRequest>(&status_bytes)
808 .expect("decode child reservation status"),
809 status_request
810 );
811 assert_eq!(
812 candid::decode_one::<RootComponentChildCreationRequest>(&creation_bytes)
813 .expect("decode child creation request"),
814 creation_request
815 );
816 assert_eq!(
817 candid::decode_one::<RootComponentChildAllocationResponse>(&response_bytes)
818 .expect("decode child response"),
819 response
820 );
821 }
822
823 #[test]
824 fn component_install_request_round_trips_through_candid() {
825 let request = RootComponentInstallRequest {
826 operation_id: [10; 32],
827 };
828 let bytes = candid::encode_one(request).expect("encode install request");
829
830 assert_eq!(
831 candid::decode_one::<RootComponentInstallRequest>(&bytes)
832 .expect("decode install request"),
833 request
834 );
835 }
836
837 #[test]
838 fn component_commit_request_round_trips_through_candid() {
839 let request = RootComponentCommitRequest {
840 operation_id: [10; 32],
841 };
842 let bytes = candid::encode_one(request).expect("encode commit request");
843
844 assert_eq!(
845 candid::decode_one::<RootComponentCommitRequest>(&bytes)
846 .expect("decode commit request"),
847 request
848 );
849 }
850
851 #[test]
852 fn component_runtime_activation_requests_round_trip_through_candid() {
853 let root_request = RootComponentRuntimeActivationRequest {
854 operation_id: [22; 32],
855 };
856 let target_request = ComponentRuntimeActivationRequest {
857 operation_id: root_request.operation_id,
858 directory_authority_hash: [23; 32],
859 };
860 let membership_request = RootComponentMembershipActivationRequest {
861 operation_id: root_request.operation_id,
862 };
863 let root_bytes = candid::encode_one(root_request).expect("encode root activation request");
864 let target_bytes =
865 candid::encode_one(target_request).expect("encode target activation request");
866 let membership_bytes =
867 candid::encode_one(membership_request).expect("encode membership activation request");
868
869 assert_eq!(
870 candid::decode_one::<RootComponentRuntimeActivationRequest>(&root_bytes)
871 .expect("decode root activation request"),
872 root_request
873 );
874 assert_eq!(
875 candid::decode_one::<ComponentRuntimeActivationRequest>(&target_bytes)
876 .expect("decode target activation request"),
877 target_request
878 );
879 assert_eq!(
880 candid::decode_one::<RootComponentMembershipActivationRequest>(&membership_bytes)
881 .expect("decode membership activation request"),
882 membership_request
883 );
884 }
885}