Skip to main content

canic_core/dto/
component_registry.rs

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