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        component_deployment::ProtectedComponentDeployment,
12        fleet_registry::{FleetDirectorySnapshot, FleetRegistryVersion},
13        root_store::RootStoreBootstrapRequest,
14    },
15    ids::{
16        CanisterRole, ComponentBinding, ComponentChildBinding, ComponentGroupMemberPath,
17        ComponentGroupPlacementId, ComponentInstanceId, ComponentSpecId, ComponentTopologyDigest,
18        FleetSubnetRootReleaseSet, ManagedCanisterBinding,
19    },
20};
21use candid::{CandidType, Principal};
22use serde::{Deserialize, Serialize};
23
24///
25/// RootComponentRegistryPreparationRequest
26///
27/// Exact authority required before an empty root-local Component Registry may be prepared.
28///
29
30#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
31pub struct RootComponentRegistryPreparationRequest {
32    pub store_bootstrap: RootStoreBootstrapRequest,
33    pub expected_fleet_registry: FleetRegistryVersion,
34}
35
36///
37/// RootComponentInitialInventoryStatus
38///
39/// Durable initial Component inventory sealed for one Fleet Subnet Root activation.
40///
41
42#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
43pub struct RootComponentInitialInventoryStatus {
44    pub fleet_activation_operation_id: [u8; 32],
45    pub component_count: u32,
46    pub inventory_hash: [u8; 32],
47    pub sealed_at_ns: u64,
48    pub directories_converged: bool,
49    pub root_runtime_activated: bool,
50}
51
52///
53/// RootComponentRegistryStatusResponse
54///
55/// Compact durable Component Registry authority and current allocation counters.
56///
57
58#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
59pub struct RootComponentRegistryStatusResponse {
60    pub fleet_subnet_root: Principal,
61    pub prepared_against_registry: FleetRegistryVersion,
62    pub release_set: FleetSubnetRootReleaseSet,
63    pub component_topology_digest: ComponentTopologyDigest,
64    pub next_allocation_sequence: u64,
65    pub reserved_component_instances: u32,
66    pub committed_component_instances: u32,
67    pub managed_descendants: u32,
68    pub known_created_component_canisters: u32,
69    pub encoded_bytes: u64,
70    pub initial_inventory: Option<RootComponentInitialInventoryStatus>,
71}
72
73///
74/// RootComponentAllocationRequest
75///
76/// Controller command naming one idempotent top-level Component reservation intent.
77///
78
79#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
80pub struct RootComponentAllocationRequest {
81    pub operation_id: [u8; 32],
82    pub component_spec: ComponentSpecId,
83}
84
85///
86/// RootComponentAllocationStatusRequest
87///
88/// Read-only lookup key for one durable top-level Component allocation operation.
89///
90
91#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
92pub struct RootComponentAllocationStatusRequest {
93    pub operation_id: [u8; 32],
94}
95
96///
97/// RootComponentChildAllocationRequest
98///
99/// Parent command naming one idempotent direct-child reservation intent.
100///
101
102#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
103pub struct RootComponentChildAllocationRequest {
104    pub operation_id: [u8; 32],
105    pub component: ComponentInstanceId,
106    pub expected_registry: ComponentRegistryHead,
107    pub child_role: CanisterRole,
108    pub application_init_args: Option<Vec<u8>>,
109}
110
111///
112/// RootComponentChildAllocationStatusRequest
113///
114/// Parent lookup key for one durable direct-child reservation.
115///
116
117#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
118pub struct RootComponentChildAllocationStatusRequest {
119    pub operation_id: [u8; 32],
120    pub component: ComponentInstanceId,
121}
122
123///
124/// RootComponentSubtreeRemovalRequest
125///
126/// Controller command durably fencing one registered child subtree.
127///
128
129#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
130pub struct RootComponentSubtreeRemovalRequest {
131    pub operation_id: [u8; 32],
132    pub component: ComponentInstanceId,
133    pub target_canister_id: Principal,
134    pub expected_registry: ComponentRegistryHead,
135}
136
137///
138/// RootComponentSubtreeRemovalAdvanceRequest
139///
140/// Controller command advancing bounded traversal from one observed durable step.
141///
142
143#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
144pub struct RootComponentSubtreeRemovalAdvanceRequest {
145    pub operation_id: [u8; 32],
146    pub component: ComponentInstanceId,
147    pub expected_traversal_steps: u32,
148}
149
150///
151/// RootComponentSubtreeRemovalStopPreparationRequest
152///
153/// Controller command freezing the exact selected leaf and root stop authority.
154///
155
156#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
157pub struct RootComponentSubtreeRemovalStopPreparationRequest {
158    pub operation_id: [u8; 32],
159    pub component: ComponentInstanceId,
160    pub expected_traversal_steps: u32,
161    pub expected_leaf_canister_id: Principal,
162    pub expected_leaf_parent_canister_id: Principal,
163}
164
165///
166/// RootComponentSubtreeRemovalStopRequest
167///
168/// Controller command reconciling and stopping one exactly prepared leaf.
169///
170
171#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
172pub struct RootComponentSubtreeRemovalStopRequest {
173    pub operation_id: [u8; 32],
174    pub component: ComponentInstanceId,
175    pub expected_traversal_steps: u32,
176    pub expected_leaf_canister_id: Principal,
177    pub expected_leaf_parent_canister_id: Principal,
178}
179
180///
181/// RootComponentSubtreeRemovalDeletePreparationRequest
182///
183/// Controller command freezing exact deletion authority from one stopped receipt.
184///
185
186#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
187pub struct RootComponentSubtreeRemovalDeletePreparationRequest {
188    pub operation_id: [u8; 32],
189    pub component: ComponentInstanceId,
190    pub expected_traversal_steps: u32,
191    pub expected_leaf_canister_id: Principal,
192    pub expected_leaf_parent_canister_id: Principal,
193}
194
195///
196/// RootComponentSubtreeRemovalDeleteRequest
197///
198/// Controller command reconciling and deleting one exactly prepared leaf.
199///
200
201#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
202pub struct RootComponentSubtreeRemovalDeleteRequest {
203    pub operation_id: [u8; 32],
204    pub component: ComponentInstanceId,
205    pub expected_traversal_steps: u32,
206    pub expected_leaf_canister_id: Principal,
207    pub expected_leaf_parent_canister_id: Principal,
208}
209
210///
211/// RootComponentSubtreeRemovalMembershipRemovalRequest
212///
213/// Controller command removing one independently deleted leaf from Registry membership.
214///
215
216#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
217pub struct RootComponentSubtreeRemovalMembershipRemovalRequest {
218    pub operation_id: [u8; 32],
219    pub component: ComponentInstanceId,
220    pub expected_traversal_steps: u32,
221    pub expected_leaf_canister_id: Principal,
222    pub expected_leaf_parent_canister_id: Principal,
223}
224
225///
226/// RootComponentSubtreeRemovalDirectorySynchronizationRequest
227///
228/// Controller command converging the post-removal Directory on surviving members.
229///
230
231#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
232pub struct RootComponentSubtreeRemovalDirectorySynchronizationRequest {
233    pub operation_id: [u8; 32],
234    pub component: ComponentInstanceId,
235    pub expected_traversal_steps: u32,
236    pub expected_leaf_canister_id: Principal,
237    pub expected_leaf_parent_canister_id: Principal,
238}
239
240///
241/// RootComponentSubtreeRemovalLeafFinalizationRequest
242///
243/// Controller command archiving one completed leaf and resuming its retained parent.
244///
245
246#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
247pub struct RootComponentSubtreeRemovalLeafFinalizationRequest {
248    pub operation_id: [u8; 32],
249    pub component: ComponentInstanceId,
250    pub expected_traversal_steps: u32,
251    pub expected_leaf_canister_id: Principal,
252    pub expected_leaf_parent_canister_id: Principal,
253}
254
255///
256/// RootComponentSubtreeRemovalStatusRequest
257///
258/// Controller lookup key for one durable child-subtree removal operation.
259///
260
261#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
262pub struct RootComponentSubtreeRemovalStatusRequest {
263    pub operation_id: [u8; 32],
264    pub component: ComponentInstanceId,
265}
266
267///
268/// RootComponentDrainingRequest
269///
270/// Controller command fencing one exact active Component against new mutation.
271///
272
273#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
274pub struct RootComponentDrainingRequest {
275    pub operation_id: [u8; 32],
276    pub component: ComponentInstanceId,
277    pub expected_registry: ComponentRegistryHead,
278}
279
280///
281/// RootComponentDrainingStatusRequest
282///
283/// Read-only lookup key for one durable Component-draining operation.
284///
285
286#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
287pub struct RootComponentDrainingStatusRequest {
288    pub operation_id: [u8; 32],
289    pub component: ComponentInstanceId,
290}
291
292///
293/// RootComponentQuiescenceRequest
294///
295/// Controller command converging and stopping one exact draining Component.
296///
297
298#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
299pub struct RootComponentQuiescenceRequest {
300    pub operation_id: [u8; 32],
301    pub component: ComponentInstanceId,
302    pub expected_registry: ComponentRegistryHead,
303}
304
305///
306/// RootComponentQuiescenceStatusRequest
307///
308/// Read-only lookup key for one draining Component's quiescence progress.
309///
310
311#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
312pub struct RootComponentQuiescenceStatusRequest {
313    pub operation_id: [u8; 32],
314    pub component: ComponentInstanceId,
315}
316
317///
318/// RootComponentDrainingAdvanceRequest
319///
320/// Controller command advancing at most one deterministic draining-removal phase.
321///
322
323#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
324pub struct RootComponentDrainingAdvanceRequest {
325    pub operation_id: [u8; 32],
326    pub component: ComponentInstanceId,
327}
328
329///
330/// RootComponentFinalInventoryRequest
331///
332/// Controller command freezing one exact empty draining Component inventory.
333///
334
335#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
336pub struct RootComponentFinalInventoryRequest {
337    pub operation_id: [u8; 32],
338    pub component: ComponentInstanceId,
339    pub expected_registry: ComponentRegistryHead,
340}
341
342///
343/// RootComponentDeletionRequest
344///
345/// Controller command reconciling one top-level deletion from frozen final inventory.
346///
347
348#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
349pub struct RootComponentDeletionRequest {
350    pub operation_id: [u8; 32],
351    pub component: ComponentInstanceId,
352    pub expected_inventory_hash: [u8; 32],
353}
354
355///
356/// RootComponentDeletionStatusRequest
357///
358/// Read-only lookup key for one durable top-level Component deletion.
359///
360
361#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
362pub struct RootComponentDeletionStatusRequest {
363    pub operation_id: [u8; 32],
364    pub component: ComponentInstanceId,
365}
366
367///
368/// RootComponentChildCreationRequest
369///
370/// Parent command continuing one already reserved direct-child operation.
371///
372
373#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
374pub struct RootComponentChildCreationRequest {
375    pub operation_id: [u8; 32],
376    pub component: ComponentInstanceId,
377}
378
379///
380/// RootComponentChildInstallRequest
381///
382/// Parent command installing and verifying one already created direct-child operation.
383///
384
385#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
386pub struct RootComponentChildInstallRequest {
387    pub operation_id: [u8; 32],
388    pub component: ComponentInstanceId,
389}
390
391///
392/// RootComponentChildCommitRequest
393///
394/// Parent command committing one already verified direct-child operation.
395///
396
397#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
398pub struct RootComponentChildCommitRequest {
399    pub operation_id: [u8; 32],
400    pub component: ComponentInstanceId,
401}
402
403///
404/// RootComponentChildDirectoryPreparationRequest
405///
406/// Parent command distributing one committed child's Directory and converging its affected members.
407///
408
409#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
410pub struct RootComponentChildDirectoryPreparationRequest {
411    pub operation_id: [u8; 32],
412    pub component: ComponentInstanceId,
413}
414
415///
416/// RootComponentChildRuntimeActivationRequest
417///
418/// Parent command activating one Directory-prepared direct-child runtime.
419///
420
421#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
422pub struct RootComponentChildRuntimeActivationRequest {
423    pub operation_id: [u8; 32],
424    pub component: ComponentInstanceId,
425}
426
427///
428/// RootComponentChildMembershipActivationRequest
429///
430/// Parent command activating one runtime-active direct child's Registry membership.
431///
432
433#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
434pub struct RootComponentChildMembershipActivationRequest {
435    pub operation_id: [u8; 32],
436    pub component: ComponentInstanceId,
437}
438
439///
440/// RootComponentCreationRequest
441///
442/// Controller command continuing one already reserved top-level Component operation.
443///
444
445#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
446pub struct RootComponentCreationRequest {
447    pub operation_id: [u8; 32],
448}
449
450///
451/// RootComponentInstallRequest
452///
453/// Controller command continuing one already created top-level Component operation.
454///
455
456#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
457pub struct RootComponentInstallRequest {
458    pub operation_id: [u8; 32],
459}
460
461///
462/// RootComponentCommitRequest
463///
464/// Controller command committing one already verified top-level Component operation.
465///
466
467#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
468pub struct RootComponentCommitRequest {
469    pub operation_id: [u8; 32],
470}
471
472///
473/// RootComponentDirectoryPreparationRequest
474///
475/// Controller command distributing exact Directories to one committed top-level Component.
476///
477
478#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
479pub struct RootComponentDirectoryPreparationRequest {
480    pub operation_id: [u8; 32],
481}
482
483///
484/// RootComponentRuntimeActivationRequest
485///
486/// Controller command activating one Directory-prepared top-level Component runtime.
487///
488
489#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
490pub struct RootComponentRuntimeActivationRequest {
491    pub operation_id: [u8; 32],
492}
493
494///
495/// RootComponentMembershipActivationRequest
496///
497/// Controller command activating one runtime-active Component's Registry membership.
498///
499
500#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
501pub struct RootComponentMembershipActivationRequest {
502    pub operation_id: [u8; 32],
503}
504
505///
506/// ComponentProvisioningOrigin
507///
508/// Authenticated causal authority retained with one top-level Component allocation.
509///
510
511#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
512pub enum ComponentProvisioningOrigin {
513    FleetAdministrator {
514        caller: Principal,
515    },
516    Component {
517        requester: Box<ComponentBinding>,
518        grant: Box<crate::config::ComponentProvisioningGrant>,
519    },
520    ComponentGroup {
521        operation_id: [u8; 32],
522        plan_hash: [u8; 32],
523        group_placement: ComponentGroupPlacementId,
524        member_path: ComponentGroupMemberPath,
525    },
526}
527
528///
529/// RootComponentAllocationPhase
530///
531/// Durable root-local progress of one top-level Component allocation operation.
532///
533
534#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
535pub enum RootComponentAllocationPhase {
536    Reserved,
537    CreationIntent,
538    Created,
539    InstallIntent,
540    Installed,
541    Verified,
542    Committed,
543    Removed,
544}
545
546///
547/// RootComponentSubtreeRemovalPhase
548///
549/// Durable root-local progress of one child-subtree removal operation.
550///
551
552#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
553#[expect(
554    clippy::large_enum_variant,
555    reason = "wire phases retain complete inline receipts for deterministic Candid responses"
556)]
557pub enum RootComponentSubtreeRemovalPhase {
558    Fenced,
559    Traversing(RootComponentSubtreeRemovalNode),
560    LeafSelected(RootComponentSubtreeRemovalNode),
561    StopIntent(RootComponentSubtreeRemovalStopIntent),
562    Stopped(RootComponentSubtreeRemovalStoppedReceipt),
563    DeleteIntent(RootComponentSubtreeRemovalDeleteIntent),
564    Deleted(RootComponentSubtreeRemovalDeletedReceipt),
565    MembershipRemoved(RootComponentSubtreeRemovalMembershipRemovedReceipt),
566    DirectorySynchronized(RootComponentSubtreeRemovalDirectorySynchronizedReceipt),
567    Completed(RootComponentSubtreeRemovalCompletedReceipt),
568}
569
570///
571/// RootComponentSubtreeRemovalNode
572///
573/// Exact registered child selected as a traversal cursor or removable leaf.
574///
575
576#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
577pub struct RootComponentSubtreeRemovalNode {
578    pub canister_id: Principal,
579    pub parent_canister_id: Principal,
580    pub role: CanisterRole,
581    pub kind: ComponentChildKind,
582    pub installed_artifact_hash: [u8; 32],
583    pub status: ComponentLifecycleStatus,
584}
585
586///
587/// RootComponentSubtreeRemovalStopIntent
588///
589/// Exact registered leaf and sole root controller frozen before a stop call.
590///
591
592#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
593pub struct RootComponentSubtreeRemovalStopIntent {
594    pub leaf: RootComponentSubtreeRemovalNode,
595    pub controller: Principal,
596}
597
598///
599/// RootComponentSubtreeRemovalStoppedReceipt
600///
601/// Frozen stop authority plus the independently observed installed module.
602///
603
604#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
605pub struct RootComponentSubtreeRemovalStoppedReceipt {
606    pub stop: RootComponentSubtreeRemovalStopIntent,
607    pub observed_module_hash: [u8; 32],
608}
609
610///
611/// RootComponentSubtreeRemovalDeleteIntent
612///
613/// Exact stopped receipt frozen before the destructive management call.
614///
615
616#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
617pub struct RootComponentSubtreeRemovalDeleteIntent {
618    pub stopped: RootComponentSubtreeRemovalStoppedReceipt,
619}
620
621///
622/// RootComponentSubtreeRemovalDeletedReceipt
623///
624/// Frozen workload-deletion authority committed after the Canister is recycled.
625///
626
627#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
628pub struct RootComponentSubtreeRemovalDeletedReceipt {
629    pub deletion: RootComponentSubtreeRemovalDeleteIntent,
630}
631
632///
633/// RootComponentSubtreeRemovalMembershipRemovedReceipt
634///
635/// Exact Registry transition retained after the independently deleted leaf is unregistered.
636///
637
638#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
639pub struct RootComponentSubtreeRemovalMembershipRemovedReceipt {
640    pub deleted: RootComponentSubtreeRemovalDeletedReceipt,
641    pub removed_from_registry: ComponentRegistryHead,
642    pub previous_descendant_content_hash: [u8; 32],
643    pub previous_committed_descendants: u32,
644    pub registry: ComponentRegistryHead,
645    pub descendant_content_hash: [u8; 32],
646    pub registry_encoded_bytes: u64,
647    pub reserved_descendants: u32,
648    pub committed_descendants: u32,
649    pub directory_synchronized_at_ns: u64,
650    pub directory_authority_hash: [u8; 32],
651    pub parent_role_instances: u32,
652    pub root_managed_descendants: u32,
653    pub root_known_created_component_canisters: u32,
654}
655
656///
657/// RootComponentSubtreeRemovalDirectoryConvergenceEvidence
658///
659/// Compact durable proof that one surviving member covered the required Directory.
660///
661
662#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
663pub struct RootComponentSubtreeRemovalDirectoryConvergenceEvidence {
664    pub operation_id: [u8; 32],
665    pub canister_id: Principal,
666    pub activation: ComponentRuntimeActivationEvidence,
667}
668
669///
670/// RootComponentSubtreeRemovalDirectorySynchronizedReceipt
671///
672/// Membership removal plus independently verified surviving-member convergence.
673///
674/// The owner is absent only when its top-level Component is durably quiescent.
675///
676
677#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
678pub struct RootComponentSubtreeRemovalDirectorySynchronizedReceipt {
679    pub membership_removed: RootComponentSubtreeRemovalMembershipRemovedReceipt,
680    pub covered_fleet_registry_revision: u64,
681    pub covered_fleet_registry_content_hash: [u8; 32],
682    pub covered_component_registry: ComponentRegistryHead,
683    pub covered_authority_hash: [u8; 32],
684    pub owning_component: Option<RootComponentSubtreeRemovalDirectoryConvergenceEvidence>,
685    pub parent: Option<RootComponentSubtreeRemovalDirectoryConvergenceEvidence>,
686}
687
688///
689/// RootComponentSubtreeRemovalCompletedReceipt
690///
691/// Terminal Registry and Directory authority after the fenced target is finalized.
692///
693
694#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
695pub struct RootComponentSubtreeRemovalCompletedReceipt {
696    pub registry: ComponentRegistryHead,
697    pub directory_authority_hash: [u8; 32],
698}
699
700///
701/// ComponentLifecycleStatus
702///
703/// Root-owned runtime lifecycle state of one committed Component Registry member.
704///
705
706#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
707pub enum ComponentLifecycleStatus {
708    Prepared,
709    Active,
710    Draining,
711    Removed,
712}
713
714///
715/// ComponentRegistryHead
716///
717/// Exact independently versioned authority of one Component Registry partition.
718///
719
720#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
721pub struct ComponentRegistryHead {
722    pub component: ComponentInstanceId,
723    pub revision: u64,
724    pub content_hash: [u8; 32],
725}
726
727///
728/// ComponentRegistryPartitionRequest
729///
730/// Read-only lookup key for one committed Component Registry partition.
731///
732
733#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
734pub struct ComponentRegistryPartitionRequest {
735    pub component: ComponentInstanceId,
736}
737
738///
739/// ComponentRegistryPartitionResponse
740///
741/// Protected top-level row and independent head of one Component Registry partition.
742///
743
744#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
745pub struct ComponentRegistryPartitionResponse {
746    pub head: ComponentRegistryHead,
747    pub binding: ComponentBinding,
748    pub provisioning_origin: ComponentProvisioningOrigin,
749    pub release_set: FleetSubnetRootReleaseSet,
750    pub status: ComponentLifecycleStatus,
751    pub reserved_descendants: u32,
752    pub committed_descendants: u32,
753    pub encoded_bytes: u64,
754}
755
756///
757/// ComponentDirectoryProvenance
758///
759/// Exact Component Registry authority from which one Component Directory is derived.
760///
761
762#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
763pub struct ComponentDirectoryProvenance {
764    pub component: ComponentBinding,
765    pub source_fleet_subnet_root: Principal,
766    pub component_registry_revision: u64,
767    pub component_registry_content_hash: [u8; 32],
768    pub synchronized_at_ns: u64,
769}
770
771///
772/// ComponentDirectoryHead
773///
774/// Compact independently versioned discovery projection for one Component tree.
775///
776
777#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
778pub struct ComponentDirectoryHead {
779    pub provenance: ComponentDirectoryProvenance,
780    pub descendant_count: u32,
781}
782
783///
784/// ComponentDirectoryHeadRequest
785///
786/// Read-only lookup key for one committed Component Directory head.
787///
788
789#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
790pub struct ComponentDirectoryHeadRequest {
791    pub component: ComponentInstanceId,
792}
793
794///
795/// ComponentDirectoryPageCursor
796///
797/// Opaque revision- and filter-bound continuation for one bounded Directory page.
798///
799
800#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
801pub struct ComponentDirectoryPageCursor(pub Vec<u8>);
802
803///
804/// ComponentDirectoryPageRequest
805///
806/// Bounded member query against one exact current Component Directory authority.
807///
808
809#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
810pub struct ComponentDirectoryPageRequest {
811    pub directory: ComponentDirectoryHead,
812    pub parent_canister_id: Option<Principal>,
813    pub role: Option<CanisterRole>,
814    pub status: Option<ComponentLifecycleStatus>,
815    pub cursor: Option<ComponentDirectoryPageCursor>,
816    pub limit: u16,
817}
818
819///
820/// ComponentDirectoryChildEntry
821///
822/// One authoritative normalized child projected with its complete protected binding.
823///
824
825#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
826pub struct ComponentDirectoryChildEntry {
827    pub binding: ComponentChildBinding,
828    pub kind: ComponentChildKind,
829    pub installed_artifact_hash: [u8; 32],
830    pub status: ComponentLifecycleStatus,
831}
832
833///
834/// ComponentDirectoryPageResponse
835///
836/// One bounded caller-scoped page under the exact requested Directory head.
837///
838
839#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
840pub struct ComponentDirectoryPageResponse {
841    pub directory: ComponentDirectoryHead,
842    pub entries: Vec<ComponentDirectoryChildEntry>,
843    pub next_cursor: Option<ComponentDirectoryPageCursor>,
844}
845
846///
847/// ComponentRuntimeDirectoryAuthority
848///
849/// Exact Fleet and Component discovery authority retained by one managed Component-tree node.
850///
851
852#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
853pub struct ComponentRuntimeDirectoryAuthority {
854    pub fleet: FleetDirectorySnapshot,
855    pub component: ComponentDirectoryHead,
856}
857
858///
859/// ComponentRuntimeDirectChild
860///
861/// Exact active direct-child projection delivered with one Component Directory authority.
862///
863
864#[derive(CandidType, Clone, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
865pub struct ComponentRuntimeDirectChild {
866    pub canister_id: Principal,
867    pub role: CanisterRole,
868}
869
870///
871/// ComponentRuntimeDirectoryPreparationRequest
872///
873/// Root-issued exact Directory preparation command for one managed Component-tree node.
874///
875
876#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
877pub struct ComponentRuntimeDirectoryPreparationRequest {
878    pub operation_id: [u8; 32],
879    pub authority: ComponentRuntimeDirectoryAuthority,
880    pub direct_children: Vec<ComponentRuntimeDirectChild>,
881}
882
883///
884/// ComponentRuntimeDirectorySynchronizationRequest
885///
886/// Root-issued replacement of one active managed Component node's current Directory authority.
887///
888
889#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
890pub struct ComponentRuntimeDirectorySynchronizationRequest {
891    pub operation_id: [u8; 32],
892    pub authority: ComponentRuntimeDirectoryAuthority,
893    pub direct_children: Vec<ComponentRuntimeDirectChild>,
894}
895
896///
897/// ComponentRuntimePhase
898///
899/// Target-local progress from installation through Component runtime activation.
900///
901
902#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
903pub enum ComponentRuntimePhase {
904    AwaitingDirectory,
905    DirectoryPrepared,
906    Active,
907}
908
909///
910/// ComponentRuntimeActivationEvidence
911///
912/// Exact retained Directory authority under which one Component runtime became Active.
913///
914
915#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
916pub struct ComponentRuntimeActivationEvidence {
917    pub directory_authority_hash: [u8; 32],
918    pub activated_at_ns: u64,
919}
920
921///
922/// ComponentRuntimeActivationRequest
923///
924/// Root-issued exact activation command for one Directory-prepared managed Component node.
925///
926
927#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
928pub struct ComponentRuntimeActivationRequest {
929    pub operation_id: [u8; 32],
930    pub directory_authority_hash: [u8; 32],
931}
932
933///
934/// ComponentRuntimeStatusResponse
935///
936/// Independently observable target-local binding and exact retained Directory authority.
937///
938
939#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
940pub struct ComponentRuntimeStatusResponse {
941    pub operation_id: [u8; 32],
942    pub binding: ManagedCanisterBinding,
943    pub deployment: Box<ProtectedComponentDeployment>,
944    pub phase: ComponentRuntimePhase,
945    pub authority: Option<ComponentRuntimeDirectoryAuthority>,
946    pub authority_hash: Option<[u8; 32]>,
947    pub direct_children_hash: Option<[u8; 32]>,
948    pub activation: Option<ComponentRuntimeActivationEvidence>,
949}
950
951///
952/// ComponentRuntimeDirectoryConvergenceEvidence
953///
954/// Stable root evidence that one active member covered at least the required Directory authority.
955///
956
957#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
958pub struct ComponentRuntimeDirectoryConvergenceEvidence {
959    pub operation_id: [u8; 32],
960    pub binding: ManagedCanisterBinding,
961    pub covered_authority: ComponentRuntimeDirectoryAuthority,
962    pub covered_authority_hash: [u8; 32],
963    pub activation: ComponentRuntimeActivationEvidence,
964}
965
966///
967/// RootComponentCreationEvidence
968///
969/// Exact Store artifact and root-owned creation settings frozen before the paid effect.
970///
971
972#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
973pub struct RootComponentCreationEvidence {
974    pub wasm_store: Principal,
975    pub payload_hash: [u8; 32],
976    pub payload_size_bytes: u64,
977    pub initial_cycles: Cycles,
978    pub controller: Principal,
979    pub canister: Option<Principal>,
980}
981
982///
983/// RootComponentInstallEvidence
984///
985/// Exact raw artifact, chunk source and immutable target binding frozen before installation.
986///
987
988#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
989pub struct RootComponentInstallEvidence {
990    pub raw_module_hash: [u8; 32],
991    pub chunk_hashes: Vec<Vec<u8>>,
992    pub binding: ComponentBinding,
993}
994
995///
996/// RootComponentChildInstallEvidence
997///
998/// Exact child module and immutable retained binding frozen before installation.
999///
1000
1001#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1002pub struct RootComponentChildInstallEvidence {
1003    pub raw_module_hash: [u8; 32],
1004    pub chunk_hashes: Vec<Vec<u8>>,
1005    pub binding: ComponentChildBinding,
1006}
1007
1008///
1009/// RootComponentAllocationResponse
1010///
1011/// Durable identity reservation returned identically for exact operation retry.
1012///
1013
1014#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1015pub struct RootComponentAllocationResponse {
1016    pub operation_id: [u8; 32],
1017    pub allocation_sequence: u64,
1018    pub component: ComponentInstanceId,
1019    pub component_spec: ComponentSpecId,
1020    pub spec_hash: [u8; 32],
1021    pub role: CanisterRole,
1022    pub provisioning_origin: ComponentProvisioningOrigin,
1023    pub release_set: FleetSubnetRootReleaseSet,
1024    pub phase: RootComponentAllocationPhase,
1025    pub creation: Option<RootComponentCreationEvidence>,
1026    pub installation: Option<RootComponentInstallEvidence>,
1027}
1028
1029///
1030/// RootComponentChildAllocationResponse
1031///
1032/// Durable direct-child lifecycle progress returned identically for exact parent retry.
1033///
1034
1035#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1036pub struct RootComponentChildAllocationResponse {
1037    pub operation_id: [u8; 32],
1038    pub component: ComponentInstanceId,
1039    pub parent_canister_id: Principal,
1040    pub parent_role: CanisterRole,
1041    pub child_role: CanisterRole,
1042    pub child_kind: ComponentChildKind,
1043    pub maximum_instances_per_parent: u32,
1044    pub maximum_descendants: u32,
1045    pub maximum_registry_bytes: u64,
1046    pub reserved_against_registry: ComponentRegistryHead,
1047    pub release_set: FleetSubnetRootReleaseSet,
1048    pub phase: RootComponentAllocationPhase,
1049    pub creation: Option<RootComponentCreationEvidence>,
1050    pub installation: Option<RootComponentChildInstallEvidence>,
1051}
1052
1053///
1054/// RootComponentSubtreeRemovalResponse
1055///
1056/// Current durable snapshot of one monotonic subtree-removal operation.
1057///
1058
1059#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1060pub struct RootComponentSubtreeRemovalResponse {
1061    pub operation_id: [u8; 32],
1062    pub component: ComponentInstanceId,
1063    pub target_canister_id: Principal,
1064    pub target_parent_canister_id: Principal,
1065    pub target_role: CanisterRole,
1066    pub target_status: ComponentLifecycleStatus,
1067    pub reserved_against_registry: ComponentRegistryHead,
1068    pub maximum_completed_leaves: u32,
1069    pub completed_leaves: u32,
1070    pub traversal_steps: u32,
1071    pub phase: RootComponentSubtreeRemovalPhase,
1072}
1073
1074///
1075/// RootComponentDrainingResponse
1076///
1077/// Exact Registry and Directory authority produced by the durable draining fence.
1078///
1079
1080#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1081pub struct RootComponentDrainingResponse {
1082    pub operation_id: [u8; 32],
1083    pub component: ComponentInstanceId,
1084    pub previous_registry: ComponentRegistryHead,
1085    pub registry: ComponentRegistryHead,
1086    pub descendant_count: u32,
1087    pub descendant_content_hash: [u8; 32],
1088    pub directory_authority_hash: [u8; 32],
1089    pub started_at_ns: u64,
1090}
1091
1092///
1093/// RootComponentQuiescenceStopIntent
1094///
1095/// Exact runtime, Directory, module and controller authority frozen before stopping.
1096///
1097
1098#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1099pub struct RootComponentQuiescenceStopIntent {
1100    pub registry: ComponentRegistryHead,
1101    pub descendant_count: u32,
1102    pub descendant_content_hash: [u8; 32],
1103    pub canister_id: Principal,
1104    pub controller: Principal,
1105    pub expected_module_hash: [u8; 32],
1106    pub covered_fleet_registry_revision: u64,
1107    pub covered_fleet_registry_content_hash: [u8; 32],
1108    pub covered_authority_hash: [u8; 32],
1109    pub runtime_operation_id: [u8; 32],
1110    pub activation: ComponentRuntimeActivationEvidence,
1111    pub prepared_at_ns: u64,
1112}
1113
1114///
1115/// RootComponentQuiescentReceipt
1116///
1117/// Durable evidence that the exact prepared Component was independently observed stopped.
1118///
1119
1120#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1121pub struct RootComponentQuiescentReceipt {
1122    pub stop: RootComponentQuiescenceStopIntent,
1123    pub observed_module_hash: [u8; 32],
1124    pub quiesced_at_ns: u64,
1125}
1126
1127///
1128/// RootComponentQuiescencePhase
1129///
1130/// Monotonic progress from pre-effect stop authority to observed quiescence.
1131///
1132
1133#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1134pub enum RootComponentQuiescencePhase {
1135    StopIntent(RootComponentQuiescenceStopIntent),
1136    Quiescent(RootComponentQuiescentReceipt),
1137}
1138
1139///
1140/// RootComponentQuiescenceResponse
1141///
1142/// Current durable quiescence progress for one draining Component.
1143///
1144
1145#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1146pub struct RootComponentQuiescenceResponse {
1147    pub operation_id: [u8; 32],
1148    pub component: ComponentInstanceId,
1149    pub phase: RootComponentQuiescencePhase,
1150}
1151
1152///
1153/// RootComponentDrainingDescendantsEmpty
1154///
1155/// Exact current Registry proof that one draining Component has no descendants.
1156///
1157
1158#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1159pub struct RootComponentDrainingDescendantsEmpty {
1160    pub registry: ComponentRegistryHead,
1161    pub descendant_content_hash: [u8; 32],
1162}
1163
1164///
1165/// RootComponentDrainingAdvancePhase
1166///
1167/// One bounded driver result: current subtree progress or exact empty inventory.
1168///
1169
1170#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1171#[expect(
1172    clippy::large_enum_variant,
1173    reason = "wire result embeds the current durable subtree snapshot without a Rust-only indirection"
1174)]
1175pub enum RootComponentDrainingAdvancePhase {
1176    DescendantRemoval(RootComponentSubtreeRemovalResponse),
1177    DescendantsEmpty(RootComponentDrainingDescendantsEmpty),
1178}
1179
1180///
1181/// RootComponentDrainingAdvanceResponse
1182///
1183/// Current bounded progress of one terminally quiescent Component drain.
1184///
1185
1186#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1187pub struct RootComponentDrainingAdvanceResponse {
1188    pub operation_id: [u8; 32],
1189    pub component: ComponentInstanceId,
1190    pub phase: RootComponentDrainingAdvancePhase,
1191}
1192
1193///
1194/// RootComponentFinalInventory
1195///
1196/// Exact empty Component Registry and current Fleet Directory authority frozen before deletion.
1197///
1198
1199#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1200pub struct RootComponentFinalInventory {
1201    pub registry: ComponentRegistryHead,
1202    pub descendant_content_hash: [u8; 32],
1203    pub registry_encoded_bytes: u64,
1204    pub directory_synchronized_at_ns: u64,
1205    pub covered_fleet_registry_revision: u64,
1206    pub covered_fleet_registry_content_hash: [u8; 32],
1207    pub directory_authority_hash: [u8; 32],
1208    pub inventory_hash: [u8; 32],
1209    pub finalized_at_ns: u64,
1210}
1211
1212///
1213/// RootComponentFinalInventoryResponse
1214///
1215/// Response-idempotent receipt for one finalized empty Component inventory.
1216///
1217
1218#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1219pub struct RootComponentFinalInventoryResponse {
1220    pub operation_id: [u8; 32],
1221    pub component: ComponentInstanceId,
1222    pub inventory: RootComponentFinalInventory,
1223}
1224
1225///
1226/// RootComponentDeletionIntent
1227///
1228/// Complete final-inventory and quiescence authority frozen before top-level deletion.
1229///
1230
1231#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1232pub struct RootComponentDeletionIntent {
1233    pub final_inventory: RootComponentFinalInventory,
1234    pub quiescence: RootComponentQuiescentReceipt,
1235    pub prepared_at_ns: u64,
1236}
1237
1238///
1239/// RootComponentDeletedReceipt
1240///
1241/// Terminal authority retained after the top-level workload Canister is recycled.
1242///
1243
1244#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1245pub struct RootComponentDeletedReceipt {
1246    pub deletion: RootComponentDeletionIntent,
1247    pub deleted_at_ns: u64,
1248}
1249
1250///
1251/// RootComponentMembershipRemovedReceipt
1252///
1253/// Terminal local-membership removal and settled root/Spec accounting authority.
1254///
1255
1256#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1257pub struct RootComponentMembershipRemovedReceipt {
1258    pub deleted: RootComponentDeletedReceipt,
1259    pub allocation_operation_id: [u8; 32],
1260    pub remaining_spec_committed_instances: u32,
1261    pub root_committed_component_instances: u32,
1262    pub root_known_created_component_canisters: u32,
1263    pub root_registry_encoded_bytes: u64,
1264    pub removed_at_ns: u64,
1265    pub removal_hash: [u8; 32],
1266}
1267
1268///
1269/// RootComponentDeletionPhase
1270///
1271/// Monotonic top-level deletion progress through terminal local-membership removal.
1272///
1273
1274#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1275pub enum RootComponentDeletionPhase {
1276    DeleteIntent(RootComponentDeletionIntent),
1277    Deleted(RootComponentDeletedReceipt),
1278    MembershipRemoved(RootComponentMembershipRemovedReceipt),
1279}
1280
1281///
1282/// RootComponentDeletionResponse
1283///
1284/// Current durable deletion progress for one finalized top-level Component.
1285///
1286
1287#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1288pub struct RootComponentDeletionResponse {
1289    pub operation_id: [u8; 32],
1290    pub component: ComponentInstanceId,
1291    pub phase: RootComponentDeletionPhase,
1292}
1293
1294///
1295/// RootComponentChildCommitResponse
1296///
1297/// Exact committed child operation, authoritative Component Registry and next Directory head.
1298///
1299
1300#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1301pub struct RootComponentChildCommitResponse {
1302    pub allocation: RootComponentChildAllocationResponse,
1303    pub registry: ComponentRegistryPartitionResponse,
1304    pub directory: ComponentDirectoryHead,
1305}
1306
1307///
1308/// RootComponentChildDirectoryPreparationResponse
1309///
1310/// Exact child preparation plus stable bounded active-member Directory coverage.
1311///
1312
1313#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1314pub struct RootComponentChildDirectoryPreparationResponse {
1315    pub committed: RootComponentChildCommitResponse,
1316    pub child: ComponentRuntimeStatusResponse,
1317    pub owning_component: ComponentRuntimeDirectoryConvergenceEvidence,
1318    pub parent: Option<ComponentRuntimeDirectoryConvergenceEvidence>,
1319}
1320
1321///
1322/// RootComponentChildRuntimeActivationResponse
1323///
1324/// Exact child commitment plus independently observed Directory-bound runtime activation.
1325///
1326
1327#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1328pub struct RootComponentChildRuntimeActivationResponse {
1329    pub committed: RootComponentChildCommitResponse,
1330    pub child: ComponentRuntimeStatusResponse,
1331}
1332
1333///
1334/// RootComponentChildMembershipActivationResponse
1335///
1336/// Original child commitment plus active Registry, Directory and target convergence evidence.
1337///
1338
1339#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1340pub struct RootComponentChildMembershipActivationResponse {
1341    pub committed: RootComponentChildCommitResponse,
1342    pub registry: ComponentRegistryPartitionResponse,
1343    pub directory: ComponentDirectoryHead,
1344    pub child: ComponentRuntimeStatusResponse,
1345}
1346
1347///
1348/// RootComponentCommitResponse
1349///
1350/// Exact committed allocation, authoritative Registry row and derived Directory head.
1351///
1352
1353#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1354pub struct RootComponentCommitResponse {
1355    pub allocation: RootComponentAllocationResponse,
1356    pub registry: ComponentRegistryPartitionResponse,
1357    pub directory: ComponentDirectoryHead,
1358}
1359
1360///
1361/// RootComponentDirectoryPreparationResponse
1362///
1363/// Exact root authority plus independently observed target-local Directory preparation.
1364///
1365
1366#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1367pub struct RootComponentDirectoryPreparationResponse {
1368    pub committed: RootComponentCommitResponse,
1369    pub target: ComponentRuntimeStatusResponse,
1370}
1371
1372///
1373/// RootComponentRuntimeActivationResponse
1374///
1375/// Exact root authority plus independently observed target-local runtime activation.
1376///
1377
1378#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1379pub struct RootComponentRuntimeActivationResponse {
1380    pub committed: RootComponentCommitResponse,
1381    pub target: ComponentRuntimeStatusResponse,
1382}
1383
1384///
1385/// RootComponentMembershipActivationResponse
1386///
1387/// Exact active Registry authority plus independently observed current target Directory.
1388///
1389
1390#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1391pub struct RootComponentMembershipActivationResponse {
1392    pub allocation: RootComponentAllocationResponse,
1393    pub registry: ComponentRegistryPartitionResponse,
1394    pub directory: ComponentDirectoryHead,
1395    pub target: ComponentRuntimeStatusResponse,
1396}
1397
1398#[cfg(test)]
1399mod tests {
1400    use super::*;
1401    use crate::{
1402        dto::root_store::RootStoreBootstrapRequest,
1403        ids::{
1404            AppId, CanonicalNetworkId, FleetCoordinatorBinding, FleetId, FleetKey,
1405            FleetRegistryAuthority, ReleaseBuildId, ReleaseBuildNonce, ReleaseSetDigest, SubnetId,
1406        },
1407    };
1408
1409    #[test]
1410    fn component_registry_contracts_round_trip_through_candid() {
1411        let request = RootComponentRegistryPreparationRequest {
1412            store_bootstrap: RootStoreBootstrapRequest {
1413                manifest_payload_size_bytes: 128,
1414            },
1415            expected_fleet_registry: FleetRegistryVersion {
1416                authority: fleet_registry_authority(),
1417                revision: 4,
1418                content_hash: [5; 32],
1419            },
1420        };
1421        let response = RootComponentRegistryStatusResponse {
1422            fleet_subnet_root: Principal::from_slice(&[6; 29]),
1423            prepared_against_registry: request.expected_fleet_registry.clone(),
1424            release_set: FleetSubnetRootReleaseSet {
1425                release_build_id: ReleaseBuildId::from_nonce(ReleaseBuildNonce::from_random_bytes(
1426                    [7; 32],
1427                )),
1428                manifest_digest: ReleaseSetDigest::from_bytes([8; 32]),
1429            },
1430            component_topology_digest: ComponentTopologyDigest::from_bytes([9; 32]),
1431            next_allocation_sequence: 1,
1432            reserved_component_instances: 0,
1433            committed_component_instances: 0,
1434            managed_descendants: 0,
1435            known_created_component_canisters: 0,
1436            encoded_bytes: 0,
1437            initial_inventory: Some(RootComponentInitialInventoryStatus {
1438                fleet_activation_operation_id: [10; 32],
1439                component_count: 0,
1440                inventory_hash: [11; 32],
1441                sealed_at_ns: 12,
1442                directories_converged: true,
1443                root_runtime_activated: true,
1444            }),
1445        };
1446        let allocation = RootComponentAllocationResponse {
1447            operation_id: [10; 32],
1448            allocation_sequence: 1,
1449            component: ComponentInstanceId::from_generated_bytes([11; 32]),
1450            component_spec: "projects".parse().expect("Component Spec ID"),
1451            spec_hash: [12; 32],
1452            role: CanisterRole::new("project_hub"),
1453            provisioning_origin: ComponentProvisioningOrigin::FleetAdministrator {
1454                caller: Principal::from_slice(&[13; 29]),
1455            },
1456            release_set: response.release_set,
1457            phase: RootComponentAllocationPhase::Reserved,
1458            creation: None,
1459            installation: None,
1460        };
1461        let created = RootComponentAllocationResponse {
1462            phase: RootComponentAllocationPhase::Created,
1463            creation: Some(RootComponentCreationEvidence {
1464                wasm_store: Principal::from_slice(&[14; 29]),
1465                payload_hash: [15; 32],
1466                payload_size_bytes: 4_096,
1467                initial_cycles: Cycles::new(5_000_000_000_000),
1468                controller: Principal::from_slice(&[6; 29]),
1469                canister: Some(Principal::from_slice(&[16; 29])),
1470            }),
1471            installation: None,
1472            ..allocation.clone()
1473        };
1474        let request_bytes = candid::encode_one(&request).expect("encode request");
1475        let response_bytes = candid::encode_one(&response).expect("encode response");
1476        let allocation_bytes = candid::encode_one(&allocation).expect("encode allocation");
1477        let created_bytes = candid::encode_one(&created).expect("encode created allocation");
1478
1479        assert_eq!(
1480            candid::decode_one::<RootComponentRegistryPreparationRequest>(&request_bytes)
1481                .expect("decode request"),
1482            request
1483        );
1484        assert_eq!(
1485            candid::decode_one::<RootComponentRegistryStatusResponse>(&response_bytes)
1486                .expect("decode response"),
1487            response
1488        );
1489        assert_eq!(
1490            candid::decode_one::<RootComponentAllocationResponse>(&allocation_bytes)
1491                .expect("decode allocation"),
1492            allocation
1493        );
1494        assert_eq!(
1495            candid::decode_one::<RootComponentAllocationResponse>(&created_bytes)
1496                .expect("decode created allocation"),
1497            created
1498        );
1499    }
1500
1501    #[test]
1502    fn component_commit_response_round_trips_through_candid() {
1503        let root = Principal::from_slice(&[6; 29]);
1504        let component = ComponentInstanceId::from_generated_bytes([11; 32]);
1505        let component_spec: ComponentSpecId = "projects".parse().expect("Component Spec ID");
1506        let release_set = FleetSubnetRootReleaseSet {
1507            release_build_id: ReleaseBuildId::from_nonce(ReleaseBuildNonce::from_random_bytes(
1508                [7; 32],
1509            )),
1510            manifest_digest: ReleaseSetDigest::from_bytes([8; 32]),
1511        };
1512        let provisioning_origin = ComponentProvisioningOrigin::FleetAdministrator {
1513            caller: Principal::from_slice(&[13; 29]),
1514        };
1515        let binding = ComponentBinding {
1516            authority: fleet_registry_authority(),
1517            component,
1518            component_spec: component_spec.clone(),
1519            spec_hash: [12; 32],
1520            role: CanisterRole::new("project_hub"),
1521            placement_subnet: SubnetId::from_principal(Principal::from_slice(&[17; 29])),
1522            fleet_subnet_root: root,
1523            canister_id: Principal::from_slice(&[16; 29]),
1524        };
1525        let head = ComponentRegistryHead {
1526            component,
1527            revision: 1,
1528            content_hash: [18; 32],
1529        };
1530        let committed = RootComponentCommitResponse {
1531            allocation: RootComponentAllocationResponse {
1532                operation_id: [10; 32],
1533                allocation_sequence: 1,
1534                component,
1535                component_spec,
1536                spec_hash: binding.spec_hash,
1537                role: binding.role.clone(),
1538                provisioning_origin: provisioning_origin.clone(),
1539                release_set,
1540                phase: RootComponentAllocationPhase::Committed,
1541                creation: Some(RootComponentCreationEvidence {
1542                    wasm_store: Principal::from_slice(&[14; 29]),
1543                    payload_hash: [15; 32],
1544                    payload_size_bytes: 4_096,
1545                    initial_cycles: Cycles::new(5_000_000_000_000),
1546                    controller: root,
1547                    canister: Some(binding.canister_id),
1548                }),
1549                installation: Some(RootComponentInstallEvidence {
1550                    raw_module_hash: [20; 32],
1551                    chunk_hashes: vec![vec![21; 32]],
1552                    binding: binding.clone(),
1553                }),
1554            },
1555            registry: ComponentRegistryPartitionResponse {
1556                head: head.clone(),
1557                binding: binding.clone(),
1558                provisioning_origin,
1559                release_set,
1560                status: ComponentLifecycleStatus::Prepared,
1561                reserved_descendants: 0,
1562                committed_descendants: 0,
1563                encoded_bytes: 2_048,
1564            },
1565            directory: ComponentDirectoryHead {
1566                provenance: ComponentDirectoryProvenance {
1567                    component: binding,
1568                    source_fleet_subnet_root: root,
1569                    component_registry_revision: head.revision,
1570                    component_registry_content_hash: head.content_hash,
1571                    synchronized_at_ns: 19,
1572                },
1573                descendant_count: 0,
1574            },
1575        };
1576        let committed_bytes = candid::encode_one(&committed).expect("encode committed allocation");
1577
1578        assert_eq!(
1579            candid::decode_one::<RootComponentCommitResponse>(&committed_bytes)
1580                .expect("decode committed allocation"),
1581            committed
1582        );
1583    }
1584
1585    #[test]
1586    fn component_directory_page_contracts_round_trip_through_candid() {
1587        let root = Principal::from_slice(&[6; 29]);
1588        let component = ComponentInstanceId::from_generated_bytes([11; 32]);
1589        let binding = ComponentBinding {
1590            authority: fleet_registry_authority(),
1591            component,
1592            component_spec: "projects".parse().expect("Component Spec ID"),
1593            spec_hash: [12; 32],
1594            role: CanisterRole::new("project_hub"),
1595            placement_subnet: SubnetId::from_principal(Principal::from_slice(&[17; 29])),
1596            fleet_subnet_root: root,
1597            canister_id: Principal::from_slice(&[16; 29]),
1598        };
1599        let directory = ComponentDirectoryHead {
1600            provenance: ComponentDirectoryProvenance {
1601                component: binding.clone(),
1602                source_fleet_subnet_root: root,
1603                component_registry_revision: 3,
1604                component_registry_content_hash: [18; 32],
1605                synchronized_at_ns: 19,
1606            },
1607            descendant_count: 1,
1608        };
1609        let request = ComponentDirectoryPageRequest {
1610            directory: directory.clone(),
1611            parent_canister_id: Some(binding.canister_id),
1612            role: Some(CanisterRole::new("project_instance")),
1613            status: Some(ComponentLifecycleStatus::Active),
1614            cursor: Some(ComponentDirectoryPageCursor(vec![20; 64])),
1615            limit: 50,
1616        };
1617        let response = ComponentDirectoryPageResponse {
1618            directory,
1619            entries: vec![ComponentDirectoryChildEntry {
1620                binding: ComponentChildBinding {
1621                    component: binding.clone(),
1622                    parent_canister_id: binding.canister_id,
1623                    role: CanisterRole::new("project_instance"),
1624                    canister_id: Principal::from_slice(&[21; 29]),
1625                },
1626                kind: ComponentChildKind::Instance,
1627                installed_artifact_hash: [22; 32],
1628                status: ComponentLifecycleStatus::Active,
1629            }],
1630            next_cursor: Some(ComponentDirectoryPageCursor(vec![23; 64])),
1631        };
1632        let request_bytes = candid::encode_one(&request).expect("encode Directory page request");
1633        let response_bytes = candid::encode_one(&response).expect("encode Directory page response");
1634
1635        assert_eq!(
1636            candid::decode_one::<ComponentDirectoryPageRequest>(&request_bytes)
1637                .expect("decode Directory page request"),
1638            request
1639        );
1640        assert_eq!(
1641            candid::decode_one::<ComponentDirectoryPageResponse>(&response_bytes)
1642                .expect("decode Directory page response"),
1643            response
1644        );
1645    }
1646
1647    fn fleet_registry_authority() -> FleetRegistryAuthority {
1648        FleetRegistryAuthority {
1649            binding: FleetCoordinatorBinding {
1650                fleet: crate::ids::FleetBinding {
1651                    fleet: FleetKey {
1652                        canonical_network_id: CanonicalNetworkId::ic_mainnet(),
1653                        fleet_id: FleetId::from_generated_bytes([1; 32]),
1654                    },
1655                    app: AppId::from("toko"),
1656                },
1657                coordinator_subnet: SubnetId::from_principal(Principal::from_slice(&[2; 29])),
1658                coordinator: Principal::from_slice(&[3; 29]),
1659            },
1660            epoch: 1,
1661        }
1662    }
1663
1664    #[test]
1665    fn component_creation_request_round_trips_through_candid() {
1666        let request = RootComponentCreationRequest {
1667            operation_id: [10; 32],
1668        };
1669        let bytes = candid::encode_one(request).expect("encode creation request");
1670
1671        assert_eq!(
1672            candid::decode_one::<RootComponentCreationRequest>(&bytes)
1673                .expect("decode creation request"),
1674            request
1675        );
1676    }
1677
1678    #[test]
1679    fn peer_component_provisioning_origin_round_trips_through_candid() {
1680        let authority = fleet_registry_authority();
1681        let requester_spec: ComponentSpecId =
1682            "projects".parse().expect("requester Component Spec ID");
1683        let target_spec: ComponentSpecId = "users".parse().expect("target Component Spec ID");
1684        let origin = ComponentProvisioningOrigin::Component {
1685            requester: Box::new(ComponentBinding {
1686                authority,
1687                component: ComponentInstanceId::from_generated_bytes([20; 32]),
1688                component_spec: requester_spec.clone(),
1689                spec_hash: [21; 32],
1690                role: CanisterRole::new("project_hub"),
1691                placement_subnet: SubnetId::from_principal(Principal::from_slice(&[22; 29])),
1692                fleet_subnet_root: Principal::from_slice(&[23; 29]),
1693                canister_id: Principal::from_slice(&[24; 29]),
1694            }),
1695            grant: Box::new(crate::config::ComponentProvisioningGrant {
1696                requester_component_spec: requester_spec,
1697                target_component_spec: target_spec,
1698                maximum_instances_per_requester_per_root: 3,
1699            }),
1700        };
1701        let bytes = candid::encode_one(&origin).expect("encode peer provisioning origin");
1702
1703        assert_eq!(
1704            candid::decode_one::<ComponentProvisioningOrigin>(&bytes)
1705                .expect("decode peer provisioning origin"),
1706            origin
1707        );
1708    }
1709
1710    #[test]
1711    fn component_group_provisioning_origin_round_trips_through_candid() {
1712        let origin = ComponentProvisioningOrigin::ComponentGroup {
1713            operation_id: [25; 32],
1714            plan_hash: [26; 32],
1715            group_placement: ComponentGroupPlacementId {
1716                deployment: "cells".parse().expect("deployment ID"),
1717                ordinal: 3,
1718            },
1719            member_path: ComponentGroupMemberPath::try_from(vec![
1720                "api".parse().expect("member ID"),
1721            ])
1722            .expect("member path"),
1723        };
1724        let bytes = candid::encode_one(&origin).expect("encode group provisioning origin");
1725
1726        assert_eq!(
1727            candid::decode_one::<ComponentProvisioningOrigin>(&bytes)
1728                .expect("decode group provisioning origin"),
1729            origin
1730        );
1731    }
1732
1733    #[test]
1734    #[expect(
1735        clippy::too_many_lines,
1736        reason = "one Candid contract test covers every subtree-removal phase receipt"
1737    )]
1738    fn component_subtree_removal_contracts_round_trip_through_candid() {
1739        let component = ComponentInstanceId::from_generated_bytes([41; 32]);
1740        let registry = ComponentRegistryHead {
1741            component,
1742            revision: 7,
1743            content_hash: [42; 32],
1744        };
1745        let request = RootComponentSubtreeRemovalRequest {
1746            operation_id: [43; 32],
1747            component,
1748            target_canister_id: Principal::from_slice(&[44; 29]),
1749            expected_registry: registry.clone(),
1750        };
1751        let status_request = RootComponentSubtreeRemovalStatusRequest {
1752            operation_id: request.operation_id,
1753            component,
1754        };
1755        let advance_request = RootComponentSubtreeRemovalAdvanceRequest {
1756            operation_id: request.operation_id,
1757            component,
1758            expected_traversal_steps: 1,
1759        };
1760        let stop_request = RootComponentSubtreeRemovalStopPreparationRequest {
1761            operation_id: request.operation_id,
1762            component,
1763            expected_traversal_steps: 2,
1764            expected_leaf_canister_id: Principal::from_slice(&[46; 29]),
1765            expected_leaf_parent_canister_id: request.target_canister_id,
1766        };
1767        let stopped = RootComponentSubtreeRemovalStoppedReceipt {
1768            observed_module_hash: [49; 32],
1769            stop: RootComponentSubtreeRemovalStopIntent {
1770                controller: Principal::from_slice(&[48; 29]),
1771                leaf: RootComponentSubtreeRemovalNode {
1772                    canister_id: Principal::from_slice(&[46; 29]),
1773                    parent_canister_id: request.target_canister_id,
1774                    role: CanisterRole::new("project_ledger"),
1775                    kind: ComponentChildKind::Singleton,
1776                    installed_artifact_hash: [47; 32],
1777                    status: ComponentLifecycleStatus::Active,
1778                },
1779            },
1780        };
1781        let response = RootComponentSubtreeRemovalResponse {
1782            operation_id: request.operation_id,
1783            component,
1784            target_canister_id: request.target_canister_id,
1785            target_parent_canister_id: Principal::from_slice(&[45; 29]),
1786            target_role: CanisterRole::new("project_instance"),
1787            target_status: ComponentLifecycleStatus::Active,
1788            reserved_against_registry: registry,
1789            maximum_completed_leaves: 4,
1790            completed_leaves: 1,
1791            traversal_steps: 2,
1792            phase: RootComponentSubtreeRemovalPhase::DirectorySynchronized(
1793                RootComponentSubtreeRemovalDirectorySynchronizedReceipt {
1794                    membership_removed: RootComponentSubtreeRemovalMembershipRemovedReceipt {
1795                        deleted: RootComponentSubtreeRemovalDeletedReceipt {
1796                            deletion: RootComponentSubtreeRemovalDeleteIntent { stopped },
1797                        },
1798                        removed_from_registry: ComponentRegistryHead {
1799                            component,
1800                            revision: 8,
1801                            content_hash: [50; 32],
1802                        },
1803                        previous_descendant_content_hash: [51; 32],
1804                        previous_committed_descendants: 4,
1805                        registry: ComponentRegistryHead {
1806                            component,
1807                            revision: 9,
1808                            content_hash: [52; 32],
1809                        },
1810                        descendant_content_hash: [53; 32],
1811                        registry_encoded_bytes: 4_096,
1812                        reserved_descendants: 1,
1813                        committed_descendants: 3,
1814                        directory_synchronized_at_ns: 54,
1815                        directory_authority_hash: [55; 32],
1816                        parent_role_instances: 0,
1817                        root_managed_descendants: 4,
1818                        root_known_created_component_canisters: 4,
1819                    },
1820                    covered_fleet_registry_revision: 6,
1821                    covered_fleet_registry_content_hash: [56; 32],
1822                    covered_component_registry: ComponentRegistryHead {
1823                        component,
1824                        revision: 9,
1825                        content_hash: [52; 32],
1826                    },
1827                    covered_authority_hash: [55; 32],
1828                    owning_component: Some(
1829                        RootComponentSubtreeRemovalDirectoryConvergenceEvidence {
1830                            operation_id: [57; 32],
1831                            canister_id: Principal::from_slice(&[58; 29]),
1832                            activation: ComponentRuntimeActivationEvidence {
1833                                directory_authority_hash: [59; 32],
1834                                activated_at_ns: 60,
1835                            },
1836                        },
1837                    ),
1838                    parent: Some(RootComponentSubtreeRemovalDirectoryConvergenceEvidence {
1839                        operation_id: [61; 32],
1840                        canister_id: request.target_canister_id,
1841                        activation: ComponentRuntimeActivationEvidence {
1842                            directory_authority_hash: [62; 32],
1843                            activated_at_ns: 63,
1844                        },
1845                    }),
1846                },
1847            ),
1848        };
1849
1850        let request_bytes = candid::encode_one(&request).expect("encode subtree removal request");
1851        let advance_bytes =
1852            candid::encode_one(advance_request).expect("encode subtree removal advance request");
1853        let stop_bytes =
1854            candid::encode_one(stop_request).expect("encode subtree removal stop request");
1855        let status_bytes =
1856            candid::encode_one(status_request).expect("encode subtree removal status request");
1857        let response_bytes =
1858            candid::encode_one(&response).expect("encode subtree removal response");
1859
1860        assert_eq!(
1861            candid::decode_one::<RootComponentSubtreeRemovalRequest>(&request_bytes)
1862                .expect("decode subtree removal request"),
1863            request
1864        );
1865        assert_eq!(
1866            candid::decode_one::<RootComponentSubtreeRemovalAdvanceRequest>(&advance_bytes)
1867                .expect("decode subtree removal advance request"),
1868            advance_request
1869        );
1870        assert_eq!(
1871            candid::decode_one::<RootComponentSubtreeRemovalStopPreparationRequest>(&stop_bytes)
1872                .expect("decode subtree removal stop request"),
1873            stop_request
1874        );
1875        assert_eq!(
1876            candid::decode_one::<RootComponentSubtreeRemovalStatusRequest>(&status_bytes)
1877                .expect("decode subtree removal status request"),
1878            status_request
1879        );
1880        assert_eq!(
1881            candid::decode_one::<RootComponentSubtreeRemovalResponse>(&response_bytes)
1882                .expect("decode subtree removal response"),
1883            response
1884        );
1885
1886        let mut quiescent_owner_response = response;
1887        let RootComponentSubtreeRemovalPhase::DirectorySynchronized(receipt) =
1888            &mut quiescent_owner_response.phase
1889        else {
1890            panic!("Directory-synchronized response");
1891        };
1892        receipt.owning_component = None;
1893        let quiescent_owner_bytes = candid::encode_one(&quiescent_owner_response)
1894            .expect("encode quiescent-owner subtree response");
1895        assert_eq!(
1896            candid::decode_one::<RootComponentSubtreeRemovalResponse>(&quiescent_owner_bytes)
1897                .expect("decode quiescent-owner subtree response"),
1898            quiescent_owner_response
1899        );
1900    }
1901
1902    #[test]
1903    fn component_draining_contracts_round_trip_through_candid() {
1904        let component = ComponentInstanceId::from_generated_bytes([60; 32]);
1905        let previous_registry = ComponentRegistryHead {
1906            component,
1907            revision: 7,
1908            content_hash: [61; 32],
1909        };
1910        let request = RootComponentDrainingRequest {
1911            operation_id: [62; 32],
1912            component,
1913            expected_registry: previous_registry.clone(),
1914        };
1915        let status_request = RootComponentDrainingStatusRequest {
1916            operation_id: request.operation_id,
1917            component,
1918        };
1919        let response = RootComponentDrainingResponse {
1920            operation_id: request.operation_id,
1921            component,
1922            previous_registry,
1923            registry: ComponentRegistryHead {
1924                component,
1925                revision: 8,
1926                content_hash: [63; 32],
1927            },
1928            descendant_count: 20_000,
1929            descendant_content_hash: [64; 32],
1930            directory_authority_hash: [65; 32],
1931            started_at_ns: 66,
1932        };
1933
1934        let request_bytes =
1935            candid::encode_one(&request).expect("encode Component draining request");
1936        let status_bytes =
1937            candid::encode_one(status_request).expect("encode Component draining status request");
1938        let response_bytes =
1939            candid::encode_one(&response).expect("encode Component draining response");
1940
1941        assert_eq!(
1942            candid::decode_one::<RootComponentDrainingRequest>(&request_bytes)
1943                .expect("decode Component draining request"),
1944            request
1945        );
1946        assert_eq!(
1947            candid::decode_one::<RootComponentDrainingStatusRequest>(&status_bytes)
1948                .expect("decode Component draining status request"),
1949            status_request
1950        );
1951        assert_eq!(
1952            candid::decode_one::<RootComponentDrainingResponse>(&response_bytes)
1953                .expect("decode Component draining response"),
1954            response
1955        );
1956    }
1957
1958    #[test]
1959    fn component_quiescence_contracts_round_trip_through_candid() {
1960        let component = ComponentInstanceId::from_generated_bytes([67; 32]);
1961        let registry = ComponentRegistryHead {
1962            component,
1963            revision: 9,
1964            content_hash: [68; 32],
1965        };
1966        let request = RootComponentQuiescenceRequest {
1967            operation_id: [69; 32],
1968            component,
1969            expected_registry: registry.clone(),
1970        };
1971        let status_request = RootComponentQuiescenceStatusRequest {
1972            operation_id: request.operation_id,
1973            component,
1974        };
1975        let stop = RootComponentQuiescenceStopIntent {
1976            registry,
1977            descendant_count: 20_000,
1978            descendant_content_hash: [70; 32],
1979            canister_id: Principal::from_slice(&[71; 29]),
1980            controller: Principal::from_slice(&[72; 29]),
1981            expected_module_hash: [73; 32],
1982            covered_fleet_registry_revision: 10,
1983            covered_fleet_registry_content_hash: [74; 32],
1984            covered_authority_hash: [75; 32],
1985            runtime_operation_id: [76; 32],
1986            activation: ComponentRuntimeActivationEvidence {
1987                directory_authority_hash: [77; 32],
1988                activated_at_ns: 78,
1989            },
1990            prepared_at_ns: 79,
1991        };
1992        let response = RootComponentQuiescenceResponse {
1993            operation_id: request.operation_id,
1994            component,
1995            phase: RootComponentQuiescencePhase::Quiescent(RootComponentQuiescentReceipt {
1996                stop,
1997                observed_module_hash: [73; 32],
1998                quiesced_at_ns: 80,
1999            }),
2000        };
2001
2002        let request_bytes = candid::encode_one(&request).expect("encode quiescence request");
2003        let status_bytes =
2004            candid::encode_one(status_request).expect("encode quiescence status request");
2005        let response_bytes = candid::encode_one(&response).expect("encode quiescence response");
2006        assert_eq!(
2007            candid::decode_one::<RootComponentQuiescenceRequest>(&request_bytes)
2008                .expect("decode quiescence request"),
2009            request
2010        );
2011        assert_eq!(
2012            candid::decode_one::<RootComponentQuiescenceStatusRequest>(&status_bytes)
2013                .expect("decode quiescence status request"),
2014            status_request
2015        );
2016        assert_eq!(
2017            candid::decode_one::<RootComponentQuiescenceResponse>(&response_bytes)
2018                .expect("decode quiescence response"),
2019            response
2020        );
2021    }
2022
2023    #[test]
2024    fn component_draining_advance_contracts_round_trip_through_candid() {
2025        let component = ComponentInstanceId::from_generated_bytes([81; 32]);
2026        let registry = ComponentRegistryHead {
2027            component,
2028            revision: 12,
2029            content_hash: [82; 32],
2030        };
2031        let request = RootComponentDrainingAdvanceRequest {
2032            operation_id: [83; 32],
2033            component,
2034        };
2035        let descendant_removal = RootComponentDrainingAdvanceResponse {
2036            operation_id: request.operation_id,
2037            component,
2038            phase: RootComponentDrainingAdvancePhase::DescendantRemoval(
2039                RootComponentSubtreeRemovalResponse {
2040                    operation_id: [84; 32],
2041                    component,
2042                    target_canister_id: Principal::from_slice(&[85; 29]),
2043                    target_parent_canister_id: Principal::from_slice(&[86; 29]),
2044                    target_role: CanisterRole::new("project_instance"),
2045                    target_status: ComponentLifecycleStatus::Active,
2046                    reserved_against_registry: registry.clone(),
2047                    maximum_completed_leaves: 20_000,
2048                    completed_leaves: 0,
2049                    traversal_steps: 0,
2050                    phase: RootComponentSubtreeRemovalPhase::Fenced,
2051                },
2052            ),
2053        };
2054        let descendants_empty = RootComponentDrainingAdvanceResponse {
2055            operation_id: request.operation_id,
2056            component,
2057            phase: RootComponentDrainingAdvancePhase::DescendantsEmpty(
2058                RootComponentDrainingDescendantsEmpty {
2059                    registry,
2060                    descendant_content_hash: [87; 32],
2061                },
2062            ),
2063        };
2064
2065        let request_bytes =
2066            candid::encode_one(request).expect("encode Component draining advance request");
2067        let removal_bytes = candid::encode_one(&descendant_removal)
2068            .expect("encode Component draining removal response");
2069        let empty_bytes = candid::encode_one(&descendants_empty)
2070            .expect("encode Component draining empty response");
2071
2072        assert_eq!(
2073            candid::decode_one::<RootComponentDrainingAdvanceRequest>(&request_bytes)
2074                .expect("decode Component draining advance request"),
2075            request
2076        );
2077        assert_eq!(
2078            candid::decode_one::<RootComponentDrainingAdvanceResponse>(&removal_bytes)
2079                .expect("decode Component draining removal response"),
2080            descendant_removal
2081        );
2082        assert_eq!(
2083            candid::decode_one::<RootComponentDrainingAdvanceResponse>(&empty_bytes)
2084                .expect("decode Component draining empty response"),
2085            descendants_empty
2086        );
2087    }
2088
2089    #[test]
2090    #[expect(
2091        clippy::too_many_lines,
2092        reason = "one wire-contract test keeps final inventory and its deletion authority aligned"
2093    )]
2094    fn component_final_inventory_contracts_round_trip_through_candid() {
2095        let component = ComponentInstanceId::from_generated_bytes([88; 32]);
2096        let registry = ComponentRegistryHead {
2097            component,
2098            revision: 21,
2099            content_hash: [89; 32],
2100        };
2101        let request = RootComponentFinalInventoryRequest {
2102            operation_id: [90; 32],
2103            component,
2104            expected_registry: registry.clone(),
2105        };
2106        let inventory = RootComponentFinalInventory {
2107            registry,
2108            descendant_content_hash: [91; 32],
2109            registry_encoded_bytes: 4_096,
2110            directory_synchronized_at_ns: 92,
2111            covered_fleet_registry_revision: 93,
2112            covered_fleet_registry_content_hash: [94; 32],
2113            directory_authority_hash: [95; 32],
2114            inventory_hash: [96; 32],
2115            finalized_at_ns: 97,
2116        };
2117        let response = RootComponentFinalInventoryResponse {
2118            operation_id: request.operation_id,
2119            component,
2120            inventory: inventory.clone(),
2121        };
2122        let deletion_request = RootComponentDeletionRequest {
2123            operation_id: request.operation_id,
2124            component,
2125            expected_inventory_hash: inventory.inventory_hash,
2126        };
2127        let deletion_status_request = RootComponentDeletionStatusRequest {
2128            operation_id: request.operation_id,
2129            component,
2130        };
2131        let deletion = RootComponentDeletionIntent {
2132            final_inventory: inventory,
2133            quiescence: RootComponentQuiescentReceipt {
2134                stop: RootComponentQuiescenceStopIntent {
2135                    registry: response.inventory.registry.clone(),
2136                    descendant_count: 0,
2137                    descendant_content_hash: response.inventory.descendant_content_hash,
2138                    canister_id: Principal::from_slice(&[98; 29]),
2139                    controller: Principal::from_slice(&[99; 29]),
2140                    expected_module_hash: [100; 32],
2141                    covered_fleet_registry_revision: 93,
2142                    covered_fleet_registry_content_hash: [94; 32],
2143                    covered_authority_hash: [101; 32],
2144                    runtime_operation_id: [102; 32],
2145                    activation: ComponentRuntimeActivationEvidence {
2146                        directory_authority_hash: [103; 32],
2147                        activated_at_ns: 104,
2148                    },
2149                    prepared_at_ns: 105,
2150                },
2151                observed_module_hash: [100; 32],
2152                quiesced_at_ns: 106,
2153            },
2154            prepared_at_ns: 107,
2155        };
2156        let deleted_receipt = RootComponentDeletedReceipt {
2157            deletion,
2158            deleted_at_ns: 108,
2159        };
2160        let deletion_response = RootComponentDeletionResponse {
2161            operation_id: request.operation_id,
2162            component,
2163            phase: RootComponentDeletionPhase::Deleted(deleted_receipt.clone()),
2164        };
2165        let membership_removed_response = RootComponentDeletionResponse {
2166            operation_id: request.operation_id,
2167            component,
2168            phase: RootComponentDeletionPhase::MembershipRemoved(
2169                RootComponentMembershipRemovedReceipt {
2170                    deleted: deleted_receipt,
2171                    allocation_operation_id: [109; 32],
2172                    remaining_spec_committed_instances: 2,
2173                    root_committed_component_instances: 3,
2174                    root_known_created_component_canisters: 4,
2175                    root_registry_encoded_bytes: 5_000,
2176                    removed_at_ns: 110,
2177                    removal_hash: [111; 32],
2178                },
2179            ),
2180        };
2181
2182        let request_bytes =
2183            candid::encode_one(&request).expect("encode Component final inventory request");
2184        let response_bytes =
2185            candid::encode_one(&response).expect("encode Component final inventory response");
2186        let deletion_request_bytes =
2187            candid::encode_one(deletion_request).expect("encode Component deletion request");
2188        let deletion_status_bytes = candid::encode_one(deletion_status_request)
2189            .expect("encode Component deletion status request");
2190        let deletion_response_bytes =
2191            candid::encode_one(&deletion_response).expect("encode Component deletion response");
2192        let membership_removed_response_bytes = candid::encode_one(&membership_removed_response)
2193            .expect("encode Component membership-removal response");
2194        assert_eq!(
2195            candid::decode_one::<RootComponentFinalInventoryRequest>(&request_bytes)
2196                .expect("decode Component final inventory request"),
2197            request
2198        );
2199        assert_eq!(
2200            candid::decode_one::<RootComponentFinalInventoryResponse>(&response_bytes)
2201                .expect("decode Component final inventory response"),
2202            response
2203        );
2204        assert_eq!(
2205            candid::decode_one::<RootComponentDeletionRequest>(&deletion_request_bytes)
2206                .expect("decode Component deletion request"),
2207            deletion_request
2208        );
2209        assert_eq!(
2210            candid::decode_one::<RootComponentDeletionStatusRequest>(&deletion_status_bytes)
2211                .expect("decode Component deletion status request"),
2212            deletion_status_request
2213        );
2214        assert_eq!(
2215            candid::decode_one::<RootComponentDeletionResponse>(&deletion_response_bytes)
2216                .expect("decode Component deletion response"),
2217            deletion_response
2218        );
2219        assert_eq!(
2220            candid::decode_one::<RootComponentDeletionResponse>(&membership_removed_response_bytes)
2221                .expect("decode Component membership-removal response"),
2222            membership_removed_response
2223        );
2224    }
2225
2226    #[test]
2227    fn component_subtree_removal_deletion_requests_round_trip_through_candid() {
2228        let prepare = RootComponentSubtreeRemovalDeletePreparationRequest {
2229            operation_id: [50; 32],
2230            component: ComponentInstanceId::from_generated_bytes([51; 32]),
2231            expected_traversal_steps: 3,
2232            expected_leaf_canister_id: Principal::from_slice(&[52; 29]),
2233            expected_leaf_parent_canister_id: Principal::from_slice(&[53; 29]),
2234        };
2235        let request = RootComponentSubtreeRemovalDeleteRequest {
2236            operation_id: prepare.operation_id,
2237            component: prepare.component,
2238            expected_traversal_steps: prepare.expected_traversal_steps,
2239            expected_leaf_canister_id: prepare.expected_leaf_canister_id,
2240            expected_leaf_parent_canister_id: prepare.expected_leaf_parent_canister_id,
2241        };
2242        let membership_request = RootComponentSubtreeRemovalMembershipRemovalRequest {
2243            operation_id: prepare.operation_id,
2244            component: prepare.component,
2245            expected_traversal_steps: prepare.expected_traversal_steps,
2246            expected_leaf_canister_id: prepare.expected_leaf_canister_id,
2247            expected_leaf_parent_canister_id: prepare.expected_leaf_parent_canister_id,
2248        };
2249        let directory_request = RootComponentSubtreeRemovalDirectorySynchronizationRequest {
2250            operation_id: prepare.operation_id,
2251            component: prepare.component,
2252            expected_traversal_steps: prepare.expected_traversal_steps,
2253            expected_leaf_canister_id: prepare.expected_leaf_canister_id,
2254            expected_leaf_parent_canister_id: prepare.expected_leaf_parent_canister_id,
2255        };
2256        let finalization_request = RootComponentSubtreeRemovalLeafFinalizationRequest {
2257            operation_id: prepare.operation_id,
2258            component: prepare.component,
2259            expected_traversal_steps: prepare.expected_traversal_steps,
2260            expected_leaf_canister_id: prepare.expected_leaf_canister_id,
2261            expected_leaf_parent_canister_id: prepare.expected_leaf_parent_canister_id,
2262        };
2263        let prepare_bytes = candid::encode_one(prepare)
2264            .expect("encode subtree removal deletion preparation request");
2265        let request_bytes =
2266            candid::encode_one(request).expect("encode subtree removal deletion request");
2267        let membership_request_bytes = candid::encode_one(membership_request)
2268            .expect("encode subtree removal membership-removal request");
2269        let directory_request_bytes = candid::encode_one(directory_request)
2270            .expect("encode subtree removal Directory synchronization request");
2271        let finalization_request_bytes = candid::encode_one(finalization_request)
2272            .expect("encode subtree removal leaf-finalization request");
2273
2274        assert_eq!(
2275            candid::decode_one::<RootComponentSubtreeRemovalDeletePreparationRequest>(
2276                &prepare_bytes
2277            )
2278            .expect("decode subtree removal deletion preparation request"),
2279            prepare
2280        );
2281        assert_eq!(
2282            candid::decode_one::<RootComponentSubtreeRemovalDeleteRequest>(&request_bytes)
2283                .expect("decode subtree removal deletion request"),
2284            request
2285        );
2286        assert_eq!(
2287            candid::decode_one::<RootComponentSubtreeRemovalMembershipRemovalRequest>(
2288                &membership_request_bytes
2289            )
2290            .expect("decode subtree removal membership-removal request"),
2291            membership_request
2292        );
2293        assert_eq!(
2294            candid::decode_one::<RootComponentSubtreeRemovalDirectorySynchronizationRequest>(
2295                &directory_request_bytes
2296            )
2297            .expect("decode subtree removal Directory synchronization request"),
2298            directory_request
2299        );
2300        assert_eq!(
2301            candid::decode_one::<RootComponentSubtreeRemovalLeafFinalizationRequest>(
2302                &finalization_request_bytes
2303            )
2304            .expect("decode subtree removal leaf-finalization request"),
2305            finalization_request
2306        );
2307    }
2308
2309    #[test]
2310    fn component_subtree_removal_stop_request_round_trips_through_candid() {
2311        let request = RootComponentSubtreeRemovalStopRequest {
2312            operation_id: [50; 32],
2313            component: ComponentInstanceId::from_generated_bytes([51; 32]),
2314            expected_traversal_steps: 3,
2315            expected_leaf_canister_id: Principal::from_slice(&[52; 29]),
2316            expected_leaf_parent_canister_id: Principal::from_slice(&[53; 29]),
2317        };
2318        let bytes =
2319            candid::encode_one(request).expect("encode subtree removal stop execution request");
2320
2321        assert_eq!(
2322            candid::decode_one::<RootComponentSubtreeRemovalStopRequest>(&bytes)
2323                .expect("decode subtree removal stop execution request"),
2324            request
2325        );
2326    }
2327
2328    #[test]
2329    #[expect(
2330        clippy::too_many_lines,
2331        reason = "one round-trip test keeps the complete child lifecycle boundary coherent"
2332    )]
2333    fn component_child_lifecycle_contracts_round_trip_through_candid() {
2334        let component = ComponentInstanceId::from_generated_bytes([11; 32]);
2335        let registry = ComponentRegistryHead {
2336            component,
2337            revision: 2,
2338            content_hash: [12; 32],
2339        };
2340        let request = RootComponentChildAllocationRequest {
2341            operation_id: [13; 32],
2342            component,
2343            expected_registry: registry.clone(),
2344            child_role: CanisterRole::new("project_instance"),
2345            application_init_args: Some(vec![9, 8, 7]),
2346        };
2347        let status_request = RootComponentChildAllocationStatusRequest {
2348            operation_id: request.operation_id,
2349            component,
2350        };
2351        let creation_request = RootComponentChildCreationRequest {
2352            operation_id: request.operation_id,
2353            component,
2354        };
2355        let install_request = RootComponentChildInstallRequest {
2356            operation_id: request.operation_id,
2357            component,
2358        };
2359        let commit_request = RootComponentChildCommitRequest {
2360            operation_id: request.operation_id,
2361            component,
2362        };
2363        let directory_request = RootComponentChildDirectoryPreparationRequest {
2364            operation_id: request.operation_id,
2365            component,
2366        };
2367        let activation_request = RootComponentChildRuntimeActivationRequest {
2368            operation_id: request.operation_id,
2369            component,
2370        };
2371        let membership_request = RootComponentChildMembershipActivationRequest {
2372            operation_id: request.operation_id,
2373            component,
2374        };
2375        let root = Principal::from_slice(&[17; 29]);
2376        let parent = Principal::from_slice(&[14; 29]);
2377        let child = Principal::from_slice(&[18; 29]);
2378        let child_binding = ComponentChildBinding {
2379            component: ComponentBinding {
2380                authority: fleet_registry_authority(),
2381                component,
2382                component_spec: "projects".parse().expect("Component Spec"),
2383                spec_hash: [19; 32],
2384                role: CanisterRole::new("project_hub"),
2385                placement_subnet: SubnetId::from_principal(Principal::from_slice(&[20; 29])),
2386                fleet_subnet_root: root,
2387                canister_id: parent,
2388            },
2389            parent_canister_id: parent,
2390            role: request.child_role.clone(),
2391            canister_id: child,
2392        };
2393        let response = RootComponentChildAllocationResponse {
2394            operation_id: request.operation_id,
2395            component,
2396            parent_canister_id: parent,
2397            parent_role: CanisterRole::new("project_hub"),
2398            child_role: request.child_role.clone(),
2399            child_kind: ComponentChildKind::Instance,
2400            maximum_instances_per_parent: 10_000,
2401            maximum_descendants: 20_000,
2402            maximum_registry_bytes: 16_777_216,
2403            reserved_against_registry: registry,
2404            release_set: FleetSubnetRootReleaseSet {
2405                release_build_id: ReleaseBuildId::from_nonce(ReleaseBuildNonce::from_random_bytes(
2406                    [15; 32],
2407                )),
2408                manifest_digest: ReleaseSetDigest::from_bytes([16; 32]),
2409            },
2410            phase: RootComponentAllocationPhase::Verified,
2411            creation: Some(RootComponentCreationEvidence {
2412                wasm_store: Principal::from_slice(&[21; 29]),
2413                payload_hash: [22; 32],
2414                payload_size_bytes: 4_096,
2415                initial_cycles: Cycles::new(5_000_000_000_000),
2416                controller: root,
2417                canister: Some(child),
2418            }),
2419            installation: Some(RootComponentChildInstallEvidence {
2420                raw_module_hash: [23; 32],
2421                chunk_hashes: vec![vec![24; 32]],
2422                binding: child_binding.clone(),
2423            }),
2424        };
2425        let commit_response = RootComponentChildCommitResponse {
2426            allocation: response.clone(),
2427            registry: ComponentRegistryPartitionResponse {
2428                head: ComponentRegistryHead {
2429                    component,
2430                    revision: 3,
2431                    content_hash: [25; 32],
2432                },
2433                binding: child_binding.component.clone(),
2434                provisioning_origin: ComponentProvisioningOrigin::FleetAdministrator {
2435                    caller: Principal::from_slice(&[26; 29]),
2436                },
2437                release_set: response.release_set,
2438                status: ComponentLifecycleStatus::Active,
2439                reserved_descendants: 0,
2440                committed_descendants: 1,
2441                encoded_bytes: 8_192,
2442            },
2443            directory: ComponentDirectoryHead {
2444                provenance: ComponentDirectoryProvenance {
2445                    component: child_binding.component.clone(),
2446                    source_fleet_subnet_root: root,
2447                    component_registry_revision: 3,
2448                    component_registry_content_hash: [25; 32],
2449                    synchronized_at_ns: 27,
2450                },
2451                descendant_count: 1,
2452            },
2453        };
2454        let runtime_authority = ComponentRuntimeDirectoryAuthority {
2455            fleet: FleetDirectorySnapshot {
2456                provenance: crate::dto::fleet_registry::FleetDirectoryProvenance {
2457                    registry: FleetRegistryVersion {
2458                        authority: fleet_registry_authority(),
2459                        revision: 4,
2460                        content_hash: [28; 32],
2461                    },
2462                    source_fleet_subnet_root: root,
2463                },
2464                fleet_subnet_roots: vec![
2465                    crate::dto::fleet_registry::FleetSubnetRootDirectoryEntry {
2466                        placement_subnet: commit_response.registry.binding.placement_subnet,
2467                        fleet_subnet_root: root,
2468                        status: crate::dto::fleet_registry::FleetSubnetRootStatus::Active,
2469                    },
2470                ],
2471            },
2472            component: commit_response.directory.clone(),
2473        };
2474        let activation = ComponentRuntimeActivationEvidence {
2475            directory_authority_hash: [29; 32],
2476            activated_at_ns: 30,
2477        };
2478        let directory_response = RootComponentChildDirectoryPreparationResponse {
2479            committed: commit_response.clone(),
2480            child: ComponentRuntimeStatusResponse {
2481                operation_id: request.operation_id,
2482                binding: ManagedCanisterBinding::ComponentChild(child_binding.clone()),
2483                deployment: Box::new(ProtectedComponentDeployment::UngroupedOrdinary {
2484                    binding: child_binding.component.clone(),
2485                }),
2486                phase: ComponentRuntimePhase::DirectoryPrepared,
2487                authority: Some(runtime_authority.clone()),
2488                authority_hash: Some([31; 32]),
2489                direct_children_hash: Some([37; 32]),
2490                activation: None,
2491            },
2492            owning_component: ComponentRuntimeDirectoryConvergenceEvidence {
2493                operation_id: [32; 32],
2494                binding: ManagedCanisterBinding::Component(child_binding.component.clone()),
2495                covered_authority: runtime_authority.clone(),
2496                covered_authority_hash: [31; 32],
2497                activation,
2498            },
2499            parent: None,
2500        };
2501        let activation_response = RootComponentChildRuntimeActivationResponse {
2502            committed: commit_response.clone(),
2503            child: ComponentRuntimeStatusResponse {
2504                operation_id: request.operation_id,
2505                binding: ManagedCanisterBinding::ComponentChild(child_binding.clone()),
2506                deployment: Box::new(ProtectedComponentDeployment::UngroupedOrdinary {
2507                    binding: child_binding.component.clone(),
2508                }),
2509                phase: ComponentRuntimePhase::Active,
2510                authority: Some(runtime_authority.clone()),
2511                authority_hash: Some([31; 32]),
2512                direct_children_hash: Some([37; 32]),
2513                activation: Some(ComponentRuntimeActivationEvidence {
2514                    directory_authority_hash: [31; 32],
2515                    activated_at_ns: 33,
2516                }),
2517            },
2518        };
2519        let active_directory = ComponentDirectoryHead {
2520            provenance: ComponentDirectoryProvenance {
2521                component: child_binding.component.clone(),
2522                source_fleet_subnet_root: root,
2523                component_registry_revision: 4,
2524                component_registry_content_hash: [34; 32],
2525                synchronized_at_ns: 35,
2526            },
2527            descendant_count: 1,
2528        };
2529        let active_authority = ComponentRuntimeDirectoryAuthority {
2530            fleet: runtime_authority.fleet,
2531            component: active_directory.clone(),
2532        };
2533        let membership_response = RootComponentChildMembershipActivationResponse {
2534            committed: commit_response.clone(),
2535            registry: ComponentRegistryPartitionResponse {
2536                head: ComponentRegistryHead {
2537                    component,
2538                    revision: 4,
2539                    content_hash: [34; 32],
2540                },
2541                binding: child_binding.component.clone(),
2542                provisioning_origin: commit_response.registry.provisioning_origin.clone(),
2543                release_set: commit_response.registry.release_set,
2544                status: ComponentLifecycleStatus::Active,
2545                reserved_descendants: 0,
2546                committed_descendants: 1,
2547                encoded_bytes: 8_256,
2548            },
2549            directory: active_directory,
2550            child: ComponentRuntimeStatusResponse {
2551                operation_id: request.operation_id,
2552                binding: ManagedCanisterBinding::ComponentChild(child_binding.clone()),
2553                deployment: Box::new(ProtectedComponentDeployment::UngroupedOrdinary {
2554                    binding: child_binding.component,
2555                }),
2556                phase: ComponentRuntimePhase::Active,
2557                authority: Some(active_authority),
2558                authority_hash: Some([36; 32]),
2559                direct_children_hash: Some([38; 32]),
2560                activation: Some(ComponentRuntimeActivationEvidence {
2561                    directory_authority_hash: [31; 32],
2562                    activated_at_ns: 33,
2563                }),
2564            },
2565        };
2566
2567        let request_bytes = candid::encode_one(&request).expect("encode child reservation");
2568        let status_bytes =
2569            candid::encode_one(status_request).expect("encode child reservation status");
2570        let creation_bytes =
2571            candid::encode_one(creation_request).expect("encode child creation request");
2572        let install_bytes =
2573            candid::encode_one(install_request).expect("encode child install request");
2574        let response_bytes = candid::encode_one(&response).expect("encode child response");
2575        let commit_request_bytes =
2576            candid::encode_one(commit_request).expect("encode child commit request");
2577        let directory_request_bytes =
2578            candid::encode_one(directory_request).expect("encode child Directory request");
2579        let activation_request_bytes =
2580            candid::encode_one(activation_request).expect("encode child activation request");
2581        let membership_request_bytes =
2582            candid::encode_one(membership_request).expect("encode child membership request");
2583        let commit_response_bytes =
2584            candid::encode_one(&commit_response).expect("encode child commit response");
2585        let directory_response_bytes =
2586            candid::encode_one(&directory_response).expect("encode child Directory response");
2587        let activation_response_bytes =
2588            candid::encode_one(&activation_response).expect("encode child activation response");
2589        let membership_response_bytes =
2590            candid::encode_one(&membership_response).expect("encode child membership response");
2591
2592        assert_eq!(
2593            candid::decode_one::<RootComponentChildAllocationRequest>(&request_bytes)
2594                .expect("decode child reservation"),
2595            request
2596        );
2597        assert_eq!(
2598            candid::decode_one::<RootComponentChildAllocationStatusRequest>(&status_bytes)
2599                .expect("decode child reservation status"),
2600            status_request
2601        );
2602        assert_eq!(
2603            candid::decode_one::<RootComponentChildCreationRequest>(&creation_bytes)
2604                .expect("decode child creation request"),
2605            creation_request
2606        );
2607        assert_eq!(
2608            candid::decode_one::<RootComponentChildInstallRequest>(&install_bytes)
2609                .expect("decode child install request"),
2610            install_request
2611        );
2612        assert_eq!(
2613            candid::decode_one::<RootComponentChildAllocationResponse>(&response_bytes)
2614                .expect("decode child response"),
2615            response
2616        );
2617        assert_eq!(
2618            candid::decode_one::<RootComponentChildCommitRequest>(&commit_request_bytes)
2619                .expect("decode child commit request"),
2620            commit_request
2621        );
2622        assert_eq!(
2623            candid::decode_one::<RootComponentChildDirectoryPreparationRequest>(
2624                &directory_request_bytes
2625            )
2626            .expect("decode child Directory request"),
2627            directory_request
2628        );
2629        assert_eq!(
2630            candid::decode_one::<RootComponentChildCommitResponse>(&commit_response_bytes)
2631                .expect("decode child commit response"),
2632            commit_response
2633        );
2634        assert_eq!(
2635            candid::decode_one::<RootComponentChildDirectoryPreparationResponse>(
2636                &directory_response_bytes
2637            )
2638            .expect("decode child Directory response"),
2639            directory_response
2640        );
2641        assert_eq!(
2642            candid::decode_one::<RootComponentChildRuntimeActivationRequest>(
2643                &activation_request_bytes
2644            )
2645            .expect("decode child activation request"),
2646            activation_request
2647        );
2648        assert_eq!(
2649            candid::decode_one::<RootComponentChildRuntimeActivationResponse>(
2650                &activation_response_bytes
2651            )
2652            .expect("decode child activation response"),
2653            activation_response
2654        );
2655        assert_eq!(
2656            candid::decode_one::<RootComponentChildMembershipActivationRequest>(
2657                &membership_request_bytes
2658            )
2659            .expect("decode child membership request"),
2660            membership_request
2661        );
2662        assert_eq!(
2663            candid::decode_one::<RootComponentChildMembershipActivationResponse>(
2664                &membership_response_bytes
2665            )
2666            .expect("decode child membership response"),
2667            membership_response
2668        );
2669    }
2670
2671    #[test]
2672    fn component_install_request_round_trips_through_candid() {
2673        let request = RootComponentInstallRequest {
2674            operation_id: [10; 32],
2675        };
2676        let bytes = candid::encode_one(request).expect("encode install request");
2677
2678        assert_eq!(
2679            candid::decode_one::<RootComponentInstallRequest>(&bytes)
2680                .expect("decode install request"),
2681            request
2682        );
2683    }
2684
2685    #[test]
2686    fn component_commit_request_round_trips_through_candid() {
2687        let request = RootComponentCommitRequest {
2688            operation_id: [10; 32],
2689        };
2690        let bytes = candid::encode_one(request).expect("encode commit request");
2691
2692        assert_eq!(
2693            candid::decode_one::<RootComponentCommitRequest>(&bytes)
2694                .expect("decode commit request"),
2695            request
2696        );
2697    }
2698
2699    #[test]
2700    fn component_runtime_activation_requests_round_trip_through_candid() {
2701        let root_request = RootComponentRuntimeActivationRequest {
2702            operation_id: [22; 32],
2703        };
2704        let target_request = ComponentRuntimeActivationRequest {
2705            operation_id: root_request.operation_id,
2706            directory_authority_hash: [23; 32],
2707        };
2708        let membership_request = RootComponentMembershipActivationRequest {
2709            operation_id: root_request.operation_id,
2710        };
2711        let root_bytes = candid::encode_one(root_request).expect("encode root activation request");
2712        let target_bytes =
2713            candid::encode_one(target_request).expect("encode target activation request");
2714        let membership_bytes =
2715            candid::encode_one(membership_request).expect("encode membership activation request");
2716
2717        assert_eq!(
2718            candid::decode_one::<RootComponentRuntimeActivationRequest>(&root_bytes)
2719                .expect("decode root activation request"),
2720            root_request
2721        );
2722        assert_eq!(
2723            candid::decode_one::<ComponentRuntimeActivationRequest>(&target_bytes)
2724                .expect("decode target activation request"),
2725            target_request
2726        );
2727        assert_eq!(
2728            candid::decode_one::<RootComponentMembershipActivationRequest>(&membership_bytes)
2729                .expect("decode membership activation request"),
2730            membership_request
2731        );
2732    }
2733}