1use crate::{
8 cdk::types::Cycles,
9 ids::{
10 CanisterRole, ComponentInstanceId, ComponentSpecId, FleetBinding, ReleaseBuildId, SubnetId,
11 },
12};
13use candid::{CandidType, Principal};
14use serde::{Deserialize, Serialize};
15use std::fmt;
16
17#[derive(
24 CandidType, Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize,
25)]
26#[serde(transparent)]
27pub struct ComponentTopologyDigest([u8; 32]);
28
29impl ComponentTopologyDigest {
30 #[must_use]
31 pub const fn from_bytes(bytes: [u8; 32]) -> Self {
32 Self(bytes)
33 }
34
35 #[must_use]
36 pub const fn as_bytes(&self) -> &[u8; 32] {
37 &self.0
38 }
39
40 #[must_use]
41 pub const fn into_bytes(self) -> [u8; 32] {
42 self.0
43 }
44}
45
46impl fmt::Display for ComponentTopologyDigest {
47 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
48 for byte in self.0 {
49 write!(formatter, "{byte:02x}")?;
50 }
51 Ok(())
52 }
53}
54
55#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
62#[serde(deny_unknown_fields)]
63pub struct CyclesFundingBudget {
64 pub window_secs: u64,
65 pub maximum_cycles: Cycles,
66}
67
68#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
70pub enum FleetFundingProfile {
71 #[serde(rename = "single_subnet")]
72 SingleSubnet,
73 #[serde(rename = "multi_subnet")]
74 MultiSubnet,
75 #[serde(rename = "preview_multi_subnet")]
76 PreviewMultiSubnet,
77}
78
79pub const COORDINATOR_ROOT_FUNDING_EXECUTION_RESERVE_FLOOR_CYCLES: u128 = 100_000_000;
81
82pub const FLEET_ROOT_FUNDING_CALL_RESERVATION_CYCLES: u128 = 42_118_809_000;
84
85pub const FLEET_SUBNET_ROOT_FUNDING_REQUEST_FLOOR_CYCLES: u128 = 42_200_000_000;
87
88pub const FLEET_SUBNET_ROOT_ICP_REFILL_FLOOR_CYCLES: u128 = 42_200_000_000;
90
91pub const MAX_FLEET_ROOT_FUNDING_SLOTS: usize = 4_096;
93
94#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
101#[serde(deny_unknown_fields)]
102pub struct FleetCoordinatorRootFundingPolicy {
103 pub funding_profile: FleetFundingProfile,
104 pub minimum_reserve_cycles: Cycles,
105 pub budget: CyclesFundingBudget,
106 pub maximum_automatic_grants: u32,
107 pub maximum_automatic_cycles: Cycles,
108}
109
110#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
117#[serde(deny_unknown_fields)]
118pub struct FleetSubnetRootFundingPolicy {
119 pub funding_profile: FleetFundingProfile,
120 pub request_threshold: Cycles,
121 pub target_balance: Cycles,
122 pub cooldown_secs: u64,
123 pub budget: CyclesFundingBudget,
124 pub maximum_automatic_grants: u32,
125 pub maximum_automatic_cycles: Cycles,
126}
127
128#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
135#[serde(deny_unknown_fields)]
136pub struct FleetSubnetRootAutomaticIcpRefillPolicy {
137 pub emergency_threshold: Cycles,
138 pub target_balance: Cycles,
139 pub maximum_automatic_refills: u32,
140 pub maximum_automatic_refill_e8s: u64,
141}
142
143#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
150#[serde(deny_unknown_fields)]
151pub struct FleetSubnetRootIcpRefillPolicy {
152 pub max_refill_e8s_per_call: u64,
153 pub window_secs: u64,
154 pub maximum_refill_e8s: u64,
155 pub minimum_icp_balance_e8s: u64,
156 pub min_xdr_permyriad_per_icp: Option<u64>,
157 pub ledger_canister_id: Option<Principal>,
158 pub cmc_canister_id: Option<Principal>,
159 pub allow_ic_system_canister_overrides: bool,
160 pub automatic: Option<FleetSubnetRootAutomaticIcpRefillPolicy>,
161}
162
163#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
170#[serde(deny_unknown_fields)]
171pub struct FleetSubnetRootFundingAuthority {
172 pub root_funding: FleetSubnetRootFundingPolicy,
173 pub icp_refill: Option<FleetSubnetRootIcpRefillPolicy>,
174}
175
176#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
183#[serde(deny_unknown_fields)]
184pub struct FleetSubnetCanisterPoolConfig {
185 pub minimum_size: u32,
187 pub maximum_size: u32,
191 pub canister_cycles: Cycles,
193}
194
195#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
202#[serde(deny_unknown_fields)]
203pub struct ComponentSpecAdmission {
204 pub component_spec: ComponentSpecId,
205 pub spec_hash: [u8; 32],
206 pub maximum_root_instances: u32,
207}
208
209#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
216#[serde(deny_unknown_fields)]
217pub struct FleetSubnetRootLimits {
218 pub maximum_component_instances: u32,
219 pub maximum_registry_bytes: u64,
220 pub maximum_wasm_store_bytes: u64,
221 pub canister_pool: FleetSubnetCanisterPoolConfig,
222 pub cycles_funding: CyclesFundingBudget,
223 pub maximum_group_placements: u32,
225}
226
227#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
234#[serde(deny_unknown_fields)]
235pub struct FleetCoordinatorBinding {
236 pub fleet: FleetBinding,
237 pub coordinator_subnet: SubnetId,
238 pub coordinator: Principal,
239}
240
241#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
248#[serde(deny_unknown_fields)]
249pub struct FleetRegistryAuthority {
250 pub binding: FleetCoordinatorBinding,
251 pub epoch: u64,
252}
253
254#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
261#[serde(deny_unknown_fields)]
262pub struct FleetSubnetRootBinding {
263 pub authority: FleetRegistryAuthority,
264 pub placement_subnet: SubnetId,
265 pub fleet_subnet_root: Principal,
266 pub component_admissions: Vec<ComponentSpecAdmission>,
267 pub component_topology_digest: ComponentTopologyDigest,
268 pub limits: FleetSubnetRootLimits,
269 pub funding: FleetSubnetRootFundingAuthority,
270}
271
272#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
279#[serde(deny_unknown_fields)]
280pub struct FleetSubnetWasmStoreAuthority {
281 pub authority: FleetRegistryAuthority,
282 pub placement_subnet: SubnetId,
283 pub fleet_subnet_root: Principal,
284 pub wasm_store: Principal,
285 pub installation_controller: Principal,
286 pub release_build_id: ReleaseBuildId,
287 pub wasm_module_hash: [u8; 32],
288}
289
290#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
297#[serde(deny_unknown_fields)]
298pub struct ComponentBinding {
299 pub authority: FleetRegistryAuthority,
300 pub component: ComponentInstanceId,
301 pub component_spec: ComponentSpecId,
302 pub spec_hash: [u8; 32],
303 pub role: CanisterRole,
304 pub placement_subnet: SubnetId,
305 pub fleet_subnet_root: Principal,
306 pub canister_id: Principal,
307}
308
309#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
316#[serde(deny_unknown_fields)]
317pub struct ComponentChildBinding {
318 pub component: ComponentBinding,
319 pub parent_canister_id: Principal,
320 pub role: CanisterRole,
321 pub canister_id: Principal,
322}
323
324#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
331#[serde(deny_unknown_fields)]
332pub enum ManagedCanisterBinding {
333 Component(ComponentBinding),
334 ComponentChild(ComponentChildBinding),
335}
336
337#[cfg(test)]
338mod tests {
339 use super::*;
340
341 #[test]
342 fn funding_profile_candid_spelling_roundtrips() {
343 for profile in [
344 FleetFundingProfile::SingleSubnet,
345 FleetFundingProfile::PreviewMultiSubnet,
346 FleetFundingProfile::MultiSubnet,
347 ] {
348 let bytes = candid::encode_one(profile).expect("encode funding profile Candid");
349 assert_eq!(
350 candid::decode_one::<FleetFundingProfile>(&bytes)
351 .expect("decode funding profile Candid"),
352 profile
353 );
354 }
355 }
356}