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    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///
24/// RootComponentRegistryPreparationRequest
25///
26/// Exact authority required before an empty root-local Component Registry may be prepared.
27///
28
29#[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///
36/// RootComponentInitialInventoryStatus
37///
38/// Durable initial Component inventory sealed for one Fleet Subnet Root activation.
39///
40
41#[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///
52/// RootComponentRegistryStatusResponse
53///
54/// Compact durable Component Registry authority and current allocation counters.
55///
56
57#[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///
73/// RootComponentAllocationRequest
74///
75/// Controller command naming one idempotent top-level Component reservation intent.
76///
77
78#[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///
85/// RootComponentAllocationStatusRequest
86///
87/// Read-only lookup key for one durable top-level Component allocation operation.
88///
89
90#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
91pub struct RootComponentAllocationStatusRequest {
92    pub operation_id: [u8; 32],
93}
94
95///
96/// RootComponentChildAllocationRequest
97///
98/// Parent command naming one idempotent direct-child reservation intent.
99///
100
101#[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///
110/// RootComponentChildAllocationStatusRequest
111///
112/// Parent lookup key for one durable direct-child reservation.
113///
114
115#[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///
122/// RootComponentSubtreeRemovalRequest
123///
124/// Controller command durably fencing one registered child subtree.
125///
126
127#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
128pub struct RootComponentSubtreeRemovalRequest {
129    pub operation_id: [u8; 32],
130    pub component: ComponentInstanceId,
131    pub target_canister_id: Principal,
132    pub expected_registry: ComponentRegistryHead,
133}
134
135///
136/// RootComponentSubtreeRemovalStatusRequest
137///
138/// Controller lookup key for one durable child-subtree removal operation.
139///
140
141#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
142pub struct RootComponentSubtreeRemovalStatusRequest {
143    pub operation_id: [u8; 32],
144    pub component: ComponentInstanceId,
145}
146
147///
148/// RootComponentChildCreationRequest
149///
150/// Parent command continuing one already reserved direct-child operation.
151///
152
153#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
154pub struct RootComponentChildCreationRequest {
155    pub operation_id: [u8; 32],
156    pub component: ComponentInstanceId,
157}
158
159///
160/// RootComponentChildInstallRequest
161///
162/// Parent command installing and verifying one already created direct-child operation.
163///
164
165#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
166pub struct RootComponentChildInstallRequest {
167    pub operation_id: [u8; 32],
168    pub component: ComponentInstanceId,
169}
170
171///
172/// RootComponentChildCommitRequest
173///
174/// Parent command committing one already verified direct-child operation.
175///
176
177#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
178pub struct RootComponentChildCommitRequest {
179    pub operation_id: [u8; 32],
180    pub component: ComponentInstanceId,
181}
182
183///
184/// RootComponentChildDirectoryPreparationRequest
185///
186/// Parent command distributing one committed child's Directory and converging its affected members.
187///
188
189#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
190pub struct RootComponentChildDirectoryPreparationRequest {
191    pub operation_id: [u8; 32],
192    pub component: ComponentInstanceId,
193}
194
195///
196/// RootComponentChildRuntimeActivationRequest
197///
198/// Parent command activating one Directory-prepared direct-child runtime.
199///
200
201#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
202pub struct RootComponentChildRuntimeActivationRequest {
203    pub operation_id: [u8; 32],
204    pub component: ComponentInstanceId,
205}
206
207///
208/// RootComponentChildMembershipActivationRequest
209///
210/// Parent command activating one runtime-active direct child's Registry membership.
211///
212
213#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
214pub struct RootComponentChildMembershipActivationRequest {
215    pub operation_id: [u8; 32],
216    pub component: ComponentInstanceId,
217}
218
219///
220/// RootComponentCreationRequest
221///
222/// Controller command continuing one already reserved top-level Component operation.
223///
224
225#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
226pub struct RootComponentCreationRequest {
227    pub operation_id: [u8; 32],
228}
229
230///
231/// RootComponentInstallRequest
232///
233/// Controller command continuing one already created top-level Component operation.
234///
235
236#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
237pub struct RootComponentInstallRequest {
238    pub operation_id: [u8; 32],
239}
240
241///
242/// RootComponentCommitRequest
243///
244/// Controller command committing one already verified top-level Component operation.
245///
246
247#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
248pub struct RootComponentCommitRequest {
249    pub operation_id: [u8; 32],
250}
251
252///
253/// RootComponentDirectoryPreparationRequest
254///
255/// Controller command distributing exact Directories to one committed top-level Component.
256///
257
258#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
259pub struct RootComponentDirectoryPreparationRequest {
260    pub operation_id: [u8; 32],
261}
262
263///
264/// RootComponentRuntimeActivationRequest
265///
266/// Controller command activating one Directory-prepared top-level Component runtime.
267///
268
269#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
270pub struct RootComponentRuntimeActivationRequest {
271    pub operation_id: [u8; 32],
272}
273
274///
275/// RootComponentMembershipActivationRequest
276///
277/// Controller command activating one runtime-active Component's Registry membership.
278///
279
280#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
281pub struct RootComponentMembershipActivationRequest {
282    pub operation_id: [u8; 32],
283}
284
285///
286/// ComponentProvisioningOrigin
287///
288/// Authenticated causal authority retained with one top-level Component allocation.
289///
290
291#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
292pub enum ComponentProvisioningOrigin {
293    FleetAdministrator { caller: Principal },
294}
295
296///
297/// RootComponentAllocationPhase
298///
299/// Durable root-local progress of one top-level Component allocation operation.
300///
301
302#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
303pub enum RootComponentAllocationPhase {
304    Reserved,
305    CreationIntent,
306    Created,
307    InstallIntent,
308    Installed,
309    Verified,
310    Committed,
311}
312
313///
314/// RootComponentSubtreeRemovalPhase
315///
316/// Durable root-local progress of one child-subtree removal operation.
317///
318
319#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
320pub enum RootComponentSubtreeRemovalPhase {
321    Fenced,
322}
323
324///
325/// ComponentLifecycleStatus
326///
327/// Root-owned runtime lifecycle state of one committed Component Registry member.
328///
329
330#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
331pub enum ComponentLifecycleStatus {
332    Prepared,
333    Active,
334    Draining,
335    Removed,
336}
337
338///
339/// ComponentRegistryHead
340///
341/// Exact independently versioned authority of one Component Registry partition.
342///
343
344#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
345pub struct ComponentRegistryHead {
346    pub component: ComponentInstanceId,
347    pub revision: u64,
348    pub content_hash: [u8; 32],
349}
350
351///
352/// ComponentRegistryPartitionRequest
353///
354/// Read-only lookup key for one committed Component Registry partition.
355///
356
357#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
358pub struct ComponentRegistryPartitionRequest {
359    pub component: ComponentInstanceId,
360}
361
362///
363/// ComponentRegistryPartitionResponse
364///
365/// Protected top-level row and independent head of one Component Registry partition.
366///
367
368#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
369pub struct ComponentRegistryPartitionResponse {
370    pub head: ComponentRegistryHead,
371    pub binding: ComponentBinding,
372    pub provisioning_origin: ComponentProvisioningOrigin,
373    pub release_set: FleetSubnetRootReleaseSet,
374    pub status: ComponentLifecycleStatus,
375    pub reserved_descendants: u32,
376    pub committed_descendants: u32,
377    pub encoded_bytes: u64,
378}
379
380///
381/// ComponentDirectoryProvenance
382///
383/// Exact Component Registry authority from which one Component Directory is derived.
384///
385
386#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
387pub struct ComponentDirectoryProvenance {
388    pub component: ComponentBinding,
389    pub source_fleet_subnet_root: Principal,
390    pub component_registry_revision: u64,
391    pub component_registry_content_hash: [u8; 32],
392    pub synchronized_at_ns: u64,
393}
394
395///
396/// ComponentDirectoryHead
397///
398/// Compact independently versioned discovery projection for one Component tree.
399///
400
401#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
402pub struct ComponentDirectoryHead {
403    pub provenance: ComponentDirectoryProvenance,
404    pub descendant_count: u32,
405}
406
407///
408/// ComponentDirectoryHeadRequest
409///
410/// Read-only lookup key for one committed Component Directory head.
411///
412
413#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
414pub struct ComponentDirectoryHeadRequest {
415    pub component: ComponentInstanceId,
416}
417
418///
419/// ComponentDirectoryPageCursor
420///
421/// Opaque revision- and filter-bound continuation for one bounded Directory page.
422///
423
424#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
425pub struct ComponentDirectoryPageCursor(pub Vec<u8>);
426
427///
428/// ComponentDirectoryPageRequest
429///
430/// Bounded member query against one exact current Component Directory authority.
431///
432
433#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
434pub struct ComponentDirectoryPageRequest {
435    pub directory: ComponentDirectoryHead,
436    pub parent_canister_id: Option<Principal>,
437    pub role: Option<CanisterRole>,
438    pub status: Option<ComponentLifecycleStatus>,
439    pub cursor: Option<ComponentDirectoryPageCursor>,
440    pub limit: u16,
441}
442
443///
444/// ComponentDirectoryChildEntry
445///
446/// One authoritative normalized child projected with its complete protected binding.
447///
448
449#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
450pub struct ComponentDirectoryChildEntry {
451    pub binding: ComponentChildBinding,
452    pub kind: ComponentChildKind,
453    pub installed_artifact_hash: [u8; 32],
454    pub status: ComponentLifecycleStatus,
455}
456
457///
458/// ComponentDirectoryPageResponse
459///
460/// One bounded caller-scoped page under the exact requested Directory head.
461///
462
463#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
464pub struct ComponentDirectoryPageResponse {
465    pub directory: ComponentDirectoryHead,
466    pub entries: Vec<ComponentDirectoryChildEntry>,
467    pub next_cursor: Option<ComponentDirectoryPageCursor>,
468}
469
470///
471/// ComponentRuntimeDirectoryAuthority
472///
473/// Exact Fleet and Component discovery authority retained by one managed Component-tree node.
474///
475
476#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
477pub struct ComponentRuntimeDirectoryAuthority {
478    pub fleet: FleetDirectorySnapshot,
479    pub component: ComponentDirectoryHead,
480}
481
482///
483/// ComponentRuntimeDirectoryPreparationRequest
484///
485/// Root-issued exact Directory preparation command for one managed Component-tree node.
486///
487
488#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
489pub struct ComponentRuntimeDirectoryPreparationRequest {
490    pub operation_id: [u8; 32],
491    pub authority: ComponentRuntimeDirectoryAuthority,
492}
493
494///
495/// ComponentRuntimeDirectorySynchronizationRequest
496///
497/// Root-issued replacement of one active managed Component node's current Directory authority.
498///
499
500#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
501pub struct ComponentRuntimeDirectorySynchronizationRequest {
502    pub operation_id: [u8; 32],
503    pub authority: ComponentRuntimeDirectoryAuthority,
504}
505
506///
507/// ComponentRuntimePhase
508///
509/// Target-local progress from installation through Component runtime activation.
510///
511
512#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
513pub enum ComponentRuntimePhase {
514    AwaitingDirectory,
515    DirectoryPrepared,
516    Active,
517}
518
519///
520/// ComponentRuntimeActivationEvidence
521///
522/// Exact retained Directory authority under which one Component runtime became Active.
523///
524
525#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
526pub struct ComponentRuntimeActivationEvidence {
527    pub directory_authority_hash: [u8; 32],
528    pub activated_at_ns: u64,
529}
530
531///
532/// ComponentRuntimeActivationRequest
533///
534/// Root-issued exact activation command for one Directory-prepared managed Component node.
535///
536
537#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
538pub struct ComponentRuntimeActivationRequest {
539    pub operation_id: [u8; 32],
540    pub directory_authority_hash: [u8; 32],
541}
542
543///
544/// ComponentRuntimeStatusResponse
545///
546/// Independently observable target-local binding and exact retained Directory authority.
547///
548
549#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
550pub struct ComponentRuntimeStatusResponse {
551    pub operation_id: [u8; 32],
552    pub binding: ManagedCanisterBinding,
553    pub phase: ComponentRuntimePhase,
554    pub authority: Option<ComponentRuntimeDirectoryAuthority>,
555    pub authority_hash: Option<[u8; 32]>,
556    pub activation: Option<ComponentRuntimeActivationEvidence>,
557}
558
559///
560/// ComponentRuntimeDirectoryConvergenceEvidence
561///
562/// Stable root evidence that one active member covered at least the required Directory authority.
563///
564
565#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
566pub struct ComponentRuntimeDirectoryConvergenceEvidence {
567    pub operation_id: [u8; 32],
568    pub binding: ManagedCanisterBinding,
569    pub covered_authority: ComponentRuntimeDirectoryAuthority,
570    pub covered_authority_hash: [u8; 32],
571    pub activation: ComponentRuntimeActivationEvidence,
572}
573
574///
575/// RootComponentCreationEvidence
576///
577/// Exact Store artifact and root-owned creation settings frozen before the paid effect.
578///
579
580#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
581pub struct RootComponentCreationEvidence {
582    pub wasm_store: Principal,
583    pub payload_hash: [u8; 32],
584    pub payload_size_bytes: u64,
585    pub initial_cycles: Cycles,
586    pub controller: Principal,
587    pub canister: Option<Principal>,
588}
589
590///
591/// RootComponentInstallEvidence
592///
593/// Exact raw artifact, chunk source and immutable target binding frozen before installation.
594///
595
596#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
597pub struct RootComponentInstallEvidence {
598    pub raw_module_hash: [u8; 32],
599    pub chunk_hashes: Vec<Vec<u8>>,
600    pub binding: ComponentBinding,
601}
602
603///
604/// RootComponentChildInstallEvidence
605///
606/// Exact child module and immutable retained binding frozen before installation.
607///
608
609#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
610pub struct RootComponentChildInstallEvidence {
611    pub raw_module_hash: [u8; 32],
612    pub chunk_hashes: Vec<Vec<u8>>,
613    pub binding: ComponentChildBinding,
614}
615
616///
617/// RootComponentAllocationResponse
618///
619/// Durable identity reservation returned identically for exact operation retry.
620///
621
622#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
623pub struct RootComponentAllocationResponse {
624    pub operation_id: [u8; 32],
625    pub allocation_sequence: u64,
626    pub component: ComponentInstanceId,
627    pub component_spec: ComponentSpecId,
628    pub spec_hash: [u8; 32],
629    pub role: CanisterRole,
630    pub provisioning_origin: ComponentProvisioningOrigin,
631    pub release_set: FleetSubnetRootReleaseSet,
632    pub phase: RootComponentAllocationPhase,
633    pub creation: Option<RootComponentCreationEvidence>,
634    pub installation: Option<RootComponentInstallEvidence>,
635}
636
637///
638/// RootComponentChildAllocationResponse
639///
640/// Durable direct-child lifecycle progress returned identically for exact parent retry.
641///
642
643#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
644pub struct RootComponentChildAllocationResponse {
645    pub operation_id: [u8; 32],
646    pub component: ComponentInstanceId,
647    pub parent_canister_id: Principal,
648    pub parent_role: CanisterRole,
649    pub child_role: CanisterRole,
650    pub child_kind: ComponentChildKind,
651    pub maximum_instances_per_parent: u32,
652    pub maximum_descendants: u32,
653    pub maximum_registry_bytes: u64,
654    pub reserved_against_registry: ComponentRegistryHead,
655    pub release_set: FleetSubnetRootReleaseSet,
656    pub phase: RootComponentAllocationPhase,
657    pub creation: Option<RootComponentCreationEvidence>,
658    pub installation: Option<RootComponentChildInstallEvidence>,
659}
660
661///
662/// RootComponentSubtreeRemovalResponse
663///
664/// Exact durable subtree fence returned identically for operation retry.
665///
666
667#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
668pub struct RootComponentSubtreeRemovalResponse {
669    pub operation_id: [u8; 32],
670    pub component: ComponentInstanceId,
671    pub target_canister_id: Principal,
672    pub target_parent_canister_id: Principal,
673    pub target_role: CanisterRole,
674    pub target_status: ComponentLifecycleStatus,
675    pub reserved_against_registry: ComponentRegistryHead,
676    pub phase: RootComponentSubtreeRemovalPhase,
677}
678
679///
680/// RootComponentChildCommitResponse
681///
682/// Exact committed child operation, authoritative Component Registry and next Directory head.
683///
684
685#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
686pub struct RootComponentChildCommitResponse {
687    pub allocation: RootComponentChildAllocationResponse,
688    pub registry: ComponentRegistryPartitionResponse,
689    pub directory: ComponentDirectoryHead,
690}
691
692///
693/// RootComponentChildDirectoryPreparationResponse
694///
695/// Exact child preparation plus stable bounded active-member Directory coverage.
696///
697
698#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
699pub struct RootComponentChildDirectoryPreparationResponse {
700    pub committed: RootComponentChildCommitResponse,
701    pub child: ComponentRuntimeStatusResponse,
702    pub owning_component: ComponentRuntimeDirectoryConvergenceEvidence,
703    pub parent: Option<ComponentRuntimeDirectoryConvergenceEvidence>,
704}
705
706///
707/// RootComponentChildRuntimeActivationResponse
708///
709/// Exact child commitment plus independently observed Directory-bound runtime activation.
710///
711
712#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
713pub struct RootComponentChildRuntimeActivationResponse {
714    pub committed: RootComponentChildCommitResponse,
715    pub child: ComponentRuntimeStatusResponse,
716}
717
718///
719/// RootComponentChildMembershipActivationResponse
720///
721/// Original child commitment plus active Registry, Directory and target convergence evidence.
722///
723
724#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
725pub struct RootComponentChildMembershipActivationResponse {
726    pub committed: RootComponentChildCommitResponse,
727    pub registry: ComponentRegistryPartitionResponse,
728    pub directory: ComponentDirectoryHead,
729    pub child: ComponentRuntimeStatusResponse,
730}
731
732///
733/// RootComponentCommitResponse
734///
735/// Exact committed allocation, authoritative Registry row and derived Directory head.
736///
737
738#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
739pub struct RootComponentCommitResponse {
740    pub allocation: RootComponentAllocationResponse,
741    pub registry: ComponentRegistryPartitionResponse,
742    pub directory: ComponentDirectoryHead,
743}
744
745///
746/// RootComponentDirectoryPreparationResponse
747///
748/// Exact root authority plus independently observed target-local Directory preparation.
749///
750
751#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
752pub struct RootComponentDirectoryPreparationResponse {
753    pub committed: RootComponentCommitResponse,
754    pub target: ComponentRuntimeStatusResponse,
755}
756
757///
758/// RootComponentRuntimeActivationResponse
759///
760/// Exact root authority plus independently observed target-local runtime activation.
761///
762
763#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
764pub struct RootComponentRuntimeActivationResponse {
765    pub committed: RootComponentCommitResponse,
766    pub target: ComponentRuntimeStatusResponse,
767}
768
769///
770/// RootComponentMembershipActivationResponse
771///
772/// Exact active Registry authority plus independently observed current target Directory.
773///
774
775#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
776pub struct RootComponentMembershipActivationResponse {
777    pub allocation: RootComponentAllocationResponse,
778    pub registry: ComponentRegistryPartitionResponse,
779    pub directory: ComponentDirectoryHead,
780    pub target: ComponentRuntimeStatusResponse,
781}
782
783#[cfg(test)]
784mod tests {
785    use super::*;
786    use crate::{
787        dto::root_store::RootStoreBootstrapRequest,
788        ids::{
789            AppId, CanonicalNetworkId, FleetCoordinatorBinding, FleetId, FleetKey,
790            FleetRegistryAuthority, ReleaseBuildId, ReleaseBuildNonce, ReleaseSetDigest, SubnetId,
791        },
792    };
793
794    #[test]
795    fn component_registry_contracts_round_trip_through_candid() {
796        let request = RootComponentRegistryPreparationRequest {
797            store_bootstrap: RootStoreBootstrapRequest {
798                manifest_payload_size_bytes: 128,
799            },
800            expected_fleet_registry: FleetRegistryVersion {
801                authority: fleet_registry_authority(),
802                revision: 4,
803                content_hash: [5; 32],
804            },
805        };
806        let response = RootComponentRegistryStatusResponse {
807            fleet_subnet_root: Principal::from_slice(&[6; 29]),
808            prepared_against_registry: request.expected_fleet_registry.clone(),
809            release_set: FleetSubnetRootReleaseSet {
810                release_build_id: ReleaseBuildId::from_nonce(ReleaseBuildNonce::from_random_bytes(
811                    [7; 32],
812                )),
813                manifest_digest: ReleaseSetDigest::from_bytes([8; 32]),
814            },
815            component_topology_digest: ComponentTopologyDigest::from_bytes([9; 32]),
816            next_allocation_sequence: 1,
817            reserved_component_instances: 0,
818            committed_component_instances: 0,
819            managed_descendants: 0,
820            known_created_component_canisters: 0,
821            encoded_bytes: 0,
822            initial_inventory: Some(RootComponentInitialInventoryStatus {
823                fleet_activation_operation_id: [10; 32],
824                component_count: 0,
825                inventory_hash: [11; 32],
826                sealed_at_ns: 12,
827                directories_converged: true,
828                root_runtime_activated: true,
829            }),
830        };
831        let allocation = RootComponentAllocationResponse {
832            operation_id: [10; 32],
833            allocation_sequence: 1,
834            component: ComponentInstanceId::from_generated_bytes([11; 32]),
835            component_spec: "projects".parse().expect("Component Spec ID"),
836            spec_hash: [12; 32],
837            role: CanisterRole::new("project_hub"),
838            provisioning_origin: ComponentProvisioningOrigin::FleetAdministrator {
839                caller: Principal::from_slice(&[13; 29]),
840            },
841            release_set: response.release_set,
842            phase: RootComponentAllocationPhase::Reserved,
843            creation: None,
844            installation: None,
845        };
846        let created = RootComponentAllocationResponse {
847            phase: RootComponentAllocationPhase::Created,
848            creation: Some(RootComponentCreationEvidence {
849                wasm_store: Principal::from_slice(&[14; 29]),
850                payload_hash: [15; 32],
851                payload_size_bytes: 4_096,
852                initial_cycles: Cycles::new(5_000_000_000_000),
853                controller: Principal::from_slice(&[6; 29]),
854                canister: Some(Principal::from_slice(&[16; 29])),
855            }),
856            installation: None,
857            ..allocation.clone()
858        };
859        let request_bytes = candid::encode_one(&request).expect("encode request");
860        let response_bytes = candid::encode_one(&response).expect("encode response");
861        let allocation_bytes = candid::encode_one(&allocation).expect("encode allocation");
862        let created_bytes = candid::encode_one(&created).expect("encode created allocation");
863
864        assert_eq!(
865            candid::decode_one::<RootComponentRegistryPreparationRequest>(&request_bytes)
866                .expect("decode request"),
867            request
868        );
869        assert_eq!(
870            candid::decode_one::<RootComponentRegistryStatusResponse>(&response_bytes)
871                .expect("decode response"),
872            response
873        );
874        assert_eq!(
875            candid::decode_one::<RootComponentAllocationResponse>(&allocation_bytes)
876                .expect("decode allocation"),
877            allocation
878        );
879        assert_eq!(
880            candid::decode_one::<RootComponentAllocationResponse>(&created_bytes)
881                .expect("decode created allocation"),
882            created
883        );
884    }
885
886    #[test]
887    fn component_commit_response_round_trips_through_candid() {
888        let root = Principal::from_slice(&[6; 29]);
889        let component = ComponentInstanceId::from_generated_bytes([11; 32]);
890        let component_spec: ComponentSpecId = "projects".parse().expect("Component Spec ID");
891        let release_set = FleetSubnetRootReleaseSet {
892            release_build_id: ReleaseBuildId::from_nonce(ReleaseBuildNonce::from_random_bytes(
893                [7; 32],
894            )),
895            manifest_digest: ReleaseSetDigest::from_bytes([8; 32]),
896        };
897        let provisioning_origin = ComponentProvisioningOrigin::FleetAdministrator {
898            caller: Principal::from_slice(&[13; 29]),
899        };
900        let binding = ComponentBinding {
901            authority: fleet_registry_authority(),
902            component,
903            component_spec: component_spec.clone(),
904            spec_hash: [12; 32],
905            role: CanisterRole::new("project_hub"),
906            placement_subnet: SubnetId::from_principal(Principal::from_slice(&[17; 29])),
907            fleet_subnet_root: root,
908            canister_id: Principal::from_slice(&[16; 29]),
909        };
910        let head = ComponentRegistryHead {
911            component,
912            revision: 1,
913            content_hash: [18; 32],
914        };
915        let committed = RootComponentCommitResponse {
916            allocation: RootComponentAllocationResponse {
917                operation_id: [10; 32],
918                allocation_sequence: 1,
919                component,
920                component_spec,
921                spec_hash: binding.spec_hash,
922                role: binding.role.clone(),
923                provisioning_origin: provisioning_origin.clone(),
924                release_set,
925                phase: RootComponentAllocationPhase::Committed,
926                creation: Some(RootComponentCreationEvidence {
927                    wasm_store: Principal::from_slice(&[14; 29]),
928                    payload_hash: [15; 32],
929                    payload_size_bytes: 4_096,
930                    initial_cycles: Cycles::new(5_000_000_000_000),
931                    controller: root,
932                    canister: Some(binding.canister_id),
933                }),
934                installation: Some(RootComponentInstallEvidence {
935                    raw_module_hash: [20; 32],
936                    chunk_hashes: vec![vec![21; 32]],
937                    binding: binding.clone(),
938                }),
939            },
940            registry: ComponentRegistryPartitionResponse {
941                head: head.clone(),
942                binding: binding.clone(),
943                provisioning_origin,
944                release_set,
945                status: ComponentLifecycleStatus::Prepared,
946                reserved_descendants: 0,
947                committed_descendants: 0,
948                encoded_bytes: 2_048,
949            },
950            directory: ComponentDirectoryHead {
951                provenance: ComponentDirectoryProvenance {
952                    component: binding,
953                    source_fleet_subnet_root: root,
954                    component_registry_revision: head.revision,
955                    component_registry_content_hash: head.content_hash,
956                    synchronized_at_ns: 19,
957                },
958                descendant_count: 0,
959            },
960        };
961        let committed_bytes = candid::encode_one(&committed).expect("encode committed allocation");
962
963        assert_eq!(
964            candid::decode_one::<RootComponentCommitResponse>(&committed_bytes)
965                .expect("decode committed allocation"),
966            committed
967        );
968    }
969
970    #[test]
971    fn component_directory_page_contracts_round_trip_through_candid() {
972        let root = Principal::from_slice(&[6; 29]);
973        let component = ComponentInstanceId::from_generated_bytes([11; 32]);
974        let binding = ComponentBinding {
975            authority: fleet_registry_authority(),
976            component,
977            component_spec: "projects".parse().expect("Component Spec ID"),
978            spec_hash: [12; 32],
979            role: CanisterRole::new("project_hub"),
980            placement_subnet: SubnetId::from_principal(Principal::from_slice(&[17; 29])),
981            fleet_subnet_root: root,
982            canister_id: Principal::from_slice(&[16; 29]),
983        };
984        let directory = ComponentDirectoryHead {
985            provenance: ComponentDirectoryProvenance {
986                component: binding.clone(),
987                source_fleet_subnet_root: root,
988                component_registry_revision: 3,
989                component_registry_content_hash: [18; 32],
990                synchronized_at_ns: 19,
991            },
992            descendant_count: 1,
993        };
994        let request = ComponentDirectoryPageRequest {
995            directory: directory.clone(),
996            parent_canister_id: Some(binding.canister_id),
997            role: Some(CanisterRole::new("project_instance")),
998            status: Some(ComponentLifecycleStatus::Active),
999            cursor: Some(ComponentDirectoryPageCursor(vec![20; 64])),
1000            limit: 50,
1001        };
1002        let response = ComponentDirectoryPageResponse {
1003            directory,
1004            entries: vec![ComponentDirectoryChildEntry {
1005                binding: ComponentChildBinding {
1006                    component: binding.clone(),
1007                    parent_canister_id: binding.canister_id,
1008                    role: CanisterRole::new("project_instance"),
1009                    canister_id: Principal::from_slice(&[21; 29]),
1010                },
1011                kind: ComponentChildKind::Instance,
1012                installed_artifact_hash: [22; 32],
1013                status: ComponentLifecycleStatus::Active,
1014            }],
1015            next_cursor: Some(ComponentDirectoryPageCursor(vec![23; 64])),
1016        };
1017        let request_bytes = candid::encode_one(&request).expect("encode Directory page request");
1018        let response_bytes = candid::encode_one(&response).expect("encode Directory page response");
1019
1020        assert_eq!(
1021            candid::decode_one::<ComponentDirectoryPageRequest>(&request_bytes)
1022                .expect("decode Directory page request"),
1023            request
1024        );
1025        assert_eq!(
1026            candid::decode_one::<ComponentDirectoryPageResponse>(&response_bytes)
1027                .expect("decode Directory page response"),
1028            response
1029        );
1030    }
1031
1032    fn fleet_registry_authority() -> FleetRegistryAuthority {
1033        FleetRegistryAuthority {
1034            binding: FleetCoordinatorBinding {
1035                fleet: crate::ids::FleetBinding {
1036                    fleet: FleetKey {
1037                        canonical_network_id: CanonicalNetworkId::ic_mainnet(),
1038                        fleet_id: FleetId::from_generated_bytes([1; 32]),
1039                    },
1040                    app: AppId::from("toko"),
1041                },
1042                coordinator_subnet: SubnetId::from_principal(Principal::from_slice(&[2; 29])),
1043                coordinator: Principal::from_slice(&[3; 29]),
1044            },
1045            epoch: 1,
1046        }
1047    }
1048
1049    #[test]
1050    fn component_creation_request_round_trips_through_candid() {
1051        let request = RootComponentCreationRequest {
1052            operation_id: [10; 32],
1053        };
1054        let bytes = candid::encode_one(request).expect("encode creation request");
1055
1056        assert_eq!(
1057            candid::decode_one::<RootComponentCreationRequest>(&bytes)
1058                .expect("decode creation request"),
1059            request
1060        );
1061    }
1062
1063    #[test]
1064    fn component_subtree_removal_contracts_round_trip_through_candid() {
1065        let component = ComponentInstanceId::from_generated_bytes([41; 32]);
1066        let registry = ComponentRegistryHead {
1067            component,
1068            revision: 7,
1069            content_hash: [42; 32],
1070        };
1071        let request = RootComponentSubtreeRemovalRequest {
1072            operation_id: [43; 32],
1073            component,
1074            target_canister_id: Principal::from_slice(&[44; 29]),
1075            expected_registry: registry.clone(),
1076        };
1077        let status_request = RootComponentSubtreeRemovalStatusRequest {
1078            operation_id: request.operation_id,
1079            component,
1080        };
1081        let response = RootComponentSubtreeRemovalResponse {
1082            operation_id: request.operation_id,
1083            component,
1084            target_canister_id: request.target_canister_id,
1085            target_parent_canister_id: Principal::from_slice(&[45; 29]),
1086            target_role: CanisterRole::new("project_instance"),
1087            target_status: ComponentLifecycleStatus::Active,
1088            reserved_against_registry: registry,
1089            phase: RootComponentSubtreeRemovalPhase::Fenced,
1090        };
1091
1092        let request_bytes = candid::encode_one(&request).expect("encode subtree removal request");
1093        let status_bytes =
1094            candid::encode_one(status_request).expect("encode subtree removal status request");
1095        let response_bytes =
1096            candid::encode_one(&response).expect("encode subtree removal response");
1097
1098        assert_eq!(
1099            candid::decode_one::<RootComponentSubtreeRemovalRequest>(&request_bytes)
1100                .expect("decode subtree removal request"),
1101            request
1102        );
1103        assert_eq!(
1104            candid::decode_one::<RootComponentSubtreeRemovalStatusRequest>(&status_bytes)
1105                .expect("decode subtree removal status request"),
1106            status_request
1107        );
1108        assert_eq!(
1109            candid::decode_one::<RootComponentSubtreeRemovalResponse>(&response_bytes)
1110                .expect("decode subtree removal response"),
1111            response
1112        );
1113    }
1114
1115    #[test]
1116    #[expect(
1117        clippy::too_many_lines,
1118        reason = "one round-trip test keeps the complete child lifecycle boundary coherent"
1119    )]
1120    fn component_child_lifecycle_contracts_round_trip_through_candid() {
1121        let component = ComponentInstanceId::from_generated_bytes([11; 32]);
1122        let registry = ComponentRegistryHead {
1123            component,
1124            revision: 2,
1125            content_hash: [12; 32],
1126        };
1127        let request = RootComponentChildAllocationRequest {
1128            operation_id: [13; 32],
1129            component,
1130            expected_registry: registry.clone(),
1131            child_role: CanisterRole::new("project_instance"),
1132        };
1133        let status_request = RootComponentChildAllocationStatusRequest {
1134            operation_id: request.operation_id,
1135            component,
1136        };
1137        let creation_request = RootComponentChildCreationRequest {
1138            operation_id: request.operation_id,
1139            component,
1140        };
1141        let install_request = RootComponentChildInstallRequest {
1142            operation_id: request.operation_id,
1143            component,
1144        };
1145        let commit_request = RootComponentChildCommitRequest {
1146            operation_id: request.operation_id,
1147            component,
1148        };
1149        let directory_request = RootComponentChildDirectoryPreparationRequest {
1150            operation_id: request.operation_id,
1151            component,
1152        };
1153        let activation_request = RootComponentChildRuntimeActivationRequest {
1154            operation_id: request.operation_id,
1155            component,
1156        };
1157        let membership_request = RootComponentChildMembershipActivationRequest {
1158            operation_id: request.operation_id,
1159            component,
1160        };
1161        let root = Principal::from_slice(&[17; 29]);
1162        let parent = Principal::from_slice(&[14; 29]);
1163        let child = Principal::from_slice(&[18; 29]);
1164        let child_binding = ComponentChildBinding {
1165            component: ComponentBinding {
1166                authority: fleet_registry_authority(),
1167                component,
1168                component_spec: "projects".parse().expect("Component Spec"),
1169                spec_hash: [19; 32],
1170                role: CanisterRole::new("project_hub"),
1171                placement_subnet: SubnetId::from_principal(Principal::from_slice(&[20; 29])),
1172                fleet_subnet_root: root,
1173                canister_id: parent,
1174            },
1175            parent_canister_id: parent,
1176            role: request.child_role.clone(),
1177            canister_id: child,
1178        };
1179        let response = RootComponentChildAllocationResponse {
1180            operation_id: request.operation_id,
1181            component,
1182            parent_canister_id: parent,
1183            parent_role: CanisterRole::new("project_hub"),
1184            child_role: request.child_role.clone(),
1185            child_kind: ComponentChildKind::Instance,
1186            maximum_instances_per_parent: 10_000,
1187            maximum_descendants: 20_000,
1188            maximum_registry_bytes: 16_777_216,
1189            reserved_against_registry: registry,
1190            release_set: FleetSubnetRootReleaseSet {
1191                release_build_id: ReleaseBuildId::from_nonce(ReleaseBuildNonce::from_random_bytes(
1192                    [15; 32],
1193                )),
1194                manifest_digest: ReleaseSetDigest::from_bytes([16; 32]),
1195            },
1196            phase: RootComponentAllocationPhase::Verified,
1197            creation: Some(RootComponentCreationEvidence {
1198                wasm_store: Principal::from_slice(&[21; 29]),
1199                payload_hash: [22; 32],
1200                payload_size_bytes: 4_096,
1201                initial_cycles: Cycles::new(5_000_000_000_000),
1202                controller: root,
1203                canister: Some(child),
1204            }),
1205            installation: Some(RootComponentChildInstallEvidence {
1206                raw_module_hash: [23; 32],
1207                chunk_hashes: vec![vec![24; 32]],
1208                binding: child_binding.clone(),
1209            }),
1210        };
1211        let commit_response = RootComponentChildCommitResponse {
1212            allocation: response.clone(),
1213            registry: ComponentRegistryPartitionResponse {
1214                head: ComponentRegistryHead {
1215                    component,
1216                    revision: 3,
1217                    content_hash: [25; 32],
1218                },
1219                binding: child_binding.component.clone(),
1220                provisioning_origin: ComponentProvisioningOrigin::FleetAdministrator {
1221                    caller: Principal::from_slice(&[26; 29]),
1222                },
1223                release_set: response.release_set,
1224                status: ComponentLifecycleStatus::Active,
1225                reserved_descendants: 0,
1226                committed_descendants: 1,
1227                encoded_bytes: 8_192,
1228            },
1229            directory: ComponentDirectoryHead {
1230                provenance: ComponentDirectoryProvenance {
1231                    component: child_binding.component.clone(),
1232                    source_fleet_subnet_root: root,
1233                    component_registry_revision: 3,
1234                    component_registry_content_hash: [25; 32],
1235                    synchronized_at_ns: 27,
1236                },
1237                descendant_count: 1,
1238            },
1239        };
1240        let runtime_authority = ComponentRuntimeDirectoryAuthority {
1241            fleet: FleetDirectorySnapshot {
1242                provenance: crate::dto::fleet_registry::FleetDirectoryProvenance {
1243                    registry: FleetRegistryVersion {
1244                        authority: fleet_registry_authority(),
1245                        revision: 4,
1246                        content_hash: [28; 32],
1247                    },
1248                    source_fleet_subnet_root: root,
1249                },
1250                fleet_subnet_roots: vec![
1251                    crate::dto::fleet_registry::FleetSubnetRootDirectoryEntry {
1252                        placement_subnet: commit_response.registry.binding.placement_subnet,
1253                        fleet_subnet_root: root,
1254                        status: crate::dto::fleet_registry::FleetSubnetRootStatus::Active,
1255                    },
1256                ],
1257            },
1258            component: commit_response.directory.clone(),
1259        };
1260        let activation = ComponentRuntimeActivationEvidence {
1261            directory_authority_hash: [29; 32],
1262            activated_at_ns: 30,
1263        };
1264        let directory_response = RootComponentChildDirectoryPreparationResponse {
1265            committed: commit_response.clone(),
1266            child: ComponentRuntimeStatusResponse {
1267                operation_id: request.operation_id,
1268                binding: ManagedCanisterBinding::ComponentChild(child_binding.clone()),
1269                phase: ComponentRuntimePhase::DirectoryPrepared,
1270                authority: Some(runtime_authority.clone()),
1271                authority_hash: Some([31; 32]),
1272                activation: None,
1273            },
1274            owning_component: ComponentRuntimeDirectoryConvergenceEvidence {
1275                operation_id: [32; 32],
1276                binding: ManagedCanisterBinding::Component(child_binding.component.clone()),
1277                covered_authority: runtime_authority.clone(),
1278                covered_authority_hash: [31; 32],
1279                activation,
1280            },
1281            parent: None,
1282        };
1283        let activation_response = RootComponentChildRuntimeActivationResponse {
1284            committed: commit_response.clone(),
1285            child: ComponentRuntimeStatusResponse {
1286                operation_id: request.operation_id,
1287                binding: ManagedCanisterBinding::ComponentChild(child_binding.clone()),
1288                phase: ComponentRuntimePhase::Active,
1289                authority: Some(runtime_authority.clone()),
1290                authority_hash: Some([31; 32]),
1291                activation: Some(ComponentRuntimeActivationEvidence {
1292                    directory_authority_hash: [31; 32],
1293                    activated_at_ns: 33,
1294                }),
1295            },
1296        };
1297        let active_directory = ComponentDirectoryHead {
1298            provenance: ComponentDirectoryProvenance {
1299                component: child_binding.component.clone(),
1300                source_fleet_subnet_root: root,
1301                component_registry_revision: 4,
1302                component_registry_content_hash: [34; 32],
1303                synchronized_at_ns: 35,
1304            },
1305            descendant_count: 1,
1306        };
1307        let active_authority = ComponentRuntimeDirectoryAuthority {
1308            fleet: runtime_authority.fleet,
1309            component: active_directory.clone(),
1310        };
1311        let membership_response = RootComponentChildMembershipActivationResponse {
1312            committed: commit_response.clone(),
1313            registry: ComponentRegistryPartitionResponse {
1314                head: ComponentRegistryHead {
1315                    component,
1316                    revision: 4,
1317                    content_hash: [34; 32],
1318                },
1319                binding: child_binding.component.clone(),
1320                provisioning_origin: commit_response.registry.provisioning_origin.clone(),
1321                release_set: commit_response.registry.release_set,
1322                status: ComponentLifecycleStatus::Active,
1323                reserved_descendants: 0,
1324                committed_descendants: 1,
1325                encoded_bytes: 8_256,
1326            },
1327            directory: active_directory,
1328            child: ComponentRuntimeStatusResponse {
1329                operation_id: request.operation_id,
1330                binding: ManagedCanisterBinding::ComponentChild(child_binding),
1331                phase: ComponentRuntimePhase::Active,
1332                authority: Some(active_authority),
1333                authority_hash: Some([36; 32]),
1334                activation: Some(ComponentRuntimeActivationEvidence {
1335                    directory_authority_hash: [31; 32],
1336                    activated_at_ns: 33,
1337                }),
1338            },
1339        };
1340
1341        let request_bytes = candid::encode_one(&request).expect("encode child reservation");
1342        let status_bytes =
1343            candid::encode_one(status_request).expect("encode child reservation status");
1344        let creation_bytes =
1345            candid::encode_one(creation_request).expect("encode child creation request");
1346        let install_bytes =
1347            candid::encode_one(install_request).expect("encode child install request");
1348        let response_bytes = candid::encode_one(&response).expect("encode child response");
1349        let commit_request_bytes =
1350            candid::encode_one(commit_request).expect("encode child commit request");
1351        let directory_request_bytes =
1352            candid::encode_one(directory_request).expect("encode child Directory request");
1353        let activation_request_bytes =
1354            candid::encode_one(activation_request).expect("encode child activation request");
1355        let membership_request_bytes =
1356            candid::encode_one(membership_request).expect("encode child membership request");
1357        let commit_response_bytes =
1358            candid::encode_one(&commit_response).expect("encode child commit response");
1359        let directory_response_bytes =
1360            candid::encode_one(&directory_response).expect("encode child Directory response");
1361        let activation_response_bytes =
1362            candid::encode_one(&activation_response).expect("encode child activation response");
1363        let membership_response_bytes =
1364            candid::encode_one(&membership_response).expect("encode child membership response");
1365
1366        assert_eq!(
1367            candid::decode_one::<RootComponentChildAllocationRequest>(&request_bytes)
1368                .expect("decode child reservation"),
1369            request
1370        );
1371        assert_eq!(
1372            candid::decode_one::<RootComponentChildAllocationStatusRequest>(&status_bytes)
1373                .expect("decode child reservation status"),
1374            status_request
1375        );
1376        assert_eq!(
1377            candid::decode_one::<RootComponentChildCreationRequest>(&creation_bytes)
1378                .expect("decode child creation request"),
1379            creation_request
1380        );
1381        assert_eq!(
1382            candid::decode_one::<RootComponentChildInstallRequest>(&install_bytes)
1383                .expect("decode child install request"),
1384            install_request
1385        );
1386        assert_eq!(
1387            candid::decode_one::<RootComponentChildAllocationResponse>(&response_bytes)
1388                .expect("decode child response"),
1389            response
1390        );
1391        assert_eq!(
1392            candid::decode_one::<RootComponentChildCommitRequest>(&commit_request_bytes)
1393                .expect("decode child commit request"),
1394            commit_request
1395        );
1396        assert_eq!(
1397            candid::decode_one::<RootComponentChildDirectoryPreparationRequest>(
1398                &directory_request_bytes
1399            )
1400            .expect("decode child Directory request"),
1401            directory_request
1402        );
1403        assert_eq!(
1404            candid::decode_one::<RootComponentChildCommitResponse>(&commit_response_bytes)
1405                .expect("decode child commit response"),
1406            commit_response
1407        );
1408        assert_eq!(
1409            candid::decode_one::<RootComponentChildDirectoryPreparationResponse>(
1410                &directory_response_bytes
1411            )
1412            .expect("decode child Directory response"),
1413            directory_response
1414        );
1415        assert_eq!(
1416            candid::decode_one::<RootComponentChildRuntimeActivationRequest>(
1417                &activation_request_bytes
1418            )
1419            .expect("decode child activation request"),
1420            activation_request
1421        );
1422        assert_eq!(
1423            candid::decode_one::<RootComponentChildRuntimeActivationResponse>(
1424                &activation_response_bytes
1425            )
1426            .expect("decode child activation response"),
1427            activation_response
1428        );
1429        assert_eq!(
1430            candid::decode_one::<RootComponentChildMembershipActivationRequest>(
1431                &membership_request_bytes
1432            )
1433            .expect("decode child membership request"),
1434            membership_request
1435        );
1436        assert_eq!(
1437            candid::decode_one::<RootComponentChildMembershipActivationResponse>(
1438                &membership_response_bytes
1439            )
1440            .expect("decode child membership response"),
1441            membership_response
1442        );
1443    }
1444
1445    #[test]
1446    fn component_install_request_round_trips_through_candid() {
1447        let request = RootComponentInstallRequest {
1448            operation_id: [10; 32],
1449        };
1450        let bytes = candid::encode_one(request).expect("encode install request");
1451
1452        assert_eq!(
1453            candid::decode_one::<RootComponentInstallRequest>(&bytes)
1454                .expect("decode install request"),
1455            request
1456        );
1457    }
1458
1459    #[test]
1460    fn component_commit_request_round_trips_through_candid() {
1461        let request = RootComponentCommitRequest {
1462            operation_id: [10; 32],
1463        };
1464        let bytes = candid::encode_one(request).expect("encode commit request");
1465
1466        assert_eq!(
1467            candid::decode_one::<RootComponentCommitRequest>(&bytes)
1468                .expect("decode commit request"),
1469            request
1470        );
1471    }
1472
1473    #[test]
1474    fn component_runtime_activation_requests_round_trip_through_candid() {
1475        let root_request = RootComponentRuntimeActivationRequest {
1476            operation_id: [22; 32],
1477        };
1478        let target_request = ComponentRuntimeActivationRequest {
1479            operation_id: root_request.operation_id,
1480            directory_authority_hash: [23; 32],
1481        };
1482        let membership_request = RootComponentMembershipActivationRequest {
1483            operation_id: root_request.operation_id,
1484        };
1485        let root_bytes = candid::encode_one(root_request).expect("encode root activation request");
1486        let target_bytes =
1487            candid::encode_one(target_request).expect("encode target activation request");
1488        let membership_bytes =
1489            candid::encode_one(membership_request).expect("encode membership activation request");
1490
1491        assert_eq!(
1492            candid::decode_one::<RootComponentRuntimeActivationRequest>(&root_bytes)
1493                .expect("decode root activation request"),
1494            root_request
1495        );
1496        assert_eq!(
1497            candid::decode_one::<ComponentRuntimeActivationRequest>(&target_bytes)
1498                .expect("decode target activation request"),
1499            target_request
1500        );
1501        assert_eq!(
1502            candid::decode_one::<RootComponentMembershipActivationRequest>(&membership_bytes)
1503                .expect("decode membership activation request"),
1504            membership_request
1505        );
1506    }
1507}