use crate::{
cdk::types::Cycles,
ids::{
CanisterRole, ComponentInstanceId, ComponentSpecId, FleetBinding, ReleaseBuildId,
ReleaseSetDigest, SubnetId,
},
};
use candid::{CandidType, Principal};
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(
CandidType, Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize,
)]
#[serde(transparent)]
pub struct ComponentTopologyDigest([u8; 32]);
impl ComponentTopologyDigest {
#[must_use]
pub const fn from_bytes(bytes: [u8; 32]) -> Self {
Self(bytes)
}
#[must_use]
pub const fn as_bytes(&self) -> &[u8; 32] {
&self.0
}
#[must_use]
pub const fn into_bytes(self) -> [u8; 32] {
self.0
}
}
impl fmt::Display for ComponentTopologyDigest {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
for byte in self.0 {
write!(formatter, "{byte:02x}")?;
}
Ok(())
}
}
#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct CyclesFundingBudget {
pub window_secs: u64,
pub maximum_cycles: Cycles,
}
#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub enum FleetFundingProfile {
#[serde(rename = "single_subnet")]
SingleSubnet,
#[serde(rename = "multi_subnet")]
MultiSubnet,
#[serde(rename = "preview_multi_subnet")]
PreviewMultiSubnet,
}
pub const COORDINATOR_ROOT_FUNDING_EXECUTION_RESERVE_FLOOR_CYCLES: u128 = 100_000_000;
pub const FLEET_ROOT_FUNDING_CALL_RESERVATION_CYCLES: u128 = 42_118_809_000;
pub const FLEET_SUBNET_ROOT_FUNDING_REQUEST_FLOOR_CYCLES: u128 = 42_200_000_000;
pub const FLEET_SUBNET_ROOT_ICP_REFILL_FLOOR_CYCLES: u128 = 42_200_000_000;
pub const MAX_FLEET_ROOT_FUNDING_SLOTS: usize = 4_096;
#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct FleetCoordinatorRootFundingPolicy {
pub funding_profile: FleetFundingProfile,
pub minimum_reserve_cycles: Cycles,
pub budget: CyclesFundingBudget,
pub maximum_automatic_grants: u32,
pub maximum_automatic_cycles: Cycles,
}
#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct FleetSubnetRootFundingPolicy {
pub funding_profile: FleetFundingProfile,
pub request_threshold: Cycles,
pub target_balance: Cycles,
pub cooldown_secs: u64,
pub budget: CyclesFundingBudget,
pub maximum_automatic_grants: u32,
pub maximum_automatic_cycles: Cycles,
}
#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct FleetSubnetRootAutomaticIcpRefillPolicy {
pub emergency_threshold: Cycles,
pub target_balance: Cycles,
pub maximum_automatic_refills: u32,
pub maximum_automatic_refill_e8s: u64,
}
#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct FleetSubnetRootIcpRefillPolicy {
pub max_refill_e8s_per_call: u64,
pub window_secs: u64,
pub maximum_refill_e8s: u64,
pub minimum_icp_balance_e8s: u64,
pub min_xdr_permyriad_per_icp: Option<u64>,
pub ledger_canister_id: Option<Principal>,
pub cmc_canister_id: Option<Principal>,
pub allow_ic_system_canister_overrides: bool,
pub automatic: Option<FleetSubnetRootAutomaticIcpRefillPolicy>,
}
#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct FleetSubnetRootFundingAuthority {
pub root_funding: FleetSubnetRootFundingPolicy,
pub icp_refill: Option<FleetSubnetRootIcpRefillPolicy>,
}
#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct FleetSubnetCanisterPoolConfig {
pub minimum_size: u32,
pub maximum_size: u32,
pub canister_cycles: Cycles,
pub creation_execution_margin: Cycles,
}
#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct ComponentSpecAdmission {
pub component_spec: ComponentSpecId,
pub spec_hash: [u8; 32],
pub maximum_root_instances: u32,
}
#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct FleetSubnetRootLimits {
pub maximum_component_instances: u32,
pub maximum_registry_bytes: u64,
pub maximum_wasm_store_bytes: u64,
pub canister_pool: FleetSubnetCanisterPoolConfig,
pub cycles_funding: CyclesFundingBudget,
pub maximum_group_placements: u32,
}
#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct FleetCoordinatorBinding {
pub fleet: FleetBinding,
pub coordinator_subnet: SubnetId,
pub coordinator: Principal,
}
#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct FleetRegistryAuthority {
pub binding: FleetCoordinatorBinding,
pub epoch: u64,
}
#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct FleetSubnetRootBinding {
pub authority: FleetRegistryAuthority,
pub placement_subnet: SubnetId,
pub fleet_subnet_root: Principal,
pub component_admissions: Vec<ComponentSpecAdmission>,
pub component_topology_digest: ComponentTopologyDigest,
pub limits: FleetSubnetRootLimits,
pub funding: FleetSubnetRootFundingAuthority,
}
#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct FleetSubnetWasmStoreAuthority {
pub authority: FleetRegistryAuthority,
pub placement_subnet: SubnetId,
pub fleet_subnet_root: Principal,
pub wasm_store: Principal,
pub installation_controller: Principal,
pub release_build_id: ReleaseBuildId,
pub wasm_module_hash: [u8; 32],
}
#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct FleetSubnetWasmStoreActivationAuthority {
pub fleet: FleetBinding,
pub operation_id: [u8; 32],
pub fleet_subnet_root: Principal,
pub wasm_store: Principal,
pub release_build_id: ReleaseBuildId,
pub component_topology_digest: ComponentTopologyDigest,
pub controllers: Vec<Principal>,
pub manifest_digest: ReleaseSetDigest,
}
#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct ComponentBinding {
pub authority: FleetRegistryAuthority,
pub component: ComponentInstanceId,
pub component_spec: ComponentSpecId,
pub spec_hash: [u8; 32],
pub role: CanisterRole,
pub placement_subnet: SubnetId,
pub fleet_subnet_root: Principal,
pub canister_id: Principal,
}
#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct ComponentChildBinding {
pub component: ComponentBinding,
pub parent_canister_id: Principal,
pub role: CanisterRole,
pub canister_id: Principal,
}
#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub enum ManagedCanisterBinding {
Component(ComponentBinding),
ComponentChild(ComponentChildBinding),
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn funding_profile_candid_spelling_roundtrips() {
for profile in [
FleetFundingProfile::SingleSubnet,
FleetFundingProfile::PreviewMultiSubnet,
FleetFundingProfile::MultiSubnet,
] {
let bytes = candid::encode_one(profile).expect("encode funding profile Candid");
assert_eq!(
candid::decode_one::<FleetFundingProfile>(&bytes)
.expect("decode funding profile Candid"),
profile
);
}
}
}