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