Skip to main content

canic_core/dto/
component_registry.rs

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