Skip to main content

canic_core/dto/
component_registry.rs

1//! Module: dto::component_registry
2//!
3//! Responsibility: carry root-local Component Registry preparation and allocation evidence.
4//! Does not own: admission policy, stable mutation, artifact resolution, or lifecycle effects.
5//! Boundary: callers name intent and Spec while the root allocates identity under verified authority.
6
7use crate::{
8    cdk::types::Cycles,
9    dto::{
10        fleet_registry::{FleetDirectorySnapshot, FleetRegistryVersion},
11        root_store::RootStoreBootstrapRequest,
12    },
13    ids::{
14        CanisterRole, ComponentBinding, ComponentInstanceId, ComponentSpecId,
15        ComponentTopologyDigest, FleetSubnetRootReleaseSet, ManagedCanisterBinding,
16    },
17};
18use candid::{CandidType, Principal};
19use serde::{Deserialize, Serialize};
20
21///
22/// RootComponentRegistryPreparationRequest
23///
24/// Exact authority required before an empty root-local Component Registry may be prepared.
25///
26
27#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
28pub struct RootComponentRegistryPreparationRequest {
29    pub store_bootstrap: RootStoreBootstrapRequest,
30    pub expected_fleet_registry: FleetRegistryVersion,
31}
32
33///
34/// RootComponentRegistryStatusResponse
35///
36/// Compact durable Component Registry authority and current allocation counters.
37///
38
39#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
40pub struct RootComponentRegistryStatusResponse {
41    pub fleet_subnet_root: Principal,
42    pub prepared_against_registry: FleetRegistryVersion,
43    pub release_set: FleetSubnetRootReleaseSet,
44    pub component_topology_digest: ComponentTopologyDigest,
45    pub next_allocation_sequence: u64,
46    pub reserved_component_instances: u32,
47    pub committed_component_instances: u32,
48    pub managed_descendants: u32,
49    pub encoded_bytes: u64,
50}
51
52///
53/// RootComponentAllocationRequest
54///
55/// Controller command naming one idempotent top-level Component reservation intent.
56///
57
58#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
59pub struct RootComponentAllocationRequest {
60    pub operation_id: [u8; 32],
61    pub component_spec: ComponentSpecId,
62}
63
64///
65/// RootComponentAllocationStatusRequest
66///
67/// Read-only lookup key for one durable top-level Component allocation operation.
68///
69
70#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
71pub struct RootComponentAllocationStatusRequest {
72    pub operation_id: [u8; 32],
73}
74
75///
76/// RootComponentCreationRequest
77///
78/// Controller command continuing one already reserved top-level Component operation.
79///
80
81#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
82pub struct RootComponentCreationRequest {
83    pub operation_id: [u8; 32],
84}
85
86///
87/// RootComponentInstallRequest
88///
89/// Controller command continuing one already created top-level Component operation.
90///
91
92#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
93pub struct RootComponentInstallRequest {
94    pub operation_id: [u8; 32],
95}
96
97///
98/// RootComponentCommitRequest
99///
100/// Controller command committing one already verified top-level Component operation.
101///
102
103#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
104pub struct RootComponentCommitRequest {
105    pub operation_id: [u8; 32],
106}
107
108///
109/// RootComponentDirectoryPreparationRequest
110///
111/// Controller command distributing exact Directories to one committed top-level Component.
112///
113
114#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
115pub struct RootComponentDirectoryPreparationRequest {
116    pub operation_id: [u8; 32],
117}
118
119///
120/// RootComponentRuntimeActivationRequest
121///
122/// Controller command activating one Directory-prepared top-level Component runtime.
123///
124
125#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
126pub struct RootComponentRuntimeActivationRequest {
127    pub operation_id: [u8; 32],
128}
129
130///
131/// RootComponentMembershipActivationRequest
132///
133/// Controller command activating one runtime-active Component's Registry membership.
134///
135
136#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
137pub struct RootComponentMembershipActivationRequest {
138    pub operation_id: [u8; 32],
139}
140
141///
142/// ComponentProvisioningOrigin
143///
144/// Authenticated causal authority retained with one top-level Component allocation.
145///
146
147#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
148pub enum ComponentProvisioningOrigin {
149    FleetAdministrator { caller: Principal },
150}
151
152///
153/// RootComponentAllocationPhase
154///
155/// Durable root-local progress of one top-level Component allocation operation.
156///
157
158#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
159pub enum RootComponentAllocationPhase {
160    Reserved,
161    CreationIntent,
162    Created,
163    InstallIntent,
164    Installed,
165    Verified,
166    Committed,
167}
168
169///
170/// ComponentLifecycleStatus
171///
172/// Root-owned runtime lifecycle state of one committed Component Registry member.
173///
174
175#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
176pub enum ComponentLifecycleStatus {
177    Prepared,
178    Active,
179    Draining,
180    Removed,
181}
182
183///
184/// ComponentRegistryHead
185///
186/// Exact independently versioned authority of one Component Registry partition.
187///
188
189#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
190pub struct ComponentRegistryHead {
191    pub component: ComponentInstanceId,
192    pub revision: u64,
193    pub content_hash: [u8; 32],
194}
195
196///
197/// ComponentRegistryPartitionRequest
198///
199/// Read-only lookup key for one committed Component Registry partition.
200///
201
202#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
203pub struct ComponentRegistryPartitionRequest {
204    pub component: ComponentInstanceId,
205}
206
207///
208/// ComponentRegistryPartitionResponse
209///
210/// Protected top-level row and independent head of one Component Registry partition.
211///
212
213#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
214pub struct ComponentRegistryPartitionResponse {
215    pub head: ComponentRegistryHead,
216    pub binding: ComponentBinding,
217    pub provisioning_origin: ComponentProvisioningOrigin,
218    pub release_set: FleetSubnetRootReleaseSet,
219    pub status: ComponentLifecycleStatus,
220    pub encoded_bytes: u64,
221}
222
223///
224/// ComponentDirectoryProvenance
225///
226/// Exact Component Registry authority from which one Component Directory is derived.
227///
228
229#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
230pub struct ComponentDirectoryProvenance {
231    pub component: ComponentBinding,
232    pub source_fleet_subnet_root: Principal,
233    pub component_registry_revision: u64,
234    pub component_registry_content_hash: [u8; 32],
235    pub synchronized_at_ns: u64,
236}
237
238///
239/// ComponentDirectoryHead
240///
241/// Compact independently versioned discovery projection for one Component tree.
242///
243
244#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
245pub struct ComponentDirectoryHead {
246    pub provenance: ComponentDirectoryProvenance,
247    pub descendant_count: u32,
248}
249
250///
251/// ComponentDirectoryHeadRequest
252///
253/// Read-only lookup key for one committed Component Directory head.
254///
255
256#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
257pub struct ComponentDirectoryHeadRequest {
258    pub component: ComponentInstanceId,
259}
260
261///
262/// ComponentRuntimeDirectoryAuthority
263///
264/// Exact Fleet and Component discovery authority retained by one managed Component-tree node.
265///
266
267#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
268pub struct ComponentRuntimeDirectoryAuthority {
269    pub fleet: FleetDirectorySnapshot,
270    pub component: ComponentDirectoryHead,
271}
272
273///
274/// ComponentRuntimeDirectoryPreparationRequest
275///
276/// Root-issued exact Directory preparation command for one managed Component-tree node.
277///
278
279#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
280pub struct ComponentRuntimeDirectoryPreparationRequest {
281    pub operation_id: [u8; 32],
282    pub authority: ComponentRuntimeDirectoryAuthority,
283}
284
285///
286/// ComponentRuntimeDirectorySynchronizationRequest
287///
288/// Root-issued replacement of one active managed Component node's current Directory authority.
289///
290
291#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
292pub struct ComponentRuntimeDirectorySynchronizationRequest {
293    pub operation_id: [u8; 32],
294    pub authority: ComponentRuntimeDirectoryAuthority,
295}
296
297///
298/// ComponentRuntimePhase
299///
300/// Target-local progress from installation through Component runtime activation.
301///
302
303#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
304pub enum ComponentRuntimePhase {
305    AwaitingDirectory,
306    DirectoryPrepared,
307    Active,
308}
309
310///
311/// ComponentRuntimeActivationEvidence
312///
313/// Exact retained Directory authority under which one Component runtime became Active.
314///
315
316#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
317pub struct ComponentRuntimeActivationEvidence {
318    pub directory_authority_hash: [u8; 32],
319    pub activated_at_ns: u64,
320}
321
322///
323/// ComponentRuntimeActivationRequest
324///
325/// Root-issued exact activation command for one Directory-prepared managed Component node.
326///
327
328#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
329pub struct ComponentRuntimeActivationRequest {
330    pub operation_id: [u8; 32],
331    pub directory_authority_hash: [u8; 32],
332}
333
334///
335/// ComponentRuntimeStatusResponse
336///
337/// Independently observable target-local binding and exact retained Directory authority.
338///
339
340#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
341pub struct ComponentRuntimeStatusResponse {
342    pub operation_id: [u8; 32],
343    pub binding: ManagedCanisterBinding,
344    pub phase: ComponentRuntimePhase,
345    pub authority: Option<ComponentRuntimeDirectoryAuthority>,
346    pub authority_hash: Option<[u8; 32]>,
347    pub activation: Option<ComponentRuntimeActivationEvidence>,
348}
349
350///
351/// RootComponentCreationEvidence
352///
353/// Exact Store artifact and root-owned creation settings frozen before the paid effect.
354///
355
356#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
357pub struct RootComponentCreationEvidence {
358    pub wasm_store: Principal,
359    pub payload_hash: [u8; 32],
360    pub payload_size_bytes: u64,
361    pub initial_cycles: Cycles,
362    pub controller: Principal,
363    pub canister: Option<Principal>,
364}
365
366///
367/// RootComponentInstallEvidence
368///
369/// Exact raw artifact, chunk source and immutable target binding frozen before installation.
370///
371
372#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
373pub struct RootComponentInstallEvidence {
374    pub raw_module_hash: [u8; 32],
375    pub chunk_hashes: Vec<Vec<u8>>,
376    pub binding: ComponentBinding,
377}
378
379///
380/// RootComponentAllocationResponse
381///
382/// Durable identity reservation returned identically for exact operation retry.
383///
384
385#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
386pub struct RootComponentAllocationResponse {
387    pub operation_id: [u8; 32],
388    pub allocation_sequence: u64,
389    pub component: ComponentInstanceId,
390    pub component_spec: ComponentSpecId,
391    pub spec_hash: [u8; 32],
392    pub role: CanisterRole,
393    pub provisioning_origin: ComponentProvisioningOrigin,
394    pub release_set: FleetSubnetRootReleaseSet,
395    pub phase: RootComponentAllocationPhase,
396    pub creation: Option<RootComponentCreationEvidence>,
397    pub installation: Option<RootComponentInstallEvidence>,
398}
399
400///
401/// RootComponentCommitResponse
402///
403/// Exact committed allocation, authoritative Registry row and derived Directory head.
404///
405
406#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
407pub struct RootComponentCommitResponse {
408    pub allocation: RootComponentAllocationResponse,
409    pub registry: ComponentRegistryPartitionResponse,
410    pub directory: ComponentDirectoryHead,
411}
412
413///
414/// RootComponentDirectoryPreparationResponse
415///
416/// Exact root authority plus independently observed target-local Directory preparation.
417///
418
419#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
420pub struct RootComponentDirectoryPreparationResponse {
421    pub committed: RootComponentCommitResponse,
422    pub target: ComponentRuntimeStatusResponse,
423}
424
425///
426/// RootComponentRuntimeActivationResponse
427///
428/// Exact root authority plus independently observed target-local runtime activation.
429///
430
431#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
432pub struct RootComponentRuntimeActivationResponse {
433    pub committed: RootComponentCommitResponse,
434    pub target: ComponentRuntimeStatusResponse,
435}
436
437///
438/// RootComponentMembershipActivationResponse
439///
440/// Exact active Registry authority plus independently observed current target Directory.
441///
442
443#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
444pub struct RootComponentMembershipActivationResponse {
445    pub allocation: RootComponentAllocationResponse,
446    pub registry: ComponentRegistryPartitionResponse,
447    pub directory: ComponentDirectoryHead,
448    pub target: ComponentRuntimeStatusResponse,
449}
450
451#[cfg(test)]
452mod tests {
453    use super::*;
454    use crate::{
455        dto::root_store::RootStoreBootstrapRequest,
456        ids::{
457            AppId, CanonicalNetworkId, FleetCoordinatorBinding, FleetId, FleetKey,
458            FleetRegistryAuthority, ReleaseBuildId, ReleaseBuildNonce, ReleaseSetDigest, SubnetId,
459        },
460    };
461
462    #[test]
463    fn component_registry_contracts_round_trip_through_candid() {
464        let request = RootComponentRegistryPreparationRequest {
465            store_bootstrap: RootStoreBootstrapRequest {
466                manifest_payload_size_bytes: 128,
467            },
468            expected_fleet_registry: FleetRegistryVersion {
469                authority: fleet_registry_authority(),
470                revision: 4,
471                content_hash: [5; 32],
472            },
473        };
474        let response = RootComponentRegistryStatusResponse {
475            fleet_subnet_root: Principal::from_slice(&[6; 29]),
476            prepared_against_registry: request.expected_fleet_registry.clone(),
477            release_set: FleetSubnetRootReleaseSet {
478                release_build_id: ReleaseBuildId::from_nonce(ReleaseBuildNonce::from_random_bytes(
479                    [7; 32],
480                )),
481                manifest_digest: ReleaseSetDigest::from_bytes([8; 32]),
482            },
483            component_topology_digest: ComponentTopologyDigest::from_bytes([9; 32]),
484            next_allocation_sequence: 1,
485            reserved_component_instances: 0,
486            committed_component_instances: 0,
487            managed_descendants: 0,
488            encoded_bytes: 0,
489        };
490        let allocation = RootComponentAllocationResponse {
491            operation_id: [10; 32],
492            allocation_sequence: 1,
493            component: ComponentInstanceId::from_generated_bytes([11; 32]),
494            component_spec: "projects".parse().expect("Component Spec ID"),
495            spec_hash: [12; 32],
496            role: CanisterRole::new("project_hub"),
497            provisioning_origin: ComponentProvisioningOrigin::FleetAdministrator {
498                caller: Principal::from_slice(&[13; 29]),
499            },
500            release_set: response.release_set,
501            phase: RootComponentAllocationPhase::Reserved,
502            creation: None,
503            installation: None,
504        };
505        let created = RootComponentAllocationResponse {
506            phase: RootComponentAllocationPhase::Created,
507            creation: Some(RootComponentCreationEvidence {
508                wasm_store: Principal::from_slice(&[14; 29]),
509                payload_hash: [15; 32],
510                payload_size_bytes: 4_096,
511                initial_cycles: Cycles::new(5_000_000_000_000),
512                controller: Principal::from_slice(&[6; 29]),
513                canister: Some(Principal::from_slice(&[16; 29])),
514            }),
515            installation: None,
516            ..allocation.clone()
517        };
518        let request_bytes = candid::encode_one(&request).expect("encode request");
519        let response_bytes = candid::encode_one(&response).expect("encode response");
520        let allocation_bytes = candid::encode_one(&allocation).expect("encode allocation");
521        let created_bytes = candid::encode_one(&created).expect("encode created allocation");
522
523        assert_eq!(
524            candid::decode_one::<RootComponentRegistryPreparationRequest>(&request_bytes)
525                .expect("decode request"),
526            request
527        );
528        assert_eq!(
529            candid::decode_one::<RootComponentRegistryStatusResponse>(&response_bytes)
530                .expect("decode response"),
531            response
532        );
533        assert_eq!(
534            candid::decode_one::<RootComponentAllocationResponse>(&allocation_bytes)
535                .expect("decode allocation"),
536            allocation
537        );
538        assert_eq!(
539            candid::decode_one::<RootComponentAllocationResponse>(&created_bytes)
540                .expect("decode created allocation"),
541            created
542        );
543    }
544
545    #[test]
546    fn component_commit_response_round_trips_through_candid() {
547        let root = Principal::from_slice(&[6; 29]);
548        let component = ComponentInstanceId::from_generated_bytes([11; 32]);
549        let component_spec: ComponentSpecId = "projects".parse().expect("Component Spec ID");
550        let release_set = FleetSubnetRootReleaseSet {
551            release_build_id: ReleaseBuildId::from_nonce(ReleaseBuildNonce::from_random_bytes(
552                [7; 32],
553            )),
554            manifest_digest: ReleaseSetDigest::from_bytes([8; 32]),
555        };
556        let provisioning_origin = ComponentProvisioningOrigin::FleetAdministrator {
557            caller: Principal::from_slice(&[13; 29]),
558        };
559        let binding = ComponentBinding {
560            authority: fleet_registry_authority(),
561            component,
562            component_spec: component_spec.clone(),
563            spec_hash: [12; 32],
564            role: CanisterRole::new("project_hub"),
565            placement_subnet: SubnetId::from_principal(Principal::from_slice(&[17; 29])),
566            fleet_subnet_root: root,
567            canister_id: Principal::from_slice(&[16; 29]),
568        };
569        let head = ComponentRegistryHead {
570            component,
571            revision: 1,
572            content_hash: [18; 32],
573        };
574        let committed = RootComponentCommitResponse {
575            allocation: RootComponentAllocationResponse {
576                operation_id: [10; 32],
577                allocation_sequence: 1,
578                component,
579                component_spec,
580                spec_hash: binding.spec_hash,
581                role: binding.role.clone(),
582                provisioning_origin: provisioning_origin.clone(),
583                release_set,
584                phase: RootComponentAllocationPhase::Committed,
585                creation: Some(RootComponentCreationEvidence {
586                    wasm_store: Principal::from_slice(&[14; 29]),
587                    payload_hash: [15; 32],
588                    payload_size_bytes: 4_096,
589                    initial_cycles: Cycles::new(5_000_000_000_000),
590                    controller: root,
591                    canister: Some(binding.canister_id),
592                }),
593                installation: Some(RootComponentInstallEvidence {
594                    raw_module_hash: [20; 32],
595                    chunk_hashes: vec![vec![21; 32]],
596                    binding: binding.clone(),
597                }),
598            },
599            registry: ComponentRegistryPartitionResponse {
600                head: head.clone(),
601                binding: binding.clone(),
602                provisioning_origin,
603                release_set,
604                status: ComponentLifecycleStatus::Prepared,
605                encoded_bytes: 2_048,
606            },
607            directory: ComponentDirectoryHead {
608                provenance: ComponentDirectoryProvenance {
609                    component: binding,
610                    source_fleet_subnet_root: root,
611                    component_registry_revision: head.revision,
612                    component_registry_content_hash: head.content_hash,
613                    synchronized_at_ns: 19,
614                },
615                descendant_count: 0,
616            },
617        };
618        let committed_bytes = candid::encode_one(&committed).expect("encode committed allocation");
619
620        assert_eq!(
621            candid::decode_one::<RootComponentCommitResponse>(&committed_bytes)
622                .expect("decode committed allocation"),
623            committed
624        );
625    }
626
627    fn fleet_registry_authority() -> FleetRegistryAuthority {
628        FleetRegistryAuthority {
629            binding: FleetCoordinatorBinding {
630                fleet: crate::ids::FleetBinding {
631                    fleet: FleetKey {
632                        canonical_network_id: CanonicalNetworkId::public_ic(),
633                        fleet_id: FleetId::from_generated_bytes([1; 32]),
634                    },
635                    app: AppId::from("toko"),
636                },
637                coordinator_subnet: SubnetId::from_principal(Principal::from_slice(&[2; 29])),
638                coordinator: Principal::from_slice(&[3; 29]),
639            },
640            epoch: 1,
641        }
642    }
643
644    #[test]
645    fn component_creation_request_round_trips_through_candid() {
646        let request = RootComponentCreationRequest {
647            operation_id: [10; 32],
648        };
649        let bytes = candid::encode_one(request).expect("encode creation request");
650
651        assert_eq!(
652            candid::decode_one::<RootComponentCreationRequest>(&bytes)
653                .expect("decode creation request"),
654            request
655        );
656    }
657
658    #[test]
659    fn component_install_request_round_trips_through_candid() {
660        let request = RootComponentInstallRequest {
661            operation_id: [10; 32],
662        };
663        let bytes = candid::encode_one(request).expect("encode install request");
664
665        assert_eq!(
666            candid::decode_one::<RootComponentInstallRequest>(&bytes)
667                .expect("decode install request"),
668            request
669        );
670    }
671
672    #[test]
673    fn component_commit_request_round_trips_through_candid() {
674        let request = RootComponentCommitRequest {
675            operation_id: [10; 32],
676        };
677        let bytes = candid::encode_one(request).expect("encode commit request");
678
679        assert_eq!(
680            candid::decode_one::<RootComponentCommitRequest>(&bytes)
681                .expect("decode commit request"),
682            request
683        );
684    }
685
686    #[test]
687    fn component_runtime_activation_requests_round_trip_through_candid() {
688        let root_request = RootComponentRuntimeActivationRequest {
689            operation_id: [22; 32],
690        };
691        let target_request = ComponentRuntimeActivationRequest {
692            operation_id: root_request.operation_id,
693            directory_authority_hash: [23; 32],
694        };
695        let membership_request = RootComponentMembershipActivationRequest {
696            operation_id: root_request.operation_id,
697        };
698        let root_bytes = candid::encode_one(root_request).expect("encode root activation request");
699        let target_bytes =
700            candid::encode_one(target_request).expect("encode target activation request");
701        let membership_bytes =
702            candid::encode_one(membership_request).expect("encode membership activation request");
703
704        assert_eq!(
705            candid::decode_one::<RootComponentRuntimeActivationRequest>(&root_bytes)
706                .expect("decode root activation request"),
707            root_request
708        );
709        assert_eq!(
710            candid::decode_one::<ComponentRuntimeActivationRequest>(&target_bytes)
711                .expect("decode target activation request"),
712            target_request
713        );
714        assert_eq!(
715            candid::decode_one::<RootComponentMembershipActivationRequest>(&membership_bytes)
716                .expect("decode membership activation request"),
717            membership_request
718        );
719    }
720}