Skip to main content

canic_core/dto/
component_registry.rs

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