1use crate::{
8 cdk::types::Cycles,
9 dto::{fleet_registry::FleetRegistryVersion, root_store::RootStoreBootstrapRequest},
10 ids::{
11 CanisterRole, ComponentInstanceId, ComponentSpecId, ComponentTopologyDigest,
12 FleetSubnetRootReleaseSet,
13 },
14};
15use candid::{CandidType, Principal};
16use serde::{Deserialize, Serialize};
17
18#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
25pub struct RootComponentRegistryPreparationRequest {
26 pub store_bootstrap: RootStoreBootstrapRequest,
27 pub expected_fleet_registry: FleetRegistryVersion,
28}
29
30#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
37pub struct RootComponentRegistryStatusResponse {
38 pub fleet_subnet_root: Principal,
39 pub prepared_against_registry: FleetRegistryVersion,
40 pub release_set: FleetSubnetRootReleaseSet,
41 pub component_topology_digest: ComponentTopologyDigest,
42 pub next_allocation_sequence: u64,
43 pub reserved_component_instances: u32,
44 pub committed_component_instances: u32,
45 pub managed_descendants: u32,
46 pub encoded_bytes: u64,
47}
48
49#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
56pub struct RootComponentAllocationRequest {
57 pub operation_id: [u8; 32],
58 pub component_spec: ComponentSpecId,
59}
60
61#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
68pub struct RootComponentAllocationStatusRequest {
69 pub operation_id: [u8; 32],
70}
71
72#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
79pub struct RootComponentCreationRequest {
80 pub operation_id: [u8; 32],
81}
82
83#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
90pub enum ComponentProvisioningOrigin {
91 FleetAdministrator { caller: Principal },
92}
93
94#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
101pub enum RootComponentAllocationPhase {
102 Reserved,
103 CreationIntent,
104 Created,
105}
106
107#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
114pub struct RootComponentCreationEvidence {
115 pub wasm_store: Principal,
116 pub payload_hash: [u8; 32],
117 pub payload_size_bytes: u64,
118 pub initial_cycles: Cycles,
119 pub controller: Principal,
120 pub canister: Option<Principal>,
121}
122
123#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
130pub struct RootComponentAllocationResponse {
131 pub operation_id: [u8; 32],
132 pub allocation_sequence: u64,
133 pub component: ComponentInstanceId,
134 pub component_spec: ComponentSpecId,
135 pub spec_hash: [u8; 32],
136 pub role: CanisterRole,
137 pub provisioning_origin: ComponentProvisioningOrigin,
138 pub release_set: FleetSubnetRootReleaseSet,
139 pub phase: RootComponentAllocationPhase,
140 pub creation: Option<RootComponentCreationEvidence>,
141}
142
143#[cfg(test)]
144mod tests {
145 use super::*;
146 use crate::{
147 dto::root_store::RootStoreBootstrapRequest,
148 ids::{
149 AppId, CanonicalNetworkId, FleetCoordinatorBinding, FleetId, FleetKey,
150 FleetRegistryAuthority, ReleaseBuildId, ReleaseBuildNonce, ReleaseSetDigest, SubnetId,
151 },
152 };
153
154 #[test]
155 fn component_registry_contracts_round_trip_through_candid() {
156 let authority = FleetRegistryAuthority {
157 binding: FleetCoordinatorBinding {
158 fleet: crate::ids::FleetBinding {
159 fleet: FleetKey {
160 canonical_network_id: CanonicalNetworkId::public_ic(),
161 fleet_id: FleetId::from_generated_bytes([1; 32]),
162 },
163 app: AppId::from("toko"),
164 },
165 coordinator_subnet: SubnetId::from_principal(Principal::from_slice(&[2; 29])),
166 coordinator: Principal::from_slice(&[3; 29]),
167 },
168 epoch: 1,
169 };
170 let request = RootComponentRegistryPreparationRequest {
171 store_bootstrap: RootStoreBootstrapRequest {
172 manifest_payload_size_bytes: 128,
173 },
174 expected_fleet_registry: FleetRegistryVersion {
175 authority,
176 revision: 4,
177 content_hash: [5; 32],
178 },
179 };
180 let response = RootComponentRegistryStatusResponse {
181 fleet_subnet_root: Principal::from_slice(&[6; 29]),
182 prepared_against_registry: request.expected_fleet_registry.clone(),
183 release_set: FleetSubnetRootReleaseSet {
184 release_build_id: ReleaseBuildId::from_nonce(ReleaseBuildNonce::from_random_bytes(
185 [7; 32],
186 )),
187 manifest_digest: ReleaseSetDigest::from_bytes([8; 32]),
188 },
189 component_topology_digest: ComponentTopologyDigest::from_bytes([9; 32]),
190 next_allocation_sequence: 1,
191 reserved_component_instances: 0,
192 committed_component_instances: 0,
193 managed_descendants: 0,
194 encoded_bytes: 0,
195 };
196 let allocation = RootComponentAllocationResponse {
197 operation_id: [10; 32],
198 allocation_sequence: 1,
199 component: ComponentInstanceId::from_generated_bytes([11; 32]),
200 component_spec: "projects".parse().expect("Component Spec ID"),
201 spec_hash: [12; 32],
202 role: CanisterRole::new("project_hub"),
203 provisioning_origin: ComponentProvisioningOrigin::FleetAdministrator {
204 caller: Principal::from_slice(&[13; 29]),
205 },
206 release_set: response.release_set,
207 phase: RootComponentAllocationPhase::Reserved,
208 creation: None,
209 };
210 let creation_request = RootComponentCreationRequest {
211 operation_id: allocation.operation_id,
212 };
213 let created = RootComponentAllocationResponse {
214 phase: RootComponentAllocationPhase::Created,
215 creation: Some(RootComponentCreationEvidence {
216 wasm_store: Principal::from_slice(&[14; 29]),
217 payload_hash: [15; 32],
218 payload_size_bytes: 4_096,
219 initial_cycles: Cycles::new(5_000_000_000_000),
220 controller: Principal::from_slice(&[6; 29]),
221 canister: Some(Principal::from_slice(&[16; 29])),
222 }),
223 ..allocation.clone()
224 };
225
226 let request_bytes = candid::encode_one(&request).expect("encode request");
227 let response_bytes = candid::encode_one(&response).expect("encode response");
228 let allocation_bytes = candid::encode_one(&allocation).expect("encode allocation");
229 let creation_request_bytes =
230 candid::encode_one(creation_request).expect("encode creation request");
231 let created_bytes = candid::encode_one(&created).expect("encode created allocation");
232
233 assert_eq!(
234 candid::decode_one::<RootComponentRegistryPreparationRequest>(&request_bytes)
235 .expect("decode request"),
236 request
237 );
238 assert_eq!(
239 candid::decode_one::<RootComponentRegistryStatusResponse>(&response_bytes)
240 .expect("decode response"),
241 response
242 );
243 assert_eq!(
244 candid::decode_one::<RootComponentAllocationResponse>(&allocation_bytes)
245 .expect("decode allocation"),
246 allocation
247 );
248 assert_eq!(
249 candid::decode_one::<RootComponentCreationRequest>(&creation_request_bytes)
250 .expect("decode creation request"),
251 creation_request
252 );
253 assert_eq!(
254 candid::decode_one::<RootComponentAllocationResponse>(&created_bytes)
255 .expect("decode created allocation"),
256 created
257 );
258 }
259}